aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorAyke van Laethem <[email protected]>2021-04-11 02:00:01 +0200
committerRon Evans <[email protected]>2021-04-12 12:07:42 +0200
commit57271d7eaa50b122706610fc5c406e0380965998 (patch)
treece4672f8077f74ec2cae3e2097ddab4ab68b95cf
parent8383552552c405d3ac2ab9104f9e7845424ded69 (diff)
downloadtinygo-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.go2
-rw-r--r--compiler/func.go18
-rw-r--r--compiler/testdata/func.ll12
-rw-r--r--src/runtime/func.go6
-rw-r--r--transform/func-lowering.go11
-rw-r--r--transform/interrupt.go6
-rw-r--r--transform/testdata/func-lowering.ll25
-rw-r--r--transform/testdata/func-lowering.out.ll13
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*)