diff options
author | Ayke van Laethem <[email protected]> | 2021-04-11 02:00:01 +0200 |
---|---|---|
committer | Ron Evans <[email protected]> | 2021-04-12 12:07:42 +0200 |
commit | 57271d7eaa50b122706610fc5c406e0380965998 (patch) | |
tree | ce4672f8077f74ec2cae3e2097ddab4ab68b95cf | |
parent | 8383552552c405d3ac2ab9104f9e7845424ded69 (diff) | |
download | tinygo-57271d7eaa50b122706610fc5c406e0380965998.tar.gz tinygo-57271d7eaa50b122706610fc5c406e0380965998.zip |
compiler: decouple func lowering from interface type codes
There is no good reason for func values to refer to interface type
codes. The only thing they need is a stable identifier for function
signatures, which is easily created as a new kind of globals. Decoupling
makes it easier to change interface related code.
-rw-r--r-- | compiler/compiler.go | 2 | ||||
-rw-r--r-- | compiler/func.go | 18 | ||||
-rw-r--r-- | compiler/testdata/func.ll | 12 | ||||
-rw-r--r-- | src/runtime/func.go | 6 | ||||
-rw-r--r-- | transform/func-lowering.go | 11 | ||||
-rw-r--r-- | transform/interrupt.go | 6 | ||||
-rw-r--r-- | transform/testdata/func-lowering.ll | 25 | ||||
-rw-r--r-- | transform/testdata/func-lowering.out.ll | 13 |
8 files changed, 48 insertions, 45 deletions
diff --git a/compiler/compiler.go b/compiler/compiler.go index acf85cd88..0afa20daf 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 7 // last change: don't rely on runtime.typecodeID struct type name +const Version = 8 // last change: don't use runtime.typecodeID in func lowering func init() { llvm.InitializeAllTargets() diff --git a/compiler/func.go b/compiler/func.go index 720cbe1f4..7472f4070 100644 --- a/compiler/func.go +++ b/compiler/func.go @@ -25,14 +25,13 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context // Closure is: {context, function pointer} funcValueScalar = funcPtr case "switch": - sigGlobal := c.getTypeCode(sig) funcValueWithSignatureGlobalName := funcPtr.Name() + "$withSignature" funcValueWithSignatureGlobal := c.mod.NamedGlobal(funcValueWithSignatureGlobalName) if funcValueWithSignatureGlobal.IsNil() { funcValueWithSignatureType := c.getLLVMRuntimeType("funcValueWithSignature") funcValueWithSignature := llvm.ConstNamedStruct(funcValueWithSignatureType, []llvm.Value{ llvm.ConstPtrToInt(funcPtr, c.uintptrType), - sigGlobal, + c.getFuncSignatureID(sig), }) funcValueWithSignatureGlobal = llvm.AddGlobal(c.mod, funcValueWithSignatureType, funcValueWithSignatureGlobalName) funcValueWithSignatureGlobal.SetInitializer(funcValueWithSignature) @@ -50,6 +49,19 @@ func (c *compilerContext) createFuncValue(builder llvm.Builder, funcPtr, context return funcValue } +// getFuncSignatureID returns a new external global for a given signature. This +// global reference is not real, it is only used during func lowering to assign +// signature types to functions and will then be removed. +func (c *compilerContext) getFuncSignatureID(sig *types.Signature) llvm.Value { + sigGlobalName := "reflect/types.funcid:" + getTypeCodeName(sig) + sigGlobal := c.mod.NamedGlobal(sigGlobalName) + if sigGlobal.IsNil() { + sigGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), sigGlobalName) + sigGlobal.SetGlobalConstant(true) + } + return sigGlobal +} + // extractFuncScalar returns some scalar that can be used in comparisons. It is // a cheap operation. func (b *builder) extractFuncScalar(funcValue llvm.Value) llvm.Value { @@ -71,7 +83,7 @@ func (b *builder) decodeFuncValue(funcValue llvm.Value, sig *types.Signature) (f funcPtr = b.CreateExtractValue(funcValue, 1, "") case "switch": llvmSig := b.getRawFuncType(sig) - sigGlobal := b.getTypeCode(sig) + sigGlobal := b.getFuncSignatureID(sig) funcPtr = b.createRuntimeCall("getFuncPtr", []llvm.Value{funcValue, sigGlobal}, "") funcPtr = b.CreateIntToPtr(funcPtr, llvmSig, "") default: diff --git a/compiler/testdata/func.ll b/compiler/testdata/func.ll index dc8b6d643..9438a92e8 100644 --- a/compiler/testdata/func.ll +++ b/compiler/testdata/func.ll @@ -3,12 +3,10 @@ source_filename = "func.go" target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128" target triple = "i686--linux" -%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* } -%runtime.interfaceMethodInfo = type { i8*, i32 } -%runtime.funcValueWithSignature = type { i32, %runtime.typecodeID* } +%runtime.funcValueWithSignature = type { i32, i8* } -@"reflect/types.type:func:{basic:int}{}" = linkonce_odr constant %runtime.typecodeID zeroinitializer -@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:int}{}" } +@"reflect/types.funcid:func:{basic:int}{}" = external constant i8 +@"main.someFunc$withSignature" = linkonce_odr constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @main.someFunc to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" } declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) @@ -19,7 +17,7 @@ entry: define hidden void @main.foo(i8* %callback.context, i32 %callback.funcptr, i8* %context, i8* %parentHandle) unnamed_addr { entry: - %0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, %runtime.typecodeID* nonnull @"reflect/types.type:func:{basic:int}{}", i8* undef, i8* null) + %0 = call i32 @runtime.getFuncPtr(i8* %callback.context, i32 %callback.funcptr, i8* nonnull @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null) %1 = icmp eq i32 %0, 0 br i1 %1, label %fpcall.throw, label %fpcall.next @@ -33,7 +31,7 @@ fpcall.next: ; preds = %entry ret void } -declare i32 @runtime.getFuncPtr(i8*, i32, %runtime.typecodeID* dereferenceable_or_null(12), i8*, i8*) +declare i32 @runtime.getFuncPtr(i8*, i32, i8* dereferenceable_or_null(1), i8*, i8*) declare void @runtime.nilPanic(i8*, i8*) diff --git a/src/runtime/func.go b/src/runtime/func.go index 96a60048b..879424b5d 100644 --- a/src/runtime/func.go +++ b/src/runtime/func.go @@ -16,13 +16,13 @@ type funcValue struct { // funcValueWithSignature is used before the func lowering pass. type funcValueWithSignature struct { - funcPtr uintptr // ptrtoint of the actual function pointer - signature *typecodeID // pointer to identify this signature (the value is undef) + funcPtr uintptr // ptrtoint of the actual function pointer + signature *uint8 // external *i8 with a name identifying the function signature } // getFuncPtr is a dummy function that may be used if the func lowering pass is // not used. It is generally too slow but may be a useful fallback to debug the // func lowering pass. -func getFuncPtr(val funcValue, signature *typecodeID) uintptr { +func getFuncPtr(val funcValue, signature *uint8) uintptr { return (*funcValueWithSignature)(unsafe.Pointer(val.id)).funcPtr } diff --git a/transform/func-lowering.go b/transform/func-lowering.go index 02341a83b..0e65bacb6 100644 --- a/transform/func-lowering.go +++ b/transform/func-lowering.go @@ -53,15 +53,14 @@ func LowerFuncValues(mod llvm.Module) { uintptrType := ctx.IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8) // Find all func values used in the program with their signatures. - funcValueWithSignaturePtr := llvm.PointerType(mod.GetTypeByName("runtime.funcValueWithSignature"), 0) signatures := map[string]*funcSignatureInfo{} for global := mod.FirstGlobal(); !global.IsNil(); global = llvm.NextGlobal(global) { var sig, funcVal llvm.Value switch { - case global.Type() == funcValueWithSignaturePtr: + case strings.HasSuffix(global.Name(), "$withSignature"): sig = llvm.ConstExtractValue(global.Initializer(), []uint32{1}) funcVal = global - case strings.HasPrefix(global.Name(), "reflect/types.type:func:{"): + case strings.HasPrefix(global.Name(), "reflect/types.funcid:func:{"): sig = global default: continue @@ -202,6 +201,12 @@ func LowerFuncValues(mod llvm.Module) { } getFuncPtrCall.EraseFromParentAsInstruction() } + + // Clean up all globals used before func lowering. + for _, obj := range info.funcValueWithSignatures { + obj.EraseFromParentAsGlobal() + } + info.sig.EraseFromParentAsGlobal() } } diff --git a/transform/interrupt.go b/transform/interrupt.go index d5d7d47e9..e90a94f09 100644 --- a/transform/interrupt.go +++ b/transform/interrupt.go @@ -132,11 +132,11 @@ func LowerInterrupts(mod llvm.Module, sizeLevel int) []error { 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(global, "internal error: func lowering global has unexpected type")) + if !strings.HasSuffix(global.Name(), "$withSignature") { + errs = append(errs, errorAt(global, "internal error: func lowering global has unexpected name: "+global.Name())) continue } + initializer := global.Initializer() ptrtoint := llvm.ConstExtractValue(initializer, []uint32{0}) if ptrtoint.IsAConstantExpr().IsNil() || ptrtoint.Opcode() != llvm.PtrToInt { errs = append(errs, errorAt(global, "internal error: func lowering global has unexpected func ptr type")) diff --git a/transform/testdata/func-lowering.ll b/transform/testdata/func-lowering.ll index c00264d91..872b3baee 100644 --- a/transform/testdata/func-lowering.ll +++ b/transform/testdata/func-lowering.ll @@ -1,18 +1,17 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" -%runtime.typecodeID = type { %runtime.typecodeID*, i32 } -%runtime.funcValueWithSignature = type { i32, %runtime.typecodeID* } +%runtime.funcValueWithSignature = type { i32, i8* } -@"reflect/types.type:func:{basic:uint8}{}" = external constant %runtime.typecodeID -@"reflect/types.type:func:{basic:int}{}" = external constant %runtime.typecodeID -@"reflect/types.type:func:{}{basic:uint32}" = external constant %runtime.typecodeID -@"func1Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func1Uint8 to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:uint8}{}" } -@"func2Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func2Uint8 to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:uint8}{}" } -@"main$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$1" to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:int}{}" } -@"main$2$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$2" to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:int}{}" } +@"reflect/types.funcid:func:{basic:uint8}{}" = external constant i8 +@"reflect/types.funcid:func:{basic:int}{}" = external constant i8 +@"reflect/types.funcid:func:{}{basic:uint32}" = external constant i8 +@"func1Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func1Uint8 to i32), i8* @"reflect/types.funcid:func:{basic:uint8}{}" } +@"func2Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func2Uint8 to i32), i8* @"reflect/types.funcid:func:{basic:uint8}{}" } +@"main$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$1" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" } +@"main$2$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$2" to i32), i8* @"reflect/types.funcid:func:{basic:int}{}" } -declare i32 @runtime.getFuncPtr(i8*, i32, %runtime.typecodeID*, i8*, i8*) +declare i32 @runtime.getFuncPtr(i8*, i32, i8*, i8*, i8*) declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*) @@ -30,7 +29,7 @@ declare void @func2Uint8(i8, i8*, i8*) ; This means that this should unconditionally nil panic. define i32 @runFuncNone(i8*, i32, i8* %context, i8* %parentHandle) { entry: - %2 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, %runtime.typecodeID* @"reflect/types.type:func:{}{basic:uint32}", i8* undef, i8* null) + %2 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, i8* @"reflect/types.funcid:func:{}{basic:uint32}", i8* undef, i8* null) %3 = inttoptr i32 %2 to i32 (i8*, i8*)* %4 = icmp eq i32 (i8*, i8*)* %3, null br i1 %4, label %fpcall.nil, label %fpcall.next @@ -49,7 +48,7 @@ fpcall.next: ; func value is nil). This call will thus be lowered to a switch statement. define void @runFunc2(i8*, i32, i8, i8* %context, i8* %parentHandle) { entry: - %3 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, %runtime.typecodeID* @"reflect/types.type:func:{basic:uint8}{}", i8* undef, i8* null) + %3 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, i8* @"reflect/types.funcid:func:{basic:uint8}{}", i8* undef, i8* null) %4 = inttoptr i32 %3 to void (i8, i8*, i8*)* %5 = icmp eq void (i8, i8*, i8*)* %4, null br i1 %5, label %fpcall.nil, label %fpcall.next @@ -66,7 +65,7 @@ fpcall.next: ; Special case for internal/task.start. define void @sleepFuncValue(i8*, i32, i8* nocapture readnone %context, i8* nocapture readnone %parentHandle) { entry: - %2 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, %runtime.typecodeID* @"reflect/types.type:func:{basic:int}{}", i8* undef, i8* null) + %2 = call i32 @runtime.getFuncPtr(i8* %0, i32 %1, i8* @"reflect/types.funcid:func:{basic:int}{}", i8* undef, i8* null) call void @"internal/task.start"(i32 %2, i8* null, i32 undef, i8* undef, i8* null) ret void } diff --git a/transform/testdata/func-lowering.out.ll b/transform/testdata/func-lowering.out.ll index deba41b0d..074fcb718 100644 --- a/transform/testdata/func-lowering.out.ll +++ b/transform/testdata/func-lowering.out.ll @@ -1,18 +1,7 @@ target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128" target triple = "wasm32-unknown-unknown-wasm" -%runtime.typecodeID = type { %runtime.typecodeID*, i32 } -%runtime.funcValueWithSignature = type { i32, %runtime.typecodeID* } - -@"reflect/types.type:func:{basic:uint8}{}" = external constant %runtime.typecodeID -@"reflect/types.type:func:{basic:int}{}" = external constant %runtime.typecodeID -@"reflect/types.type:func:{}{basic:uint32}" = external constant %runtime.typecodeID -@"func1Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func1Uint8 to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:uint8}{}" } -@"func2Uint8$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i8, i8*, i8*)* @func2Uint8 to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:uint8}{}" } -@"main$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$1" to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:int}{}" } -@"main$2$withSignature" = constant %runtime.funcValueWithSignature { i32 ptrtoint (void (i32, i8*, i8*)* @"main$2" to i32), %runtime.typecodeID* @"reflect/types.type:func:{basic:int}{}" } - -declare i32 @runtime.getFuncPtr(i8*, i32, %runtime.typecodeID*, i8*, i8*) +declare i32 @runtime.getFuncPtr(i8*, i32, i8*, i8*, i8*) declare void @"internal/task.start"(i32, i8*, i32, i8*, i8*) |