Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions compiler/compiler.go
Original file line number Diff line number Diff line change
Expand Up @@ -1535,6 +1535,15 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
b.setDebugLocation(getPos(instr))
}

switch instr := instr.(type) {
case *ssa.Call:
b.markReflectMethodUse(&instr.Call)
case *ssa.Defer:
b.markReflectMethodUse(&instr.Call)
case *ssa.Go:
b.markReflectMethodUse(&instr.Call)
}

switch instr := instr.(type) {
case ssa.Value:
if value, err := b.createExpr(instr); err != nil {
Expand Down Expand Up @@ -1603,6 +1612,36 @@ func (b *builder) createInstruction(instr ssa.Instruction) {
}
}

func (b *builder) markReflectMethodUse(call *ssa.CallCommon) {
pkg := b.fn.Pkg
if pkg == nil && b.fn.Origin() != nil {
pkg = b.fn.Origin().Pkg
}
if pkg != nil {
switch pkg.Pkg.Path() {
case "reflect", "internal/reflectlite":
return
}
}

var method *types.Func
if call.IsInvoke() {
method = call.Method
} else if callee := call.StaticCallee(); callee != nil {
if object := callee.Object(); object != nil {
method, _ = object.(*types.Func)
}
}
if method == nil || method.Pkg() == nil || method.Pkg().Path() != "reflect" {
return
}
switch method.Name() {
case "Method", "MethodByName", "Methods":
attr := b.ctx.CreateStringAttribute("tinygo-reflect-method", "")
b.llvmFn.AddFunctionAttr(attr)
}
}

func (b *builder) setValue(value ssa.Value, llvmValue llvm.Value) {
if b.isAggregateValue(value.Type()) && !llvmValue.IsNil() && llvmValue.Type().TypeKind() == llvm.PointerTypeKind {
b.indirectValues[value] = llvmValue
Expand Down
48 changes: 38 additions & 10 deletions compiler/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,13 @@ func (c *compilerContext) getTypeCode(typ types.Type) llvm.Value {
}
methods = append(methods, fn)
}
methodEntryType := types.NewStruct([]*types.Var{
types.NewVar(token.NoPos, nil, "signature", types.Typ[types.UnsafePointer]),
types.NewVar(token.NoPos, nil, "name", types.Typ[types.UnsafePointer]),
}, nil)
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)))),
types.NewVar(token.NoPos, nil, "methods", types.NewArray(methodEntryType, int64(len(methods)))),
}, nil)
methodSetValue := c.getMethodSetValue(methods)
switch typ := typ.(type) {
Expand Down Expand Up @@ -1183,12 +1187,15 @@ func (c *compilerContext) getMethodsString(itf *types.Interface) string {
}

// getMethodSetValue creates the method set struct value for a list of methods.
// The struct contains a length and a sorted array of method signature pointers.
// The struct contains a length and a sorted array of {signature, name} entries.
// Each entry pairs a method signature pointer (for Implements comparison) with
// a pointer to the method's null-terminated name string.
func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
// Create a sorted list of method signature global names.
type methodRef struct {
name string
value llvm.Value
sigGlobalName string
methodName string
sigValue llvm.Value
}
var refs []methodRef
for _, method := range methods {
Expand Down Expand Up @@ -1219,23 +1226,44 @@ func (c *compilerContext) getMethodSetValue(methods []*types.Func) llvm.Value {
value.AddMetadata(0, diglobal)
}
}
refs = append(refs, methodRef{globalName, value})
refs = append(refs, methodRef{globalName, name, value})
}
sort.Slice(refs, func(i, j int) bool {
return refs[i].name < refs[j].name
return refs[i].sigGlobalName < refs[j].sigGlobalName
})

var values []llvm.Value
pairType := c.ctx.StructType([]llvm.Type{c.dataPtrType, c.dataPtrType}, false)
var pairs []llvm.Value
for _, ref := range refs {
values = append(values, ref.value)
nameGlobal := c.getMethodNameGlobal(ref.methodName)
pair := c.ctx.ConstStruct([]llvm.Value{ref.sigValue, nameGlobal}, false)
pairs = append(pairs, pair)
}

return c.ctx.ConstStruct([]llvm.Value{
llvm.ConstInt(c.uintptrType, uint64(len(values)), false),
llvm.ConstArray(c.dataPtrType, values),
llvm.ConstInt(c.uintptrType, uint64(len(pairs)), false),
llvm.ConstArray(pairType, pairs),
}, false)
}

// getMethodNameGlobal returns a global containing the null-terminated method
// name string, creating it if needed.
func (c *compilerContext) getMethodNameGlobal(name string) llvm.Value {
globalName := "reflect/types.methodname:" + name
g := c.mod.NamedGlobal(globalName)
if !g.IsNil() {
return g
}
nameBytes := c.ctx.ConstString(name+"\x00", false)
g = llvm.AddGlobal(c.mod, nameBytes.Type(), globalName)
g.SetInitializer(nameBytes)
g.SetGlobalConstant(true)
g.SetLinkage(llvm.LinkOnceODRLinkage)
g.SetAlignment(1)
g.SetUnnamedAddr(true)
return g
}

// 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.
Expand Down
9 changes: 5 additions & 4 deletions compiler/testdata/go1.27.ll
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ target triple = "wasm32-unknown-wasi"
%runtime._interface = type { ptr, ptr }

