mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-07-26 22:58:41 +00:00
Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 19e2d12517 |
+90
-27
@@ -10,6 +10,7 @@ import (
|
||||
"fmt"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
@@ -180,6 +181,15 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
typeFieldTypes := []*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "kind", types.Typ[types.Int8]),
|
||||
}
|
||||
var methods []*types.Func
|
||||
for i := 0; i < ms.Len(); i++ {
|
||||
methods = append(methods, ms.At(i).Obj().(*types.Func))
|
||||
}
|
||||
methodSetType := types.NewStruct([]*types.Var{
|
||||
types.NewVar(token.NoPos, nil, "length", types.Typ[types.Uintptr]),
|
||||
types.NewVar(token.NoPos, nil, "methods", types.NewArray(types.Typ[types.UnsafePointer], int64(len(methods)))),
|
||||
}, nil)
|
||||
methodSetValue := c.getMethodSetValue(methods)
|
||||
switch typ := typ.(type) {
|
||||
case *types.Basic:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
@@ -196,6 +206,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "underlying", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "pkgpath", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
types.NewVar(token.NoPos, nil, "name", types.NewArray(types.Typ[types.Int8], int64(len(pkgname)+1+len(name)+1))),
|
||||
)
|
||||
case *types.Chan:
|
||||
@@ -214,6 +225,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "numMethods", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "elementType", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
case *types.Array:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
@@ -238,12 +250,13 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
types.NewVar(token.NoPos, nil, "size", types.Typ[types.Uint32]),
|
||||
types.NewVar(token.NoPos, nil, "numFields", types.Typ[types.Uint16]),
|
||||
types.NewVar(token.NoPos, nil, "fields", types.NewArray(c.getRuntimeType("structField"), int64(typ.NumFields()))),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
case *types.Interface:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
types.NewVar(token.NoPos, nil, "methods", methodSetType),
|
||||
)
|
||||
// TODO: methods
|
||||
case *types.Signature:
|
||||
typeFieldTypes = append(typeFieldTypes,
|
||||
types.NewVar(token.NoPos, nil, "ptrTo", types.Typ[types.UnsafePointer]),
|
||||
@@ -294,6 +307,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
c.getTypeCode(types.NewPointer(typ)), // ptrTo
|
||||
c.getTypeCode(typ.Underlying()), // underlying
|
||||
pkgPathPtr, // pkgpath pointer
|
||||
methodSetValue, // methods
|
||||
c.ctx.ConstString(pkgname+"."+name+"\x00", false), // name
|
||||
}
|
||||
metabyte |= 1 << 5 // "named" flag
|
||||
@@ -323,6 +337,7 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
typeFields = []llvm.Value{
|
||||
llvm.ConstInt(c.ctx.Int16Type(), uint64(numMethods), false), // numMethods
|
||||
c.getTypeCode(typ.Elem()),
|
||||
methodSetValue, // methods
|
||||
}
|
||||
case *types.Array:
|
||||
typeFields = []llvm.Value{
|
||||
@@ -404,9 +419,12 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
|
||||
}))
|
||||
}
|
||||
typeFields = append(typeFields, llvm.ConstArray(structFieldType, fields))
|
||||
typeFields = append(typeFields, methodSetValue)
|
||||
case *types.Interface:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: methods
|
||||
typeFields = []llvm.Value{
|
||||
c.getTypeCode(types.NewPointer(typ)),
|
||||
methodSetValue,
|
||||
}
|
||||
case *types.Signature:
|
||||
typeFields = []llvm.Value{c.getTypeCode(types.NewPointer(typ))}
|
||||
// TODO: params, return values, etc
|
||||
@@ -685,7 +703,7 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
assertedType := b.getLLVMType(expr.AssertedType)
|
||||
|
||||
actualTypeNum := b.CreateExtractValue(itf, 0, "interface.type")
|
||||
commaOk := llvm.Value{}
|
||||
var commaOk llvm.Value
|
||||
|
||||
if intf, ok := expr.AssertedType.Underlying().(*types.Interface); ok {
|
||||
if intf.Empty() {
|
||||
@@ -693,17 +711,16 @@ func (b *builder) createTypeAssert(expr *ssa.TypeAssert) llvm.Value {
|
||||
// This type assertion always succeeds, so we can just set commaOk to true.
|
||||
commaOk = llvm.ConstInt(b.ctx.Int1Type(), 1, true)
|
||||
} else {
|
||||
// Type assert on interface type with methods.
|
||||
// This is a call to an interface type assert function.
|
||||
// The interface lowering pass will define this function by filling it
|
||||
// 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
|
||||
// the main Go compiler, where the runtime checks whether the type
|
||||
// implements each method of the interface. See:
|
||||
// Type assert using interface type with methods.
|
||||
// This is implemented using a runtime call, which checks that the
|
||||
// type implements each method of the interface.
|
||||
// For comparison, here is how the Go compiler does this (which is
|
||||
// very similar):
|
||||
// https://research.swtch.com/interfaces
|
||||
fn := b.getInterfaceImplementsFunc(expr.AssertedType)
|
||||
commaOk = b.CreateCall(fn.GlobalValueType(), fn, []llvm.Value{actualTypeNum}, "")
|
||||
commaOk = b.createRuntimeCall("typeImplementsMethodSet", []llvm.Value{
|
||||
actualTypeNum,
|
||||
b.getInterfaceMethodSet(intf),
|
||||
}, "")
|
||||
}
|
||||
} else {
|
||||
name, _ := getTypeCodeName(expr.AssertedType)
|
||||
@@ -780,20 +797,66 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
|
||||
return strings.Join(methods, "; ")
|
||||
}
|
||||
|
||||
// getInterfaceImplementsFunc returns a declared function that works as a type
|
||||
// switch. The interface lowering pass will define this function.
|
||||
func (c *compilerContext) getInterfaceImplementsFunc(assertedType types.Type) llvm.Value {
|
||||
s, _ := getTypeCodeName(assertedType.Underlying())
|
||||
fnName := s + ".$typeassert"
|
||||
llvmFn := c.mod.NamedFunction(fnName)
|
||||
if llvmFn.IsNil() {
|
||||
llvmFnType := llvm.FunctionType(c.ctx.Int1Type(), []llvm.Type{c.dataPtrType}, false)
|
||||
llvmFn = llvm.AddFunction(c.mod, fnName, llvmFnType)
|
||||
c.addStandardDeclaredAttributes(llvmFn)
|
||||
methods := c.getMethodsString(assertedType.Underlying().(*types.Interface))
|
||||
llvmFn.AddFunctionAttr(c.ctx.CreateStringAttribute("tinygo-methods", methods))
|
||||
// Get the global that contains an interface method set, creating it if needed.
|
||||
func (c *compilerContext) getInterfaceMethodSet(t *types.Interface) llvm.Value {
|
||||
s, _ := getTypeCodeName(t)
|
||||
methodSetName := s + "$itfmethods"
|
||||
methodSet := c.mod.NamedFunction(methodSetName)
|
||||
if methodSet.IsNil() {
|
||||
var methods []*types.Func
|
||||
for i := 0; i < t.NumMethods(); i++ {
|
||||
methods = append(methods, t.Method(i))
|
||||
}
|
||||
if len(methods) == 0 {
|
||||
// This *should* be unreachable: the caller checks whether the
|
||||
// interface is empty before creating a method set.
|
||||
panic("unreachable")
|
||||
}
|
||||
|
||||
methodSetValue := c.getMethodSetValue(methods)
|
||||
methodSet = llvm.AddGlobal(c.mod, methodSetValue.Type(), methodSetName)
|
||||
methodSet.SetInitializer(methodSetValue)
|
||||
methodSet.SetGlobalConstant(true)
|
||||
methodSet.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
methodSet.SetAlignment(c.targetData.ABITypeAlignment(methodSetValue.Type()))
|
||||
methodSet.SetUnnamedAddr(true)
|
||||
}
|
||||
return llvmFn
|
||||
|
||||
return methodSet
|
||||
}
|
||||
|
||||
// Get the method set value that is used in a number of type structs.
|
||||
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
|
||||
// Create a sorted list of method names.
|
||||
var methodRefNames []string
|
||||
for _, method := range methods {
|
||||
name := method.Name()
|
||||
if !token.IsExported(name) {
|
||||
name = method.Pkg().Path() + "." + name
|
||||
}
|
||||
s, _ := getTypeCodeName(method.Type())
|
||||
methodRefNames = append(methodRefNames, "reflect/types.signature:"+name+":"+s)
|
||||
}
|
||||
sort.Strings(methodRefNames)
|
||||
|
||||
// Turn this slice of strings in a slice of global variables.
|
||||
var methodRefValues []llvm.Value
|
||||
for _, name := range methodRefNames {
|
||||
value := c.mod.NamedGlobal(name)
|
||||
if value.IsNil() {
|
||||
value = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), name)
|
||||
value.SetInitializer(llvm.ConstNull(c.ctx.Int8Type()))
|
||||
value.SetGlobalConstant(true)
|
||||
value.SetLinkage(llvm.LinkOnceODRLinkage)
|
||||
value.SetAlignment(1)
|
||||
}
|
||||
methodRefValues = append(methodRefValues, value)
|
||||
}
|
||||
|
||||
return c.ctx.ConstStruct([]llvm.Value{
|
||||
llvm.ConstInt(c.uintptrType, uint64(len(methodRefValues)), false),
|
||||
llvm.ConstArray(c.dataPtrType, methodRefValues),
|
||||
}, false)
|
||||
}
|
||||
|
||||
// getInvokeFunction returns the thunk to call the given interface method. The
|
||||
|
||||
Vendored
+1
-1
@@ -22,7 +22,7 @@ target triple = "wasm32-unknown-wasi"
|
||||
@"runtime/gc.layout:62-2000000000000001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00 " }
|
||||
@"runtime/gc.layout:62-0001" = linkonce_odr unnamed_addr constant { i32, [8 x i8] } { i32 62, [8 x i8] c"\01\00\00\00\00\00\00\00" }
|
||||
@"reflect/types.type:basic:complex128" = linkonce_odr constant { i8, ptr } { i8 80, ptr @"reflect/types.type:pointer:basic:complex128" }, align 4
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128" }, align 4
|
||||
@"reflect/types.type:pointer:basic:complex128" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:complex128", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
|
||||
Vendored
+31
-31
@@ -7,15 +7,19 @@ target triple = "wasm32-unknown-wasi"
|
||||
%runtime._string = type { ptr, i32 }
|
||||
|
||||
@"reflect/types.type:basic:int" = linkonce_odr constant { i8, ptr } { i8 -62, ptr @"reflect/types.type:pointer:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type:pointer:basic:int" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:basic:int", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:named:error", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.signature:Error:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [7 x i8] } { i8 116, i16 1, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", ptr @"reflect/types.type.pkgpath.empty", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, [7 x i8] c".error\00" }, align 4
|
||||
@"reflect/types.type.pkgpath.empty" = linkonce_odr unnamed_addr constant [1 x i8] zeroinitializer, align 1
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, i16, ptr, { i32, [0 x ptr] } } { i8 -43, i16 0, ptr @"reflect/types.type:interface:{String:func:{}{basic:string}}", { i32, [0 x ptr] } zeroinitializer }, align 4
|
||||
@"reflect/types.signature:String:func:{}{basic:string}" = linkonce_odr constant i8 0, align 1
|
||||
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x ptr] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] } }, align 4
|
||||
@"reflect/types.typeid:basic:int" = external constant i8
|
||||
@"interface:{Error:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Error:func:{}{basic:string}"] }, align 4
|
||||
@"interface:{String:func:{}{basic:string}}$itfmethods" = linkonce_odr unnamed_addr constant { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:String:func:{}{basic:string}"] }, align 4
|
||||
|
||||
; Function Attrs: allockind("alloc,zeroed") allocsize(0)
|
||||
declare noalias nonnull ptr @runtime.alloc(i32, ptr, ptr) #0
|
||||
@@ -32,8 +36,8 @@ entry:
|
||||
define hidden %runtime._interface @main.simpleType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:basic:int", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:basic:int", ptr null }
|
||||
}
|
||||
|
||||
@@ -41,8 +45,8 @@ entry:
|
||||
define hidden %runtime._interface @main.pointerType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:basic:int", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:basic:int", ptr null }
|
||||
}
|
||||
|
||||
@@ -50,8 +54,8 @@ entry:
|
||||
define hidden %runtime._interface @main.interfaceType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:named:error", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:named:error", ptr null }
|
||||
}
|
||||
|
||||
@@ -59,15 +63,15 @@ entry:
|
||||
define hidden %runtime._interface @main.anonymousInterfaceType(ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr nonnull %stackalloc, ptr undef) #5
|
||||
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._interface { ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", ptr null }
|
||||
}
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isInt(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #7
|
||||
%typecode = call i1 @runtime.typeAssert(ptr %itf.typecode, ptr nonnull @"reflect/types.typeid:basic:int", ptr undef) #5
|
||||
br i1 %typecode, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -82,7 +86,7 @@ declare i1 @runtime.typeAssert(ptr, ptr dereferenceable_or_null(1), ptr) #1
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isError(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{Error:func:{}{basic:string}}$itfmethods", ptr undef) #5
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -92,12 +96,12 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #3
|
||||
declare i1 @runtime.typeImplementsMethodSet(ptr, ptr, ptr) #1
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i1 @main.isStringer(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr %itf.typecode) #7
|
||||
%0 = call i1 @runtime.typeImplementsMethodSet(ptr %itf.typecode, ptr nonnull @"interface:{String:func:{}{basic:string}}$itfmethods", ptr undef) #5
|
||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||
|
||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||
@@ -107,34 +111,30 @@ typeassert.ok: ; preds = %entry
|
||||
br label %typeassert.next
|
||||
}
|
||||
|
||||
declare i1 @"interface:{String:func:{}{basic:string}}.$typeassert"(ptr) #4
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden i8 @main.callFooMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #7
|
||||
%0 = call i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr %itf.value, i32 3, ptr %itf.typecode, ptr undef) #5
|
||||
ret i8 %0
|
||||
}
|
||||
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #5
|
||||
declare i8 @"interface:{String:func:{}{basic:string},main.foo:func:{basic:int}{basic:uint8}}.foo$invoke"(ptr, i32, ptr, ptr) #3
|
||||
|
||||
; Function Attrs: nounwind
|
||||
define hidden %runtime._string @main.callErrorMethod(ptr %itf.typecode, ptr %itf.value, ptr %context) unnamed_addr #2 {
|
||||
entry:
|
||||
%stackalloc = alloca i8, align 1
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #7
|
||||
%0 = call %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr %itf.value, ptr %itf.typecode, ptr undef) #5
|
||||
%1 = extractvalue %runtime._string %0, 0
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #7
|
||||
call void @runtime.trackPointer(ptr %1, ptr nonnull %stackalloc, ptr undef) #5
|
||||
ret %runtime._string %0
|
||||
}
|
||||
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #6
|
||||
declare %runtime._string @"interface:{Error:func:{}{basic:string}}.Error$invoke"(ptr, ptr, ptr) #4
|
||||
|
||||
attributes #0 = { allockind("alloc,zeroed") allocsize(0) "alloc-family"="runtime.alloc" "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #1 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #2 = { nounwind "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-methods"="reflect/methods.String() string" }
|
||||
attributes #5 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #6 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #7 = { nounwind }
|
||||
attributes #3 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="main.$methods.foo(int) uint8" "tinygo-methods"="reflect/methods.String() string; main.$methods.foo(int) uint8" }
|
||||
attributes #4 = { "target-features"="+bulk-memory,+mutable-globals,+nontrapping-fptoint,+sign-ext" "tinygo-invoke"="reflect/methods.Error() string" "tinygo-methods"="reflect/methods.Error() string" }
|
||||
attributes #5 = { nounwind }
|
||||
|
||||
@@ -421,64 +421,6 @@ func (r *runner) run(fn *function, params []value, parentMem *memoryView, indent
|
||||
// Special function that will trigger an error.
|
||||
// This is used to test error reporting.
|
||||
return nil, mem, r.errorAt(inst, errors.New("test error"))
|
||||
case strings.HasSuffix(callFn.name, ".$typeassert"):
|
||||
if r.debug {
|
||||
fmt.Fprintln(os.Stderr, indent+"interface assert:", operands[1:])
|
||||
}
|
||||
|
||||
// Load various values for the interface implements check below.
|
||||
typecodePtr, err := operands[1].asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
// typecodePtr always point to the numMethod field in the type
|
||||
// description struct. The methodSet, when present, comes right
|
||||
// before the numMethod field (the compiler doesn't generate
|
||||
// method sets for concrete types without methods).
|
||||
// Considering that the compiler doesn't emit interface type
|
||||
// asserts for interfaces with no methods (as the always succeed)
|
||||
// then if the offset is zero, this assert must always fail.
|
||||
if typecodePtr.offset() == 0 {
|
||||
locals[inst.localIndex] = literalValue{uint8(0)}
|
||||
break
|
||||
}
|
||||
typecodePtrOffset, err := typecodePtr.addOffset(-int64(r.pointerSize))
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSetPtr, err := mem.load(typecodePtrOffset, r.pointerSize).asPointer(r)
|
||||
if err != nil {
|
||||
return nil, mem, r.errorAt(inst, err)
|
||||
}
|
||||
methodSet := mem.get(methodSetPtr.index()).llvmGlobal.Initializer()
|
||||
numMethods := int(r.builder.CreateExtractValue(methodSet, 0, "").ZExtValue())
|
||||
llvmFn := inst.llvmInst.CalledValue()
|
||||
methodSetAttr := llvmFn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||
methodSetString := methodSetAttr.GetStringValue()
|
||||
|
||||
// Make a set of all the methods on the concrete type, for
|
||||
// easier checking in the next step.
|
||||
concreteTypeMethods := map[string]struct{}{}
|
||||
for i := 0; i < numMethods; i++ {
|
||||
methodInfo := r.builder.CreateExtractValue(methodSet, 1, "")
|
||||
name := r.builder.CreateExtractValue(methodInfo, i, "").Name()
|
||||
concreteTypeMethods[name] = struct{}{}
|
||||
}
|
||||
|
||||
// Check whether all interface methods are also in the list
|
||||
// of defined methods calculated above. This is the interface
|
||||
// assert itself.
|
||||
assertOk := uint8(1) // i1 true
|
||||
for _, name := range strings.Split(methodSetString, "; ") {
|
||||
if _, ok := concreteTypeMethods[name]; !ok {
|
||||
// There is a method on the interface that is not
|
||||
// implemented by the type. The assertion will fail.
|
||||
assertOk = 0 // i1 false
|
||||
break
|
||||
}
|
||||
}
|
||||
// If assertOk is still 1, the assertion succeeded.
|
||||
locals[inst.localIndex] = literalValue{assertOk}
|
||||
case strings.HasSuffix(callFn.name, "$invoke"):
|
||||
// This thunk is the interface method dispatcher: it is called
|
||||
// with all regular parameters and a type code. It will then
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package reflect
|
||||
|
||||
// This file defines functions that logically belong in the runtime but that
|
||||
// depend on the internals of the reflect package and are therefore better
|
||||
// implemented directly in the reflect package.
|
||||
|
||||
import (
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Return whether the concrete type implements the asserted method set.
|
||||
// This function is called both from the compiler and from the reflect package.
|
||||
//
|
||||
//go:linkname typeImplementsMethodSet runtime.typeImplementsMethodSet
|
||||
func typeImplementsMethodSet(concreteType, assertedMethodSet unsafe.Pointer) bool {
|
||||
if concreteType == nil {
|
||||
// This can happen when doing a type assert on a nil interface value.
|
||||
// According to the Go language specification, a type assert on a nil
|
||||
// interface always fails.
|
||||
return false
|
||||
}
|
||||
|
||||
const ptrSize = unsafe.Sizeof((*byte)(nil))
|
||||
itfNumMethod := *(*uintptr)(assertedMethodSet)
|
||||
if itfNumMethod == 0 {
|
||||
// While the compiler shouldn't emit such type asserts, the reflect
|
||||
// package may do so.
|
||||
// This is actually needed for correctness: for example a value of type
|
||||
// int may be assigned to a variable of type interface{}.
|
||||
return true
|
||||
}
|
||||
|
||||
// Pull the method set out of the concrete type.
|
||||
var methods *methodSet
|
||||
metaByte := *(*uint8)(concreteType)
|
||||
if metaByte&flagNamed != 0 {
|
||||
concreteType := (*namedType)(concreteType)
|
||||
methods = &concreteType.methods
|
||||
} else if metaByte&kindMask == uint8(Struct) {
|
||||
concreteType := (*structType)(concreteType)
|
||||
methods = &concreteType.methods
|
||||
} else if metaByte&kindMask == uint8(Pointer) {
|
||||
concreteType := (*ptrType)(concreteType)
|
||||
methods = &concreteType.methods
|
||||
} else {
|
||||
// Other types don't have a method set.
|
||||
return false
|
||||
}
|
||||
concreteTypePtr := unsafe.Pointer(&methods.methods)
|
||||
concreteTypeEnd := unsafe.Add(concreteTypePtr, uintptr(methods.length)*ptrSize)
|
||||
|
||||
// Iterate over each method in the interface method set, and check whether
|
||||
// the method exists in the method set of the concrete type.
|
||||
// Both method sets are sorted in the same way, so we can use a simple loop
|
||||
// to check for a match.
|
||||
assertedTypePtr := unsafe.Add(assertedMethodSet, ptrSize)
|
||||
assertedTypeEnd := unsafe.Add(assertedTypePtr, itfNumMethod*ptrSize)
|
||||
for assertedTypePtr != assertedTypeEnd {
|
||||
assertedMethod := *(*unsafe.Pointer)(assertedTypePtr)
|
||||
|
||||
// Search for the same method in the concrete type.
|
||||
for {
|
||||
if concreteTypePtr == concreteTypeEnd {
|
||||
// Reached the end of the concrete type method set.
|
||||
// The method wasn't found, so the type assert failed.
|
||||
return false
|
||||
}
|
||||
concreteMethod := *(*unsafe.Pointer)(concreteTypePtr)
|
||||
concreteTypePtr = unsafe.Add(concreteTypePtr, ptrSize)
|
||||
if concreteMethod == assertedMethod {
|
||||
// Found the method in the concrete type. Continue with the
|
||||
// next in the interface method set.
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// The method was found. Continue with the next.
|
||||
assertedTypePtr = unsafe.Add(assertedTypePtr, ptrSize)
|
||||
}
|
||||
|
||||
// All methods in the interface were found.
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
// Copyright 2011 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package reflect_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"io"
|
||||
. "reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
//func TestImplicitMapConversion(t *testing.T) {
|
||||
// // Test implicit conversions in MapIndex and SetMapIndex.
|
||||
// {
|
||||
// // direct
|
||||
// m := make(map[int]int)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#1 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#1 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert interface key
|
||||
// m := make(map[any]int)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#2 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#2 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert interface value
|
||||
// m := make(map[int]any)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#3 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#3 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert both interface key and interface value
|
||||
// m := make(map[any]any)
|
||||
// mv := ValueOf(m)
|
||||
// mv.SetMapIndex(ValueOf(1), ValueOf(2))
|
||||
// x, ok := m[1]
|
||||
// if x != 2 {
|
||||
// t.Errorf("#4 after SetMapIndex(1,2): %d, %t (map=%v)", x, ok, m)
|
||||
// }
|
||||
// if n := mv.MapIndex(ValueOf(1)).Interface().(int); n != 2 {
|
||||
// t.Errorf("#4 MapIndex(1) = %d", n)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert both, with non-empty interfaces
|
||||
// m := make(map[io.Reader]io.Writer)
|
||||
// mv := ValueOf(m)
|
||||
// b1 := new(bytes.Buffer)
|
||||
// b2 := new(bytes.Buffer)
|
||||
// mv.SetMapIndex(ValueOf(b1), ValueOf(b2))
|
||||
// x, ok := m[b1]
|
||||
// if x != b2 {
|
||||
// t.Errorf("#5 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
|
||||
// }
|
||||
// if p := mv.MapIndex(ValueOf(b1)).Elem().UnsafePointer(); p != unsafe.Pointer(b2) {
|
||||
// t.Errorf("#5 MapIndex(b1) = %#x want %p", p, b2)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert channel direction
|
||||
// m := make(map[<-chan int]chan int)
|
||||
// mv := ValueOf(m)
|
||||
// c1 := make(chan int)
|
||||
// c2 := make(chan int)
|
||||
// mv.SetMapIndex(ValueOf(c1), ValueOf(c2))
|
||||
// x, ok := m[c1]
|
||||
// if x != c2 {
|
||||
// t.Errorf("#6 after SetMapIndex(c1, c2): %p (!= %p), %t (map=%v)", x, c2, ok, m)
|
||||
// }
|
||||
// if p := mv.MapIndex(ValueOf(c1)).UnsafePointer(); p != ValueOf(c2).UnsafePointer() {
|
||||
// t.Errorf("#6 MapIndex(c1) = %#x want %p", p, c2)
|
||||
// }
|
||||
// }
|
||||
// {
|
||||
// // convert identical underlying types
|
||||
// type MyBuffer bytes.Buffer
|
||||
// m := make(map[*MyBuffer]*bytes.Buffer)
|
||||
// mv := ValueOf(m)
|
||||
// b1 := new(MyBuffer)
|
||||
// b2 := new(bytes.Buffer)
|
||||
// mv.SetMapIndex(ValueOf(b1), ValueOf(b2))
|
||||
// x, ok := m[b1]
|
||||
// if x != b2 {
|
||||
// t.Errorf("#7 after SetMapIndex(b1, b2): %p (!= %p), %t (map=%v)", x, b2, ok, m)
|
||||
// }
|
||||
// if p := mv.MapIndex(ValueOf(b1)).UnsafePointer(); p != unsafe.Pointer(b2) {
|
||||
// t.Errorf("#7 MapIndex(b1) = %#x want %p", p, b2)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//}
|
||||
//
|
||||
//func TestImplicitSetConversion(t *testing.T) {
|
||||
// // Assume TestImplicitMapConversion covered the basics.
|
||||
// // Just make sure conversions are being applied at all.
|
||||
// var r io.Reader
|
||||
// b := new(bytes.Buffer)
|
||||
// rv := ValueOf(&r).Elem()
|
||||
// rv.Set(ValueOf(b))
|
||||
// if r != b {
|
||||
// t.Errorf("after Set: r=%T(%v)", r, r)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestImplicitSendConversion(t *testing.T) {
|
||||
// c := make(chan io.Reader, 10)
|
||||
// b := new(bytes.Buffer)
|
||||
// ValueOf(c).Send(ValueOf(b))
|
||||
// if bb := <-c; bb != b {
|
||||
// t.Errorf("Received %p != %p", bb, b)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestImplicitCallConversion(t *testing.T) {
|
||||
// // Arguments must be assignable to parameter types.
|
||||
// fv := ValueOf(io.WriteString)
|
||||
// b := new(strings.Builder)
|
||||
// fv.Call([]Value{ValueOf(b), ValueOf("hello world")})
|
||||
// if b.String() != "hello world" {
|
||||
// t.Errorf("After call: string=%q want %q", b.String(), "hello world")
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func TestImplicitAppendConversion(t *testing.T) {
|
||||
// // Arguments must be assignable to the slice's element type.
|
||||
// s := []io.Reader{}
|
||||
// sv := ValueOf(&s).Elem()
|
||||
// b := new(bytes.Buffer)
|
||||
// sv.Set(Append(sv, ValueOf(b)))
|
||||
// if len(s) != 1 || s[0] != b {
|
||||
// t.Errorf("after append: s=%v want [%p]", s, b)
|
||||
// }
|
||||
//}
|
||||
|
||||
var implementsTests = []struct {
|
||||
x any
|
||||
t any
|
||||
b bool
|
||||
}{
|
||||
{new(*bytes.Buffer), new(io.Reader), true},
|
||||
{new(bytes.Buffer), new(io.Reader), false},
|
||||
{new(*bytes.Buffer), new(io.ReaderAt), false},
|
||||
{new(*ast.Ident), new(ast.Expr), true},
|
||||
{new(*notAnExpr), new(ast.Expr), false},
|
||||
{new(*ast.Ident), new(notASTExpr), false},
|
||||
{new(notASTExpr), new(ast.Expr), false},
|
||||
{new(ast.Expr), new(notASTExpr), false},
|
||||
{new(*notAnExpr), new(notASTExpr), true},
|
||||
}
|
||||
|
||||
type notAnExpr struct{}
|
||||
|
||||
func (notAnExpr) Pos() token.Pos { return token.NoPos }
|
||||
func (notAnExpr) End() token.Pos { return token.NoPos }
|
||||
func (notAnExpr) exprNode() {}
|
||||
|
||||
type notASTExpr interface {
|
||||
Pos() token.Pos
|
||||
End() token.Pos
|
||||
exprNode()
|
||||
}
|
||||
|
||||
func TestImplements(t *testing.T) {
|
||||
for _, tt := range implementsTests {
|
||||
xv := TypeOf(tt.x).Elem()
|
||||
xt := TypeOf(tt.t).Elem()
|
||||
if b := xv.Implements(xt); b != tt.b {
|
||||
t.Errorf("(%s).Implements(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: AssignableTo should be possible to implement now that Implements()
|
||||
// mostly works.
|
||||
|
||||
//var assignableTests = []struct {
|
||||
// x any
|
||||
// t any
|
||||
// b bool
|
||||
//}{
|
||||
// {new(chan int), new(<-chan int), true},
|
||||
// {new(<-chan int), new(chan int), false},
|
||||
// {new(*int), new(IntPtr), true},
|
||||
// {new(IntPtr), new(*int), true},
|
||||
// {new(IntPtr), new(IntPtr1), false},
|
||||
// {new(Ch), new(<-chan any), true},
|
||||
// // test runs implementsTests too
|
||||
//}
|
||||
//
|
||||
//type IntPtr *int
|
||||
//type IntPtr1 *int
|
||||
//type Ch <-chan any
|
||||
//
|
||||
//func TestAssignableTo(t *testing.T) {
|
||||
// for _, tt := range append(assignableTests, implementsTests...) {
|
||||
// xv := TypeOf(tt.x).Elem()
|
||||
// xt := TypeOf(tt.t).Elem()
|
||||
// if b := xv.AssignableTo(xt); b != tt.b {
|
||||
// t.Errorf("(%s).AssignableTo(%s) = %v, want %v", xv.String(), xt.String(), b, tt.b)
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
+24
-3
@@ -23,6 +23,7 @@
|
||||
// meta uint8
|
||||
// nmethods uint16
|
||||
// elementType *typeStruct
|
||||
// methods methodSet
|
||||
// - array types (see arrayType)
|
||||
// meta uint8
|
||||
// nmethods uint16 (0)
|
||||
@@ -44,7 +45,8 @@
|
||||
// pkgpath *byte // package path; null terminated
|
||||
// numField uint16
|
||||
// fields [...]structField // the remaining fields are all of type structField
|
||||
// - interface types (this is missing the interface methods):
|
||||
// methods methodSet
|
||||
// - interface types:
|
||||
// meta uint8
|
||||
// ptrTo *typeStruct
|
||||
// - signature types (this is missing input and output parameters):
|
||||
@@ -56,6 +58,7 @@
|
||||
// ptrTo *typeStruct
|
||||
// elem *typeStruct // underlying type
|
||||
// pkgpath *byte // pkgpath; null terminated
|
||||
// methods methodSet
|
||||
// name [1]byte // actual name; null terminated
|
||||
//
|
||||
// The type struct is essentially a union of all the above types. Which it is,
|
||||
@@ -420,6 +423,7 @@ type ptrType struct {
|
||||
rawType
|
||||
numMethod uint16
|
||||
elem *rawType
|
||||
methods methodSet
|
||||
}
|
||||
|
||||
type arrayType struct {
|
||||
@@ -445,6 +449,7 @@ type namedType struct {
|
||||
ptrTo *rawType
|
||||
elem *rawType
|
||||
pkg *byte
|
||||
methods methodSet
|
||||
name [1]byte
|
||||
}
|
||||
|
||||
@@ -463,6 +468,7 @@ type structType struct {
|
||||
size uint32
|
||||
numField uint16
|
||||
fields [1]structField // the remaining fields are all of type structField
|
||||
methods methodSet
|
||||
}
|
||||
|
||||
type structField struct {
|
||||
@@ -470,6 +476,18 @@ type structField struct {
|
||||
data unsafe.Pointer // various bits of information, packed in a byte array
|
||||
}
|
||||
|
||||
type interfaceType struct {
|
||||
rawType
|
||||
ptrTo *rawType
|
||||
methods methodSet
|
||||
}
|
||||
|
||||
// Method set, as emitted by the compiler.
|
||||
type methodSet struct {
|
||||
length uintptr
|
||||
methods [0]*byte // variable number of methods
|
||||
}
|
||||
|
||||
// Equivalent to (go/types.Type).Underlying(): if this is a named type return
|
||||
// the underlying type, else just return the type itself.
|
||||
func (t *rawType) underlying() *rawType {
|
||||
@@ -972,7 +990,8 @@ func (t *rawType) Implements(u Type) bool {
|
||||
if u.Kind() != Interface {
|
||||
panic("reflect: non-interface type passed to Type.Implements")
|
||||
}
|
||||
return t.AssignableTo(u)
|
||||
u_itf := (*interfaceType)(unsafe.Pointer(u.(*rawType).underlying()))
|
||||
return typeImplementsMethodSet(unsafe.Pointer(t), unsafe.Pointer(&u_itf.methods))
|
||||
}
|
||||
|
||||
// Comparable returns whether values of this type can be compared to each other.
|
||||
@@ -1046,7 +1065,9 @@ func readStringZ(data unsafe.Pointer) string {
|
||||
|
||||
func (t *rawType) name() string {
|
||||
ntype := (*namedType)(unsafe.Pointer(t))
|
||||
return readStringZ(unsafe.Pointer(&ntype.name[0]))
|
||||
ptr := unsafe.Pointer(&ntype.name[0])
|
||||
ptr = unsafe.Add(ptr, uintptr(ntype.methods.length)*unsafe.Sizeof(unsafe.Pointer(nil)))
|
||||
return readStringZ(ptr)
|
||||
}
|
||||
|
||||
func (t *rawType) Name() string {
|
||||
|
||||
@@ -82,6 +82,9 @@ func reflectValueEqual(x, y reflect.Value) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// Implemented in the reflect package.
|
||||
func typeImplementsMethodSet(actualTypeNum, assertedMethodSet unsafe.Pointer) bool
|
||||
|
||||
// interfaceTypeAssert is called when a type assert without comma-ok still
|
||||
// returns false.
|
||||
func interfaceTypeAssert(ok bool) {
|
||||
|
||||
Vendored
+3
@@ -22,6 +22,7 @@ func main() {
|
||||
printItf(array)
|
||||
printItf(ArrayStruct{3, array})
|
||||
printItf(SmallPair{3, 5})
|
||||
printItf(nil)
|
||||
s := Stringer(thing)
|
||||
println("Stringer.String():", s.String())
|
||||
var itf interface{} = s
|
||||
@@ -150,6 +151,8 @@ func printItf(val interface{}) {
|
||||
println("is struct{n int `foo:\"bar\"`}")
|
||||
case Foo:
|
||||
println("is Foo:", val)
|
||||
case nil:
|
||||
println("is nil")
|
||||
default:
|
||||
println("is ?")
|
||||
}
|
||||
|
||||
Vendored
+1
@@ -16,6 +16,7 @@ is Tuple: 1 7 11 13
|
||||
ArrayStruct.Print: 4 3
|
||||
is Tuple: 0 8 16 24
|
||||
SmallPair.Print: 3 5
|
||||
is nil
|
||||
Stringer.String(): foo
|
||||
Stringer.(*Thing).String(): foo
|
||||
s has String() method: foo
|
||||
|
||||
@@ -272,13 +272,6 @@ func (p *lowerInterfacesPass) run() error {
|
||||
p.defineInterfaceMethodFunc(fn, itf, signature)
|
||||
}
|
||||
|
||||
// Define all interface type assert functions.
|
||||
for _, fn := range interfaceAssertFunctions {
|
||||
methodsAttr := fn.GetStringAttributeAtIndex(-1, "tinygo-methods")
|
||||
itf := p.interfaces[methodsAttr.GetStringValue()]
|
||||
p.defineInterfaceImplementsFunc(fn, itf)
|
||||
}
|
||||
|
||||
// Replace each type assert with an actual type comparison or (if the type
|
||||
// assert is impossible) the constant false.
|
||||
llvmFalse := llvm.ConstInt(p.ctx.Int1Type(), 0, false)
|
||||
@@ -428,66 +421,6 @@ func (p *lowerInterfacesPass) getSignature(name string) *signatureInfo {
|
||||
return p.signatures[name]
|
||||
}
|
||||
|
||||
// defineInterfaceImplementsFunc defines the interface type assert function. It
|
||||
// checks whether the given interface type (passed as an argument) is one of the
|
||||
// types it implements.
|
||||
//
|
||||
// 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
|
||||
// types by the LLVM simplifycfg pass.
|
||||
func (p *lowerInterfacesPass) defineInterfaceImplementsFunc(fn llvm.Value, itf *interfaceInfo) {
|
||||
// Create the function and function signature.
|
||||
fn.Param(0).SetName("actualType")
|
||||
fn.SetLinkage(llvm.InternalLinkage)
|
||||
fn.SetUnnamedAddr(true)
|
||||
AddStandardAttributes(fn, p.config)
|
||||
|
||||
// Start the if/else chain at the entry block.
|
||||
entry := p.ctx.AddBasicBlock(fn, "entry")
|
||||
thenBlock := p.ctx.AddBasicBlock(fn, "then")
|
||||
p.builder.SetInsertPointAtEnd(entry)
|
||||
|
||||
if p.dibuilder != nil {
|
||||
difile := p.getDIFile("<Go interface assert>")
|
||||
diFuncType := p.dibuilder.CreateSubroutineType(llvm.DISubroutineType{
|
||||
File: difile,
|
||||
})
|
||||
difunc := p.dibuilder.CreateFunction(difile, llvm.DIFunction{
|
||||
Name: "(Go interface assert)",
|
||||
File: difile,
|
||||
Line: 0,
|
||||
Type: diFuncType,
|
||||
LocalToUnit: true,
|
||||
IsDefinition: true,
|
||||
ScopeLine: 0,
|
||||
Flags: llvm.FlagPrototyped,
|
||||
Optimized: true,
|
||||
})
|
||||
fn.SetSubprogram(difunc)
|
||||
p.builder.SetCurrentDebugLocation(0, 0, difunc, llvm.Metadata{})
|
||||
}
|
||||
|
||||
// Iterate over all possible types. Each iteration creates a new branch
|
||||
// either to the 'then' block (success) or the .next block, for the next
|
||||
// check.
|
||||
actualType := fn.Param(0)
|
||||
for _, typ := range itf.types {
|
||||
nextBlock := p.ctx.AddBasicBlock(fn, typ.name+".next")
|
||||
cmp := p.builder.CreateICmp(llvm.IntEQ, actualType, typ.typecodeGEP, typ.name+".icmp")
|
||||
p.builder.CreateCondBr(cmp, thenBlock, nextBlock)
|
||||
p.builder.SetInsertPointAtEnd(nextBlock)
|
||||
}
|
||||
|
||||
// The builder is now inserting at the last *.next block. Once we reach
|
||||
// this point, all types have been checked so the type assert will have
|
||||
// failed.
|
||||
p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 0, false))
|
||||
|
||||
// Fill 'then' block (type assert was successful).
|
||||
p.builder.SetInsertPointAtEnd(thenBlock)
|
||||
p.builder.CreateRet(llvm.ConstInt(p.ctx.Int1Type(), 1, false))
|
||||
}
|
||||
|
||||
// defineInterfaceMethodFunc defines this thunk by calling the concrete method
|
||||
// of the type that implements this interface.
|
||||
//
|
||||
|
||||
@@ -65,7 +65,6 @@ func Optimize(mod llvm.Module, config *compileopts.Config) []error {
|
||||
|
||||
// Run TinyGo-specific optimization passes.
|
||||
OptimizeStringToBytes(mod)
|
||||
OptimizeReflectImplements(mod)
|
||||
maxStackSize := config.MaxStackAlloc()
|
||||
OptimizeAllocs(mod, nil, maxStackSize, nil)
|
||||
err = LowerInterfaces(mod, config)
|
||||
|
||||
@@ -4,8 +4,6 @@ package transform
|
||||
// calls.
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"tinygo.org/x/go-llvm"
|
||||
)
|
||||
|
||||
@@ -100,79 +98,3 @@ func OptimizeStringEqual(mod llvm.Module) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// OptimizeReflectImplements optimizes the following code:
|
||||
//
|
||||
// implements := someType.Implements(someInterfaceType)
|
||||
//
|
||||
// where someType is an arbitrary reflect.Type and someInterfaceType is a
|
||||
// reflect.Type of interface kind, to the following code:
|
||||
//
|
||||
// _, implements := someType.(interfaceType)
|
||||
//
|
||||
// if the interface type is known at compile time (that is, someInterfaceType is
|
||||
// a LLVM constant aggregate). This optimization is especially important for the
|
||||
// encoding/json package, which uses this method.
|
||||
//
|
||||
// As of this writing, the (reflect.Type).Interface method has not yet been
|
||||
// implemented so this optimization is critical for the encoding/json package.
|
||||
func OptimizeReflectImplements(mod llvm.Module) {
|
||||
implementsSignature := mod.NamedGlobal("reflect/methods.Implements(reflect.Type) bool")
|
||||
if implementsSignature.IsNil() {
|
||||
return
|
||||
}
|
||||
|
||||
builder := mod.Context().NewBuilder()
|
||||
defer builder.Dispose()
|
||||
|
||||
// Look up the (reflect.Value).Implements() method.
|
||||
var implementsFunc llvm.Value
|
||||
for fn := mod.FirstFunction(); !fn.IsNil(); fn = llvm.NextFunction(fn) {
|
||||
attr := fn.GetStringAttributeAtIndex(-1, "tinygo-invoke")
|
||||
if attr.IsNil() {
|
||||
continue
|
||||
}
|
||||
if attr.GetStringValue() == "reflect/methods.Implements(reflect.Type) bool" {
|
||||
implementsFunc = fn
|
||||
break
|
||||
}
|
||||
}
|
||||
if implementsFunc.IsNil() {
|
||||
// Doesn't exist in the program, so nothing to do.
|
||||
return
|
||||
}
|
||||
|
||||
for _, call := range getUses(implementsFunc) {
|
||||
if call.IsACallInst().IsNil() {
|
||||
continue
|
||||
}
|
||||
interfaceType := stripPointerCasts(call.Operand(2))
|
||||
if interfaceType.IsAGlobalVariable().IsNil() {
|
||||
// Interface is unknown at compile time. This can't be optimized.
|
||||
continue
|
||||
}
|
||||
|
||||
if strings.HasPrefix(interfaceType.Name(), "reflect/types.type:named:") {
|
||||
// Get the underlying type.
|
||||
interfaceType = stripPointerCasts(builder.CreateExtractValue(interfaceType.Initializer(), 3, ""))
|
||||
}
|
||||
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
|
||||
}
|
||||
typeAssertFunction := mod.NamedFunction(strings.TrimPrefix(interfaceType.Name(), "reflect/types.type:") + ".$typeassert")
|
||||
if typeAssertFunction.IsNil() {
|
||||
continue
|
||||
}
|
||||
|
||||
// Replace Implements call with the type assert call.
|
||||
builder.SetInsertPointBefore(call)
|
||||
implements := builder.CreateCall(typeAssertFunction.GlobalValueType(), typeAssertFunction, []llvm.Value{
|
||||
call.Operand(0), // typecode to check
|
||||
}, "")
|
||||
call.ReplaceAllUsesWith(implements)
|
||||
call.EraseFromParentAsInstruction()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,11 +22,3 @@ func TestOptimizeStringEqual(t *testing.T) {
|
||||
transform.OptimizeStringEqual(mod)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOptimizeReflectImplements(t *testing.T) {
|
||||
t.Parallel()
|
||||
testTransform(t, "testdata/reflect-implements", func(mod llvm.Module) {
|
||||
// Run optimization pass.
|
||||
transform.OptimizeReflectImplements(mod)
|
||||
})
|
||||
}
|
||||
|
||||
Vendored
-28
@@ -16,10 +16,7 @@ target triple = "armv7m-none-eabi"
|
||||
declare i1 @runtime.typeAssert(ptr, ptr)
|
||||
declare void @runtime.printuint8(i8)
|
||||
declare void @runtime.printint16(i16)
|
||||
declare void @runtime.printint32(i32)
|
||||
declare void @runtime.printptr(i32)
|
||||
declare void @runtime.printnl()
|
||||
declare void @runtime.nilPanic(ptr)
|
||||
|
||||
define void @printInterfaces() {
|
||||
call void @printInterface(ptr @"reflect/types.type:basic:int", ptr inttoptr (i32 5 to ptr))
|
||||
@@ -30,25 +27,6 @@ define void @printInterfaces() {
|
||||
}
|
||||
|
||||
define void @printInterface(ptr %typecode, ptr %value) {
|
||||
%isUnmatched = call i1 @Unmatched$typeassert(ptr %typecode)
|
||||
br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
|
||||
|
||||
typeswitch.Unmatched:
|
||||
%unmatched = ptrtoint ptr %value to i32
|
||||
call void @runtime.printptr(i32 %unmatched)
|
||||
call void @runtime.printnl()
|
||||
ret void
|
||||
|
||||
typeswitch.notUnmatched:
|
||||
%isDoubler = call i1 @Doubler$typeassert(ptr %typecode)
|
||||
br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
||||
|
||||
typeswitch.Doubler:
|
||||
%doubler.result = call i32 @"Doubler.Double$invoke"(ptr %value, ptr %typecode, ptr undef)
|
||||
call void @runtime.printint32(i32 %doubler.result)
|
||||
ret void
|
||||
|
||||
typeswitch.notDoubler:
|
||||
%isByte = call i1 @runtime.typeAssert(ptr %typecode, ptr nonnull @"reflect/types.typeid:basic:uint8")
|
||||
br i1 %isByte, label %typeswitch.byte, label %typeswitch.notByte
|
||||
|
||||
@@ -86,10 +64,4 @@ define i32 @"(Number).Double$invoke"(ptr %receiverPtr, ptr %context) {
|
||||
|
||||
declare i32 @"Doubler.Double$invoke"(ptr %receiver, ptr %typecode, ptr %context) #0
|
||||
|
||||
declare i1 @Doubler$typeassert(ptr %typecode) #1
|
||||
|
||||
declare i1 @Unmatched$typeassert(ptr %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()" }
|
||||
|
||||
Vendored
+2
-65
@@ -12,14 +12,8 @@ declare void @runtime.printuint8(i8)
|
||||
|
||||
declare void @runtime.printint16(i16)
|
||||
|
||||
declare void @runtime.printint32(i32)
|
||||
|
||||
declare void @runtime.printptr(i32)
|
||||
|
||||
declare void @runtime.printnl()
|
||||
|
||||
declare void @runtime.nilPanic(ptr)
|
||||
|
||||
define void @printInterfaces() {
|
||||
call void @printInterface(ptr @"reflect/types.type:basic:int", ptr inttoptr (i32 5 to ptr))
|
||||
call void @printInterface(ptr @"reflect/types.type:basic:uint8", ptr inttoptr (i8 120 to ptr))
|
||||
@@ -28,35 +22,16 @@ define void @printInterfaces() {
|
||||
}
|
||||
|
||||
define void @printInterface(ptr %typecode, ptr %value) {
|
||||
%isUnmatched = call i1 @"Unmatched$typeassert"(ptr %typecode)
|
||||
br i1 %isUnmatched, label %typeswitch.Unmatched, label %typeswitch.notUnmatched
|
||||
|
||||
typeswitch.Unmatched: ; preds = %0
|
||||
%unmatched = ptrtoint ptr %value to i32
|
||||
call void @runtime.printptr(i32 %unmatched)
|
||||
call void @runtime.printnl()
|
||||
ret void
|
||||
|
||||
typeswitch.notUnmatched: ; preds = %0
|
||||
%isDoubler = call i1 @"Doubler$typeassert"(ptr %typecode)
|
||||
br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler
|
||||
|
||||
typeswitch.Doubler: ; preds = %typeswitch.notUnmatched
|
||||
%doubler.result = call i32 @"Doubler.Double$invoke"(ptr %value, ptr %typecode, ptr undef)
|
||||
call void @runtime.printint32(i32 %doubler.result)
|
||||
ret void
|
||||
|
||||
typeswitch.notDoubler: ; preds = %typeswitch.notUnmatched
|
||||
%typeassert.ok = icmp eq ptr @"reflect/types.type:basic:uint8", %typecode
|
||||
br i1 %typeassert.ok, label %typeswitch.byte, label %typeswitch.notByte
|
||||
|
||||
typeswitch.byte: ; preds = %typeswitch.notDoubler
|
||||
typeswitch.byte: ; preds = %0
|
||||
%byte = ptrtoint ptr %value to i8
|
||||
call void @runtime.printuint8(i8 %byte)
|
||||
call void @runtime.printnl()
|
||||
ret void
|
||||
|
||||
typeswitch.notByte: ; preds = %typeswitch.notDoubler
|
||||
typeswitch.notByte: ; preds = %0
|
||||
br i1 false, label %typeswitch.int16, label %typeswitch.notInt16
|
||||
|
||||
typeswitch.int16: ; preds = %typeswitch.notByte
|
||||
@@ -79,41 +54,3 @@ define i32 @"(Number).Double$invoke"(ptr %receiverPtr, ptr %context) {
|
||||
%ret = call i32 @"(Number).Double"(i32 %receiver, ptr undef)
|
||||
ret i32 %ret
|
||||
}
|
||||
|
||||
define internal i32 @"Doubler.Double$invoke"(ptr %receiver, ptr %actualType, ptr %context) unnamed_addr #0 {
|
||||
entry:
|
||||
%"named:Number.icmp" = icmp eq ptr %actualType, @"reflect/types.type:named:Number"
|
||||
br i1 %"named:Number.icmp", label %"named:Number", label %"named:Number.next"
|
||||
|
||||
"named:Number": ; preds = %entry
|
||||
%0 = call i32 @"(Number).Double$invoke"(ptr %receiver, ptr undef)
|
||||
ret i32 %0
|
||||
|
||||
"named:Number.next": ; preds = %entry
|
||||
call void @runtime.nilPanic(ptr undef)
|
||||
unreachable
|
||||
}
|
||||
|
||||
define internal i1 @"Doubler$typeassert"(ptr %actualType) unnamed_addr #1 {
|
||||
entry:
|
||||
%"named:Number.icmp" = icmp eq ptr %actualType, @"reflect/types.type:named:Number"
|
||||
br i1 %"named:Number.icmp", label %then, label %"named:Number.next"
|
||||
|
||||
then: ; preds = %entry
|
||||
ret i1 true
|
||||
|
||||
"named:Number.next": ; preds = %entry
|
||||
ret i1 false
|
||||
}
|
||||
|
||||
define internal i1 @"Unmatched$typeassert"(ptr %actualType) unnamed_addr #2 {
|
||||
entry:
|
||||
ret i1 false
|
||||
|
||||
then: ; No predecessors!
|
||||
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()" }
|
||||
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
target datalayout = "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-f64:32:64-f80:32-n8:16:32-S128"
|
||||
target triple = "i686--linux"
|
||||
|
||||
%runtime._interface = type { ptr, ptr }
|
||||
|
||||
@"reflect/types.type:named:error" = internal constant { i8, i16, ptr, ptr } { i8 52, i16 0, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = internal constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = internal constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = internal constant { i8, i16, ptr } { i8 21, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||
@"reflect/types.type:pointer:named:reflect.rawType" = internal constant { ptr, i8, i16, ptr } { ptr null, i8 21, i16 0, ptr null }, align 4
|
||||
@"reflect/methods.Implements(reflect.Type) bool" = internal constant i8 0, align 1
|
||||
|
||||
; var errorType = reflect.TypeOf((*error)(nil)).Elem()
|
||||
; func isError(typ reflect.Type) bool {
|
||||
; return typ.Implements(errorType)
|
||||
; }
|
||||
; The type itself is stored in %typ.value, %typ.typecode just refers to the
|
||||
; type of reflect.Type. This function can be optimized because errorType is
|
||||
; known at compile time (after the interp pass has run).
|
||||
define i1 @main.isError(ptr %typ.typecode, ptr %typ.value, ptr %context) {
|
||||
entry:
|
||||
%result = call i1 @"reflect.Type.Implements$invoke"(ptr %typ.value, ptr getelementptr inbounds ({ ptr, i8, ptr }, ptr @"reflect/types.type:pointer:named:reflect.rawType", i32 0, i32 1), ptr @"reflect/types.type:named:error", ptr %typ.typecode, ptr undef)
|
||||
ret i1 %result
|
||||
}
|
||||
|
||||
; This Implements method call can not be optimized because itf is not known at
|
||||
; compile time.
|
||||
; func isUnknown(typ, itf reflect.Type) bool {
|
||||
; return typ.Implements(itf)
|
||||
; }
|
||||
define i1 @main.isUnknown(ptr %typ.typecode, ptr %typ.value, ptr %itf.typecode, ptr %itf.value, ptr %context) {
|
||||
entry:
|
||||
%result = call i1 @"reflect.Type.Implements$invoke"(ptr %typ.value, ptr %itf.typecode, ptr %itf.value, ptr %typ.typecode, ptr undef)
|
||||
ret i1 %result
|
||||
}
|
||||
|
||||
declare i1 @"reflect.Type.Implements$invoke"(ptr, ptr, ptr, ptr, ptr) #0
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %0) #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" }
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
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"
|
||||
|
||||
@"reflect/types.type:named:error" = internal constant { i8, i16, ptr, ptr } { i8 52, i16 0, ptr @"reflect/types.type:pointer:named:error", ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:interface:{Error:func:{}{basic:string}}" = internal constant { i8, ptr } { i8 20, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}" = internal constant { i8, ptr } { i8 21, ptr @"reflect/types.type:interface:{Error:func:{}{basic:string}}" }, align 4
|
||||
@"reflect/types.type:pointer:named:error" = internal constant { i8, i16, ptr } { i8 21, i16 0, ptr @"reflect/types.type:named:error" }, align 4
|
||||
@"reflect/types.type:pointer:named:reflect.rawType" = internal constant { ptr, i8, i16, ptr } { ptr null, i8 21, i16 0, ptr null }, align 4
|
||||
@"reflect/methods.Implements(reflect.Type) bool" = internal constant i8 0, align 1
|
||||
|
||||
define i1 @main.isError(ptr %typ.typecode, ptr %typ.value, ptr %context) {
|
||||
entry:
|
||||
%0 = call i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr %typ.value)
|
||||
ret i1 %0
|
||||
}
|
||||
|
||||
define i1 @main.isUnknown(ptr %typ.typecode, ptr %typ.value, ptr %itf.typecode, ptr %itf.value, ptr %context) {
|
||||
entry:
|
||||
%result = call i1 @"reflect.Type.Implements$invoke"(ptr %typ.value, ptr %itf.typecode, ptr %itf.value, ptr %typ.typecode, ptr undef)
|
||||
ret i1 %result
|
||||
}
|
||||
|
||||
declare i1 @"reflect.Type.Implements$invoke"(ptr, ptr, ptr, ptr, ptr) #0
|
||||
|
||||
declare i1 @"interface:{Error:func:{}{basic:string}}.$typeassert"(ptr) #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" }
|
||||
Reference in New Issue
Block a user