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  <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.
Этот коммит содержится в:
Ayke van Laethem 2021-11-03 01:24:25 +01:00 коммит произвёл Ron Evans
родитель 30bbdd5aeb
коммит edcece33ca
20 изменённых файлов: 306 добавлений и 402 удалений

Просмотреть файл

@ -846,6 +846,17 @@ func (b *builder) createFunction() {
b.llvmFn.AddFunctionAttr(noinline) 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. // Add debug info, if needed.
if b.Debug { if b.Debug {
if b.fn.Synthetic == "package initializer" { if b.fn.Synthetic == "package initializer" {

Просмотреть файл

@ -78,11 +78,24 @@ func (b *builder) extractFuncContext(funcValue llvm.Value) llvm.Value {
// value. This may be an expensive operation. // value. This may be an expensive operation.
func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) { func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (funcPtr, context llvm.Value) {
context = b.CreateExtractValue(funcValue, 0, "") context = b.CreateExtractValue(funcValue, 0, "")
llvmSig := b.getRawFuncType(sig)
switch b.FuncImplementation { switch b.FuncImplementation {
case "doubleword": 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": 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) sigGlobal := b.getFuncSignatureID(sig)
funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "")
funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "") funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "")

Просмотреть файл

@ -36,6 +36,8 @@ func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, erro
// Fall back to a generic error. // Fall back to a generic error.
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") 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 // Create a new global of type runtime/interrupt.handle. Globals of this
// type are lowered in the interrupt lowering pass. // 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.SetGlobalConstant(true)
global.SetUnnamedAddr(true) global.SetUnnamedAddr(true)
initializer := llvm.ConstNull(globalLLVMType) initializer := llvm.ConstNull(globalLLVMType)
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0}) initializer = llvm.ConstInsertValue(initializer, funcContext, []uint32{0})
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(b.intType, uint64(id.Int64()), true), []uint32{1, 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) global.SetInitializer(initializer)
// Add debug info to the interrupt global. // Add debug info to the interrupt global.

Просмотреть файл

@ -26,6 +26,7 @@ type functionInfo struct {
linkName string // go:linkname, go:export - The name that we map for the particular module -> importName linkName string // go:linkname, go:export - The name that we map for the particular module -> importName
section string // go:section - object file section name section string // go:section - object file section name
exported bool // go:export, CGo exported bool // go:export, CGo
interrupt bool // go:interrupt
nobounds bool // go:nobounds nobounds bool // go:nobounds
variadic bool // go:variadic (CGo only) variadic bool // go:variadic (CGo only)
inline inlineType // go:inline inline inlineType // go:inline
@ -251,6 +252,10 @@ func (info *functionInfo) parsePragmas(f *ssa.Function) {
importName = parts[1] importName = parts[1]
info.exported = true info.exported = true
case "//go:interrupt":
if hasUnsafeImport(f.Pkg.Pkg) {
info.interrupt = true
}
case "//go:wasm-module": case "//go:wasm-module":
// Alternative comment for setting the import module. // Alternative comment for setting the import module.
if len(parts) != 2 { if len(parts) != 2 {

Просмотреть файл

@ -4,6 +4,7 @@ package machine
import ( import (
"image/color" "image/color"
"runtime/interrupt"
"runtime/volatile" "runtime/volatile"
"unsafe" "unsafe"
) )
@ -11,20 +12,20 @@ import (
// Interrupt numbers as used on the GameBoy Advance. Register them with // Interrupt numbers as used on the GameBoy Advance. Register them with
// runtime/interrupt.New. // runtime/interrupt.New.
const ( const (
IRQ_VBLANK = 0 IRQ_VBLANK = interrupt.IRQ_VBLANK
IRQ_HBLANK = 1 IRQ_HBLANK = interrupt.IRQ_HBLANK
IRQ_VCOUNT = 2 IRQ_VCOUNT = interrupt.IRQ_VCOUNT
IRQ_TIMER0 = 3 IRQ_TIMER0 = interrupt.IRQ_TIMER0
IRQ_TIMER1 = 4 IRQ_TIMER1 = interrupt.IRQ_TIMER1
IRQ_TIMER2 = 5 IRQ_TIMER2 = interrupt.IRQ_TIMER2
IRQ_TIMER3 = 6 IRQ_TIMER3 = interrupt.IRQ_TIMER3
IRQ_COM = 7 IRQ_COM = interrupt.IRQ_COM
IRQ_DMA0 = 8 IRQ_DMA0 = interrupt.IRQ_DMA0
IRQ_DMA1 = 9 IRQ_DMA1 = interrupt.IRQ_DMA1
IRQ_DMA2 = 10 IRQ_DMA2 = interrupt.IRQ_DMA2
IRQ_DMA3 = 11 IRQ_DMA3 = interrupt.IRQ_DMA3
IRQ_KEYPAD = 12 IRQ_KEYPAD = interrupt.IRQ_KEYPAD
IRQ_GAMEPAK = 13 IRQ_GAMEPAK = interrupt.IRQ_GAMEPAK
) )
// Make it easier to directly write to I/O RAM. // Make it easier to directly write to I/O RAM.

Просмотреть файл

@ -2,6 +2,8 @@
// to define interrupts and to enable/disable them. // to define interrupts and to enable/disable them.
package interrupt package interrupt
import "unsafe"
// Interrupt provides direct access to hardware interrupts. You can configure // Interrupt provides direct access to hardware interrupts. You can configure
// this interrupt through this interface. // 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 // individually be enabled/disabled, the compiler should create a pseudo-call
// (like runtime/interrupt.use()) that keeps the interrupt alive. // (like runtime/interrupt.use()) that keeps the interrupt alive.
type handle struct { type handle struct {
handler func(Interrupt) context unsafe.Pointer
funcPtr uintptr
Interrupt Interrupt
} }

Просмотреть файл

@ -74,7 +74,7 @@ func handleInterrupt() {
riscv.MSTATUS.SetBits(0x8) riscv.MSTATUS.SetBits(0x8)
// Call registered interrupt handler(s) // Call registered interrupt handler(s)
callInterruptHandler(int(interruptNumber)) esp.HandleInterrupt(int(interruptNumber))
// disable CPU interrupts // disable CPU interrupts
riscv.MSTATUS.ClearBits(0x8) riscv.MSTATUS.ClearBits(0x8)
@ -107,8 +107,3 @@ func handleException(mcause uintptr) {
riscv.Asm("wfi") 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)

Просмотреть файл

@ -7,6 +7,23 @@ import (
"unsafe" "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 ( var (
regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200))) regInterruptEnable = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000200)))
regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202))) regInterruptRequestFlags = (*volatile.Register16)(unsafe.Pointer(uintptr(0x4000202)))
@ -30,10 +47,44 @@ func handleInterrupt() {
} }
} }
// callInterruptHandler is a compiler-generated function that calls the // Pseudo function call that is replaced by the compiler with the actual
// appropriate interrupt handler for the given interrupt ID. // functions registered through interrupt.New. If there are none, calls will be
//go:linkname callInterruptHandler runtime.callInterruptHandler // replaced with 'unreachablecalls will be replaced with 'unreachable'.
func callInterruptHandler(id int) //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. // State represents the previous global interrupt state.
type State uint8 type State uint8

