github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/compiler/interrupt.go (about) 1 package compiler 2 3 import ( 4 "strconv" 5 "strings" 6 7 "golang.org/x/tools/go/ssa" 8 "tinygo.org/x/go-llvm" 9 ) 10 11 // createInterruptGlobal creates a new runtime/interrupt.Interrupt struct that 12 // will be lowered to a real interrupt during interrupt lowering. 13 // 14 // This two-stage approach allows unused interrupts to be optimized away if 15 // necessary. 16 func (b *builder) createInterruptGlobal(instr *ssa.CallCommon) (llvm.Value, error) { 17 // Get the interrupt number, which must be a compile-time constant. 18 id, ok := instr.Args[0].(*ssa.Const) 19 if !ok { 20 return llvm.Value{}, b.makeError(instr.Pos(), "interrupt ID is not a constant") 21 } 22 23 // Get the func value, which also must be a compile time constant. 24 // Note that bound functions are allowed if the function has a pointer 25 // receiver and is a global. This is rather strict but still allows for 26 // idiomatic Go code. 27 funcValue := b.getValue(instr.Args[1], getPos(instr)) 28 if funcValue.IsAConstant().IsNil() { 29 // Try to determine the cause of the non-constantness for a nice error 30 // message. 31 switch instr.Args[1].(type) { 32 case *ssa.MakeClosure: 33 // This may also be a bound method. 34 return llvm.Value{}, b.makeError(instr.Pos(), "closures are not supported in interrupt.New") 35 } 36 // Fall back to a generic error. 37 return llvm.Value{}, b.makeError(instr.Pos(), "interrupt function must be constant") 38 } 39 funcRawPtr, funcContext := b.decodeFuncValue(funcValue) 40 funcPtr := llvm.ConstPtrToInt(funcRawPtr, b.uintptrType) 41 42 // Create a new global of type runtime/interrupt.handle. Globals of this 43 // type are lowered in the interrupt lowering pass. 44 globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type() 45 globalLLVMType := b.getLLVMType(globalType) 46 globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10) 47 global := llvm.AddGlobal(b.mod, globalLLVMType, globalName) 48 global.SetVisibility(llvm.HiddenVisibility) 49 global.SetGlobalConstant(true) 50 global.SetUnnamedAddr(true) 51 initializer := llvm.ConstNull(globalLLVMType) 52 initializer = b.CreateInsertValue(initializer, funcContext, 0, "") 53 initializer = b.CreateInsertValue(initializer, funcPtr, 1, "") 54 initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{ 55 llvm.ConstInt(b.intType, uint64(id.Int64()), true), 56 }), 2, "") 57 global.SetInitializer(initializer) 58 59 // Add debug info to the interrupt global. 60 if b.Debug { 61 pos := b.program.Fset.Position(instr.Pos()) 62 diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{ 63 Name: "interrupt" + strconv.FormatInt(id.Int64(), 10), 64 LinkageName: globalName, 65 File: b.getDIFile(pos.Filename), 66 Line: pos.Line, 67 Type: b.getDIType(globalType), 68 Expr: b.dibuilder.CreateExpression(nil), 69 LocalToUnit: false, 70 }) 71 global.AddMetadata(0, diglobal) 72 } 73 74 // Create the runtime/interrupt.Interrupt type. It is a struct with a single 75 // member of type int. 76 num := llvm.ConstPtrToInt(global, b.intType) 77 interrupt := llvm.ConstNamedStruct(b.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num}) 78 79 // Add dummy "use" call for AVR, because interrupts may be used even though 80 // they are never referenced again. This is unlike Cortex-M or the RISC-V 81 // PLIC where each interrupt must be enabled using the interrupt number, and 82 // thus keeps the Interrupt object alive. 83 // This call is removed during interrupt lowering. 84 if strings.HasPrefix(b.Triple, "avr") { 85 useFn := b.mod.NamedFunction("runtime/interrupt.use") 86 if useFn.IsNil() { 87 useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false) 88 useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType) 89 } 90 b.CreateCall(useFn.GlobalValueType(), useFn, []llvm.Value{interrupt}, "") 91 } 92 93 return interrupt, nil 94 }