compiler: simplify interface lowering
This commit simplifies the IR a little bit: instead of calling pseudo-functions runtime.interfaceImplements and runtime.interfaceMethod, real declared functions are being called that are then defined in the interface lowering pass. This should simplify the interaction between various transformation passes. It also reduces the number of lines of code, which is generally a good thing.
Этот коммит содержится в:
родитель
90076f9401
коммит
a4afc3b4b0
14 изменённых файлов: 355 добавлений и 397 удалений
|
@ -1344,9 +1344,9 @@ func (b *builder) createBuiltin(argTypes []types.Type, argValues []llvm.Value, c
|
||||||
//
|
//
|
||||||
// This is also where compiler intrinsics are implemented.
|
// This is also where compiler intrinsics are implemented.
|
||||||
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
|
func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error) {
|
||||||
if instr.IsInvoke() {
|
var params []llvm.Value
|
||||||
fnCast, args := b.getInvokeCall(instr)
|
for _, param := range instr.Args {
|
||||||
return b.createCall(fnCast, args, ""), nil
|
params = append(params, b.getValue(param))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to call the function directly for trivially static calls.
|
// Try to call the function directly for trivially static calls.
|
||||||
|
@ -1417,12 +1417,20 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||||
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
} else if call, ok := instr.Value.(*ssa.Builtin); ok {
|
||||||
// Builtin function (append, close, delete, etc.).)
|
// Builtin function (append, close, delete, etc.).)
|
||||||
var argTypes []types.Type
|
var argTypes []types.Type
|
||||||
var argValues []llvm.Value
|
|
||||||
for _, arg := range instr.Args {
|
for _, arg := range instr.Args {
|
||||||
argTypes = append(argTypes, arg.Type())
|
argTypes = append(argTypes, arg.Type())
|
||||||
argValues = append(argValues, b.getValue(arg))
|
|
||||||
}
|
}
|
||||||
return b.createBuiltin(argTypes, argValues, call.Name(), instr.Pos())
|
return b.createBuiltin(argTypes, params, call.Name(), instr.Pos())
|
||||||
|
} else if instr.IsInvoke() {
|
||||||
|
// Interface method call (aka invoke call).
|
||||||
|
itf := b.getValue(instr.Value) // interface value (runtime._interface)
|
||||||
|
typecode := b.CreateExtractValue(itf, 0, "invoke.func.typecode")
|
||||||
|
value := b.CreateExtractValue(itf, 1, "invoke.func.value") // receiver
|
||||||
|
// Prefix the params with receiver value and suffix with typecode.
|
||||||
|
params = append([]llvm.Value{value}, params...)
|
||||||
|
params = append(params, typecode)
|
||||||
|
callee = b.getInvokeFunction(instr)
|
||||||
|
context = llvm.Undef(b.i8ptrType)
|
||||||
} else {
|
} else {
|
||||||
// Function pointer.
|
// Function pointer.
|
||||||
value := b.getValue(instr.Value)
|
value := b.getValue(instr.Value)
|
||||||
|
@ -1432,11 +1440,6 @@ func (b *builder) createFunctionCall(instr *ssa.CallCommon) (llvm.Value, error)
|
||||||
b.createNilCheck(instr.Value, callee, "fpcall")
|
b.createNilCheck(instr.Value, callee, "fpcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
var params []llvm.Value
|
|
||||||
for _, param := range instr.Args {
|
|
||||||
params = append(params, b.getValue(param))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !exported {
|
if !exported {
|
||||||
// This function takes a context parameter.
|
// This function takes a context parameter.
|
||||||
// Add it to the end of the parameter list.
|
// Add it to the end of the parameter list.
|
||||||
|
|
|
@ -331,10 +331,10 @@ func (b *builder) createRunDefers() {
|
||||||
//Pass context
|
//Pass context
|
||||||
forwardParams = append(forwardParams, context)
|
forwardParams = append(forwardParams, context)
|
||||||
} else {
|
} else {
|
||||||
// Isolate the typecode.
|
// Move typecode from the start to the end of the list of
|
||||||
typecode := forwardParams[0]
|
// parameters.
|
||||||
forwardParams = forwardParams[1:]
|
forwardParams = append(forwardParams[1:], forwardParams[0])
|
||||||
fnPtr = b.getInvokePtr(callback, typecode)
|
fnPtr = b.getInvokeFunction(callback)
|
||||||
|
|
||||||
// Add the context parameter. An interface call cannot also be a
|
// Add the context parameter. An interface call cannot also be a
|
||||||
// closure but we have to supply the parameter anyway for platforms
|
// closure but we have to supply the parameter anyway for platforms
|
||||||
|
|
|
@ -47,6 +47,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||||
var length int64
|
var length int64
|
||||||
var methodSet llvm.Value
|
var methodSet llvm.Value
|
||||||
var ptrTo llvm.Value
|
var ptrTo llvm.Value
|
||||||
|
var typeAssert llvm.Value
|
||||||
switch typ := typ.(type) {
|
switch typ := typ.(type) {
|
||||||
case *types.Named:
|
case *types.Named:
|
||||||
references = c.getTypeCode(typ.Underlying())
|
references = c.getTypeCode(typ.Underlying())
|
||||||
|
@ -69,6 +70,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||||
}
|
}
|
||||||
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
if _, ok := typ.Underlying().(*types.Interface); !ok {
|
||||||
methodSet = c.getTypeMethodSet(typ)
|
methodSet = c.getTypeMethodSet(typ)
|
||||||
|
} else {
|
||||||
|
typeAssert = c.getInterfaceImplementsFunc(typ)
|
||||||
|
typeAssert = llvm.ConstPtrToInt(typeAssert, c.uintptrType)
|
||||||
}
|
}
|
||||||
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
if _, ok := typ.Underlying().(*types.Pointer); !ok {
|
||||||
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
ptrTo = c.getTypeCode(types.NewPointer(typ))
|
||||||
|
@ -87,6 +91,9 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||||
if !ptrTo.IsNil() {
|
if !ptrTo.IsNil() {
|
||||||
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
|
globalValue = llvm.ConstInsertValue(globalValue, ptrTo, []uint32{3})
|
||||||
}
|
}
|
||||||
|
if !typeAssert.IsNil() {
|
||||||
|
globalValue = llvm.ConstInsertValue(globalValue, typeAssert, []uint32{4})
|
||||||
|
}
|
||||||
global.SetInitializer(globalValue)
|
global.SetInitializer(globalValue)
|
||||||
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
global.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||||
global.SetGlobalConstant(true)
|
global.SetGlobalConstant(true)
|
||||||
|
@ -193,7 +200,11 @@ func getTypeCodeName(t types.Type) string {
|
||||||
case *types.Interface:
|
case *types.Interface:
|
||||||
methods := make([]string, t.NumMethods())
|
methods := make([]string, t.NumMethods())
|
||||||
for i := 0; i < t.NumMethods(); i++ {
|
for i := 0; i < t.NumMethods(); i++ {
|
||||||
methods[i] = t.Method(i).Name() + ":" + getTypeCodeName(t.Method(i).Type())
|
name := t.Method(i).Name()
|
||||||
|
if !token.IsExported(name) {
|
||||||
|
name = t.Method(i).Pkg().Path() + "." + name
|
||||||
|
}
|
||||||
|
methods[i] = name + ":" + getTypeCodeName(t.Method(i).Type())
|
||||||
}
|
}
|
||||||
return "interface:" + "{" + strings.Join(methods, ",") + "}"
|
return "interface:" + "{" + strings.Join(methods, ",") + "}"
|
||||||
case *types.Map:
|
case *types.Map:
|
||||||
|
@ -306,10 +317,9 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value {
|
||||||
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
return llvm.ConstGEP(global, []llvm.Value{zero, zero})
|
||||||
}
|
}
|
||||||
|
|
||||||
// getMethodSignature returns a global variable which is a reference to an
|
// getMethodSignatureName returns a unique name (that can be used as the name of
|
||||||
// external *i8 indicating the indicating the signature of this method. It is
|
// a global) for the given method.
|
||||||
// used during the interface lowering pass.
|
func (c *compilerContext) getMethodSignatureName(method *types.Func) string {
|
||||||
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
|
||||||
signature := methodSignature(method)
|
signature := methodSignature(method)
|
||||||
var globalName string
|
var globalName string
|
||||||
if token.IsExported(method.Name()) {
|
if token.IsExported(method.Name()) {
|
||||||
|
@ -317,6 +327,14 @@ func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
||||||
} else {
|
} else {
|
||||||
globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
|
globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature
|
||||||
}
|
}
|
||||||
|
return globalName
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMethodSignature returns a global variable which is a reference to an
|
||||||
|
// external *i8 indicating the indicating the signature of this method. It is
|
||||||
|
// used during the interface lowering pass.
|
||||||
|
func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value {
|
||||||
|
globalName := c.getMethodSignatureName(method)
|
||||||
signatureGlobal := c.mod.NamedGlobal(globalName)
|
signatureGlobal := c.mod.NamedGlobal(globalName)
|
||||||
if signatureGlobal.IsNil() {
|
if signatureGlobal.IsNil() {
|
||||||
// TODO: put something useful in these globals, such as the method
|
// TODO: put something useful in these globals, such as the method
|
||||||
|
@ -345,15 +363,16 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||||
commaOk := llvm.Value{}
|
commaOk := llvm.Value{}
|
||||||
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
if _, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||||
// Type assert on interface type.
|
// Type assert on interface type.
|
||||||
// This pseudo call will be lowered in the interface lowering pass to a
|
// This is a call to an interface type assert function.
|
||||||
// real call which checks whether the provided typecode is any of the
|
// The interface lowering pass will define this function by filling it
|
||||||
// concrete types that implements this interface.
|
// with a type switch over all concrete types that implement this
|
||||||
|
// interface, and returning whether it's one of the matched types.
|
||||||
// This is very different from how interface asserts are implemented in
|
// This is very different from how interface asserts are implemented in
|
||||||
// the main Go compiler, where the runtime checks whether the type
|
// the main Go compiler, where the runtime checks whether the type
|
||||||
// implements each method of the interface. See:
|
// implements each method of the interface. See:
|
||||||
// https://research.swtch.com/interfaces
|
// https://research.swtch.com/interfaces
|
||||||
methodSet := b.getInterfaceMethodSet(expr.AssertedType)
|
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||||
commaOk = b.createRuntimeCall("interfaceImplements", []llvm.Value{actualTypeNum, methodSet}, "")
|
commaOk = b.CreateCall(fn, []llvm.Value{actualTypeNum}, "")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
globalName := "reflect/types.typeid:" + getTypeCodeName(expr.AssertedType)
|
||||||
|
@ -420,39 +439,50 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInvokePtr creates an interface function pointer lookup for the specified invoke instruction, using a specified typecode.
|
// getMethodsString returns a string to be used in the "tinygo-methods" string
|
||||||
func (b *builder) getInvokePtr(instr *ssa.CallCommon, typecode llvm.Value) llvm.Value {
|
// attribute for interface functions.
|
||||||
llvmFnType := b.getRawFuncType(instr.Method.Type().(*types.Signature))
|
func (c *compilerContext) getMethodsString(itf *types.Interface) string {
|
||||||
values := []llvm.Value{
|
methods := make([]string, itf.NumMethods())
|
||||||
typecode,
|
for i := range methods {
|
||||||
b.getInterfaceMethodSet(instr.Value.Type()),
|
methods[i] = c.getMethodSignatureName(itf.Method(i))
|
||||||
b.getMethodSignature(instr.Method),
|
|
||||||
}
|
}
|
||||||
fn := b.createRuntimeCall("interfaceMethod", values, "invoke.func")
|
return strings.Join(methods, "; ")
|
||||||
return b.CreateIntToPtr(fn, llvmFnType, "invoke.func.cast")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInvokeCall creates and returns the function pointer and parameters of an
|
// getInterfaceImplementsfunc returns a declared function that works as a type
|
||||||
// interface call.
|
// switch. The interface lowering pass will define this function.
|
||||||
func (b *builder) getInvokeCall(instr *ssa.CallCommon) (llvm.Value, []llvm.Value) {
|
func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
|
||||||
// Call an interface method with dynamic dispatch.
|
fnName := getTypeCodeName(assertedType.Underlying()) + ".$typeassert"
|
||||||
itf := b.getValue(instr.Value) // interface
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
|
if llvmFn.IsNil() {
|
||||||
typecode := b.CreateExtractValue(itf, 0, "invoke.typecode")
|
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.uintptrType}, false)
|
||||||
fnCast := b.getInvokePtr(instr, typecode)
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
receiverValue := b.CreateExtractValue(itf, 1, "invoke.func.receiver")
|
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||||
args := []llvm.Value{receiverValue}
|
|
||||||
for _, arg := range instr.Args {
|
|
||||||
args = append(args, b.getValue(arg))
|
|
||||||
}
|
}
|
||||||
// Add the context parameter. An interface call never takes a context but we
|
return llvmFn
|
||||||
// have to supply the parameter anyway.
|
}
|
||||||
args = append(args, llvm.Undef(b.i8ptrType))
|
|
||||||
// Add the parent goroutine handle.
|
|
||||||
args = append(args, llvm.Undef(b.i8ptrType))
|
|
||||||
|
|
||||||
return fnCast, args
|
// getInvokeFunction returns the thunk to call the given interface method. The
|
||||||
|
// thunk is declared, not defined: it will be defined by the interface lowering
|
||||||
|
// pass.
|
||||||
|
func (c *compilerContext) getInvokeFunction(instr *ssa.CallCommon) llvm.Value {
|
||||||
|
fnName := getTypeCodeName(instr.Value.Type().Underlying()) + "." + instr.Method.Name() + "$invoke"
|
||||||
|
llvmFn := c.mod.NamedFunction(fnName)
|
||||||
|
if llvmFn.IsNil() {
|
||||||
|
sig := instr.Method.Type().(*types.Signature)
|
||||||
|
var paramTuple []*types.Var
|
||||||
|
for i := 0; i < sig.Params().Len(); i++ {
|
||||||
|
paramTuple = append(paramTuple, sig.Params().At(i))
|
||||||
|
}
|
||||||
|
paramTuple = append(paramTuple, types.NewVar(token.NoPos, nil, "$typecode", types.Typ[types.Uintptr]))
|
||||||
|
llvmFnType := c.getRawFuncType(types.NewSignature(sig.Recv(), types.NewTuple(paramTuple...), sig.Results(), false)).ElementType()
|
||||||
|
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-invoke", c.getMethodSignatureName(instr.Method)))
|
||||||
|
methods := c.getMethodsString(instr.Value.Type().Underlying().(*types.Interface))
|
||||||
|
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||||
|
}
|
||||||
|
return llvmFn
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
|
// getInterfaceInvokeWrapper returns a wrapper for the given method so it can be
|
||||||
|
|
9
compiler/testdata/interface.go
предоставленный
9
compiler/testdata/interface.go
предоставленный
|
@ -49,6 +49,15 @@ func isStringer(itf interface{}) bool {
|
||||||
return ok
|
return ok
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type fooInterface interface {
|
||||||
|
String() string
|
||||||
|
foo(int) byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func callFooMethod(itf fooInterface) uint8 {
|
||||||
|
return itf.foo(3)
|
||||||
|
}
|
||||||
|
|
||||||
func callErrorMethod(itf error) string {
|
func callErrorMethod(itf error) string {
|
||||||
return itf.Error()
|
return itf.Error()
|
||||||
}
|
}
|
||||||
|
|
48
compiler/testdata/interface.ll
предоставленный
48
compiler/testdata/interface.ll
предоставленный
|
@ -3,25 +3,24 @@ source_filename = "interface.go"
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
target datalayout = "e-m:e-p:32:32-i64:64-n32:64-S128"
|
||||||
target triple = "wasm32-unknown-wasi"
|
target triple = "wasm32-unknown-wasi"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID* }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
%runtime._interface = type { i32, i8* }
|
%runtime._interface = type { i32, i8* }
|
||||||
%runtime._string = type { i8*, i32 }
|
%runtime._string = type { i8*, i32 }
|
||||||
|
|
||||||
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int" }
|
@"reflect/types.type:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* null, i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:basic:int", i32 0 }
|
||||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
@"reflect/types.type:pointer:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:named:error", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error" }
|
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:named:error", i32 ptrtoint (i1 (i32)* @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
|
||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }
|
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", i32 ptrtoint (i1 (i32)* @"interface:{Error:func:{}{basic:string}}.$typeassert" to i32) }
|
||||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
|
@"reflect/methods.Error() string" = linkonce_odr constant i8 0, align 1
|
||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
||||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null }
|
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{String:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }
|
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{String() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", i32 ptrtoint (i1 (i32)* @"interface:{String:func:{}{basic:string}}.$typeassert" to i32) }
|
||||||
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
|
@"reflect/methods.String() string" = linkonce_odr constant i8 0, align 1
|
||||||
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"]
|
@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.String() string"]
|
||||||
@"reflect/types.typeid:basic:int" = external constant i8
|
@"reflect/types.typeid:basic:int" = external constant i8
|
||||||
@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
|
||||||
|
|
||||||
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*)
|
||||||
|
|
||||||
|
@ -49,12 +48,16 @@ entry:
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:named:error" to i32), i8* null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32) #1
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
define hidden %runtime._interface @main.anonymousInterfaceType(i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
ret %runtime._interface { i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" to i32), i8* null }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32) #2
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
define hidden i1 @main.isInt(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
|
@ -73,7 +76,7 @@ declare i1 @runtime.typeAssert(i32, i8* dereferenceable_or_null(1), i8*, i8*)
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
define hidden i1 @main.isError(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* undef, i8* null) #0
|
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
|
@ -83,12 +86,10 @@ typeassert.next: ; preds = %typeassert.ok, %ent
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @runtime.interfaceImplements(i32, i8** dereferenceable_or_null(4), i8*, i8*)
|
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
define hidden i1 @main.isStringer(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @runtime.interfaceImplements(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"reflect/types.interface:interface{String() string}$interface", i32 0, i32 0), i8* undef, i8* null) #0
|
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(i32 %itf.typecode) #0
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.ok: ; preds = %entry
|
typeassert.ok: ; preds = %entry
|
||||||
|
@ -98,15 +99,26 @@ typeassert.next: ; preds = %typeassert.ok, %ent
|
||||||
ret i1 %0
|
ret i1 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define hidden i8 @main.callFooMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
|
entry:
|
||||||
|
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8* %itf.value, i32 3, i32 %itf.typecode, i8* undef, i8* undef) #0
|
||||||
|
ret i8 %0
|
||||||
|
}
|
||||||
|
|
||||||
|
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(i8*, i32, i32, i8*, i8*) #3
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %itf.typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"error$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Error() string", i8* undef, i8* null) #0
|
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8* %itf.value, i32 %itf.typecode, i8* undef, i8* undef) #0
|
||||||
%invoke.func.cast = inttoptr i32 %invoke.func to %runtime._string (i8*, i8*, i8*)*
|
|
||||||
%0 = call %runtime._string %invoke.func.cast(i8* %itf.value, i8* undef, i8* undef) #0
|
|
||||||
ret %runtime._string %0
|
ret %runtime._string %0
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i32 @runtime.interfaceMethod(i32, i8** dereferenceable_or_null(4), i8* dereferenceable_or_null(1), i8*, i8*)
|
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(i8*, i32, i8*, i8*) #4
|
||||||
|
|
||||||
attributes #0 = { nounwind }
|
attributes #0 = { nounwind }
|
||||||
|
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
attributes #2 = { "tinygo-methods"="reflect/methods.String() string" }
|
||||||
|
attributes #3 = { "tinygo-invoke"="main.$methods.foo(int) byte" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) byte" }
|
||||||
|
attributes #4 = { "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
|
|
@ -378,7 +378,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
} else {
|
} else {
|
||||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||||
}
|
}
|
||||||
case callFn.name == "runtime.interfaceImplements":
|
case strings.HasSuffix(callFn.name, ".$typeassert"):
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
||||||
}
|
}
|
||||||
|
@ -393,11 +393,9 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
||||||
interfaceMethodSetPtr, err := operands[2].asPointer(r)
|
llvmFn := inst.llvmInst.CalledValue()
|
||||||
if err != nil {
|
methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||||
return nil, mem, r.errorAt(inst, err)
|
methodSetString := methodSetAttr.GetStringValue()
|
||||||
}
|
|
||||||
interfaceMethodSet := mem.get(interfaceMethodSetPtr.index()).llvmGlobal.Initializer()
|
|
||||||
|
|
||||||
// Make a set of all the methods on the concrete type, for
|
// Make a set of all the methods on the concrete type, for
|
||||||
// easier checking in the next step.
|
// easier checking in the next step.
|
||||||
|
@ -412,8 +410,7 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
// of defined methods calculated above. This is the interface
|
// of defined methods calculated above. This is the interface
|
||||||
// assert itself.
|
// assert itself.
|
||||||
assertOk := uint8(1) // i1 true
|
assertOk := uint8(1) // i1 true
|
||||||
for i := 0; i < interfaceMethodSet.Type().ArrayLength(); i++ {
|
for _, name := range strings.Split(methodSetString, "; ") {
|
||||||
name := llvm.ConstExtractValue(interfaceMethodSet, []uint32{uint32(i)}).Name()
|
|
||||||
if _, ok := concreteTypeMethods[name]; !ok {
|
if _, ok := concreteTypeMethods[name]; !ok {
|
||||||
// There is a method on the interface that is not
|
// There is a method on the interface that is not
|
||||||
// implemented by the type. The assertion will fail.
|
// implemented by the type. The assertion will fail.
|
||||||
|
@ -423,20 +420,20 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
}
|
}
|
||||||
// If assertOk is still 1, the assertion succeeded.
|
// If assertOk is still 1, the assertion succeeded.
|
||||||
locals[inst.localIndex] = literalValue{assertOk}
|
locals[inst.localIndex] = literalValue{assertOk}
|
||||||
case callFn.name == "runtime.interfaceMethod":
|
case strings.HasSuffix(callFn.name, "$invoke"):
|
||||||
// This builtin returns the function (which may be a thunk) to
|
// This thunk is the interface method dispatcher: it is called
|
||||||
// invoke a method on an interface. It does not call the method.
|
// with all regular parameters and a type code. It will then
|
||||||
|
// call the concrete method for it.
|
||||||
if r.debug {
|
if r.debug {
|
||||||
fmt.Fprintln(os.Stderr, indent+"interface method:", operands[1:])
|
fmt.Fprintln(os.Stderr, indent+"invoke method:", operands[1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the first param, which is the type code (ptrtoint of the
|
// Load the type code of the interface value.
|
||||||
// type code global).
|
typecodeIDBitCast, err := operands[len(operands)-3].toLLVMValue(inst.llvmInst.Operand(len(operands)-4).Type(), &mem)
|
||||||
typecodeIDPtrToInt, err := operands[1].toLLVMValue(inst.llvmInst.Operand(0).Type(), &mem)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, mem, r.errorAt(inst, err)
|
return nil, mem, r.errorAt(inst, err)
|
||||||
}
|
}
|
||||||
typecodeID := typecodeIDPtrToInt.Operand(0).Initializer()
|
typecodeID := typecodeIDBitCast.Operand(0).Initializer()
|
||||||
|
|
||||||
// Load the method set, which is part of the typecodeID object.
|
// Load the method set, which is part of the typecodeID object.
|
||||||
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
methodSet := llvm.ConstExtractValue(typecodeID, []uint32{2}).Operand(0).Initializer()
|
||||||
|
@ -444,7 +441,10 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
// We don't need to load the interface method set.
|
// We don't need to load the interface method set.
|
||||||
|
|
||||||
// Load the signature of the to-be-called function.
|
// Load the signature of the to-be-called function.
|
||||||
signature := inst.llvmInst.Operand(2)
|
llvmFn := inst.llvmInst.CalledValue()
|
||||||
|
invokeAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-invoke")
|
||||||
|
invokeName := invokeAttr.GetStringValue()
|
||||||
|
signature := r.mod.NamedGlobal(invokeName)
|
||||||
|
|
||||||
// Iterate through all methods, looking for the one method that
|
// Iterate through all methods, looking for the one method that
|
||||||
// should be returned.
|
// should be returned.
|
||||||
|
@ -457,9 +457,13 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if method.IsNil() {
|
if method.IsNil() {
|
||||||
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+signature.Name()))
|
return nil, mem, r.errorAt(inst, errors.New("could not find method: "+invokeName))
|
||||||
}
|
}
|
||||||
locals[inst.localIndex] = r.getValue(method)
|
|
||||||
|
// Change the to-be-called function to the underlying method to
|
||||||
|
// be called and fall through to the default case.
|
||||||
|
callFn = r.getFunction(method)
|
||||||
|
fallthrough
|
||||||
default:
|
default:
|
||||||
if len(callFn.blocks) == 0 {
|
if len(callFn.blocks) == 0 {
|
||||||
// Call to a function declaration without a definition
|
// Call to a function declaration without a definition
|
||||||
|
|
|
@ -117,6 +117,10 @@ type typecodeID struct {
|
||||||
// Keeping the type struct alive here is important so that values from
|
// Keeping the type struct alive here is important so that values from
|
||||||
// reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
|
// reflect.New (which uses reflect.PtrTo) can be used in type asserts etc.
|
||||||
ptrTo *typecodeID
|
ptrTo *typecodeID
|
||||||
|
|
||||||
|
// typeAssert is a ptrtoint of a declared interface assert function.
|
||||||
|
// It only exists to make the rtcalls pass easier.
|
||||||
|
typeAssert uintptr
|
||||||
}
|
}
|
||||||
|
|
||||||
// structField is used by the compiler to pass information to the interface
|
// structField is used by the compiler to pass information to the interface
|
||||||
|
@ -133,11 +137,3 @@ type structField struct {
|
||||||
// asserts. Also, it is replaced with const false if this type assert can never
|
// asserts. Also, it is replaced with const false if this type assert can never
|
||||||
// happen.
|
// happen.
|
||||||
func typeAssert(actualType uintptr, assertedType *uint8) bool
|
func typeAssert(actualType uintptr, assertedType *uint8) bool
|
||||||
|
|
||||||
// Pseudo function call that returns whether a given type implements all methods
|
|
||||||
// of the given interface.
|
|
||||||
func interfaceImplements(typecode uintptr, interfaceMethodSet **uint8) bool
|
|
||||||
|
|
||||||
// Pseudo function that returns a function pointer to the method to call.
|
|
||||||
// See the interface lowering pass for how this is lowered to a real call.
|
|
||||||
func interfaceMethod(typecode uintptr, interfaceMethodSet **uint8, signature *uint8) uintptr
|
|
||||||
|
|
|
@ -3,32 +3,27 @@ package transform
|
||||||
// This file provides function to lower interface intrinsics to their final LLVM
|
// This file provides function to lower interface intrinsics to their final LLVM
|
||||||
// form, optimizing them in the process.
|
// form, optimizing them in the process.
|
||||||
//
|
//
|
||||||
// During SSA construction, the following pseudo-calls are created:
|
// During SSA construction, the following pseudo-call is created (see
|
||||||
|
// src/runtime/interface.go):
|
||||||
// runtime.typeAssert(typecode, assertedType)
|
// runtime.typeAssert(typecode, assertedType)
|
||||||
// runtime.interfaceImplements(typecode, interfaceMethodSet)
|
// Additionally, interface type asserts and interface invoke functions are
|
||||||
// runtime.interfaceMethod(typecode, interfaceMethodSet, signature)
|
// declared but not defined, so the optimizer will leave them alone.
|
||||||
// See src/runtime/interface.go for details.
|
|
||||||
// These calls are to declared but not defined functions, so the optimizer will
|
|
||||||
// leave them alone.
|
|
||||||
//
|
//
|
||||||
// This pass lowers the above functions to their final form:
|
// This pass lowers these functions to their final form:
|
||||||
//
|
//
|
||||||
// typeAssert:
|
// typeAssert:
|
||||||
// Replaced with an icmp instruction so it can be directly used in a type
|
// Replaced with an icmp instruction so it can be directly used in a type
|
||||||
// switch. This is very easy to optimize for LLVM: it will often translate a
|
// switch. This is very easy to optimize for LLVM: it will often translate a
|
||||||
// type switch into a regular switch statement.
|
// type switch into a regular switch statement.
|
||||||
//
|
//
|
||||||
// interfaceImplements:
|
// interface type assert:
|
||||||
// This call is translated into a call that checks whether the underlying
|
// These functions are defined by creating a big type switch over all the
|
||||||
// type is one of the types implementing this interface.
|
// concrete types implementing this interface.
|
||||||
//
|
//
|
||||||
// interfaceMethod:
|
// interface invoke:
|
||||||
// This call is replaced with a call to a function that calls the
|
// These functions are defined with a similar type switch, but instead of
|
||||||
// appropriate method depending on the underlying type.
|
// checking for the appropriate type, these functions will call the
|
||||||
// When there is only one type implementing this interface, this call is
|
// underlying method instead.
|
||||||
// translated into a direct call of that method.
|
|
||||||
// When there is no type implementing this interface, this code is marked
|
|
||||||
// unreachable as there is no way such an interface could be constructed.
|
|
||||||
//
|
//
|
||||||
// Note that this way of implementing interfaces is very different from how the
|
// Note that this way of implementing interfaces is very different from how the
|
||||||
// main Go compiler implements them. For more details on how the main Go
|
// main Go compiler implements them. For more details on how the main Go
|
||||||
|
@ -45,29 +40,10 @@ import (
|
||||||
// any method in particular.
|
// any method in particular.
|
||||||
type signatureInfo struct {
|
type signatureInfo struct {
|
||||||
name string
|
name string
|
||||||
global llvm.Value
|
|
||||||
methods []*methodInfo
|
methods []*methodInfo
|
||||||
interfaces []*interfaceInfo
|
interfaces []*interfaceInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// methodName takes a method name like "func String()" and returns only the
|
|
||||||
// name, which is "String" in this case.
|
|
||||||
func (s *signatureInfo) methodName() string {
|
|
||||||
var methodName string
|
|
||||||
if strings.HasPrefix(s.name, "reflect/methods.") {
|
|
||||||
methodName = s.name[len("reflect/methods."):]
|
|
||||||
} else if idx := strings.LastIndex(s.name, ".$methods."); idx >= 0 {
|
|
||||||
methodName = s.name[idx+len(".$methods."):]
|
|
||||||
} else {
|
|
||||||
panic("could not find method name")
|
|
||||||
}
|
|
||||||
if openingParen := strings.IndexByte(methodName, '('); openingParen < 0 {
|
|
||||||
panic("no opening paren in signature name")
|
|
||||||
} else {
|
|
||||||
return methodName[:openingParen]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// methodInfo describes a single method on a concrete type.
|
// methodInfo describes a single method on a concrete type.
|
||||||
type methodInfo struct {
|
type methodInfo struct {
|
||||||
*signatureInfo
|
*signatureInfo
|
||||||
|
@ -98,21 +74,9 @@ func (t *typeInfo) getMethod(signature *signatureInfo) *methodInfo {
|
||||||
// interfaceInfo keeps information about a Go interface type, including all
|
// interfaceInfo keeps information about a Go interface type, including all
|
||||||
// methods it has.
|
// methods it has.
|
||||||
type interfaceInfo struct {
|
type interfaceInfo struct {
|
||||||
name string // name with $interface suffix
|
name string // "tinygo-methods" attribute
|
||||||
methodSet llvm.Value // global which this interfaceInfo describes
|
signatures map[string]*signatureInfo // method set
|
||||||
signatures []*signatureInfo // method set
|
types []*typeInfo // types this interface implements
|
||||||
types []*typeInfo // types this interface implements
|
|
||||||
assertFunc llvm.Value // runtime.interfaceImplements replacement
|
|
||||||
methodFuncs map[*signatureInfo]llvm.Value // runtime.interfaceMethod replacements for each signature
|
|
||||||
}
|
|
||||||
|
|
||||||
// id removes the $interface suffix from the name and returns the clean
|
|
||||||
// interface name including import path.
|
|
||||||
func (itf *interfaceInfo) id() string {
|
|
||||||
if !strings.HasSuffix(itf.name, "$interface") {
|
|
||||||
panic("interface type does not have $interface suffix: " + itf.name)
|
|
||||||
}
|
|
||||||
return itf.name[:len(itf.name)-len("$interface")]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// lowerInterfacesPass keeps state related to the interface lowering pass. The
|
// lowerInterfacesPass keeps state related to the interface lowering pass. The
|
||||||
|
@ -172,25 +136,26 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find all interface method calls.
|
// Find all interface type asserts and interface method thunks.
|
||||||
interfaceMethod := p.mod.NamedFunction("runtime.interfaceMethod")
|
var interfaceAssertFunctions []llvm.Value
|
||||||
interfaceMethodUses := getUses(interfaceMethod)
|
var interfaceInvokeFunctions []llvm.Value
|
||||||
for _, use := range interfaceMethodUses {
|
for fn := p.mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
methodSet := use.Operand(1).Operand(0)
|
methodsAttr := fn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||||
name := methodSet.Name()
|
if methodsAttr.IsNil() {
|
||||||
if _, ok := p.interfaces[name]; !ok {
|
continue
|
||||||
p.addInterface(methodSet)
|
|
||||||
}
|
}
|
||||||
}
|
if !hasUses(fn) {
|
||||||
|
// Don't bother defining this function.
|
||||||
// Find all interface type asserts.
|
continue
|
||||||
interfaceImplements := p.mod.NamedFunction("runtime.interfaceImplements")
|
}
|
||||||
interfaceImplementsUses := getUses(interfaceImplements)
|
p.addInterface(methodsAttr.GetStringValue())
|
||||||
for _, use := range interfaceImplementsUses {
|
invokeAttr := fn.GetStringAttributeAtIndex(-1, "tinygo-invoke")
|
||||||
methodSet := use.Operand(1).Operand(0)
|
if invokeAttr.IsNil() {
|
||||||
name := methodSet.Name()
|
// Type assert.
|
||||||
if _, ok := p.interfaces[name]; !ok {
|
interfaceAssertFunctions = append(interfaceAssertFunctions, fn)
|
||||||
p.addInterface(methodSet)
|
} else {
|
||||||
|
// Interface invoke.
|
||||||
|
interfaceInvokeFunctions = append(interfaceInvokeFunctions, fn)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,63 +219,20 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace all interface methods with their uses, if possible.
|
// Define all interface invoke thunks.
|
||||||
for _, use := range interfaceMethodUses {
|
for _, fn := range interfaceInvokeFunctions {
|
||||||
typecode := use.Operand(0)
|
methodsAttr := fn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||||
signature := p.signatures[use.Operand(2).Name()]
|
invokeAttr := fn.GetStringAttributeAtIndex(-1, "tinygo-invoke")
|
||||||
|
itf := p.interfaces[methodsAttr.GetStringValue()]
|
||||||
methodSet := use.Operand(1).Operand(0) // global variable
|
signature := itf.signatures[invokeAttr.GetStringValue()]
|
||||||
itf := p.interfaces[methodSet.Name()]
|
p.defineInterfaceMethodFunc(fn, itf, signature)
|
||||||
|
|
||||||
// Delegate calling the right function to a special wrapper function.
|
|
||||||
inttoptrs := getUses(use)
|
|
||||||
if len(inttoptrs) != 1 || inttoptrs[0].IsAIntToPtrInst().IsNil() {
|
|
||||||
return errorAt(use, "internal error: expected exactly one inttoptr use of runtime.interfaceMethod")
|
|
||||||
}
|
|
||||||
inttoptr := inttoptrs[0]
|
|
||||||
calls := getUses(inttoptr)
|
|
||||||
for _, call := range calls {
|
|
||||||
// Set up parameters for the call. First copy the regular params...
|
|
||||||
params := make([]llvm.Value, call.OperandsCount())
|
|
||||||
paramTypes := make([]llvm.Type, len(params))
|
|
||||||
for i := 0; i < len(params)-1; i++ {
|
|
||||||
params[i] = call.Operand(i)
|
|
||||||
paramTypes[i] = params[i].Type()
|
|
||||||
}
|
|
||||||
// then add the typecode to the end of the list.
|
|
||||||
params[len(params)-1] = typecode
|
|
||||||
paramTypes[len(params)-1] = p.uintptrType
|
|
||||||
|
|
||||||
// Create a function that redirects the call to the destination
|
|
||||||
// call, after selecting the right concrete type.
|
|
||||||
redirector := p.getInterfaceMethodFunc(itf, signature, call.Type(), paramTypes)
|
|
||||||
|
|
||||||
// Replace the old lookup/inttoptr/call with the new call.
|
|
||||||
p.builder.SetInsertPointBefore(call)
|
|
||||||
retval := p.builder.CreateCall(redirector, append(params, llvm.ConstNull(llvm.PointerType(p.ctx.Int8Type(), 0))), "")
|
|
||||||
if retval.Type().TypeKind() != llvm.VoidTypeKind {
|
|
||||||
call.ReplaceAllUsesWith(retval)
|
|
||||||
}
|
|
||||||
call.EraseFromParentAsInstruction()
|
|
||||||
}
|
|
||||||
inttoptr.EraseFromParentAsInstruction()
|
|
||||||
use.EraseFromParentAsInstruction()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace all typeasserts on interface types with matches on their concrete
|
// Define all interface type assert functions.
|
||||||
// types, if possible.
|
for _, fn := range interfaceAssertFunctions {
|
||||||
for _, use := range interfaceImplementsUses {
|
methodsAttr := fn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||||
actualType := use.Operand(0)
|
itf := p.interfaces[methodsAttr.GetStringValue()]
|
||||||
|
p.defineInterfaceImplementsFunc(fn, itf)
|
||||||
methodSet := use.Operand(1).Operand(0) // global variable
|
|
||||||
itf := p.interfaces[methodSet.Name()]
|
|
||||||
// Create a function that does a type switch on all available types
|
|
||||||
// that implement this interface.
|
|
||||||
fn := p.getInterfaceImplementsFunc(itf)
|
|
||||||
p.builder.SetInsertPointBefore(use)
|
|
||||||
commaOk := p.builder.CreateCall(fn, []llvm.Value{actualType}, "typeassert.ok")
|
|
||||||
use.ReplaceAllUsesWith(commaOk)
|
|
||||||
use.EraseFromParentAsInstruction()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Replace each type assert with an actual type comparison or (if the type
|
// Replace each type assert with an actual type comparison or (if the type
|
||||||
|
@ -337,11 +259,14 @@ func (p *lowerInterfacesPass) run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove all method sets, which are now unnecessary and inhibit later
|
// Remove all method sets, which are now unnecessary and inhibit later
|
||||||
// optimizations if they are left in place.
|
// optimizations if they are left in place. Also remove references to the
|
||||||
|
// interface type assert functions just to be sure.
|
||||||
|
zeroUintptr := llvm.ConstNull(p.uintptrType)
|
||||||
for _, t := range p.types {
|
for _, t := range p.types {
|
||||||
initializer := t.typecode.Initializer()
|
initializer := t.typecode.Initializer()
|
||||||
methodSet := llvm.ConstExtractValue(initializer, []uint32{2})
|
methodSet := llvm.ConstExtractValue(initializer, []uint32{2})
|
||||||
initializer = llvm.ConstInsertValue(initializer, llvm.ConstNull(methodSet.Type()), []uint32{2})
|
initializer = llvm.ConstInsertValue(initializer, llvm.ConstNull(methodSet.Type()), []uint32{2})
|
||||||
|
initializer = llvm.ConstInsertValue(initializer, zeroUintptr, []uint32{4})
|
||||||
t.typecode.SetInitializer(initializer)
|
t.typecode.SetInitializer(initializer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -366,7 +291,7 @@ func (p *lowerInterfacesPass) addTypeMethods(t *typeInfo, methodSet llvm.Value)
|
||||||
signatureGlobal := llvm.ConstExtractValue(methodData, []uint32{0})
|
signatureGlobal := llvm.ConstExtractValue(methodData, []uint32{0})
|
||||||
signatureName := signatureGlobal.Name()
|
signatureName := signatureGlobal.Name()
|
||||||
function := llvm.ConstExtractValue(methodData, []uint32{1}).Operand(0)
|
function := llvm.ConstExtractValue(methodData, []uint32{1}).Operand(0)
|
||||||
signature := p.getSignature(signatureName, signatureGlobal)
|
signature := p.getSignature(signatureName)
|
||||||
method := &methodInfo{
|
method := &methodInfo{
|
||||||
function: function,
|
function: function,
|
||||||
signatureInfo: signature,
|
signatureInfo: signature,
|
||||||
|
@ -378,53 +303,43 @@ func (p *lowerInterfacesPass) addTypeMethods(t *typeInfo, methodSet llvm.Value)
|
||||||
|
|
||||||
// addInterface reads information about an interface, which is the
|
// addInterface reads information about an interface, which is the
|
||||||
// fully-qualified name and the signatures of all methods it has.
|
// fully-qualified name and the signatures of all methods it has.
|
||||||
func (p *lowerInterfacesPass) addInterface(methodSet llvm.Value) {
|
func (p *lowerInterfacesPass) addInterface(methodsString string) {
|
||||||
name := methodSet.Name()
|
if _, ok := p.interfaces[methodsString]; ok {
|
||||||
t := &interfaceInfo{
|
return
|
||||||
name: name,
|
|
||||||
methodSet: methodSet,
|
|
||||||
}
|
}
|
||||||
p.interfaces[name] = t
|
t := &interfaceInfo{
|
||||||
methodSet = methodSet.Initializer() // get global value from getelementptr
|
name: methodsString,
|
||||||
for i := 0; i < methodSet.Type().ArrayLength(); i++ {
|
signatures: make(map[string]*signatureInfo),
|
||||||
signatureGlobal := llvm.ConstExtractValue(methodSet, []uint32{uint32(i)})
|
}
|
||||||
signatureName := signatureGlobal.Name()
|
p.interfaces[methodsString] = t
|
||||||
signature := p.getSignature(signatureName, signatureGlobal)
|
for _, method := range strings.Split(methodsString, "; ") {
|
||||||
|
signature := p.getSignature(method)
|
||||||
signature.interfaces = append(signature.interfaces, t)
|
signature.interfaces = append(signature.interfaces, t)
|
||||||
t.signatures = append(t.signatures, signature)
|
t.signatures[method] = signature
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getSignature returns a new *signatureInfo, creating it if it doesn't already
|
// getSignature returns a new *signatureInfo, creating it if it doesn't already
|
||||||
// exist.
|
// exist.
|
||||||
func (p *lowerInterfacesPass) getSignature(name string, global llvm.Value) *signatureInfo {
|
func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
|
||||||
if _, ok := p.signatures[name]; !ok {
|
if _, ok := p.signatures[name]; !ok {
|
||||||
p.signatures[name] = &signatureInfo{
|
p.signatures[name] = &signatureInfo{
|
||||||
name: name,
|
name: name,
|
||||||
global: global,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return p.signatures[name]
|
return p.signatures[name]
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInterfaceImplementsFunc returns a function that checks whether a given
|
// defineInterfaceImplementsFunc defines the interface type assert function. It
|
||||||
// interface type implements a given interface, by checking all possible types
|
// checks whether the given interface type (passed as an argument) is one of the
|
||||||
// that implement this interface.
|
// types it implements.
|
||||||
//
|
//
|
||||||
// The type match is implemented using an if/else chain over all possible types.
|
// The type match is implemented using an if/else chain over all possible types.
|
||||||
// This if/else chain is easily converted to a big switch over all possible
|
// This if/else chain is easily converted to a big switch over all possible
|
||||||
// types by the LLVM simplifycfg pass.
|
// types by the LLVM simplifycfg pass.
|
||||||
func (p *lowerInterfacesPass) getInterfaceImplementsFunc(itf *interfaceInfo) llvm.Value {
|
func (p *lowerInterfacesPass) defineInterfaceImplementsFunc(fn llvm.Value, itf *interfaceInfo) {
|
||||||
if !itf.assertFunc.IsNil() {
|
|
||||||
return itf.assertFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the function and function signature.
|
// Create the function and function signature.
|
||||||
// TODO: debug info
|
// TODO: debug info
|
||||||
fnName := itf.id() + "$typeassert"
|
|
||||||
fnType := llvm.FunctionType(p.ctx.Int1Type(), []llvm.Type{p.uintptrType}, false)
|
|
||||||
fn := llvm.AddFunction(p.mod, fnName, fnType)
|
|
||||||
itf.assertFunc = fn
|
|
||||||
fn.Param(0).SetName("actualType")
|
fn.Param(0).SetName("actualType")
|
||||||
fn.SetLinkage(llvm.InternalLinkage)
|
fn.SetLinkage(llvm.InternalLinkage)
|
||||||
fn.SetUnnamedAddr(true)
|
fn.SetUnnamedAddr(true)
|
||||||
|
@ -456,33 +371,21 @@ func (p *lowerInterfacesPass) getInterfaceImplementsFunc(itf *interfaceInfo) llv
|
||||||
// Fill 'then' block (type assert was successful).
|
// Fill 'then' block (type assert was successful).
|
||||||
p.builder.SetInsertPointAtEnd(thenBlock)
|
p.builder.SetInsertPointAtEnd(thenBlock)
|
||||||
p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 1, false))
|
p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 1, false))
|
||||||
|
|
||||||
return itf.assertFunc
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// getInterfaceMethodFunc returns a thunk for calling a method on an interface.
|
// defineInterfaceMethodFunc defines this thunk by calling the concrete method
|
||||||
|
// of the type that implements this interface.
|
||||||
//
|
//
|
||||||
// Matching the actual type is implemented using an if/else chain over all
|
// Matching the actual type is implemented using an if/else chain over all
|
||||||
// possible types. This is later converted to a switch statement by the LLVM
|
// possible types. This is later converted to a switch statement by the LLVM
|
||||||
// simplifycfg pass.
|
// simplifycfg pass.
|
||||||
func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signature *signatureInfo, returnType llvm.Type, paramTypes []llvm.Type) llvm.Value {
|
func (p *lowerInterfacesPass) defineInterfaceMethodFunc(fn llvm.Value, itf *interfaceInfo, signature *signatureInfo) {
|
||||||
if fn, ok := itf.methodFuncs[signature]; ok {
|
parentHandle := fn.LastParam()
|
||||||
// This function has already been created.
|
context := llvm.PrevParam(parentHandle)
|
||||||
return fn
|
actualType := llvm.PrevParam(context)
|
||||||
}
|
context.SetName("context")
|
||||||
if itf.methodFuncs == nil {
|
actualType.SetName("actualType")
|
||||||
// initialize the above map
|
parentHandle.SetName("parentHandle")
|
||||||
itf.methodFuncs = make(map[*signatureInfo]llvm.Value)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Construct the function name, which is of the form:
|
|
||||||
// (main.Stringer).String
|
|
||||||
fnName := "(" + itf.id() + ")." + signature.methodName()
|
|
||||||
fnType := llvm.FunctionType(returnType, append(paramTypes, llvm.PointerType(p.ctx.Int8Type(), 0)), false)
|
|
||||||
fn := llvm.AddFunction(p.mod, fnName, fnType)
|
|
||||||
llvm.PrevParam(fn.LastParam()).SetName("actualType")
|
|
||||||
fn.LastParam().SetName("parentHandle")
|
|
||||||
itf.methodFuncs[signature] = fn
|
|
||||||
fn.SetLinkage(llvm.InternalLinkage)
|
fn.SetLinkage(llvm.InternalLinkage)
|
||||||
fn.SetUnnamedAddr(true)
|
fn.SetUnnamedAddr(true)
|
||||||
if p.sizeLevel >= 2 {
|
if p.sizeLevel >= 2 {
|
||||||
|
@ -494,17 +397,20 @@ func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signatu
|
||||||
// Collect the params that will be passed to the functions to call.
|
// Collect the params that will be passed to the functions to call.
|
||||||
// These params exclude the receiver (which may actually consist of multiple
|
// These params exclude the receiver (which may actually consist of multiple
|
||||||
// parts).
|
// parts).
|
||||||
params := make([]llvm.Value, fn.ParamsCount()-3)
|
params := make([]llvm.Value, fn.ParamsCount()-4)
|
||||||
for i := range params {
|
for i := range params {
|
||||||
params[i] = fn.Param(i + 1)
|
params[i] = fn.Param(i + 1)
|
||||||
}
|
}
|
||||||
|
params = append(params,
|
||||||
|
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
|
||||||
|
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
|
||||||
|
)
|
||||||
|
|
||||||
// Start chain in the entry block.
|
// Start chain in the entry block.
|
||||||
entry := p.ctx.AddBasicBlock(fn, "entry")
|
entry := p.ctx.AddBasicBlock(fn, "entry")
|
||||||
p.builder.SetInsertPointAtEnd(entry)
|
p.builder.SetInsertPointAtEnd(entry)
|
||||||
|
|
||||||
// Define all possible functions that can be called.
|
// Define all possible functions that can be called.
|
||||||
actualType := llvm.PrevParam(fn.LastParam())
|
|
||||||
for _, typ := range itf.types {
|
for _, typ := range itf.types {
|
||||||
// Create type check (if/else).
|
// Create type check (if/else).
|
||||||
bb := p.ctx.AddBasicBlock(fn, typ.name)
|
bb := p.ctx.AddBasicBlock(fn, typ.name)
|
||||||
|
@ -562,6 +468,4 @@ func (p *lowerInterfacesPass) getInterfaceMethodFunc(itf *interfaceInfo, signatu
|
||||||
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
|
llvm.Undef(llvm.PointerType(p.ctx.Int8Type(), 0)),
|
||||||
}, "")
|
}, "")
|
||||||
p.builder.CreateUnreachable()
|
p.builder.CreateUnreachable()
|
||||||
|
|
||||||
return fn
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -109,77 +109,64 @@ func OptimizeReflectImplements(mod llvm.Module) {
|
||||||
if implementsSignature.IsNil() {
|
if implementsSignature.IsNil() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
interfaceMethod := mod.NamedFunction("runtime.interfaceMethod")
|
|
||||||
if interfaceMethod.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
interfaceImplements := mod.NamedFunction("runtime.interfaceImplements")
|
|
||||||
if interfaceImplements.IsNil() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
builder := mod.Context().NewBuilder()
|
builder := mod.Context().NewBuilder()
|
||||||
defer builder.Dispose()
|
defer builder.Dispose()
|
||||||
|
|
||||||
// Get a few useful object for use later.
|
// Get a few useful object for use later.
|
||||||
zero := llvm.ConstInt(mod.Context().Int32Type(), 0, false)
|
|
||||||
uintptrType := mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
uintptrType := mod.Context().IntType(llvm.NewTargetData(mod.DataLayout()).PointerSize() * 8)
|
||||||
|
|
||||||
defer llvm.VerifyModule(mod, llvm.PrintMessageAction)
|
// Look up the (reflect.Value).Implements() method.
|
||||||
|
var implementsFunc llvm.Value
|
||||||
for _, use := range getUses(implementsSignature) {
|
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||||
if use.IsACallInst().IsNil() {
|
attr := fn.GetStringAttributeAtIndex(-1, "tinygo-invoke")
|
||||||
|
if attr.IsNil() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if use.CalledValue() != interfaceMethod {
|
if attr.GetStringValue() == "reflect/methods.Implements(reflect.Type) bool" {
|
||||||
continue
|
implementsFunc = fn
|
||||||
}
|
break
|
||||||
for _, bitcast := range getUses(use) {
|
|
||||||
if !bitcast.IsABitCastInst().IsNil() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
for _, call := range getUses(bitcast) {
|
|
||||||
// Try to get the interface method set.
|
|
||||||
interfaceTypeBitCast := call.Operand(2)
|
|
||||||
if interfaceTypeBitCast.IsAConstantExpr().IsNil() || interfaceTypeBitCast.Opcode() != llvm.BitCast {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
interfaceType := interfaceTypeBitCast.Operand(0)
|
|
||||||
if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
|
|
||||||
// Get the underlying type.
|
|
||||||
interfaceType = llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{0})
|
|
||||||
}
|
|
||||||
if !strings.HasPrefix(interfaceType.Name(), "reflect/types.type:interface:") {
|
|
||||||
// This is an error. The Type passed to Implements should be
|
|
||||||
// of interface type. Ignore it here (don't report it), it
|
|
||||||
// will be reported at runtime.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if interfaceType.IsAGlobalVariable().IsNil() {
|
|
||||||
// Interface is unknown at compile time. This can't be
|
|
||||||
// optimized.
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// Get the 'references' field of the runtime.typecodeID, which
|
|
||||||
// is a bitcast of an interface method set.
|
|
||||||
interfaceMethodSet := llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{0}).Operand(0)
|
|
||||||
|
|
||||||
builder.SetInsertPointBefore(call)
|
|
||||||
implements := builder.CreateCall(interfaceImplements, []llvm.Value{
|
|
||||||
builder.CreatePtrToInt(call.Operand(0), uintptrType, ""), // typecode to check
|
|
||||||
llvm.ConstGEP(interfaceMethodSet, []llvm.Value{zero, zero}), // method set to check against
|
|
||||||
llvm.Undef(llvm.PointerType(mod.Context().Int8Type(), 0)),
|
|
||||||
llvm.Undef(llvm.PointerType(mod.Context().Int8Type(), 0)),
|
|
||||||
}, "")
|
|
||||||
call.ReplaceAllUsesWith(implements)
|
|
||||||
call.EraseFromParentAsInstruction()
|
|
||||||
}
|
|
||||||
if !hasUses(bitcast) {
|
|
||||||
bitcast.EraseFromParentAsInstruction()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !hasUses(use) {
|
|
||||||
use.EraseFromParentAsInstruction()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if implementsFunc.IsNil() {
|
||||||
|
// Doesn't exist in the program, so nothing to do.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, call := range getUses(implementsFunc) {
|
||||||
|
if call.IsACallInst().IsNil() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
interfaceTypeBitCast := call.Operand(2)
|
||||||
|
if interfaceTypeBitCast.IsAConstantExpr().IsNil() || interfaceTypeBitCast.Opcode() != llvm.BitCast {
|
||||||
|
// The asserted interface is not constant, so can't optimize this
|
||||||
|
// code.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
interfaceType := interfaceTypeBitCast.Operand(0)
|
||||||
|
if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
|
||||||
|
// Get the underlying type.
|
||||||
|
interfaceType = llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{0})
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(interfaceType.Name(), "reflect/types.type:interface:") {
|
||||||
|
// This is an error. The Type passed to Implements should be of
|
||||||
|
// interface type. Ignore it here (don't report it), it will be
|
||||||
|
// reported at runtime.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if interfaceType.IsAGlobalVariable().IsNil() {
|
||||||
|
// Interface is unknown at compile time. This can't be optimized.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
typeAssertFunction := llvm.ConstExtractValue(interfaceType.Initializer(), []uint32{4}).Operand(0)
|
||||||
|
|
||||||
|
// Replace Implements call with the type assert call.
|
||||||
|
builder.SetInsertPointBefore(call)
|
||||||
|
implements := builder.CreateCall(typeAssertFunction, []llvm.Value{
|
||||||
|
builder.CreatePtrToInt(call.Operand(0), uintptrType, ""), // typecode to check
|
||||||
|
}, "")
|
||||||
|
call.ReplaceAllUsesWith(implements)
|
||||||
|
call.EraseFromParentAsInstruction()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
34
transform/testdata/interface.ll
предоставленный
34
transform/testdata/interface.ll
предоставленный
|
@ -1,7 +1,7 @@
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
|
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
target triple = "armv7m-none-eabi"
|
target triple = "armv7m-none-eabi"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
|
||||||
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
|
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
|
||||||
|
@ -9,15 +9,11 @@ target triple = "armv7m-none-eabi"
|
||||||
@"reflect/types.typeid:basic:int16" = external constant i8
|
@"reflect/types.typeid:basic:int16" = external constant i8
|
||||||
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
|
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
|
||||||
@"reflect/methods.NeverImplementedMethod()" = linkonce_odr constant i8 0
|
@"reflect/methods.NeverImplementedMethod()" = linkonce_odr constant i8 0
|
||||||
@"Unmatched$interface" = private constant [1 x i8*] [i8* @"reflect/methods.NeverImplementedMethod()"]
|
|
||||||
@"reflect/methods.Double() int" = linkonce_odr constant i8 0
|
@"reflect/methods.Double() int" = linkonce_odr constant i8 0
|
||||||
@"Doubler$interface" = private constant [1 x i8*] [i8* @"reflect/methods.Double() int"]
|
@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"reflect/methods.Double() int", i32 ptrtoint (i32 (i8*, i8*, i8*)* @"(Number).Double$invoke" to i32) }]
|
||||||
@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"reflect/methods.Double() int", i32 ptrtoint (i32 (i8*, i8*)* @"(Number).Double$invoke" to i32) }]
|
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([1 x %runtime.interfaceMethodInfo], [1 x %runtime.interfaceMethodInfo]* @"Number$methodset", i32 0, i32 0), %runtime.typecodeID* null, i32 0 }
|
||||||
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([1 x %runtime.interfaceMethodInfo], [1 x %runtime.interfaceMethodInfo]* @"Number$methodset", i32 0, i32 0) }
|
|
||||||
|
|
||||||
declare i1 @runtime.interfaceImplements(i32, i8**)
|
|
||||||
declare i1 @runtime.typeAssert(i32, i8*)
|
declare i1 @runtime.typeAssert(i32, i8*)
|
||||||
declare i32 @runtime.interfaceMethod(i32, i8**, i8*)
|
|
||||||
declare void @runtime.printuint8(i8)
|
declare void @runtime.printuint8(i8)
|
||||||
declare void @runtime.printint16(i16)
|
declare void @runtime.printint16(i16)
|
||||||
declare void @runtime.printint32(i32)
|
declare void @runtime.printint32(i32)
|
||||||
|
@ -34,7 +30,7 @@ define void @printInterfaces() {
|
||||||
}
|
}
|
||||||
|
|
||||||
define void @printInterface(i32 %typecode, i8* %value) {
|
define void @printInterface(i32 %typecode, i8* %value) {
|
||||||
%isUnmatched = call i1 @runtime.interfaceImplements(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Unmatched$interface", i32 0, i32 0))
|
%isUnmatched = call i1 @Unmatched$typeassert(i32 %typecode)
|
||||||
br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
|
br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
|
||||||
|
|
||||||
typeswitch.Unmatched:
|
typeswitch.Unmatched:
|
||||||
|
@ -44,13 +40,11 @@ typeswitch.Unmatched:
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
typeswitch.notUnmatched:
|
typeswitch.notUnmatched:
|
||||||
%isDoubler = call i1 @runtime.interfaceImplements(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Doubler$interface", i32 0, i32 0))
|
%isDoubler = call i1 @Doubler$typeassert(i32 %typecode)
|
||||||
br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
||||||
|
|
||||||
typeswitch.Doubler:
|
typeswitch.Doubler:
|
||||||
%doubler.func = call i32 @runtime.interfaceMethod(i32 %typecode, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"Doubler$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Double() int")
|
%doubler.result = call i32 @"Doubler.Double$invoke"(i8* %value, i32 %typecode, i8* undef, i8* undef)
|
||||||
%doubler.func.cast = inttoptr i32 %doubler.func to i32 (i8*, i8*)*
|
|
||||||
%doubler.result = call i32 %doubler.func.cast(i8* %value, i8* null)
|
|
||||||
call void @runtime.printint32(i32 %doubler.result)
|
call void @runtime.printint32(i32 %doubler.result)
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
|
@ -79,13 +73,23 @@ typeswitch.notInt16:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define i32 @"(Number).Double"(i32 %receiver, i8* %parentHandle) {
|
define i32 @"(Number).Double"(i32 %receiver, i8* %context, i8* %parentHandle) {
|
||||||
%ret = mul i32 %receiver, 2
|
%ret = mul i32 %receiver, 2
|
||||||
ret i32 %ret
|
ret i32 %ret
|
||||||
}
|
}
|
||||||
|
|
||||||
define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) {
|
define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %context, i8* %parentHandle) {
|
||||||
%receiver = ptrtoint i8* %receiverPtr to i32
|
%receiver = ptrtoint i8* %receiverPtr to i32
|
||||||
%ret = call i32 @"(Number).Double"(i32 %receiver, i8* null)
|
%ret = call i32 @"(Number).Double"(i32 %receiver, i8* undef, i8* null)
|
||||||
ret i32 %ret
|
ret i32 %ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i32 @"Doubler.Double$invoke"(i8* %receiver, i32 %typecode, i8* %context, i8* %parentHandle) #0
|
||||||
|
|
||||||
|
declare i1 @Doubler$typeassert(i32 %typecode) #1
|
||||||
|
|
||||||
|
declare i1 @Unmatched$typeassert(i32 %typecode) #2
|
||||||
|
|
||||||
|
attributes #0 = { "tinygo-invoke"="reflect/methods.Double() int" "tinygo-methods"="reflect/methods.Double() int" }
|
||||||
|
attributes #1 = { "tinygo-methods"="reflect/methods.Double() int" }
|
||||||
|
attributes #2 = { "tinygo-methods"="reflect/methods.NeverImplementedMethod()" }
|
||||||
|
|
40
transform/testdata/interface.out.ll
предоставленный
40
transform/testdata/interface.out.ll
предоставленный
|
@ -1,12 +1,12 @@
|
||||||
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
|
target datalayout = "e-m:e-p:32:32-i64:64-v128:64:128-a:0:32-n32-S64"
|
||||||
target triple = "armv7m-none-eabi"
|
target triple = "armv7m-none-eabi"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
|
||||||
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
|
@"reflect/types.type:basic:uint8" = private constant %runtime.typecodeID zeroinitializer
|
||||||
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
|
@"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer
|
||||||
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null }
|
@"reflect/types.type:named:Number" = private constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:int", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 0 }
|
||||||
|
|
||||||
declare void @runtime.printuint8(i8)
|
declare void @runtime.printuint8(i8)
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ define void @printInterfaces() {
|
||||||
}
|
}
|
||||||
|
|
||||||
define void @printInterface(i32 %typecode, i8* %value) {
|
define void @printInterface(i32 %typecode, i8* %value) {
|
||||||
%typeassert.ok1 = call i1 @"Unmatched$typeassert"(i32 %typecode)
|
%isUnmatched = call i1 @"Unmatched$typeassert"(i32 %typecode)
|
||||||
br i1 %typeassert.ok1, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
|
br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
|
||||||
|
|
||||||
typeswitch.Unmatched: ; preds = %0
|
typeswitch.Unmatched: ; preds = %0
|
||||||
%unmatched = ptrtoint i8* %value to i32
|
%unmatched = ptrtoint i8* %value to i32
|
||||||
|
@ -38,17 +38,17 @@ typeswitch.Unmatched: ; preds = %0
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
typeswitch.notUnmatched: ; preds = %0
|
typeswitch.notUnmatched: ; preds = %0
|
||||||
%typeassert.ok = call i1 @"Doubler$typeassert"(i32 %typecode)
|
%isDoubler = call i1 @"Doubler$typeassert"(i32 %typecode)
|
||||||
br i1 %typeassert.ok, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
||||||
|
|
||||||
typeswitch.Doubler: ; preds = %typeswitch.notUnmatched
|
typeswitch.Doubler: ; preds = %typeswitch.notUnmatched
|
||||||
%1 = call i32 @"(Doubler).Double"(i8* %value, i8* null, i32 %typecode, i8* null)
|
%doubler.result = call i32 @"Doubler.Double$invoke"(i8* %value, i32 %typecode, i8* undef, i8* undef)
|
||||||
call void @runtime.printint32(i32 %1)
|
call void @runtime.printint32(i32 %doubler.result)
|
||||||
ret void
|
ret void
|
||||||
|
|
||||||
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
|
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
|
||||||
%typeassert.ok2 = icmp eq i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:uint8" to i32), %typecode
|
%typeassert.ok = icmp eq i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:basic:uint8" to i32), %typecode
|
||||||
br i1 %typeassert.ok2, label %typeswitch.byte, label %typeswitch.notByte
|
br i1 %typeassert.ok, label %typeswitch.byte, label %typeswitch.notByte
|
||||||
|
|
||||||
typeswitch.byte: ; preds = %typeswitch.notDoubler
|
typeswitch.byte: ; preds = %typeswitch.notDoubler
|
||||||
%byte = ptrtoint i8* %value to i8
|
%byte = ptrtoint i8* %value to i8
|
||||||
|
@ -69,32 +69,32 @@ typeswitch.notInt16: ; preds = %typeswitch.notByte
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
define i32 @"(Number).Double"(i32 %receiver, i8* %parentHandle) {
|
define i32 @"(Number).Double"(i32 %receiver, i8* %context, i8* %parentHandle) {
|
||||||
%ret = mul i32 %receiver, 2
|
%ret = mul i32 %receiver, 2
|
||||||
ret i32 %ret
|
ret i32 %ret
|
||||||
}
|
}
|
||||||
|
|
||||||
define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %parentHandle) {
|
define i32 @"(Number).Double$invoke"(i8* %receiverPtr, i8* %context, i8* %parentHandle) {
|
||||||
%receiver = ptrtoint i8* %receiverPtr to i32
|
%receiver = ptrtoint i8* %receiverPtr to i32
|
||||||
%ret = call i32 @"(Number).Double"(i32 %receiver, i8* null)
|
%ret = call i32 @"(Number).Double"(i32 %receiver, i8* undef, i8* null)
|
||||||
ret i32 %ret
|
ret i32 %ret
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i32 @"(Doubler).Double"(i8* %0, i8* %1, i32 %actualType, i8* %parentHandle) unnamed_addr {
|
define internal i32 @"Doubler.Double$invoke"(i8* %receiver, i32 %actualType, i8* %context, i8* %parentHandle) unnamed_addr #0 {
|
||||||
entry:
|
entry:
|
||||||
%"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
|
%"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
|
||||||
br i1 %"named:Number.icmp", label %"named:Number", label %"named:Number.next"
|
br i1 %"named:Number.icmp", label %"named:Number", label %"named:Number.next"
|
||||||
|
|
||||||
"named:Number": ; preds = %entry
|
"named:Number": ; preds = %entry
|
||||||
%2 = call i32 @"(Number).Double$invoke"(i8* %0, i8* %1)
|
%0 = call i32 @"(Number).Double$invoke"(i8* %receiver, i8* undef, i8* undef)
|
||||||
ret i32 %2
|
ret i32 %0
|
||||||
|
|
||||||
"named:Number.next": ; preds = %entry
|
"named:Number.next": ; preds = %entry
|
||||||
call void @runtime.nilPanic(i8* undef, i8* undef)
|
call void @runtime.nilPanic(i8* undef, i8* undef)
|
||||||
unreachable
|
unreachable
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr {
|
define internal i1 @"Doubler$typeassert"(i32 %actualType) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
|
%"named:Number.icmp" = icmp eq i32 %actualType, ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:Number" to i32)
|
||||||
br i1 %"named:Number.icmp", label %then, label %"named:Number.next"
|
br i1 %"named:Number.icmp", label %then, label %"named:Number.next"
|
||||||
|
@ -106,10 +106,14 @@ then: ; preds = %entry
|
||||||
ret i1 false
|
ret i1 false
|
||||||
}
|
}
|
||||||
|
|
||||||
define internal i1 @"Unmatched$typeassert"(i32 %actualType) unnamed_addr {
|
define internal i1 @"Unmatched$typeassert"(i32 %actualType) unnamed_addr #2 {
|
||||||
entry:
|
entry:
|
||||||
ret i1 false
|
ret i1 false
|
||||||
|
|
||||||
then: ; No predecessors!
|
then: ; No predecessors!
|
||||||
ret i1 true
|
ret i1 true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
attributes #0 = { "tinygo-invoke"="reflect/methods.Double() int" "tinygo-methods"="reflect/methods.Double() int" }
|
||||||
|
attributes #1 = { "tinygo-methods"="reflect/methods.Double() int" }
|
||||||
|
attributes #2 = { "tinygo-methods"="reflect/methods.NeverImplementedMethod()" }
|
||||||
|
|
26
transform/testdata/reflect-implements.ll
предоставленный
26
transform/testdata/reflect-implements.ll
предоставленный
|
@ -1,24 +1,20 @@
|
||||||
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 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"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
|
||||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
|
||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
|
||||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0
|
@"reflect/methods.Error() string" = linkonce_odr constant i8 0
|
||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
||||||
@"reflect/methods.Align() int" = linkonce_odr constant i8 0
|
@"reflect/methods.Align() int" = linkonce_odr constant i8 0
|
||||||
@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0
|
@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0
|
||||||
@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"]
|
@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"]
|
||||||
@"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0) }
|
@"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0), %runtime.typecodeID* null, i32 0 }
|
||||||
@"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer
|
@"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer
|
||||||
@"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
@"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
||||||
|
|
||||||
declare i1 @runtime.interfaceImplements(i32, i8**, i8*, i8*)
|
|
||||||
|
|
||||||
declare i32 @runtime.interfaceMethod(i32, i8**, i8*, i8*, i8*)
|
|
||||||
|
|
||||||
; var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
; var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||||
; func isError(typ reflect.Type) bool {
|
; func isError(typ reflect.Type) bool {
|
||||||
; return typ.Implements(errorType)
|
; return typ.Implements(errorType)
|
||||||
|
@ -28,9 +24,7 @@ declare i32 @runtime.interfaceMethod(i32, i8**, i8*, i8*, i8*)
|
||||||
; known at compile time (after the interp pass has run).
|
; known at compile time (after the interp pass has run).
|
||||||
define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context, i8* %parentHandle) {
|
define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context, i8* %parentHandle) {
|
||||||
entry:
|
entry:
|
||||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Implements(reflect.Type) bool", i8* undef, i8* null)
|
%result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:reflect.rawType" to i32), i8* bitcast (%runtime.typecodeID* @"reflect/types.type:named:error" to i8*), i32 %typ.typecode, i8* undef, i8* undef)
|
||||||
%invoke.func.cast = inttoptr i32 %invoke.func to i1 (i8*, i32, i8*, i8*, i8*)*
|
|
||||||
%result = call i1 %invoke.func.cast(i8* %typ.value, i32 ptrtoint (%runtime.typecodeID* @"reflect/types.type:named:reflect.rawType" to i32), i8* bitcast (%runtime.typecodeID* @"reflect/types.type:named:error" to i8*), i8* undef, i8* undef)
|
|
||||||
ret i1 %result
|
ret i1 %result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,8 +35,12 @@ entry:
|
||||||
; }
|
; }
|
||||||
define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) {
|
define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) {
|
||||||
entry:
|
entry:
|
||||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Implements(reflect.Type) bool", i8* undef, i8* null)
|
%result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i32 %typ.typecode, i8* undef, i8* undef)
|
||||||
%invoke.func.cast = inttoptr i32 %invoke.func to i1 (i8*, i32, i8*, i8*, i8*)*
|
|
||||||
%result = call i1 %invoke.func.cast(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* undef, i8* undef)
|
|
||||||
ret i1 %result
|
ret i1 %result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i1 @"reflect.Type.Implements$invoke"(i8*, i32, i8*, i32, i8*, i8*) #0
|
||||||
|
declare i1 @"error.$typeassert"(i32) #1
|
||||||
|
|
||||||
|
attributes #0 = { "tinygo-invoke"="reflect/methods.Implements(reflect.Type) bool" "tinygo-methods"="reflect/methods.Align() int; reflect/methods.Implements(reflect.Type) bool" }
|
||||||
|
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
|
25
transform/testdata/reflect-implements.out.ll
предоставленный
25
transform/testdata/reflect-implements.out.ll
предоставленный
|
@ -1,35 +1,36 @@
|
||||||
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 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"
|
target triple = "i686--linux"
|
||||||
|
|
||||||
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo* }
|
%runtime.typecodeID = type { %runtime.typecodeID*, i32, %runtime.interfaceMethodInfo*, %runtime.typecodeID*, i32 }
|
||||||
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
%runtime.interfaceMethodInfo = type { i8*, i32 }
|
||||||
|
|
||||||
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null }
|
@"reflect/types.type:named:error" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:interface:{Error:func:{}{basic:string}}", i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
|
||||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null }
|
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* bitcast ([1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface" to %runtime.typecodeID*), i32 0, %runtime.interfaceMethodInfo* null, %runtime.typecodeID* null, i32 ptrtoint (i1 (i32)* @"error.$typeassert" to i32) }
|
||||||
@"reflect/methods.Error() string" = linkonce_odr constant i8 0
|
@"reflect/methods.Error() string" = linkonce_odr constant i8 0
|
||||||
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"]
|
||||||
@"reflect/methods.Align() int" = linkonce_odr constant i8 0
|
@"reflect/methods.Align() int" = linkonce_odr constant i8 0
|
||||||
@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0
|
@"reflect/methods.Implements(reflect.Type) bool" = linkonce_odr constant i8 0
|
||||||
@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"]
|
@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"reflect/methods.Align() int", i8* @"reflect/methods.Implements(reflect.Type) bool"]
|
||||||
@"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0) }
|
@"reflect/types.type:named:reflect.rawType" = linkonce_odr constant %runtime.typecodeID { %runtime.typecodeID* @"reflect/types.type:basic:uintptr", i32 0, %runtime.interfaceMethodInfo* getelementptr inbounds ([20 x %runtime.interfaceMethodInfo], [20 x %runtime.interfaceMethodInfo]* @"reflect.rawType$methodset", i32 0, i32 0), %runtime.typecodeID* null, i32 0 }
|
||||||
@"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer
|
@"reflect.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer
|
||||||
@"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
@"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer
|
||||||
|
|
||||||
declare i1 @runtime.interfaceImplements(i32, i8**, i8*, i8*)
|
|
||||||
|
|
||||||
declare i32 @runtime.interfaceMethod(i32, i8**, i8*, i8*, i8*)
|
|
||||||
|
|
||||||
define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context, i8* %parentHandle) {
|
define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context, i8* %parentHandle) {
|
||||||
entry:
|
entry:
|
||||||
%0 = ptrtoint i8* %typ.value to i32
|
%0 = ptrtoint i8* %typ.value to i32
|
||||||
%1 = call i1 @runtime.interfaceImplements(i32 %0, i8** getelementptr inbounds ([1 x i8*], [1 x i8*]* @"reflect/types.interface:interface{Error() string}$interface", i32 0, i32 0), i8* undef, i8* undef)
|
%1 = call i1 @"error.$typeassert"(i32 %0)
|
||||||
ret i1 %1
|
ret i1 %1
|
||||||
}
|
}
|
||||||
|
|
||||||
define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) {
|
define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) {
|
||||||
entry:
|
entry:
|
||||||
%invoke.func = call i32 @runtime.interfaceMethod(i32 %typ.typecode, i8** getelementptr inbounds ([2 x i8*], [2 x i8*]* @"reflect.Type$interface", i32 0, i32 0), i8* nonnull @"reflect/methods.Implements(reflect.Type) bool", i8* undef, i8* null)
|
%result = call i1 @"reflect.Type.Implements$invoke"(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i32 %typ.typecode, i8* undef, i8* undef)
|
||||||
%invoke.func.cast = inttoptr i32 %invoke.func to i1 (i8*, i32, i8*, i8*, i8*)*
|
|
||||||
%result = call i1 %invoke.func.cast(i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* undef, i8* undef)
|
|
||||||
ret i1 %result
|
ret i1 %result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare i1 @"reflect.Type.Implements$invoke"(i8*, i32, i8*, i32, i8*, i8*) #0
|
||||||
|
|
||||||
|
declare i1 @"error.$typeassert"(i32) #1
|
||||||
|
|
||||||
|
attributes #0 = { "tinygo-invoke"="reflect/methods.Implements(reflect.Type) bool" "tinygo-methods"="reflect/methods.Align() int; reflect/methods.Implements(reflect.Type) bool" }
|
||||||
|
attributes #1 = { "tinygo-methods"="reflect/methods.Error() string" }
|
||||||
|
|
|
@ -38,6 +38,12 @@ func ExternalInt64AsPtr(mod llvm.Module) error {
|
||||||
// coroutine lowering.
|
// coroutine lowering.
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
if !fn.GetStringAttributeAtIndex(-1, "tinygo-methods").IsNil() {
|
||||||
|
// These are internal functions (interface method call, interface
|
||||||
|
// type assert) that will be lowered by the interface lowering pass.
|
||||||
|
// Don't transform them.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
hasInt64 := false
|
hasInt64 := false
|
||||||
paramTypes := []llvm.Type{}
|
paramTypes := []llvm.Type{}
|
||||||
|
|
Загрузка…
Создание таблицы
Сослаться в новой задаче