mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-05 03:27:48 +00:00
edcece33ca
Instead of doing everything in the interrupt lowering pass, generate
some more code in gen-device to declare interrupt handler functions and
do some work in the compiler so that interrupt lowering becomes a lot
simpler.
This has several benefits:
- Overall code is smaller, in particular the interrupt lowering pass.
- The code should be a bit less "magical" and instead a bit easier to
read. In particular, instead of having a magic
runtime.callInterruptHandler (that is fully written by the interrupt
lowering pass), the runtime calls a generated function like
device/sifive.InterruptHandler where this switch already exists in
code.
- Debug information is improved. This can be helpful during actual
debugging but is also useful for other uses of DWARF debug
information.
For an example on debug information improvement, this is what a
backtrace might look like before this commit:
Breakpoint 1, 0x00000b46 in UART0_IRQHandler ()
(gdb) bt
#0 0x00000b46 in UART0_IRQHandler ()
#1 <signal handler called>
[..etc]
Notice that the debugger doesn't see the source code location where it
has stopped.
After this commit, breaking at the same line might look like this:
Breakpoint 1, (*machine.UART).handleInterrupt (arg1=..., uart=<optimized out>) at /home/ayke/src/github.com/tinygo-org/tinygo/src/machine/machine_nrf.go:200
200 uart.Receive(byte(nrf.UART0.RXD.Get()))
(gdb) bt
#0 (*machine.UART).handleInterrupt (arg1=..., uart=<optimized out>) at /home/ayke/src/github.com/tinygo-org/tinygo/src/machine/machine_nrf.go:200
#1 UART0_IRQHandler () at /home/ayke/src/github.com/tinygo-org/tinygo/src/device/nrf/nrf51.go:176
#2 <signal handler called>
[..etc]
By now, the debugger sees an actual source location for UART0_IRQHandler
(in the generated file) and an inlined function.
37 lines
1.5 KiB
Go
37 lines
1.5 KiB
Go
// Package interrupt provides access to hardware interrupts. It provides a way
|
|
// to define interrupts and to enable/disable them.
|
|
package interrupt
|
|
|
|
import "unsafe"
|
|
|
|
// Interrupt provides direct access to hardware interrupts. You can configure
|
|
// this interrupt through this interface.
|
|
//
|
|
// Do not use the zero value of an Interrupt object. Instead, call New to obtain
|
|
// an interrupt handle.
|
|
type Interrupt struct {
|
|
// Make this number unexported so it cannot be set directly. This provides
|
|
// some encapsulation.
|
|
num int
|
|
}
|
|
|
|
// New is a compiler intrinsic that creates a new Interrupt object. You may call
|
|
// it only once, and must pass constant parameters to it. That means that the
|
|
// interrupt ID must be a Go constant and that the handler must be a simple
|
|
// function: closures are not supported.
|
|
func New(id int, handler func(Interrupt)) Interrupt
|
|
|
|
// handle is used internally, between IR generation and interrupt lowering. The
|
|
// frontend will create runtime/interrupt.handle objects, cast them to an int,
|
|
// and use that in an Interrupt object. That way the compiler will be able to
|
|
// optimize away all interrupt handles that are never used in a program.
|
|
// This system only works when interrupts need to be enabled before use and this
|
|
// is done only through calling Enable() on this object. If interrups cannot
|
|
// individually be enabled/disabled, the compiler should create a pseudo-call
|
|
// (like runtime/interrupt.use()) that keeps the interrupt alive.
|
|
type handle struct {
|
|
context unsafe.Pointer
|
|
funcPtr uintptr
|
|
Interrupt
|
|
}
|