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