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 // It must have an alignment of 1, otherwise LLVM thinks a ptrtoint of the
45 // global has the lower bits unset.
46 globalType := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
47 globalLLVMType := b.getLLVMType(globalType)
48 globalName := b.fn.Package().Pkg.Path() + "$interrupt" + strconv.FormatInt(id.Int64(), 10)
49 global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
50 global.SetVisibility(llvm.HiddenVisibility)
51 global.SetGlobalConstant(true)
52 global.SetUnnamedAddr(true)
53 global.SetAlignment(1)
54 initializer := llvm.ConstNull(globalLLVMType)
55 initializer = b.CreateInsertValue(initializer, funcContext, 0, "")
56 initializer = b.CreateInsertValue(initializer, funcPtr, 1, "")
57 initializer = b.CreateInsertValue(initializer, llvm.ConstNamedStruct(globalLLVMType.StructElementTypes()[2], []llvm.Value{
58 llvm.ConstInt(b.intType, uint64(id.Int64()), true),
59 }), 2, "")
60 global.SetInitializer(initializer)
61 62 // Add debug info to the interrupt global.
63 if b.Debug {
64 pos := b.program.Fset.Position(instr.Pos())
65 diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
66 Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
67 LinkageName: globalName,
68 File: b.getDIFile(pos.Filename),
69 Line: pos.Line,
70 Type: b.getDIType(globalType),
71 Expr: b.dibuilder.CreateExpression(nil),
72 LocalToUnit: false,
73 })
74 global.AddMetadata(0, diglobal)
75 }
76 77 // Create the runtime/interrupt.Interrupt type. It is a struct with a single
78 // member of type int.
79 num := llvm.ConstPtrToInt(global, b.intType)
80 interrupt := llvm.ConstNamedStruct(b.mod.GetTypeByName("runtime/interrupt.Interrupt"), []llvm.Value{num})
81 82 // Add dummy "use" call for AVR, because interrupts may be used even though
83 // they are never referenced again. This is unlike Cortex-M or the RISC-V
84 // PLIC where each interrupt must be enabled using the interrupt number, and
85 // thus keeps the Interrupt object alive.
86 // This call is removed during interrupt lowering.
87 if strings.HasPrefix(b.Triple, "avr") {
88 useFn := b.mod.NamedFunction("runtime/interrupt.use")
89 if useFn.IsNil() {
90 useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
91 useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
92 }
93 b.CreateCall(useFn.GlobalValueType(), useFn, []llvm.Value{interrupt}, "")
94 }
95 96 return interrupt, nil
97 }
98