From f2e8d7112cbe9a05d411c23a98cb99edfcf45369 Mon Sep 17 00:00:00 2001 From: Ayke van Laethem Date: Fri, 4 Jun 2021 14:43:25 +0200 Subject: [PATCH] compiler: refactor method names This commit includes two changes: * It makes unexported interface methods package-private, so that it's not possible to type-assert on an unexported method in a different package. * It makes the globals used to identify interface methods defined globals, so that they can (eventually) be left in the program for an eventual non-LTO build mode. --- compiler/compiler.go | 2 +- compiler/interface.go | 15 +++++++++++++-- compiler/testdata/interface.ll | 12 ++++++------ transform/interface-lowering.go | 10 +++++++--- transform/rtcalls.go | 2 +- transform/testdata/interface.ll | 12 ++++++------ transform/testdata/reflect-implements.ll | 14 +++++++------- transform/testdata/reflect-implements.out.ll | 12 ++++++------ 8 files changed, 47 insertions(+), 32 deletions(-) diff --git a/compiler/compiler.go b/compiler/compiler.go index f25832ac..925b61bf 100644 --- a/compiler/compiler.go +++ b/compiler/compiler.go @@ -23,7 +23,7 @@ import ( // Version of the compiler pacakge. Must be incremented each time the compiler // package changes in a way that affects the generated LLVM module. // This version is independent of the TinyGo version number. -const Version = 10 // last change: context parameter in go wrapper +const Version = 11 // last change: change method name globals func init() { llvm.InitializeAllTargets() diff --git a/compiler/interface.go b/compiler/interface.go index eda7e365..5d8895d7 100644 --- a/compiler/interface.go +++ b/compiler/interface.go @@ -311,10 +311,21 @@ func (c *compilerContext) getInterfaceMethodSet(typ types.Type) llvm.Value { // used during the interface lowering pass. func (c *compilerContext) getMethodSignature(method *types.Func) llvm.Value { signature := methodSignature(method) - signatureGlobal := c.mod.NamedGlobal("func " + signature) + var globalName string + if token.IsExported(method.Name()) { + globalName = "reflect/methods." + signature + } else { + globalName = method.Type().(*types.Signature).Recv().Pkg().Path() + ".$methods." + signature + } + signatureGlobal := c.mod.NamedGlobal(globalName) if signatureGlobal.IsNil() { - signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), "func "+signature) + // TODO: put something useful in these globals, such as the method + // signature. Useful to one day implement reflect.Value.Method(n). + signatureGlobal = llvm.AddGlobal(c.mod, c.ctx.Int8Type(), globalName) + signatureGlobal.SetInitializer(llvm.ConstInt(c.ctx.Int8Type(), 0, false)) + signatureGlobal.SetLinkage(llvm.LinkOnceODRLinkage) signatureGlobal.SetGlobalConstant(true) + signatureGlobal.SetAlignment(1) } return signatureGlobal } diff --git a/compiler/testdata/interface.ll b/compiler/testdata/interface.ll index eb217bdc..be9e229a 100644 --- a/compiler/testdata/interface.ll +++ b/compiler/testdata/interface.ll @@ -13,15 +13,15 @@ target triple = "wasm32--wasi" @"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: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: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}}" } -@"func Error() string" = external constant i8 -@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] +@"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.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:{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: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}}" } -@"func String() string" = external constant i8 -@"reflect/types.interface:interface{String() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func String() string"] +@"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.typeid:basic:int" = external constant i8 -@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] +@"error$interface" = linkonce_odr constant [1 x i8*] [i8* @"reflect/methods.Error() string"] declare noalias nonnull i8* @runtime.alloc(i32, i8*, i8*) @@ -92,7 +92,7 @@ typeassert.next: ; preds = %typeassert.ok, %ent define hidden %runtime._string @main.callErrorMethod(i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) unnamed_addr { 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 @"func Error() string", i8* undef, i8* null) + %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) %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) ret %runtime._string %0 diff --git a/transform/interface-lowering.go b/transform/interface-lowering.go index 79b3361a..57c8c76d 100644 --- a/transform/interface-lowering.go +++ b/transform/interface-lowering.go @@ -54,10 +54,14 @@ type signatureInfo struct { // methodName takes a method name like "func String()" and returns only the // name, which is "String" in this case. func (s *signatureInfo) methodName() string { - if !strings.HasPrefix(s.name, "func ") { - panic("signature must start with \"func \"") + 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") } - methodName := s.name[len("func "):] if openingParen := strings.IndexByte(methodName, '('); openingParen < 0 { panic("no opening paren in signature name") } else { diff --git a/transform/rtcalls.go b/transform/rtcalls.go index 0ced0bfe..c10b3c28 100644 --- a/transform/rtcalls.go +++ b/transform/rtcalls.go @@ -105,7 +105,7 @@ func OptimizeStringEqual(mod llvm.Module) { // 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("func Implements(reflect.Type) bool") + implementsSignature := mod.NamedGlobal("reflect/methods.Implements(reflect.Type) bool") if implementsSignature.IsNil() { return } diff --git a/transform/testdata/interface.ll b/transform/testdata/interface.ll index c67595a2..2101725a 100644 --- a/transform/testdata/interface.ll +++ b/transform/testdata/interface.ll @@ -8,11 +8,11 @@ target triple = "armv7m-none-eabi" @"reflect/types.typeid:basic:uint8" = external constant i8 @"reflect/types.typeid:basic:int16" = external constant i8 @"reflect/types.type:basic:int" = private constant %runtime.typecodeID zeroinitializer -@"func NeverImplementedMethod()" = external constant i8 -@"Unmatched$interface" = private constant [1 x i8*] [i8* @"func NeverImplementedMethod()"] -@"func Double() int" = external constant i8 -@"Doubler$interface" = private constant [1 x i8*] [i8* @"func Double() int"] -@"Number$methodset" = private constant [1 x %runtime.interfaceMethodInfo] [%runtime.interfaceMethodInfo { i8* @"func Double() int", i32 ptrtoint (i32 (i8*, i8*)* @"(Number).Double$invoke" to i32) }] +@"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 +@"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*)* @"(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) } declare i1 @runtime.interfaceImplements(i32, i8**) @@ -48,7 +48,7 @@ typeswitch.notUnmatched: br i1 %isDoubler, label %typeswitch.Doubler, label %typeswitch.notDoubler 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 @"func Double() int") + %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.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) diff --git a/transform/testdata/reflect-implements.ll b/transform/testdata/reflect-implements.ll index 22c76485..29f38243 100644 --- a/transform/testdata/reflect-implements.ll +++ b/transform/testdata/reflect-implements.ll @@ -6,11 +6,11 @@ target triple = "i686--linux" @"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: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 } -@"func Error() string" = external constant i8 -@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] -@"func Align() int" = external constant i8 -@"func Implements(reflect.Type) bool" = external constant i8 -@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"func Align() int", i8* @"func Implements(reflect.Type) bool"] +@"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/methods.Align() int" = 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/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.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer @"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer @@ -28,7 +28,7 @@ declare i32 @runtime.interfaceMethod(i32, i8**, i8*, i8*, i8*) ; known at compile time (after the interp pass has run). define i1 @main.isError(i32 %typ.typecode, i8* %typ.value, i8* %context, i8* %parentHandle) { 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 @"func Implements(reflect.Type) bool", i8* undef, i8* null) + %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) %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 @@ -41,7 +41,7 @@ entry: ; } define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) { 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 @"func Implements(reflect.Type) bool", i8* undef, i8* null) + %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) %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 diff --git a/transform/testdata/reflect-implements.out.ll b/transform/testdata/reflect-implements.out.ll index 03d97769..f1bc92c1 100644 --- a/transform/testdata/reflect-implements.out.ll +++ b/transform/testdata/reflect-implements.out.ll @@ -6,11 +6,11 @@ target triple = "i686--linux" @"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: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 } -@"func Error() string" = external constant i8 -@"reflect/types.interface:interface{Error() string}$interface" = linkonce_odr constant [1 x i8*] [i8* @"func Error() string"] -@"func Align() int" = external constant i8 -@"func Implements(reflect.Type) bool" = external constant i8 -@"reflect.Type$interface" = linkonce_odr constant [2 x i8*] [i8* @"func Align() int", i8* @"func Implements(reflect.Type) bool"] +@"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/methods.Align() int" = 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/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.rawType$methodset" = linkonce_odr constant [20 x %runtime.interfaceMethodInfo] zeroinitializer @"reflect/types.type:basic:uintptr" = linkonce_odr constant %runtime.typecodeID zeroinitializer @@ -28,7 +28,7 @@ entry: define i1 @main.isUnknown(i32 %typ.typecode, i8* %typ.value, i32 %itf.typecode, i8* %itf.value, i8* %context, i8* %parentHandle) { 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 @"func Implements(reflect.Type) bool", i8* undef, i8* null) + %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) %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