mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-03 02:27:48 +00:00
dd04f34059
The ir package has long lost its original purpose (which was doing some analysis and optimization on the Go SSA directly). There is very little of it left, which is best integrated directly in the compiler package to avoid unnecessary abstraction.
93 lines
3.7 KiB
Go
93 lines
3.7 KiB
Go
package compiler
|
|
|
|
import (
|
|
"strconv"
|
|
"strings"
|
|
|
|
"golang.org/x/tools/go/ssa"
|
|
"tinygo.org/x/go-llvm"
|
|
)
|
|
|
|
// createInterruptGlobal 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 (b *builder) createInterruptGlobal(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{}, b.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 := b.getValue(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{}, b.makeError(instr.Pos(), "closures are not supported in interrupt.New")
|
|
}
|
|
// Fall back to a generic error.
|
|
return llvm.Value{}, b.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 := b.program.ImportedPackage("runtime/interrupt").Type("handle").Type()
|
|
globalLLVMType := b.getLLVMType(globalType)
|
|
globalName := "runtime/interrupt.$interrupt" + strconv.FormatInt(id.Int64(), 10)
|
|
if global := b.mod.NamedGlobal(globalName); !global.IsNil() {
|
|
return llvm.Value{}, b.makeError(instr.Pos(), "interrupt redeclared in this program")
|
|
}
|
|
global := llvm.AddGlobal(b.mod, globalLLVMType, globalName)
|
|
global.SetLinkage(llvm.PrivateLinkage)
|
|
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})
|
|
global.SetInitializer(initializer)
|
|
|
|
// Add debug info to the interrupt global.
|
|
if b.Debug() {
|
|
pos := b.program.Fset.Position(instr.Pos())
|
|
diglobal := b.dibuilder.CreateGlobalVariableExpression(b.getDIFile(pos.Filename), llvm.DIGlobalVariableExpression{
|
|
Name: "interrupt" + strconv.FormatInt(id.Int64(), 10),
|
|
LinkageName: globalName,
|
|
File: b.getDIFile(pos.Filename),
|
|
Line: pos.Line,
|
|
Type: b.getDIType(globalType),
|
|
Expr: b.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, b.intType)
|
|
interrupt := llvm.ConstNamedStruct(b.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(b.Triple(), "avr") {
|
|
useFn := b.mod.NamedFunction("runtime/interrupt.use")
|
|
if useFn.IsNil() {
|
|
useFnType := llvm.FunctionType(b.ctx.VoidType(), []llvm.Type{interrupt.Type()}, false)
|
|
useFn = llvm.AddFunction(b.mod, "runtime/interrupt.use", useFnType)
|
|
}
|
|
b.CreateCall(useFn, []llvm.Value{interrupt}, "")
|
|
}
|
|
|
|
return interrupt, nil
|
|
}
|