Просмотреть файл

@ -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

Просмотреть файл

@ -65,7 +65,7 @@ func handleInterrupt() {
// Claim this interrupt. // Claim this interrupt.
id := sifive.PLIC.CLAIM.Get() id := sifive.PLIC.CLAIM.Get()
// Call the interrupt handler, if any is registered for this ID. // Call the interrupt handler, if any is registered for this ID.
callInterruptHandler(int(id)) sifive.HandleInterrupt(int(id))
// Complete this interrupt. // Complete this interrupt.
sifive.PLIC.CLAIM.Set(id) sifive.PLIC.CLAIM.Set(id)
} }
@ -147,7 +147,3 @@ func handleException(code uint) {
println() println()
abort() abort()
} }
// callInterruptHandler is a compiler-generated function that calls the
// appropriate interrupt handler for the given interrupt ID.
func callInterruptHandler(id int)

Просмотреть файл

@ -85,7 +85,7 @@ func handleInterrupt() {
// Claim this interrupt. // Claim this interrupt.
id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get() id := kendryte.PLIC.TARGETS[hartId].CLAIM.Get()
// Call the interrupt handler, if any is registered for this ID. // Call the interrupt handler, if any is registered for this ID.
callInterruptHandler(int(id)) kendryte.HandleInterrupt(int(id))
// Complete this interrupt. // Complete this interrupt.
kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id) kendryte.PLIC.TARGETS[hartId].CLAIM.Set(id)
} }
@ -153,7 +153,3 @@ func handleException(code uint64) {
println() println()
abort() abort()
} }
// callInterruptHandler is a compiler-generated function that calls the
// appropriate interrupt handler for the given interrupt ID.
func callInterruptHandler(id int)