@"reflect/types.signature:Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
@"reflect/types.type:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] } { ptr @"named:main.genericMethod$methodset", i8 122, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, { i32, [1 x ptr] } }, ptr @"reflect/types.type:pointer:named:main.genericMethod", i32 0, i32 1), ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] }, [19 x i8] c"main.genericMethod\00" }, align 4
@"reflect/types.methodname:Regular" = linkonce_odr unnamed_addr constant [8 x i8] c"Regular\00", align 1
@"reflect/types.type:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [19 x i8] } { ptr @"named:main.genericMethod$methodset", i8 122, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, { i32, [1 x { ptr, ptr }] } }, ptr @"reflect/types.type:pointer:named:main.genericMethod", i32 0, i32 1), ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}", ptr @"reflect/types.methodname:Regular" }] }, [19 x i8] c"main.genericMethod\00" }, align 4
@"reflect/types.type.pkgpath:main" = linkonce_odr unnamed_addr constant [5 x i8] c"main\00", align 1
@"reflect/types.type:pointer:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, { i32, [1 x ptr] } } { ptr @"pointer:named:main.genericMethod$methodset", i8 -43, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), { i32, [1 x ptr] } { i32 1, [1 x ptr] [ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}"] } }, align 4
@"reflect/types.type:pointer:named:main.genericMethod" = linkonce_odr constant { ptr, i8, i16, ptr, { i32, [1 x { ptr, ptr }] } } { ptr @"pointer:named:main.genericMethod$methodset", i8 -43, i16 -32767, ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Regular:func:{basic:int}{basic:int}", ptr @"reflect/types.methodname:Regular" }] } }, align 4
@"reflect/methods.Regular:func:{basic:int}{basic:int}" = linkonce_odr constant i8 0, align 1
@"main$string" = internal unnamed_addr constant [18 x i8] c"main.genericMethod", align 1
@"main$string.1" = internal unnamed_addr constant [7 x i8] c"Regular", align 1
Expand All @@ -21,7 +22,7 @@ target triple = "wasm32-unknown-wasi"
@"reflect/types.type:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, [23 x i8] } { i8 122, i16 0, ptr @"reflect/types.type:pointer:named:main.onlyGenericMethod", ptr @"reflect/types.type:struct:{}", ptr @"reflect/types.type.pkgpath:main", [23 x i8] c"main.onlyGenericMethod\00" }, align 4
@"reflect/types.type:pointer:named:main.onlyGenericMethod" = linkonce_odr constant { i8, i16, ptr } { i8 -43, i16 0, ptr @"reflect/types.type:named:main.onlyGenericMethod" }, align 4

declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
declare void @runtime.trackPointer(ptr readonly captures(none), ptr, ptr) #0

; Function Attrs: nounwind
define hidden void @main.init(ptr %context) unnamed_addr #1 {
Expand All @@ -43,7 +44,7 @@ entry:
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
call void @runtime.trackPointer(ptr nonnull @"reflect/types.type:named:main.onlyGenericMethod", ptr nonnull %stackalloc, ptr undef) #2
call void @runtime.trackPointer(ptr null, ptr nonnull %stackalloc, ptr undef) #2
ret { %runtime._interface, %runtime._interface } { %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x ptr] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), ptr null }, %runtime._interface { ptr @"reflect/types.type:named:main.onlyGenericMethod", ptr null } }
ret { %runtime._interface, %runtime._interface } { %runtime._interface { ptr getelementptr ({ ptr, i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [19 x i8] }, ptr @"reflect/types.type:named:main.genericMethod", i32 0, i32 1), ptr null }, %runtime._interface { ptr @"reflect/types.type:named:main.onlyGenericMethod", ptr null } }
}

; Function Attrs: nounwind
Expand Down
8 changes: 5 additions & 3 deletions compiler/testdata/interface.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ target triple = "wasm32-unknown-wasi"
@"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.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 -32767, 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.methodname:Error" = linkonce_odr unnamed_addr constant [6 x i8] c"Error\00", align 1
@"reflect/types.type:named:error" = linkonce_odr constant { i8, i16, ptr, ptr, ptr, { i32, [1 x { ptr, ptr }] }, [7 x i8] } { i8 116, i16 -32767, 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, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Error:func:{}{basic:string}", ptr @"reflect/types.methodname:Error" }] }, [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, { 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:interface:{Error:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x { ptr, ptr }] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{Error:func:{}{basic:string}}", { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:Error:func:{}{basic:string}", ptr @"reflect/types.methodname:Error" }] } }, 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.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.methodname:String" = linkonce_odr unnamed_addr constant [7 x i8] c"String\00", align 1
@"reflect/types.type:interface:{String:func:{}{basic:string}}" = linkonce_odr constant { i8, ptr, { i32, [1 x { ptr, ptr }] } } { i8 84, ptr @"reflect/types.type:pointer:interface:{String:func:{}{basic:string}}", { i32, [1 x { ptr, ptr }] } { i32 1, [1 x { ptr, ptr }] [{ ptr, ptr } { ptr @"reflect/types.signature:String:func:{}{basic:string}", ptr @"reflect/types.methodname:String" }] } }, align 4
@"reflect/types.typeid:basic:int" = external constant i8

declare void @runtime.trackPointer(ptr nocapture readonly, ptr, ptr) #0
Expand Down
Loading
Loading