WIP interrupts via ptrtoint handler

This commit is contained in:
Ayke van Laethem
2020-01-01 23:14:54 +01:00
parent 9cdc2fa768
commit 5e6f1725ac
17 changed files with 238 additions and 186 deletions
+2
View File
@@ -1309,6 +1309,8 @@ func (c *Compiler) parseCall(frame *Frame, instr *ssa.CallCommon) (llvm.Value, e
return c.emitVolatileLoad(frame, instr)
case strings.HasPrefix(name, "runtime/volatile.Store"):
return c.emitVolatileStore(frame, instr)
case name == "runtime/interrupt.New":
return c.emitInterruptGlobal(frame, instr)
}
targetFunc := c.ir.GetFunction(fn)
+91
View File
@@ -0,0 +1,91 @@
package compiler
import (
"strconv"
"strings"
"golang.org/x/tools/go/ssa"
"tinygo.org/x/go-llvm"
)
// emitInterruptGlobal creates a new runtime/interrupt.Interrupt struct that
// will be lowered to a real interrupt during interrupt lowering.
//
// This two-stage approach allows unused interrupts to be optimized away if
// necessary.
func (c *Compiler) emitInterruptGlobal(frame *Frame, instr *ssa.CallCommon) (llvm.Value, error) {
// Get the interrupt number, which must be a compile-time constant.
id, ok := instr.Args[0].(*ssa.Const)
if !ok {
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt ID is not a constant")
}
// Get the func value, which also must be a compile time constant.
// Note that bound functions are allowed if the function has a pointer
// receiver and is a global. This is rather strict but still allows for
// idiomatic Go code.
funcValue := c.getValue(frame, instr.Args[1])
if funcValue.IsAConstant().IsNil() {
// Try to determine the cause of the non-constantness for a nice error
// message.
switch instr.Args[1].(type) {
case *ssa.MakeClosure:
// This may also be a bound method.
return llvm.Value{}, c.makeError(instr.Pos(), "closures are not supported in interrupt.New")
}
// Fall back to a generic error.
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt function must be constant")
}
// Create a new global of type runtime/interrupt.handle. Globals of this
// type are lowered in the interrupt lowering pass.
globalType := c.ir.Program.ImportedPackage("runtime/interrupt").Type("handle").Type()
globalLLVMType := c.getLLVMType(globalType)
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
if global := c.mod.NamedGlobal(globalName); !global.IsNil() {
return llvm.Value{}, c.makeError(instr.Pos(), "interrupt redeclared in this program")
}
global := llvm.AddGlobal(c.mod, globalLLVMType, globalName)
global.SetLinkage(llvm.PrivateLinkage)
global.SetGlobalConstant(true)
initializer := llvm.ConstNull(globalLLVMType)
initializer = llvm.ConstInsertValue(initializer, funcValue, []uint32{0})
initializer = llvm.ConstInsertValue(initializer, llvm.ConstInt(c.intType, uint64(id.Int64()), true), []uint32{1, 0})
global.SetInitializer(initializer)
// Add debug info to the interrupt global.
if c.Debug() {
pos := c.ir.Program.Fset.Position(instr.Pos())
diglobal := c.dibuilder.CreateGlobalVariableExpression(c.difiles[pos.Filename], llvm.DIGlobalVariableExpression{
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
LinkageName: globalName,
File: c.getDIFile(pos.Filename),
Line: pos.Line,
Type: c.getDIType(globalType),
Expr: c.dibuilder.CreateExpression(nil),
LocalToUnit: false,
})
global.AddMetadata(0, diglobal)
}
// Create the runtime/interrupt.Interrupt type. It is a struct with a single
// member of type int.
num := llvm.ConstPtrToInt(global, c.intType)
interrupt := llvm.ConstNamedStruct(c.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
// Add dummy "use" call for AVR, because interrupts may be used even though
// they are never referenced again. This is unlike Cortex-M or the RISC-V
// PLIC where each interrupt must be enabled using the interrupt number, and
// thus keeps the Interrupt object alive.
// This call is removed during interrupt lowering.
if strings.HasPrefix(c.Triple(), "avr") {
useFn := c.mod.NamedFunction("runtime/interrupt.use")
if useFn.IsNil() {
useFnType := llvm.FunctionType(c.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
useFn = llvm.AddFunction(c.mod, "runtime/interrupt.use", useFnType)
}
c.builder.CreateCall(useFn, []llvm.Value{interrupt}, "")
}
return interrupt, nil
}
+8 -11
View File
@@ -2,7 +2,10 @@
package machine
import "device/sam"
import (
"device/sam"
"runtime/interrupt"
)
// UART1 on the Arduino Nano 33 connects to the onboard NINA-W102 WiFi chip.
var (
@@ -13,11 +16,6 @@ var (
}
)
//go:export SERCOM3_IRQHandler
func handleUART1() {
defaultUART1Handler()
}
// UART2 on the Arduino Nano 33 connects to the normal TX/RX pins.
var (
UART2 = UART{
@@ -27,11 +25,10 @@ var (
}
)
//go:export SERCOM5_IRQHandler
func handleUART2() {
// should reset IRQ
UART2.Receive(byte((UART2.Bus.DATA.Get() & 0xFF)))
UART2.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
func init() {
// Work around circular definitions.
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM3, UART1.handleInterrupt)
UART2.interrupt = interrupt.New(sam.IRQ_SERCOM5, UART2.handleInterrupt)
}
// I2C on the Arduino Nano 33.
+6 -5
View File
@@ -2,7 +2,10 @@
package machine
import "device/stm32"
import (
"device/stm32"
"runtime/interrupt"
)
// https://wiki.stm32duino.com/index.php?title=File:Bluepillpinout.gif
const (
@@ -61,14 +64,12 @@ var (
UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART1,
IRQVal: stm32.IRQ_USART1,
}
UART1 = &UART0
)
//go:export USART1_IRQHandler
func handleUART1() {
UART1.Receive(byte((UART1.Bus.DR.Get() & 0xFF)))
func init() {
UART0.interrupt = interrupt.New(stm32.IRQ_USART1, UART0.handleInterrupt)
}
// SPI pins
@@ -2,7 +2,10 @@
package machine
import "device/sam"
import (
"device/sam"
"runtime/interrupt"
)
// UART1 on the Circuit Playground Express.
var (
@@ -13,9 +16,8 @@ var (
}
)
//go:export SERCOM1_IRQHandler
func handleUART1() {
defaultUART1Handler()
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM4, UART1.handleInterrupt)
}
// I2C on the Circuit Playground Express.
+6 -4
View File
@@ -2,7 +2,10 @@
package machine
import "device/sam"
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
@@ -60,9 +63,8 @@ var (
}
)
//go:export SERCOM1_IRQHandler
func handleUART1() {
defaultUART1Handler()
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
}
// I2C pins
+3 -3
View File
@@ -4,6 +4,7 @@ package machine
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
@@ -62,9 +63,8 @@ var (
}
)
//go:export SERCOM1_IRQHandler
func handleUART1() {
defaultUART1Handler()
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM1, UART1.handleInterrupt)
}
// I2C pins
+6 -5
View File
@@ -2,7 +2,10 @@
package machine
import "device/stm32"
import (
"device/stm32"
"runtime/interrupt"
)
const (
PA0 = portA + 0
@@ -100,14 +103,12 @@ var (
UART0 = UART{
Buffer: NewRingBuffer(),
Bus: stm32.USART2,
IRQVal: stm32.IRQ_USART2,
}
UART2 = &UART0
)
//go:export USART2_IRQHandler
func handleUART2() {
UART2.Receive(byte((UART2.Bus.DR.Get() & 0xFF)))
func init() {
UART0.interrupt = interrupt.New(stm32.IRQ_USART2, UART0.handleInterrupt)
}
// SPI pins
+6 -4
View File
@@ -2,7 +2,10 @@
package machine
import "device/sam"
import (
"device/sam"
"runtime/interrupt"
)
// used to reset into bootloader
const RESET_MAGIC_VALUE = 0xf01669ef
@@ -51,9 +54,8 @@ var (
}
)
//go:export SERCOM1_IRQHandler
func handleUART1() {
defaultUART1Handler()
func init() {
UART1.interrupt = interrupt.New(sam.IRQ_SERCOM0, UART1.handleInterrupt)
}
// SPI pins
+10 -11
View File
@@ -293,9 +293,10 @@ func waitADCSync() {
// UART on the SAMD21.
type UART struct {
Buffer *RingBuffer
Bus *sam.SERCOM_USART_Type
SERCOM uint8
Buffer *RingBuffer
Bus *sam.SERCOM_USART_Type
SERCOM uint8
interrupt interrupt.Interrupt
}
var (
@@ -402,10 +403,7 @@ func (uart UART) Configure(config UARTConfig) error {
uart.Bus.INTENSET.Set(sam.SERCOM_USART_INTENSET_RXC)
// Enable RX IRQ.
// IRQ lines are in the same order as SERCOM instance numbers on SAMD21
// chips, so the IRQ number can be trivially determined from the SERCOM
// number.
arm.EnableIRQ(sam.IRQ_SERCOM0 + uint32(uart.SERCOM))
uart.interrupt.Enable()
return nil
}
@@ -433,11 +431,12 @@ func (uart UART) WriteByte(c byte) error {
return nil
}
// defaultUART1Handler handles the UART1 IRQ.
func defaultUART1Handler() {
// handleInterrupt should be called from the appropriate interrupt handler for
// this UART instance.
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
// should reset IRQ
UART1.Receive(byte((UART1.Bus.DATA.Get() & 0xFF)))
UART1.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
uart.Receive(byte((uart.Bus.DATA.Get() & 0xFF)))
uart.Bus.INTFLAG.SetBits(sam.SERCOM_USART_INTFLAG_RXC)
}
// I2C on the SAMD21.
+7 -26
View File
@@ -11,6 +11,7 @@ import (
"device/arm"
"device/sam"
"errors"
"runtime/interrupt"
"unsafe"
)
@@ -1402,11 +1403,11 @@ func (usbcdc USBCDC) Configure(config UARTConfig) {
// enable USB
sam.USB_DEVICE.CTRLA.SetBits(sam.USB_DEVICE_CTRLA_ENABLE)
// enable IRQ
arm.EnableIRQ(sam.IRQ_USB_OTHER)
arm.EnableIRQ(sam.IRQ_USB_SOF_HSOF)
arm.EnableIRQ(sam.IRQ_USB_TRCPT0)
arm.EnableIRQ(sam.IRQ_USB_TRCPT1)
// enable IRQ at highest priority
interrupt.New(sam.IRQ_USB_OTHER, handleUSBIRQ).Enable()
interrupt.New(sam.IRQ_USB_SOF_HSOF, handleUSBIRQ).Enable()
interrupt.New(sam.IRQ_USB_TRCPT0, handleUSBIRQ).Enable()
interrupt.New(sam.IRQ_USB_TRCPT1, handleUSBIRQ).Enable()
}
func handlePadCalibration() {
@@ -1452,27 +1453,7 @@ func handlePadCalibration() {
sam.USB_DEVICE.PADCAL.SetBits(calibTrim << sam.USB_DEVICE_PADCAL_TRIM_Pos)
}
//go:export USB_OTHER_IRQHandler
func handleUSBOther() {
handleUSBIRQ()
}
//go:export USB_SOF_HSOF_IRQHandler
func handleUSBSOFHSOF() {
handleUSBIRQ()
}
//go:export USB_TRCPT0_IRQHandler
func handleUSBTRCPT0() {
handleUSBIRQ()
}
//go:export USB_TRCPT1_IRQHandler
func handleUSBTRCPT1() {
handleUSBIRQ()
}
func handleUSBIRQ() {
func handleUSBIRQ(interrupt.Interrupt) {
// reset all interrupt flags
flags := sam.USB_DEVICE.INTFLAG.Get()
sam.USB_DEVICE.INTFLAG.Set(flags)
+12 -6
View File
@@ -5,9 +5,9 @@ package machine
// Peripheral abstraction layer for the stm32.
import (
"device/arm"
"device/stm32"
"errors"
"runtime/interrupt"
)
func CPUFrequency() uint32 {
@@ -111,9 +111,9 @@ func (p Pin) Get() bool {
// UART
type UART struct {
Buffer *RingBuffer
Bus *stm32.USART_Type
IRQVal uint32
Buffer *RingBuffer
Bus *stm32.USART_Type
interrupt interrupt.Interrupt
}
// Configure the UART.
@@ -155,8 +155,8 @@ func (uart UART) Configure(config UARTConfig) {
uart.Bus.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
// Enable RX IRQ
arm.SetPriority(uart.IRQVal, 0xc0)
arm.EnableIRQ(uart.IRQVal)
uart.interrupt.SetPriority(0xc0)
uart.interrupt.Enable()
}
// SetBaudRate sets the communication speed for the UART.
@@ -182,6 +182,12 @@ func (uart UART) WriteByte(c byte) error {
return nil
}
// handleInterrupt should be called from the appropriate interrupt handler for
// this UART instance.
func (uart *UART) handleInterrupt(interrupt.Interrupt) {
uart.Receive(byte((uart.Bus.DR.Get() & 0xFF)))
}
// SPI on the STM32.
type SPI struct {
Bus *stm32.SPI_Type
+6 -8
View File
@@ -5,8 +5,8 @@ package machine
// Peripheral abstraction layer for the stm32.
import (
"device/arm"
"device/stm32"
"runtime/interrupt"
)
func CPUFrequency() uint32 {
@@ -203,8 +203,11 @@ func (uart UART) Configure(config UARTConfig) {
stm32.USART2.CR1.Set(stm32.USART_CR1_TE | stm32.USART_CR1_RE | stm32.USART_CR1_RXNEIE | stm32.USART_CR1_UE)
// Enable RX IRQ.
arm.SetPriority(stm32.IRQ_USART2, 0xc0)
arm.EnableIRQ(stm32.IRQ_USART2)
intr := interrupt.New(stm32.IRQ_USART2, func(interrupt.Interrupt) {
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
})
intr.SetPriority(0xc0)
intr.Enable()
}
// WriteByte writes a byte of data to the UART.
@@ -215,8 +218,3 @@ func (uart UART) WriteByte(c byte) error {
}
return nil
}
//go:export USART2_IRQHandler
func handleUSART2() {
UART1.Receive(byte((stm32.USART2.DR.Get() & 0xFF)))
}
+5
View File
@@ -23,3 +23,8 @@ func New(id int, handler func(Interrupt)) Interrupt
// 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
type handle struct {
handler func(Interrupt)
Interrupt
}
+5 -4
View File
@@ -6,6 +6,7 @@ import (
"device/arm"
"device/stm32"
"machine"
"runtime/interrupt"
"runtime/volatile"
)
@@ -101,8 +102,9 @@ func initRTC() {
func initTIM() {
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
arm.EnableIRQ(stm32.IRQ_TIM3)
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
intr.SetPriority(0xc3)
intr.Enable()
}
const asyncScheduler = false
@@ -186,8 +188,7 @@ func timerSleep(ticks uint32) {
}
}
//go:export TIM3_IRQHandler
func handleTIM3() {
func handleTIM3(interrupt.Interrupt) {
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
// Disable the timer.
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
+9 -8
View File
@@ -6,6 +6,7 @@ import (
"device/arm"
"device/stm32"
"machine"
"runtime/interrupt"
"runtime/volatile"
)
@@ -121,8 +122,9 @@ var timerWakeup volatile.Register8
func initTIM3() {
stm32.RCC.APB1ENR.SetBits(stm32.RCC_APB1ENR_TIM3EN)
arm.SetPriority(stm32.IRQ_TIM3, 0xc3)
arm.EnableIRQ(stm32.IRQ_TIM3)
intr := interrupt.New(stm32.IRQ_TIM3, handleTIM3)
intr.SetPriority(0xc3)
intr.Enable()
}
// Enable the TIM7 clock.(tick count)
@@ -139,8 +141,9 @@ func initTIM7() {
// Enable the timer.
stm32.TIM7.CR1.SetBits(stm32.TIM_CR1_CEN)
arm.SetPriority(stm32.IRQ_TIM7, 0xc1)
arm.EnableIRQ(stm32.IRQ_TIM7)
intr := interrupt.New(stm32.IRQ_TIM7, handleTIM7)
intr.SetPriority(0xc1)
intr.Enable()
}
const asyncScheduler = false
@@ -183,8 +186,7 @@ func timerSleep(ticks uint32) {
}
}
//go:export TIM3_IRQHandler
func handleTIM3() {
func handleTIM3(interrupt.Interrupt) {
if stm32.TIM3.SR.HasBits(stm32.TIM_SR_UIF) {
// Disable the timer.
stm32.TIM3.CR1.ClearBits(stm32.TIM_CR1_CEN)
@@ -197,8 +199,7 @@ func handleTIM3() {
}
}
//go:export TIM7_IRQHandler
func handleTIM7() {
func handleTIM7(interrupt.Interrupt) {
if stm32.TIM7.SR.HasBits(stm32.TIM_SR_UIF) {
// clear the update flag
stm32.TIM7.SR.ClearBits(stm32.TIM_SR_UIF)
+50 -87
View File
@@ -47,52 +47,43 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
call.EraseFromParentAsInstruction()
}
create := mod.NamedFunction("runtime/interrupt.New")
if create.IsNil() {
// No interrupt handlers to create.
return nil
}
ctx := mod.Context()
nullptr := llvm.ConstNull(llvm.PointerType(ctx.Int8Type(), 0))
builder := ctx.NewBuilder()
defer builder.Dispose()
var dibuilder *llvm.DIBuilder
// Create a function type with the signature of an interrupt handler.
fnType := llvm.FunctionType(ctx.VoidType(), nil, false)
for _, call := range getUses(create) {
if call.IsACallInst().IsNil() {
errs = append(errs, errorAt(call, "expected a call to runtime/interrupt.New"))
continue
}
if call.OperandsCount() != 6 {
// 6 params:
// * 1 for the IRQ number
// * 2 for the function
// * 2 extra: context and parentHandle
// * one more for the called value?
errs = append(errs, errorAt(call, fmt.Sprintf("unexpected interrupt.New signature, expected 6 operands, got %d", call.OperandsCount())))
continue
}
num := call.Operand(0)
if num.IsAConstant().IsNil() {
errs = append(errs, errorAt(call, "non-constant interrupt number"))
handleType := mod.GetTypeByName("runtime/interrupt.handle")
if handleType.IsNil() {
// Nothing to do here.
return errs
}
handlePtrType := llvm.PointerType(handleType, 0)
var handlers []llvm.Value
for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) {
if global.Type() != handlePtrType {
continue
}
handlers = append(handlers, global)
}
for _, global := range handlers {
initializer := global.Initializer()
num := llvm.ConstExtractValue(initializer, []uint32{1, 0})
name := handlerNames[num.SExtValue()]
if name == "" {
errs = append(errs, errorAt(call, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue())))
errs = append(errs, errorAt(global, fmt.Sprintf("cannot find interrupt name for number %d", num.SExtValue())))
continue
}
// Create the func value.
handlerContext := call.Operand(1)
handlerFuncPtr := call.Operand(2)
handlerContext := llvm.ConstExtractValue(initializer, []uint32{0, 0})
handlerFuncPtr := llvm.ConstExtractValue(initializer, []uint32{0, 1})
if isFunctionLocal(handlerContext) || isFunctionLocal(handlerFuncPtr) {
errs = append(errs, errorAt(call, "func value must be constant"))
errs = append(errs, errorAt(global, "func value must be constant"))
continue
}
if !handlerFuncPtr.IsAConstantExpr().IsNil() && handlerFuncPtr.Opcode() == llvm.PtrToInt {
@@ -100,23 +91,23 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
// switch statement.
global := handlerFuncPtr.Operand(0)
if global.IsAGlobalValue().IsNil() {
errs = append(errs, errorAt(call, "internal error: expected a global for func lowering"))
errs = append(errs, errorAt(global, "internal error: expected a global for func lowering"))
continue
}
initializer := global.Initializer()
if initializer.Type() != mod.GetTypeByName("runtime.funcValueWithSignature") {
errs = append(errs, errorAt(call, "internal error: func lowering global has unexpected type"))
errs = append(errs, errorAt(global, "internal error: func lowering global has unexpected type"))
continue
}
ptrtoint := llvm.ConstExtractValue(initializer, []uint32{0})
if ptrtoint.IsAConstantExpr().IsNil() || ptrtoint.Opcode() != llvm.PtrToInt {
errs = append(errs, errorAt(call, "internal error: func lowering global has unexpected func ptr type"))
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(call, "internal error: unexpected LLVM types in func value"))
errs = append(errs, errorAt(global, "internal error: unexpected LLVM types in func value"))
continue
}
@@ -129,14 +120,14 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
// Don't bother with a precise error message (listing the
// previsous location) because this should not normally happen
// anyway.
errs = append(errs, errorAt(call, name+" redeclared with a different signature"))
errs = append(errs, errorAt(global, name+" redeclared with a different signature"))
continue
} else if fn.IsDeclaration() {
} else if !fn.IsDeclaration() {
// Interrupt handler was already defined. Check the first
// instruction (which should be a call) whether this handler would
// be identical anyway.
firstInst := fn.FirstBasicBlock().FirstInstruction()
if !firstInst.IsACallInst().IsNil() && firstInst.OperandsCount() == 4 && firstInst.Operand(0) == num && firstInst.Operand(1) != handlerContext {
if !firstInst.IsACallInst().IsNil() && firstInst.OperandsCount() == 4 && firstInst.CalledValue() == handlerFuncPtr && firstInst.Operand(0) == num && firstInst.Operand(1) == handlerContext {
// Already defined and apparently identical, so assume this is
// fine.
continue
@@ -147,7 +138,7 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
if fnPos.IsValid() {
errValue += "\n\tprevious declaration at " + fnPos.String()
}
errs = append(errs, errorAt(call, errValue))
errs = append(errs, errorAt(global, errValue))
continue
}
@@ -161,61 +152,33 @@ func LowerInterruptRegistrations(mod llvm.Module) []error {
fn.SetFunctionCallConv(85) // CallingConv::AVR_SIGNAL
}
// Attach a proper debug location to this function.
// Use the location of the call instruction, because as far as the user
// is concerned that is where the interrupt is defined. This is
// especially relevant for multiple definition errors, where you'd
// really want the previous interrupt.New call location to be used for
// easy debugging.
loc := call.InstructionDebugLoc()
if !loc.IsNil() {
if dibuilder == nil {
dibuilder = llvm.NewDIBuilder(mod)
defer func() {
dibuilder.Finalize()
dibuilder.Destroy()
}()
// Must create a new compile unit for some reason.
dibuilder.CreateCompileUnit(llvm.DICompileUnit{
File: "<interrupt-lowering>",
Language: 0xb, // DW_LANG_C99 (0xc, off-by-one?)
Producer: "TinyGo",
Optimized: true,
})
}
// Attach debug info to the function.
file := loc.LocationScope().ScopeFile()
diFnType := dibuilder.CreateSubroutineType(llvm.DISubroutineType{
File: file,
})
difunc := dibuilder.CreateFunction(file, llvm.DIFunction{
Name: name,
LinkageName: name,
File: file,
Line: int(loc.LocationLine()),
Type: diFnType,
LocalToUnit: false,
IsDefinition: true,
ScopeLine: 0,
Flags: llvm.FlagPrototyped,
Optimized: true,
})
fn.SetSubprogram(difunc)
// Use the same location inside the thunk.
builder.SetCurrentDebugLocation(loc.LocationLine(), loc.LocationColumn(), difunc, llvm.Metadata{})
}
// Fill the function declaration with the forwarding call.
builder.CreateCall(handlerFuncPtr, []llvm.Value{num, handlerContext, nullptr}, "")
builder.CreateRetVoid()
// Replace the function call.
interruptValue := llvm.ConstNamedStruct(mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
call.ReplaceAllUsesWith(interruptValue)
for _, user := range getUses(global) {
if user.IsAConstantExpr().IsNil() || user.Opcode() != llvm.PtrToInt {
errs = append(errs, errorAt(global, "internal error: expected a ptrtoint"))
continue
}
user.ReplaceAllUsesWith(num)
}
global.EraseFromParentAsGlobal()
}
// Remove now-useless runtime/interrupt.use calls. These are used for some
// platforms like AVR that do not need to enable interrupts to use them, so
// need another way to keep them alive.
// After interrupts have been lowered, this call is useless and would cause
// a linker error so must be removed.
for _, call := range getUses(mod.NamedFunction("runtime/interrupt.use")) {
if call.IsACallInst().IsNil() {
errs = append(errs, errorAt(call, "internal error: expected call to runtime/interrupt.use"))
continue
}
call.EraseFromParentAsInstruction()
}
return errs
}