Просмотреть файл

@ -288,7 +288,6 @@ func writeGo(outdir string, device *Device) error {
package {{.pkgName}} package {{.pkgName}}
import ( import (
"runtime/interrupt"
"runtime/volatile" "runtime/volatile"
"unsafe" "unsafe"
) )
@ -306,11 +305,18 @@ const ({{range .interrupts}}
IRQ_max = {{.interruptMax}} // Highest interrupt number on this device. IRQ_max = {{.interruptMax}} // Highest interrupt number on this device.
) )
// Map interrupt numbers to function names. // Pseudo function call that is replaced by the compiler with the actual
// These aren't real calls, they're removed by the compiler. // functions registered through interrupt.New.
var ({{range .interrupts}} //go:linkname callHandlers runtime/interrupt.callHandlers
_ = interrupt.Register(IRQ_{{.Name}}, "__vector_{{.Name}}"){{end}} func callHandlers(num int)
)
{{- range .interrupts}}
//export __vector_{{.Name}}
//go:interrupt
func interrupt{{.Name}}() {
callHandlers(IRQ_{{.Name}})
}
{{- end}}
// Peripherals. // Peripherals.
var ({{range .peripherals}} var ({{range .peripherals}}

Просмотреть файл

@ -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{ t := template.Must(template.New("go").Funcs(template.FuncMap{
"bytesNeeded": func(i, j uint64) uint64 { return j - i }, "bytesNeeded": func(i, j uint64) uint64 { return j - i },
"isMultiline": isMultiline, "isMultiline": isMultiline,
@ -846,7 +855,6 @@ func writeGo(outdir string, device *Device, interruptSystem string) error {
package {{.pkgName}} package {{.pkgName}}
import ( import (
{{if eq .interruptSystem "hardware"}}"runtime/interrupt"{{end}}
"runtime/volatile" "runtime/volatile"
"unsafe" "unsafe"
) )
@ -876,14 +884,29 @@ const (
IRQ_max = {{.interruptMax}} 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"}} {{- if eq .interruptSystem "hardware"}}
// Map interrupt numbers to function names. {{- range .interruptHandlers}}
// These aren't real calls, they're removed by the compiler. //export {{.HandlerName}}
var ( func interrupt{{.Name}}() {
{{- range .device.Interrupts}} callHandlers(IRQ_{{.Name}})
_ = interrupt.Register(IRQ_{{.Name}}, "{{.HandlerName}}") }
{{- end}} {{- end}}
) {{- end}}
{{- if eq .interruptSystem "software"}}
func HandleInterrupt(num int) {
switch num {
{{- range .interruptHandlers}}
case IRQ_{{.Name}}:
callHandlers(IRQ_{{.Name}})
{{- end}}
}
}
{{- end}} {{- end}}
// Peripherals. // Peripherals.
@ -901,10 +924,11 @@ var (
`)) `))
err = t.Execute(w, map[string]interface{}{ err = t.Execute(w, map[string]interface{}{
"device": device, "device": device,
"pkgName": filepath.Base(strings.TrimRight(outdir, "/")), "pkgName": filepath.Base(strings.TrimRight(outdir, "/")),
"interruptMax": maxInterruptValue, "interruptMax": maxInterruptValue,
"interruptSystem": interruptSystem, "interruptSystem": interruptSystem,
"interruptHandlers": interruptHandlers,
}) })
if err != nil { if err != nil {
return err return err

Просмотреть файл

@ -2,11 +2,8 @@ package transform
import ( import (
"fmt" "fmt"
"sort"
"strconv"
"strings" "strings"
"github.com/tinygo-org/tinygo/compileopts"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
@ -15,70 +12,29 @@ import (
// //
// The operation is as follows. The compiler creates the following during IR // The operation is as follows. The compiler creates the following during IR
// generation: // 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 // * runtime/interrupt.handle objects that store the (constant) interrupt ID and
// interrupt handler func value. // interrupt handler func value.
// //
// This pass then creates the specially named interrupt handler names that // This pass then replaces those callHandlers calls with calls to the actual
// simply call the registered handlers. This might seem like it causes extra // interrupt handlers. If there are no interrupt handlers for the given call,
// overhead, but in fact inlining and const propagation will eliminate most if // the interrupt handler is removed. For hardware vectoring, that means that the
// not all of that. // entire function is removed. For software vectoring, that means that the call
func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error { // 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 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() ctx := mod.Context()
i8ptrType := llvm.PointerType(ctx.Int8Type(), 0) i8ptrType := llvm.PointerType(ctx.Int8Type(), 0)
nullptr := llvm.ConstNull(i8ptrType)
builder := ctx.NewBuilder() builder := ctx.NewBuilder()
defer builder.Dispose() 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 // Collect a map of interrupt handle objects. The fact that they still
// exist in the IR indicates that they could not be optimized away, // exist in the IR indicates that they could not be optimized away,
// therefore we need to make real interrupt handlers for them. // 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") handleType := mod.GetTypeByName("runtime/interrupt.handle")
if !handleType.IsNil() { if !handleType.IsNil() {
handlePtrType := llvm.PointerType(handleType, 0) 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 // Get the interrupt number from the initializer
initializer := global.Initializer() initializer := global.Initializer()
num := int(llvm.ConstExtractValue(initializer, []uint32{1, 0}).SExtValue()) num := llvm.ConstExtractValue(initializer, []uint32{2, 0}).SExtValue()
pkg := packageFromInterruptHandle(global) pkg := packageFromInterruptHandle(global)
handles, exists := handleMap[num] handles, exists := handleMap[num]
@ -101,8 +57,8 @@ func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error {
// for the wrapper function, failing the build. // for the wrapper function, failing the build.
if exists && packageFromInterruptHandle(handles[0]) != pkg { if exists && packageFromInterruptHandle(handles[0]) != pkg {
errs = append(errs, errorAt(global, errs = append(errs, errorAt(global,
fmt.Sprintf("handlers for interrupt %d (%s) in multiple packages: %s and %s", fmt.Sprintf("handlers for interrupt %d in multiple packages: %s and %s",
num, handlerNames[int64(num)], pkg, packageFromInterruptHandle(handles[0])))) num, pkg, packageFromInterruptHandle(handles[0]))))
continue continue
} }
@ -110,146 +66,77 @@ func LowerInterrupts(mod llvm.Module, config *compileopts.Config) []error {
} }
} }
// Output interrupts in numerical order for reproducible builds (Go map // Discover interrupts. The runtime/interrupt.callHandlers call is a
// intentionally randomizes iteration order of maps). // compiler intrinsic that is replaced with the handlers for the given
interrupts := make([]int, 0, len(handleMap)) // function.
for k := range handleMap { for _, call := range getUses(mod.NamedFunction("runtime/interrupt.callHandlers")) {
interrupts = append(interrupts, k) if call.IsACallInst().IsNil() {
} errs = append(errs, errorAt(call, "expected a call to runtime/interrupt.callHandlers?"))
sort.Ints(interrupts) continue
}
// Iterate over all handle objects, replacing their ptrtoint uses with a num := call.Operand(0)
// real interrupt ID and creating an interrupt handler for them. if num.IsAConstantInt().IsNil() {
for _, interrupt := range interrupts { errs = append(errs, errorAt(call, "non-constant interrupt number?"))
handles := handleMap[interrupt] call.InstructionParent().Parent().Dump()
continue
}
// There is always at least one handler for each interrupt number. We handlers := handleMap[num.SExtValue()]
// arbitrarily take the first handler to attach any errors to. if len(handlers) != 0 {
first := handles[0] // This interrupt has at least one handler.
// Replace the callHandlers call with (possibly multiple) calls to
initializer := first.Initializer() // these handlers.
num := llvm.ConstExtractValue(initializer, []uint32{1, 0}) builder.SetInsertPointBefore(call)
name := handlerNames[num.SExtValue()] for _, handler := range handlers {
initializer := handler.Initializer()
isSoftwareVectored := false context := llvm.ConstExtractValue(initializer, []uint32{0})
if name == "" { funcPtr := llvm.ConstExtractValue(initializer, []uint32{1}).Operand(0)
// No function name was defined for this interrupt number, which builder.CreateCall(funcPtr, []llvm.Value{
// probably means one of two things: num,
// * runtime/interrupt.Register wasn't called to give the interrupt context,
// number a function name (such as on Cortex-M). llvm.Undef(i8ptrType),
// * 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). call.EraseFromParentAsInstruction()
if hasSoftwareVectoring { } else {
isSoftwareVectored = true // No handlers. Remove the call.
if name == "" { fn := call.InstructionParent().Parent()
// Name doesn't matter, so pick something unique. if fn.Linkage() == llvm.ExternalLinkage {
name = "runtime/interrupt.interruptHandler" + strconv.FormatInt(num.SExtValue(), 10) // Hardware vectoring. Remove the function entirely (redirecting
} // it to the default handler).
fn.ReplaceAllUsesWith(llvm.Undef(fn.Type()))
fn.EraseFromParentAsFunction()
} else { } else {
errs = append(errs, errorAt(first, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue()))) // Software vectoring. Erase the instruction and replace it with
continue // 'unreachable'.
} builder.SetInsertPointBefore(call)
} builder.CreateUnreachable()
// Erase all instructions that follow the unreachable
// Check for an existing interrupt handler, and report it as an error if // instruction (which is a block terminator).
// there is one. inst := call
fn := mod.NamedFunction(name) for !inst.IsNil() {
if fn.IsNil() { next := llvm.NextInstruction(inst)
fn = llvm.AddFunction(mod, name, fnType) inst.EraseFromParentAsInstruction()
} else if fn.Type().ElementType() != fnType { inst = next
// 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
} }
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. // Replace all ptrtoint uses of the interrupt handler globals with the real
// In practice, the called function will often be inlined which avoids // interrupt ID.
// the extra indirection. // This can now be safely done after interrupts have been lowered, doing it
handlerFuncPtrType := llvm.PointerType(llvm.FunctionType(ctx.VoidType(), []llvm.Type{num.Type(), i8ptrType, i8ptrType}, false), handlerFuncPtr.Type().PointerAddressSpace()) // earlier may result in this interrupt handler being optimized away
handlerFuncPtr = llvm.ConstBitCast(handlerFuncPtr, handlerFuncPtrType) // entirely (which is not what we want).
builder.CreateCall(handlerFuncPtr, []llvm.Value{num, handlerContext, nullptr}, "") for num, handlers := range handleMap {
for _, handler := range handlers {
// 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.
for _, user := range getUses(handler) { for _, user := range getUses(handler) {
if user.IsAConstantExpr().IsNil() || user.Opcode() != llvm.PtrToInt { if user.IsAConstantExpr().IsNil() || user.Opcode() != llvm.PtrToInt {
errs = append(errs, errorAt(handler, "internal error: expected a ptrtoint")) errs = append(errs, errorAt(handler, "internal error: expected a ptrtoint"))
continue continue
} }
user.ReplaceAllUsesWith(num) user.ReplaceAllUsesWith(llvm.ConstInt(user.Type(), uint64(num), true))
} }
// The runtime/interrput.handle struct can finally be removed. // 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. // better to do it now to be sure.
handler.EraseFromParentAsGlobal() 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 // Remove now-useless runtime/interrupt.use calls. These are used for some

Просмотреть файл

@ -3,24 +3,19 @@ package transform_test
import ( import (
"testing" "testing"
"github.com/tinygo-org/tinygo/compileopts"
"github.com/tinygo-org/tinygo/transform" "github.com/tinygo-org/tinygo/transform"
"tinygo.org/x/go-llvm" "tinygo.org/x/go-llvm"
) )
func TestInterruptLowering(t *testing.T) { func TestInterruptLowering(t *testing.T) {
t.Parallel() t.Parallel()
for _, subtest := range []string{"avr", "cortexm"} { testTransform(t, "testdata/interrupt", func(mod llvm.Module) {
t.Run(subtest, func(t *testing.T) { errs := transform.LowerInterrupts(mod)
testTransform(t, "testdata/interrupt-"+subtest, func(mod llvm.Module) { if len(errs) != 0 {
errs := transform.LowerInterrupts(mod, &compileopts.Config{Options: &compileopts.Options{Opt: "2"}}) t.Fail()
if len(errs) != 0 { for _, err := range errs {
t.Fail() t.Error(err)
for _, err := range errs { }
t.Error(err) }
} })
}
})
})
}
} }

Просмотреть файл

@ -73,7 +73,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
return []error{err} return []error{err}
} }
errs := LowerInterrupts(mod, config) errs := LowerInterrupts(mod)
if len(errs) > 0 { if len(errs) > 0 {
return errs return errs
} }
@ -105,7 +105,7 @@ func Optimize(mod llvm.Module, config *compileopts.Config, optLevel, sizeLevel i
if config.FuncImplementation() == "switch" { if config.FuncImplementation() == "switch" {
LowerFuncValues(mod) LowerFuncValues(mod)
} }
errs := LowerInterrupts(mod, config) errs := LowerInterrupts(mod)
if len(errs) > 0 { if len(errs) > 0 {
return errs return errs
} }

33
transform/testdata/interrupt-avr.ll предоставленный
Просмотреть файл

@ -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
}

35
transform/testdata/interrupt-avr.out.ll предоставленный
Просмотреть файл

@ -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
}

Просмотреть файл

@ -4,27 +4,55 @@ target triple = "armv7em-none-eabi"
%machine.UART = type { %machine.RingBuffer* } %machine.UART = type { %machine.RingBuffer* }
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" } %machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
%"runtime/volatile.Register8" = type { i8 } %"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.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.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" }
@"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer @"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.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone)
declare void @"device/arm.SetPriority"(i32, 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 { define void @runtime.initAll(i8* nocapture readnone, i8* nocapture readnone) unnamed_addr {
entry: 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.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 @"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 ret void
} }

Просмотреть файл

@ -4,18 +4,19 @@ target triple = "armv7em-none-eabi"
%machine.UART = type { %machine.RingBuffer* } %machine.UART = type { %machine.RingBuffer* }
%machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" } %machine.RingBuffer = type { [128 x %"runtime/volatile.Register8"], %"runtime/volatile.Register8", %"runtime/volatile.Register8" }
%"runtime/volatile.Register8" = type { i8 } %"runtime/volatile.Register8" = type { i8 }
%"runtime/interrupt.Interrupt" = type { i32 }
@machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" } @machine.UART0 = internal global %machine.UART { %machine.RingBuffer* @"machine$alloc.335" }
@"machine$alloc.335" = internal global %machine.RingBuffer zeroinitializer @"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.EnableIRQ"(i32, i8* nocapture readnone, i8* nocapture readnone)
declare void @"device/arm.SetPriority"(i32, 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 { define void @runtime.initAll(i8* nocapture readnone %0, i8* nocapture readnone %1) unnamed_addr {
entry: entry:
call void @"device/arm.SetPriority"(i32 2, i32 192, i8* undef, i8* undef) call void @"device/arm.SetPriority"(i32 2, i32 192, i8* undef, i8* undef)
@ -23,6 +24,29 @@ entry:
ret void 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) { define internal void @"(*machine.UART).handleInterrupt$bound"(i32 %0, i8* nocapture %context, i8* nocapture readnone %parentHandle) {
entry: entry:
%unpack.ptr = bitcast i8* %context to %machine.UART* %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) 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
}