From edcece33caeb0453c105efad799a94239c128dff Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Wed, 3 Nov 2021 01:24:25 +0100 Subject: [PATCH] transform: refactor interrupt lowering 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 [..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=) 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=) 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 [..etc] By now, the debugger sees an actual source location for UART0_IRQHandler (in the generated file) and an inlined function. --- compiler/compiler.go | 11 + compiler/func.go | 17 +- compiler/interrupt.go | 7 +- compiler/symbol.go | 5 + src/machine/machine_gameboyadvance.go | 29 +- src/runtime/interrupt/interrupt.go | 5 +- src/runtime/interrupt/interrupt_esp32c3.go | 7 +- .../interrupt/interrupt_gameboyadvance.go | 59 +++- src/runtime/interrupt/interrupt_hwvector.go | 8 - src/runtime/runtime_fe310.go | 6 +- src/runtime/runtime_k210.go | 6 +- tools/gen-device-avr/gen-device-avr.go | 18 +- tools/gen-device-svd/gen-device-svd.go | 46 ++- transform/interrupt.go | 311 ++++-------------- transform/interrupt_test.go | 23 +- transform/optimizer.go | 4 +- transform/testdata/interrupt-avr.ll | 33 -- transform/testdata/interrupt-avr.out.ll | 35 -- .../{interrupt-cortexm.ll => interrupt.ll} | 42 ++- ...errupt-cortexm.out.ll => interrupt.out.ll} | 36 +- 20 files changed, 306 insertions(+), 402 deletions(-) delete mode 100644 src/runtime/interrupt/interrupt_hwvector.go delete mode 100644 transform/testdata/interrupt-avr.ll delete mode 100644 transform/testdata/interrupt-avr.out.ll rename transform/testdata/{interrupt-cortexm.ll => interrupt.ll} (56%) rename transform/testdata/{interrupt-cortexm.out.ll => interrupt.out.ll} (64%) diff --git a/compiler/compiler.go b/compiler/compiler.go index 5caf932d..fabfa54b 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -846,6 +846,17 @@ func (b *builder) createFunction() { b.llvmFn.AddFunctionAttr(noinline) } + if b.info.interrupt { + // Mark this function as an interrupt. + // This is necessary on MCUs that don't push caller saved registers when + // entering an interrupt, such as on AVR. + if strings.HasPrefix(b.Triple, "avr") { + b.llvmFn.AddFunctionAttr(b.ctx.CreateStringAttribute("signal", "")) + } else { + b.addError(b.fn.Pos(), "//go:interrupt not supported on this architecture") + } + } + // Add debug info, if needed. if b.Debug { if b.fn.Synthetic == "package initializer" { diff --git a/compiler/func.go b/compiler/func.go index a82d1f1b..fa940570 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -78,11 +78,24 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value { // value. This may be an expensive operation. func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { context = b.CreateExtractValue(funcValue, 0, "") - llvmSig := b.getRawFuncType(sig) switch b.FuncImplementation { case "doubleword": - funcPtr = b.CreateBitCast(b.CreateExtractValue(funcValue, 1, ""), llvmSig, "") + bitcast := b.CreateExtractValue(funcValue, 1, "") + if !bitcast.IsAConstantExpr().IsNil() && bitcast.Opcode() == llvm.BitCast { + funcPtr = bitcast.Operand(0) + return + } + llvmSig := b.getRawFuncType(sig) + funcPtr = b.CreateBitCast(bitcast, llvmSig, "") case "switch": + if !funcValue.IsAConstant().IsNil() { + // If this is a constant func value, the underlying function is + // known and can be returned directly. + funcValueWithSignatureGlobal := llvm.ConstExtractValue(funcValue, []uint32{1}).Operand(0) + funcPtr = llvm.ConstExtractValue(funcValueWithSignatureGlobal.Initializer(), []uint32{0}).Operand(0) + return + } + llvmSig := b.getRawFuncType(sig) sigGlobal := b.getFuncSignatureID(sig) funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "") diff --git a/compiler/interrupt.go b/compiler/interrupt.go index 2437ad45..bcd407cd 100644 --- a/compiler/interrupt.go +++ b/compiler/interrupt.go @@ -36,6 +36,8 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro // Fall back to a generic error. return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") } + funcRawPtr, funcContext := b.decodeFuncValue(funcValue, nil) + funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType) // Create a new global of type runtime/interrupt.handle. Globals of this // type are lowered in the interrupt lowering pass. @@ -47,8 +49,9 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro global.SetGlobalConstant(true) global.SetUnnamedAddr(true) initializer := llvm.ConstNull(globalLLVMType) - initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0}) - initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{1, 0}) + initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0}) + initializer = llvm.ConstInsertValue(initializer, funcPtr, []uint32{1}) + initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{2, 0}) global.SetInitializer(initializer) // Add debug info to the interrupt global. diff --git a/compiler/symbol.go b/compiler/symbol.go index 469066af..902277a5 100644 --- a/compiler/symbol.go +++ b/compiler/symbol.go @@ -26,6 +26,7 @@ type functionInfo struct { linkName string // go:linkname, go:export - The name that we map for the particular module -> importName section string // go:section - object file section name exported bool // go:export, CGo + interrupt bool // go:interrupt nobounds bool // go:nobounds variadic bool // go:variadic (CGo only) inline inlineType // go:inline @@ -251,6 +252,10 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) { importName = parts[1] info.exported = true + case "//go:interrupt": + if hasUnsafeImport(f.Pkg.Pkg) { + info.interrupt = true + } case "//go:wasm-module": // Alternative comment for setting the import module. if len(parts) != 2 { diff --git a/src/machine/machine_gameboyadvance.go b/src/machine/machine_gameboyadvance.go index 100c2bce..9387749e 100644 --- a/src/machine/machine_gameboyadvance.go +++ b/src/machine/machine_gameboyadvance.go @@ -4,6 +4,7 @@ package machine import ( "image/color" + "runtime/interrupt" "runtime/volatile" "unsafe" ) @@ -11,20 +12,20 @@ import ( // Interrupt numbers as used on the GameBoy Advance. Register them with // runtime/interrupt.New. const ( - IRQ_VBLANK = 0 - IRQ_HBLANK = 1 - IRQ_VCOUNT = 2 - IRQ_TIMER0 = 3 - IRQ_TIMER1 = 4 - IRQ_TIMER2 = 5 - IRQ_TIMER3 = 6 - IRQ_COM = 7 - IRQ_DMA0 = 8 - IRQ_DMA1 = 9 - IRQ_DMA2 = 10 - IRQ_DMA3 = 11 - IRQ_KEYPAD = 12 - IRQ_GAMEPAK = 13 + IRQ_VBLANK = interrupt.IRQ_VBLANK + IRQ_HBLANK = interrupt.IRQ_HBLANK + IRQ_VCOUNT = interrupt.IRQ_VCOUNT + IRQ_TIMER0 = interrupt.IRQ_TIMER0 + IRQ_TIMER1 = interrupt.IRQ_TIMER1 + IRQ_TIMER2 = interrupt.IRQ_TIMER2 + IRQ_TIMER3 = interrupt.IRQ_TIMER3 + IRQ_COM = interrupt.IRQ_COM + IRQ_DMA0 = interrupt.IRQ_DMA0 + IRQ_DMA1 = interrupt.IRQ_DMA1 + IRQ_DMA2 = interrupt.IRQ_DMA2 + IRQ_DMA3 = interrupt.IRQ_DMA3 + IRQ_KEYPAD = interrupt.IRQ_KEYPAD + IRQ_GAMEPAK = interrupt.IRQ_GAMEPAK ) // Make it easier to directly write to I/O RAM. diff --git a/src/runtime/interrupt/interrupt.go b/src/runtime/interrupt/interrupt.go index 498389b6..a8cf6f4e 100644 --- a/src/runtime/interrupt/interrupt.go +++ b/src/runtime/interrupt/interrupt.go @@ -2,6 +2,8 @@ // 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. // @@ -28,6 +30,7 @@ func New(id int, handler func(Interrupt)) Interrupt // individually be enabled/disabled, the compiler should create a pseudo-call // (like runtime/interrupt.use()) that keeps the interrupt alive. type handle struct { - handler func(Interrupt) + context unsafe.Pointer + funcPtr uintptr Interrupt } diff --git a/src/runtime/interrupt/interrupt_esp32c3.go b/src/runtime/interrupt/interrupt_esp32c3.go index 20b055f8..a3781488 100644 --- a/src/runtime/interrupt/interrupt_esp32c3.go +++ b/src/runtime/interrupt/interrupt_esp32c3.go @@ -74,7 +74,7 @@ func handleInterrupt() { riscv.MSTATUS.SetBits(0x8) // Call registered interrupt handler(s) - callInterruptHandler(int(interruptNumber)) + esp.HandleInterrupt(int(interruptNumber)) // disable CPU interrupts riscv.MSTATUS.ClearBits(0x8) @@ -107,8 +107,3 @@ func handleException(mcause uintptr) { riscv.Asm("wfi") } } - -// callInterruptHandler is a compiler-generated function that calls the -// appropriate interrupt handler for the given interrupt ID. -//go:linkname callInterruptHandler runtime.callInterruptHandler -func callInterruptHandler(id int) diff --git a/src/runtime/interrupt/interrupt_gameboyadvance.go b/src/runtime/interrupt/interrupt_gameboyadvance.go index 658a2b9c..b0055070 100644 --- a/src/runtime/interrupt/interrupt_gameboyadvance.go +++ b/src/runtime/interrupt/interrupt_gameboyadvance.go @@ -7,6 +7,23 @@ import ( "unsafe" ) +const ( + IRQ_VBLANK = 0 + IRQ_HBLANK = 1 + IRQ_VCOUNT = 2 + IRQ_TIMER0 = 3 + IRQ_TIMER1 = 4 + IRQ_TIMER2 = 5 + IRQ_TIMER3 = 6 + IRQ_COM = 7 + IRQ_DMA0 = 8 + IRQ_DMA1 = 9 + IRQ_DMA2 = 10 + IRQ_DMA3 = 11 + IRQ_KEYPAD = 12 + IRQ_GAMEPAK = 13 +) + var ( regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200))) regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202))) @@ -30,10 +47,44 @@ func handleInterrupt() { } } -// callInterruptHandler is a compiler-generated function that calls the -// appropriate interrupt handler for the given interrupt ID. -//go:linkname callInterruptHandler runtime.callInterruptHandler -func callInterruptHandler(id int) +// Pseudo function call that is replaced by the compiler with the actual +// functions registered through interrupt.New. If there are none, calls will be +// replaced with 'unreachablecalls will be replaced with 'unreachable'. +//go:linkname callHandlers runtime/interrupt.callHandlers +func callHandlers(num int) + +func callInterruptHandler(id int) { + switch id { + case IRQ_VBLANK: + callHandlers(IRQ_VBLANK) + case IRQ_HBLANK: + callHandlers(IRQ_HBLANK) + case IRQ_VCOUNT: + callHandlers(IRQ_VCOUNT) + case IRQ_TIMER0: + callHandlers(IRQ_TIMER0) + case IRQ_TIMER1: + callHandlers(IRQ_TIMER1) + case IRQ_TIMER2: + callHandlers(IRQ_TIMER2) + case IRQ_TIMER3: + callHandlers(IRQ_TIMER3) + case IRQ_COM: + callHandlers(IRQ_COM) + case IRQ_DMA0: + callHandlers(IRQ_DMA0) + case IRQ_DMA1: + callHandlers(IRQ_DMA1) + case IRQ_DMA2: + callHandlers(IRQ_DMA2) + case IRQ_DMA3: + callHandlers(IRQ_DMA3) + case IRQ_KEYPAD: + callHandlers(IRQ_KEYPAD) + case IRQ_GAMEPAK: + callHandlers(IRQ_GAMEPAK) + } +} // State represents the previous global interrupt state. type State uint8 diff --git a/src/runtime/interrupt/interrupt_hwvector.go b/src/runtime/interrupt/interrupt_hwvector.go deleted file mode 100644 index f210da58..00000000 --- a/src/runtime/interrupt/interrupt_hwvector.go +++ /dev/null @@ -1,8 +0,0 @@ -// +build avr cortexm - -package interrupt - -// Register is used to declare an interrupt. You should not normally call this -// function: it is only for telling the compiler about the mapping between an -// interrupt number and the interrupt handler name. -func Register(id int, handlerName string) int diff --git a/src/runtime/runtime_fe310.go b/src/runtime/runtime_fe310.go index 5cfa7a69..33d8e483 100644 --- a/src/runtime/runtime_fe310.go +++ b/src/runtime/runtime_fe310.go @@ -65,7 +65,7 @@ func handleInterrupt() { // Claim this interrupt. id := sifive.PLIC.CLAIM.Get() // Call the interrupt handler, if any is registered for this ID. - callInterruptHandler(int(id)) + sifive.HandleInterrupt(int(id)) // Complete this interrupt. sifive.PLIC.CLAIM.Set(id) } @@ -147,7 +147,3 @@ func handleException(code uint) { println() abort() } - -// callInterruptHandler is a compiler-generated function that calls the -// appropriate interrupt handler for the given interrupt ID. -func callInterruptHandler(id int) diff --git a/src/runtime/runtime_k210.go b/src/runtime/runtime_k210.go index c94153ba..dc8ee044 100644 --- a/src/runtime/runtime_k210.go +++ b/src/runtime/runtime_k210.go @@ -85,7 +85,7 @@ func handleInterrupt() { // Claim this interrupt. id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get() // Call the interrupt handler, if any is registered for this ID. - callInterruptHandler(int(id)) + kendryte.HandleInterrupt(int(id)) // Complete this interrupt. kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id) } @@ -153,7 +153,3 @@ func handleException(code uint64) { println() abort() } - -// callInterruptHandler is a compiler-generated function that calls the -// appropriate interrupt handler for the given interrupt ID. -func callInterruptHandler(id int) diff --git a/tools/gen-device-avr/gen-device-avr.go b/tools/gen-device-avr/gen-device-avr.go index 9b9c627a..c553ec30 100755 --- a/tools/gen-device-avr/gen-device-avr.go +++ b/tools/gen-device-avr/gen-device-avr.go @@ -288,7 +288,6 @@ func writeGo(outdir string, device *Device) error { package {{.pkgName}} import ( - "runtime/interrupt" "runtime/volatile" "unsafe" ) @@ -306,11 +305,18 @@ const ({{range .interrupts}} IRQ_max = {{.interruptMax}} // Highest interrupt number on this device. ) -// Map interrupt numbers to function names. -// These aren't real calls, they're removed by the compiler. -var ({{range .interrupts}} - _ = interrupt.Register(IRQ_{{.Name}}, "__vector_{{.Name}}"){{end}} -) +// Pseudo function call that is replaced by the compiler with the actual +// functions registered through interrupt.New. +//go:linkname callHandlers runtime/interrupt.callHandlers +func callHandlers(num int) + +{{- range .interrupts}} +//export __vector_{{.Name}} +//go:interrupt +func interrupt{{.Name}}() { + callHandlers(IRQ_{{.Name}}) +} +{{- end}} // Peripherals. var ({{range .peripherals}} diff --git a/tools/gen-device-svd/gen-device-svd.go b/tools/gen-device-svd/gen-device-svd.go index 752286f4..e50ff299 100755 --- a/tools/gen-device-svd/gen-device-svd.go +++ b/tools/gen-device-svd/gen-device-svd.go @@ -831,6 +831,15 @@ func writeGo(outdir string, device *Device, interruptSystem string) error { } } + interruptHandlerMap := make(map[string]*Interrupt) + var interruptHandlers []*Interrupt + for _, intr := range device.Interrupts { + if _, ok := interruptHandlerMap[intr.HandlerName]; !ok { + interruptHandlerMap[intr.HandlerName] = intr + interruptHandlers = append(interruptHandlers, intr) + } + } + t := template.Must(template.New("go").Funcs(template.FuncMap{ "bytesNeeded": func(i, j uint64) uint64 { return j - i }, "isMultiline": isMultiline, @@ -846,7 +855,6 @@ func writeGo(outdir string, device *Device, interruptSystem string) error { package {{.pkgName}} import ( -{{if eq .interruptSystem "hardware"}}"runtime/interrupt"{{end}} "runtime/volatile" "unsafe" ) @@ -876,14 +884,29 @@ const ( IRQ_max = {{.interruptMax}} ) +// Pseudo function call that is replaced by the compiler with the actual +// functions registered through interrupt.New. +//go:linkname callHandlers runtime/interrupt.callHandlers +func callHandlers(num int) + {{- if eq .interruptSystem "hardware"}} -// Map interrupt numbers to function names. -// These aren't real calls, they're removed by the compiler. -var ( -{{- range .device.Interrupts}} - _ = interrupt.Register(IRQ_{{.Name}}, "{{.HandlerName}}") +{{- range .interruptHandlers}} +//export {{.HandlerName}} +func interrupt{{.Name}}() { + callHandlers(IRQ_{{.Name}}) +} {{- end}} -) +{{- end}} + +{{- if eq .interruptSystem "software"}} +func HandleInterrupt(num int) { + switch num { + {{- range .interruptHandlers}} + case IRQ_{{.Name}}: + callHandlers(IRQ_{{.Name}}) + {{- end}} + } +} {{- end}} // Peripherals. @@ -901,10 +924,11 @@ var ( `)) err = t.Execute(w, map[string]interface{}{ - "device": device, - "pkgName": filepath.Base(strings.TrimRight(outdir, "/")), - "interruptMax": maxInterruptValue, - "interruptSystem": interruptSystem, + "device": device, + "pkgName": filepath.Base(strings.TrimRight(outdir, "/")), + "interruptMax": maxInterruptValue, + "interruptSystem": interruptSystem, + "interruptHandlers": interruptHandlers, }) if err != nil { return err diff --git a/transform/interrupt.go b/transform/interrupt.go index 11fab18c..10548ef8 100644 --- a/transform/interrupt.go +++ b/transform/interrupt.go @@ -2,11 +2,8 @@ package transform import ( "fmt" - "sort" - "strconv" "strings" - "github.com/tinygo-org/tinygo/compileopts" "tinygo.org/x/go-llvm" ) @@ -15,70 +12,29 @@ import ( // // The operation is as follows. The compiler creates the following during IR // generation: -// * calls to runtime/interrupt.Register that map interrupt IDs to ISR names. +// * calls to runtime/interrupt.callHandlers with an interrupt number. // * runtime/interrupt.handle objects that store the (constant) interrupt ID and // interrupt handler func value. // -// This pass then creates the specially named interrupt handler names that -// simply call the registered handlers. This might seem like it causes extra -// overhead, but in fact inlining and const propagation will eliminate most if -// not all of that. -func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error { +// This pass then replaces those callHandlers calls with calls to the actual +// interrupt handlers. If there are no interrupt handlers for the given call, +// the interrupt handler is removed. For hardware vectoring, that means that the +// entire function is removed. For software vectoring, that means that the call +// is replaced with an 'unreachable' instruction. +// This might seem like it causes extra overhead, but in fact inlining and const +// propagation will eliminate most if not all of that. +func LowerInterrupts(mod llvm.Module) []error { var errs []error - // Discover interrupts. The runtime/interrupt.Register call is a compiler - // intrinsic that maps interrupt numbers to handler names. - handlerNames := map[int64]string{} - for _, call := range getUses(mod.NamedFunction("runtime/interrupt.Register")) { - if call.IsACallInst().IsNil() { - errs = append(errs, errorAt(call, "expected a call to runtime/interrupt.Register?")) - continue - } - - num := call.Operand(0) - if num.IsAConstant().IsNil() { - errs = append(errs, errorAt(call, "non-constant interrupt number?")) - continue - } - - // extract the interrupt name - nameStrGEP := call.Operand(1) - if nameStrGEP.IsAConstantExpr().IsNil() || nameStrGEP.Opcode() != llvm.GetElementPtr { - errs = append(errs, errorAt(call, "expected a string operand?")) - continue - } - nameStrPtr := nameStrGEP.Operand(0) // note: assuming it's a GEP to the first byte - nameStrLen := call.Operand(2) - if nameStrPtr.IsAGlobalValue().IsNil() || !nameStrPtr.IsGlobalConstant() || nameStrLen.IsAConstant().IsNil() { - errs = append(errs, errorAt(call, "non-constant interrupt name?")) - continue - } - - // keep track of this name - name := string(getGlobalBytes(nameStrPtr)[:nameStrLen.SExtValue()]) - handlerNames[num.SExtValue()] = name - - // remove this pseudo-call - call.ReplaceAllUsesWith(llvm.ConstNull(call.Type())) - call.EraseFromParentAsInstruction() - } - - hasSoftwareVectoring := hasUses(mod.NamedFunction("runtime.callInterruptHandler")) - softwareVector := make(map[int64]llvm.Value) - ctx := mod.Context() i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) - nullptr := llvm.ConstNull(i8ptrType) builder := ctx.NewBuilder() defer builder.Dispose() - // Create a function type with the signature of an interrupt handler. - fnType := llvm.FunctionType(ctx.VoidType(), nil, false) - // Collect a map of interrupt handle objects. The fact that they still // exist in the IR indicates that they could not be optimized away, // therefore we need to make real interrupt handlers for them. - handleMap := map[int][]llvm.Value{} + handleMap := map[int64][]llvm.Value{} handleType := mod.GetTypeByName("runtime/interrupt.handle") if !handleType.IsNil() { handlePtrType := llvm.PointerType(handleType, 0) @@ -89,7 +45,7 @@ func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error { // Get the interrupt number from the initializer initializer := global.Initializer() - num := int(llvm.ConstExtractValue(initializer, []uint32{1, 0}).SExtValue()) + num := llvm.ConstExtractValue(initializer, []uint32{2, 0}).SExtValue() pkg := packageFromInterruptHandle(global) handles, exists := handleMap[num] @@ -101,8 +57,8 @@ func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error { // for the wrapper function, failing the build. if exists && packageFromInterruptHandle(handles[0]) != pkg { errs = append(errs, errorAt(global, - fmt.Sprintf("handlers for interrupt %d (%s) in multiple packages: %s and %s", - num, handlerNames[int64(num)], pkg, packageFromInterruptHandle(handles[0])))) + fmt.Sprintf("handlers for interrupt %d in multiple packages: %s and %s", + num, pkg, packageFromInterruptHandle(handles[0])))) continue } @@ -110,146 +66,77 @@ func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error { } } - // Output interrupts in numerical order for reproducible builds (Go map - // intentionally randomizes iteration order of maps). - interrupts := make([]int, 0, len(handleMap)) - for k := range handleMap { - interrupts = append(interrupts, k) - } - sort.Ints(interrupts) + // Discover interrupts. The runtime/interrupt.callHandlers call is a + // compiler intrinsic that is replaced with the handlers for the given + // function. + for _, call := range getUses(mod.NamedFunction("runtime/interrupt.callHandlers")) { + if call.IsACallInst().IsNil() { + errs = append(errs, errorAt(call, "expected a call to runtime/interrupt.callHandlers?")) + continue + } - // Iterate over all handle objects, replacing their ptrtoint uses with a - // real interrupt ID and creating an interrupt handler for them. - for _, interrupt := range interrupts { - handles := handleMap[interrupt] + num := call.Operand(0) + if num.IsAConstantInt().IsNil() { + errs = append(errs, errorAt(call, "non-constant interrupt number?")) + call.InstructionParent().Parent().Dump() + continue + } - // There is always at least one handler for each interrupt number. We - // arbitrarily take the first handler to attach any errors to. - first := handles[0] - - initializer := first.Initializer() - num := llvm.ConstExtractValue(initializer, []uint32{1, 0}) - name := handlerNames[num.SExtValue()] - - isSoftwareVectored := false - if name == "" { - // No function name was defined for this interrupt number, which - // probably means one of two things: - // * runtime/interrupt.Register wasn't called to give the interrupt - // number a function name (such as on Cortex-M). - // * We're using software vectoring instead of hardware vectoring, - // which means the name of the handler doesn't matter (it will - // probably be inlined anyway). - if hasSoftwareVectoring { - isSoftwareVectored = true - if name == "" { - // Name doesn't matter, so pick something unique. - name = "runtime/interrupt.interruptHandler" + strconv.FormatInt(num.SExtValue(), 10) - } + handlers := handleMap[num.SExtValue()] + if len(handlers) != 0 { + // This interrupt has at least one handler. + // Replace the callHandlers call with (possibly multiple) calls to + // these handlers. + builder.SetInsertPointBefore(call) + for _, handler := range handlers { + initializer := handler.Initializer() + context := llvm.ConstExtractValue(initializer, []uint32{0}) + funcPtr := llvm.ConstExtractValue(initializer, []uint32{1}).Operand(0) + builder.CreateCall(funcPtr, []llvm.Value{ + num, + context, + llvm.Undef(i8ptrType), + }, "") + } + call.EraseFromParentAsInstruction() + } else { + // No handlers. Remove the call. + fn := call.InstructionParent().Parent() + if fn.Linkage() == llvm.ExternalLinkage { + // Hardware vectoring. Remove the function entirely (redirecting + // it to the default handler). + fn.ReplaceAllUsesWith(llvm.Undef(fn.Type())) + fn.EraseFromParentAsFunction() } else { - errs = append(errs, errorAt(first, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue()))) - continue - } - } - - // Check for an existing interrupt handler, and report it as an error if - // there is one. - fn := mod.NamedFunction(name) - if fn.IsNil() { - fn = llvm.AddFunction(mod, name, fnType) - } else if fn.Type().ElementType() != fnType { - // Don't bother with a precise error message (listing the previsous - // location) because this should not normally happen anyway. - errs = append(errs, errorAt(first, name+" redeclared with a different signature")) - continue - } else if !fn.IsDeclaration() { - errValue := name + " redeclared in this program" - fnPos := getPosition(fn) - if fnPos.IsValid() { - errValue += "\n\tprevious declaration at " + fnPos.String() - } - errs = append(errs, errorAt(first, errValue)) - continue - } - - // Create the wrapper function which is the actual interrupt handler - // that is inserted in the interrupt vector. - fn.SetUnnamedAddr(true) - AddStandardAttributes(fn, config) - fn.SetSection(".text." + name) - if isSoftwareVectored { - fn.SetLinkage(llvm.InternalLinkage) - softwareVector[num.SExtValue()] = fn - } - entryBlock := ctx.AddBasicBlock(fn, "entry") - builder.SetInsertPointAtEnd(entryBlock) - - // Set the 'interrupt' flag if needed on this platform. - if strings.HasPrefix(mod.Target(), "avr") { - // This special calling convention is needed on AVR to save and - // restore all clobbered registers, instead of just the ones that - // would need to be saved/restored in a normal function call. - // Note that the AVR_INTERRUPT calling convention would enable - // interrupts right at the beginning of the handler, potentially - // leading to lots of nested interrupts and a stack overflow. - fn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL - } - - // For each handle (i.e. each call to interrupt.New), check the usage, - // output a call to the actual handler function and clean-up the handle - // that is no longer needed. - for _, handler := range handles { - // Extract the func value. - initializer := handler.Initializer() - handlerContext := llvm.ConstExtractValue(initializer, []uint32{0, 0}) - handlerFuncPtr := llvm.ConstExtractValue(initializer, []uint32{0, 1}) - if !handlerContext.IsConstant() || !handlerFuncPtr.IsConstant() { - // This should have been checked already in the compiler. - errs = append(errs, errorAt(handler, "func value must be constant")) - continue - } - if !handlerFuncPtr.IsAConstantExpr().IsNil() && handlerFuncPtr.Opcode() == llvm.PtrToInt { - // This is a ptrtoint: the IR was created for func lowering using a - // switch statement. - global := handlerFuncPtr.Operand(0) - if global.IsAGlobalValue().IsNil() { - errs = append(errs, errorAt(global, "internal error: expected a global for func lowering")) - continue + // Software vectoring. Erase the instruction and replace it with + // 'unreachable'. + builder.SetInsertPointBefore(call) + builder.CreateUnreachable() + // Erase all instructions that follow the unreachable + // instruction (which is a block terminator). + inst := call + for !inst.IsNil() { + next := llvm.NextInstruction(inst) + inst.EraseFromParentAsInstruction() + inst = next } - if !strings.HasSuffix(global.Name(), "$withSignature") { - errs = append(errs, errorAt(global, "internal error: func lowering global has unexpected name: "+global.Name())) - continue - } - initializer := global.Initializer() - ptrtoint := llvm.ConstExtractValue(initializer, []uint32{0}) - if ptrtoint.IsAConstantExpr().IsNil() || ptrtoint.Opcode() != llvm.PtrToInt { - errs = append(errs, errorAt(global, "internal error: func lowering global has unexpected func ptr type")) - continue - } - handlerFuncPtr = ptrtoint.Operand(0) - } - if handlerFuncPtr.Type().TypeKind() != llvm.PointerTypeKind || handlerFuncPtr.Type().ElementType().TypeKind() != llvm.FunctionTypeKind { - errs = append(errs, errorAt(handler, "internal error: unexpected LLVM types in func value")) - continue } + } + } - // Fill the function declaration with the forwarding call. - // In practice, the called function will often be inlined which avoids - // the extra indirection. - handlerFuncPtrType := llvm.PointerType(llvm.FunctionType(ctx.VoidType(), []llvm.Type{num.Type(), i8ptrType, i8ptrType}, false), handlerFuncPtr.Type().PointerAddressSpace()) - handlerFuncPtr = llvm.ConstBitCast(handlerFuncPtr, handlerFuncPtrType) - builder.CreateCall(handlerFuncPtr, []llvm.Value{num, handlerContext, nullptr}, "") - - // Replace all ptrtoint uses of the global with the interrupt constant. - // That can only now be safely done after the interrupt handler has been - // created, doing it before the interrupt handler is created might - // result in this interrupt handler being optimized away entirely. + // Replace all ptrtoint uses of the interrupt handler globals with the real + // interrupt ID. + // This can now be safely done after interrupts have been lowered, doing it + // earlier may result in this interrupt handler being optimized away + // entirely (which is not what we want). + for num, handlers := range handleMap { + for _, handler := range handlers { for _, user := range getUses(handler) { if user.IsAConstantExpr().IsNil() || user.Opcode() != llvm.PtrToInt { errs = append(errs, errorAt(handler, "internal error: expected a ptrtoint")) continue } - user.ReplaceAllUsesWith(num) + user.ReplaceAllUsesWith(llvm.ConstInt(user.Type(), uint64(num), true)) } // The runtime/interrput.handle struct can finally be removed. @@ -257,58 +144,6 @@ func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error { // better to do it now to be sure. handler.EraseFromParentAsGlobal() } - - // The wrapper function has no return value - builder.CreateRetVoid() - } - // Create a dispatcher function that calls the appropriate interrupt handler - // for each interrupt ID. This is used in the case of software vectoring. - // The function looks like this: - // func callInterruptHandler(id int) { - // switch id { - // case IRQ_UART: - // interrupt.interruptHandler3() - // case IRQ_FOO: - // interrupt.interruptHandler7() - // default: - // // do nothing - // } - if hasSoftwareVectoring { - // Create a sorted list of interrupt vector IDs. - ids := make([]int64, 0, len(softwareVector)) - for id := range softwareVector { - ids = append(ids, id) - } - sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] }) - - // Start creating the function body with the big switch. - dispatcher := mod.NamedFunction("runtime.callInterruptHandler") - entryBlock := ctx.AddBasicBlock(dispatcher, "entry") - defaultBlock := ctx.AddBasicBlock(dispatcher, "default") - builder.SetInsertPointAtEnd(entryBlock) - interruptID := dispatcher.Param(0) - sw := builder.CreateSwitch(interruptID, defaultBlock, len(ids)) - - // Create a switch case for each interrupt ID that calls the appropriate - // handler. - for _, id := range ids { - block := ctx.AddBasicBlock(dispatcher, "interrupt"+strconv.FormatInt(id, 10)) - builder.SetInsertPointAtEnd(block) - builder.CreateCall(softwareVector[id], nil, "") - builder.CreateRetVoid() - sw.AddCase(llvm.ConstInt(interruptID.Type(), uint64(id), true), block) - } - - // Create a default case that just returns. - // Perhaps it is better to call some default interrupt handler here that - // logs an error? - builder.SetInsertPointAtEnd(defaultBlock) - builder.CreateRetVoid() - - // Make sure the dispatcher is optimized. - // Without this, it will probably not get inlined. - dispatcher.SetLinkage(llvm.InternalLinkage) - dispatcher.SetUnnamedAddr(true) } // Remove now-useless runtime/interrupt.use calls. These are used for some diff --git a/transform/interrupt_test.go b/transform/interrupt_test.go index 4cf7e6de..b80c9b8d 100644 --- a/transform/interrupt_test.go +++ b/transform/interrupt_test.go @@ -3,24 +3,19 @@ package transform_test import ( "testing" - "github.com/tinygo-org/tinygo/compileopts" "github.com/tinygo-org/tinygo/transform" "tinygo.org/x/go-llvm" ) func TestInterruptLowering(t *testing.T) { t.Parallel() - for _, subtest := range []string{"avr", "cortexm"} { - t.Run(subtest, func(t *testing.T) { - testTransform(t, "testdata/interrupt-"+subtest, func(mod llvm.Module) { - errs := transform.LowerInterrupts(mod, &compileopts.Config{Options: &compileopts.Options{Opt: "2"}}) - if len(errs) != 0 { - t.Fail() - for _, err := range errs { - t.Error(err) - } - } - }) - }) - } + testTransform(t, "testdata/interrupt", func(mod llvm.Module) { + errs := transform.LowerInterrupts(mod) + if len(errs) != 0 { + t.Fail() + for _, err := range errs { + t.Error(err) + } + } + }) } diff --git a/transform/optimizer.go b/transform/optimizer.go index ec48a119..bd6d2168 100644 --- a/transform/optimizer.go +++ b/transform/optimizer.go @@ -73,7 +73,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i return []error{err} } - errs := LowerInterrupts(mod, config) + errs := LowerInterrupts(mod) if len(errs) > 0 { return errs } @@ -105,7 +105,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i if config.FuncImplementation() == "switch" { LowerFuncValues(mod) } - errs := LowerInterrupts(mod, config) + errs := LowerInterrupts(mod) if len(errs) > 0 { return errs } diff --git a/transform/testdata/interrupt-avr.ll b/transform/testdata/interrupt-avr.ll deleted file mode 100644 index 80570c0b..00000000 --- a/transform/testdata/interrupt-avr.ll +++ /dev/null @@ -1,33 +0,0 @@ -target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8" -target triple = "avr-unknown-unknown" - -%"runtime/interrupt.handle" = type { %runtime.funcValue, %"runtime/interrupt.Interrupt" } %runtime.funcValue = type { i8*, i16 } -%runtime.typecodeID = type { %runtime.typecodeID*, i16 } -%runtime.funcValueWithSignature = type { i16, %runtime.typecodeID* } -%machine.UART = type { %machine.RingBuffer* } -%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" } -%"runtime/volatile.Register8" = type { i8 } -%"runtime/interrupt.Interrupt" = type { i32 } - -@"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" = external constant %runtime.typecodeID -@"(machine.UART).Configure$1$withSignature" = internal constant %runtime.funcValueWithSignature { i16 ptrtoint (void (i32, i8*, i8*) addrspace(1)* @"(machine.UART).Configure$1" to i16), %runtime.typecodeID* @"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" } -@"runtime/interrupt.$interrupt18" = private unnamed_addr constant %"runtime/interrupt.handle" { %runtime.funcValue { i8* undef, i16 ptrtoint (%runtime.funcValueWithSignature* @"(machine.UART).Configure$1$withSignature" to i16) }, %"runtime/interrupt.Interrupt" { i32 18 } } -@machine.UART0 = internal global %machine.UART zeroinitializer -@"device/avr.init$string.18" = internal unnamed_addr constant [17 x i8] c"__vector_USART_RX" - -declare void @"(machine.UART).Configure$1"(i32, i8*, i8*) unnamed_addr addrspace(1) - -declare i32 @"runtime/interrupt.Register"(i32, i8*, i16, i8*, i8*) addrspace(1) - -declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") addrspace(1) - -define void @"(machine.UART).Configure"(%machine.RingBuffer*, i32, i8, i8, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) { - call addrspace(1) void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt" { i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt18" to i32) }) - ret void -} - -define void @"device/avr.init"(i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) { -entry: - %0 = call addrspace(1) i32 @"runtime/interrupt.Register"(i32 18, i8* getelementptr inbounds ([17 x i8], [17 x i8]* @"device/avr.init$string.18", i32 0, i32 0), i16 17, i8* undef, i8* undef) - ret void -} diff --git a/transform/testdata/interrupt-avr.out.ll b/transform/testdata/interrupt-avr.out.ll deleted file mode 100644 index 3f3c881f..00000000 --- a/transform/testdata/interrupt-avr.out.ll +++ /dev/null @@ -1,35 +0,0 @@ -target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8" -target triple = "avr-unknown-unknown" - -%runtime.typecodeID = type { %runtime.typecodeID*, i16 } -%runtime.funcValueWithSignature = type { i16, %runtime.typecodeID* } -%machine.UART = type { %machine.RingBuffer* } -%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" } -%"runtime/volatile.Register8" = type { i8 } -%"runtime/interrupt.Interrupt" = type { i32 } - -@"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" = external constant %runtime.typecodeID -@"(machine.UART).Configure$1$withSignature" = internal constant %runtime.funcValueWithSignature { i16 ptrtoint (void (i32, i8*, i8*) addrspace(1)* @"(machine.UART).Configure$1" to i16), %runtime.typecodeID* @"reflect/types.type:func:{named:runtime/interrupt.Interrupt}{}" } -@machine.UART0 = internal global %machine.UART zeroinitializer -@"device/avr.init$string.18" = internal unnamed_addr constant [17 x i8] c"__vector_USART_RX" - -declare void @"(machine.UART).Configure$1"(i32, i8*, i8*) unnamed_addr addrspace(1) - -declare i32 @"runtime/interrupt.Register"(i32, i8*, i16, i8*, i8*) addrspace(1) - -declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") addrspace(1) - -define void @"(machine.UART).Configure"(%machine.RingBuffer* %0, i32 %1, i8 %2, i8 %3, i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) { - ret void -} - -define void @"device/avr.init"(i8* %context, i8* %parentHandle) unnamed_addr addrspace(1) { -entry: - ret void -} - -define avr_signalcc void @__vector_USART_RX() unnamed_addr addrspace(1) section ".text.__vector_USART_RX" { -entry: - call addrspace(1) void @"(machine.UART).Configure$1"(i32 18, i8* undef, i8* null) - ret void -} diff --git a/transform/testdata/interrupt-cortexm.ll b/transform/testdata/interrupt.ll similarity index 56% rename from transform/testdata/interrupt-cortexm.ll rename to transform/testdata/interrupt.ll index 2c156b5b..e46fbe6f 100644 --- a/transform/testdata/interrupt-cortexm.ll +++ b/transform/testdata/interrupt.ll @@ -4,27 +4,55 @@ target triple = "armv7em-none-eabi" %machine.UART = type { %machine.RingBuffer* } %machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" } %"runtime/volatile.Register8" = type { i8 } -%"runtime/interrupt.handle" = type { { i8*, void (i32, i8*, i8*)* }, %"runtime/interrupt.Interrupt" } +%"runtime/interrupt.handle" = type { i8*, i32, %"runtime/interrupt.Interrupt" } %"runtime/interrupt.Interrupt" = type { i32 } -@"runtime/interrupt.$interrupt2" = private unnamed_addr constant %"runtime/interrupt.handle" { { i8*, void (i32, i8*, i8*)* } { i8* bitcast (%machine.UART* @machine.UART0 to i8*), void (i32, i8*, i8*)* @"(*machine.UART).handleInterrupt$bound" }, %"runtime/interrupt.Interrupt" { i32 2 } } +@"runtime/interrupt.$interrupt2" = private unnamed_addr constant %"runtime/interrupt.handle" { i8* bitcast (%machine.UART* @machine.UART0 to i8*), i32 ptrtoint (void (i32, i8*, i8*)* @"(*machine.UART).handleInterrupt$bound" to i32), %"runtime/interrupt.Interrupt" { i32 2 } } @machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" } @"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer -@"device/nrf.init$string.2" = internal unnamed_addr constant [23 x i8] c"UARTE0_UART0_IRQHandler" -@"device/nrf.init$string.3" = internal unnamed_addr constant [44 x i8] c"SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler" -declare i32 @"runtime/interrupt.Register"(i32, i8*, i32, i8*, i8*) local_unnamed_addr +declare void @"runtime/interrupt.callHandlers"(i32, i8*, i8*) local_unnamed_addr declare void @"device/arm.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone) declare void @"device/arm.SetPriority"(i32, i32, i8* nocapture readnone, i8* nocapture readnone) +declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") + define void @runtime.initAll(i8* nocapture readnone, i8* nocapture readnone) unnamed_addr { entry: - %2 = call i32 @"runtime/interrupt.Register"(i32 2, i8* getelementptr inbounds ([23 x i8], [23 x i8]* @"device/nrf.init$string.2", i32 0, i32 0), i32 23, i8* undef, i8* undef) - %3 = call i32 @"runtime/interrupt.Register"(i32 3, i8* getelementptr inbounds ([44 x i8], [44 x i8]* @"device/nrf.init$string.3", i32 0, i32 0), i32 44, i8* undef, i8* undef) call void @"device/arm.SetPriority"(i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt2" to i32), i32 192, i8* undef, i8* undef) call void @"device/arm.EnableIRQ"(i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt2" to i32), i8* undef, i8* undef) + call void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt" { i32 ptrtoint (%"runtime/interrupt.handle"* @"runtime/interrupt.$interrupt2" to i32) }) + ret void +} + +define void @UARTE0_UART0_IRQHandler() { + call void @"runtime/interrupt.callHandlers"(i32 2, i8* undef, i8* undef) + ret void +} + +define void @NFCT_IRQHandler() { + call void @"runtime/interrupt.callHandlers"(i32 5, i8* undef, i8* undef) + ret void +} + +define internal void @interruptSWVector(i32 %num) { +entry: + switch i32 %num, label %switch.done [ + i32 2, label %switch.body2 + i32 5, label %switch.body5 + ] + +switch.body2: + call void @"runtime/interrupt.callHandlers"(i32 2, i8* undef, i8* undef) + ret void + +switch.body5: + call void @"runtime/interrupt.callHandlers"(i32 5, i8* undef, i8* undef) + ret void + +switch.done: ret void } diff --git a/transform/testdata/interrupt-cortexm.out.ll b/transform/testdata/interrupt.out.ll similarity index 64% rename from transform/testdata/interrupt-cortexm.out.ll rename to transform/testdata/interrupt.out.ll index 2e35f263..78f52ff6 100644 --- a/transform/testdata/interrupt-cortexm.out.ll +++ b/transform/testdata/interrupt.out.ll @@ -4,18 +4,19 @@ target triple = "armv7em-none-eabi" %machine.UART = type { %machine.RingBuffer* } %machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" } %"runtime/volatile.Register8" = type { i8 } +%"runtime/interrupt.Interrupt" = type { i32 } @machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" } @"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer -@"device/nrf.init$string.2" = internal unnamed_addr constant [23 x i8] c"UARTE0_UART0_IRQHandler" -@"device/nrf.init$string.3" = internal unnamed_addr constant [44 x i8] c"SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler" -declare i32 @"runtime/interrupt.Register"(i32, i8*, i32, i8*, i8*) local_unnamed_addr +declare void @"runtime/interrupt.callHandlers"(i32, i8*, i8*) local_unnamed_addr declare void @"device/arm.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone) declare void @"device/arm.SetPriority"(i32, i32, i8* nocapture readnone, i8* nocapture readnone) +declare void @"runtime/interrupt.use"(%"runtime/interrupt.Interrupt") + define void @runtime.initAll(i8* nocapture readnone %0, i8* nocapture readnone %1) unnamed_addr { entry: call void @"device/arm.SetPriority"(i32 2, i32 192, i8* undef, i8* undef) @@ -23,6 +24,29 @@ entry: ret void } +define void @UARTE0_UART0_IRQHandler() { + call void @"(*machine.UART).handleInterrupt$bound"(i32 2, i8* bitcast (%machine.UART* @machine.UART0 to i8*), i8* undef) + ret void +} + +define internal void @interruptSWVector(i32 %num) { +entry: + switch i32 %num, label %switch.done [ + i32 2, label %switch.body2 + i32 5, label %switch.body5 + ] + +switch.body2: ; preds = %entry + call void @"(*machine.UART).handleInterrupt$bound"(i32 2, i8* bitcast (%machine.UART* @machine.UART0 to i8*), i8* undef) + ret void + +switch.body5: ; preds = %entry + unreachable + +switch.done: ; preds = %entry + ret void +} + define internal void @"(*machine.UART).handleInterrupt$bound"(i32 %0, i8* nocapture %context, i8* nocapture readnone %parentHandle) { entry: %unpack.ptr = bitcast i8* %context to %machine.UART* @@ -31,9 +55,3 @@ entry: } declare void @"(*machine.UART).handleInterrupt"(%machine.UART* nocapture, i32, i8* nocapture readnone, i8* nocapture readnone) - -define void @UARTE0_UART0_IRQHandler() unnamed_addr section ".text.UARTE0_UART0_IRQHandler" { -entry: - call void @"(*machine.UART).handleInterrupt$bound"(i32 2, i8* bitcast (%machine.UART* @machine.UART0 to i8*), i8* null) - ret void -}