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.
Этот коммит содержится в:
родитель
8383552552
коммит
57271d7eaa
8 изменённых файлов: 48 добавлений и 45 удалений
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
12
compiler/testdata/func.ll
предоставленный
12
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*)
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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"))
|
||||
|
|
25
transform/testdata/func-lowering.ll
предоставленный
25
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
|
||||
}
|
||||
|
|
13
transform/testdata/func-lowering.out.ll
предоставленный
13
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*)
|
||||
|
||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче