mirror of
https://github.com/tinygo-org/tinygo.git
synced 2026-08-18 03:24:00 +00:00
compiler: canonicalize generic instance identities
x/tools SSA names and go/types strings can preserve the source spelling of type arguments, so aliases can make distinct instances collide. Use the canonical type encoding for function names, synthetic local type owners, instantiated named types, and method sets.
This commit is contained in:
+25
-12
@@ -599,8 +599,21 @@ func (c *compilerContext) getTypeCodeName(t types.Type) (name string, isLocal bo
|
|||||||
case *types.Named:
|
case *types.Named:
|
||||||
tn := t.Obj()
|
tn := t.Obj()
|
||||||
if tn.Pkg() == nil || tn.Parent() == tn.Pkg().Scope() {
|
if tn.Pkg() == nil || tn.Parent() == tn.Pkg().Scope() {
|
||||||
// Package-scope or builtin: the printed name is unique.
|
name := tn.Name()
|
||||||
return "named:" + t.String(), false
|
if tn.Pkg() != nil {
|
||||||
|
name = tn.Pkg().Path() + "." + name
|
||||||
|
}
|
||||||
|
isLocal := false
|
||||||
|
if targs := t.TypeArgs(); targs.Len() != 0 {
|
||||||
|
parts := make([]string, targs.Len())
|
||||||
|
for i := range parts {
|
||||||
|
var local bool
|
||||||
|
parts[i], local = c.getTypeCodeName(targs.At(i))
|
||||||
|
isLocal = isLocal || local
|
||||||
|
}
|
||||||
|
name += "[" + strings.Join(parts, ",") + "]"
|
||||||
|
}
|
||||||
|
return "named:" + name, isLocal
|
||||||
}
|
}
|
||||||
if tn.Parent() != nil {
|
if tn.Parent() != nil {
|
||||||
// Ordinary function-local type. Use the un-//line-adjusted
|
// Ordinary function-local type. Use the un-//line-adjusted
|
||||||
@@ -707,8 +720,8 @@ func (c *compilerContext) getTypeCodeName(t types.Type) (name string, isLocal bo
|
|||||||
// Synthetic TypeNames are produced by generic instantiation: two
|
// Synthetic TypeNames are produced by generic instantiation: two
|
||||||
// instantiations of the same generic function (e.g. F[int] and
|
// instantiations of the same generic function (e.g. F[int] and
|
||||||
// F[string]) produce TypeNames with the same printed name and the
|
// F[string]) produce TypeNames with the same printed name and the
|
||||||
// same source position, so each is named with the enclosing
|
// same source position, so each is named with the enclosing instance's
|
||||||
// instance's RelString as prefix. RelString encodes the type
|
// canonical function name as prefix. The function name encodes the type
|
||||||
// arguments, matching Go's runtime behavior, where F[int].Inner and
|
// arguments, matching Go's runtime behavior, where F[int].Inner and
|
||||||
// F[string].Inner are distinct types even when Inner does not mention
|
// F[string].Inner are distinct types even when Inner does not mention
|
||||||
// the type parameter.
|
// the type parameter.
|
||||||
@@ -717,9 +730,9 @@ func (c *compilerContext) getTypeCodeName(t types.Type) (name string, isLocal bo
|
|||||||
// of F[int] is compiled in every package that calls F[int]); its
|
// of F[int] is compiled in every package that calls F[int]); its
|
||||||
// reflect/types.type:* global has LinkOnceODRLinkage and is merged by
|
// reflect/types.type:* global has LinkOnceODRLinkage and is merged by
|
||||||
// name at link time. The chosen name therefore depends only on
|
// name at link time. The chosen name therefore depends only on
|
||||||
// intrinsic SSA properties (RelString and the raw token.Pos used as a
|
// intrinsic SSA properties (the canonical function name and raw token.Pos),
|
||||||
// sort key), so any package compiling the same instance produces the
|
// so any package compiling the same instance produces the same
|
||||||
// same identifier.
|
// identifier.
|
||||||
//
|
//
|
||||||
// Ordinary function-local TypeNames (TypeName.Parent() != nil) are
|
// Ordinary function-local TypeNames (TypeName.Parent() != nil) are
|
||||||
// not handled here: they are nameable only inside their declaring
|
// not handled here: they are nameable only inside their declaring
|
||||||
@@ -807,9 +820,8 @@ func (c *compilerContext) scanLocalTypes(ssaPkg *ssa.Package) {
|
|||||||
|
|
||||||
// registerSyntheticLocalTypes walks every type reachable from fn's
|
// registerSyntheticLocalTypes walks every type reachable from fn's
|
||||||
// body and records each synthetic *types.Named (TypeName.Parent() ==
|
// body and records each synthetic *types.Named (TypeName.Parent() ==
|
||||||
// nil) in c.localTypeNames. Each is named with fn.RelString as the
|
// nil) in c.localTypeNames. Each is named with the canonical function
|
||||||
// owning function plus a per-function counter assigned in source
|
// name plus a per-function counter assigned in source order.
|
||||||
// order.
|
|
||||||
//
|
//
|
||||||
// First-writer-wins: a *types.Named already present in
|
// First-writer-wins: a *types.Named already present in
|
||||||
// c.localTypeNames is left alone, so a synthetic type reachable from
|
// c.localTypeNames is left alone, so a synthetic type reachable from
|
||||||
@@ -920,7 +932,7 @@ func (c *compilerContext) registerSyntheticLocalTypes(fn *ssa.Function) {
|
|||||||
sort.Slice(found, func(i, j int) bool {
|
sort.Slice(found, func(i, j int) bool {
|
||||||
return found[i].Obj().Pos() < found[j].Obj().Pos()
|
return found[i].Obj().Pos() < found[j].Obj().Pos()
|
||||||
})
|
})
|
||||||
enclosing := fn.RelString(nil)
|
enclosing := c.canonicalFunctionName(fn)
|
||||||
for i, named := range found {
|
for i, named := range found {
|
||||||
c.localTypeNames.Set(named, fmt.Sprintf("%s.%s$%d", enclosing, named.Obj().Name(), i))
|
c.localTypeNames.Set(named, fmt.Sprintf("%s.%s$%d", enclosing, named.Obj().Name(), i))
|
||||||
}
|
}
|
||||||
@@ -929,7 +941,8 @@ func (c *compilerContext) registerSyntheticLocalTypes(fn *ssa.Function) {
|
|||||||
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
// getTypeMethodSet returns a reference (GEP) to a global method set. This
|
||||||
// method set should be unreferenced after the interface lowering pass.
|
// method set should be unreferenced after the interface lowering pass.
|
||||||
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
func (c *compilerContext) getTypeMethodSet(typ types.Type) llvm.Value {
|
||||||
globalName := typ.String() + "$methodset"
|
typeName, _ := c.getTypeCodeName(typ)
|
||||||
|
globalName := typeName + "$methodset"
|
||||||
global := c.mod.NamedGlobal(globalName)
|
global := c.mod.NamedGlobal(globalName)
|
||||||
if global.IsNil() {
|
if global.IsNil() {
|
||||||
ms := c.program.MethodSets.MethodSet(typ)
|
ms := c.program.MethodSets.MethodSet(typ)
|
||||||
|
|||||||
+5
-19
@@ -320,13 +320,7 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
|||||||
}
|
}
|
||||||
info := functionInfo{
|
info := functionInfo{
|
||||||
// Pick the default linkName.
|
// Pick the default linkName.
|
||||||
linkName: f.RelString(nil),
|
linkName: c.canonicalFunctionName(f),
|
||||||
}
|
|
||||||
|
|
||||||
// RelString is not unique for local type arguments, so add a suffix
|
|
||||||
// when needed.
|
|
||||||
if suffix := c.localTypeArgsSuffix(f); suffix != "" {
|
|
||||||
info.linkName += suffix
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for a few runtime functions that are treated specially.
|
// Check for a few runtime functions that are treated specially.
|
||||||
@@ -353,24 +347,16 @@ func (c *compilerContext) getFunctionInfo(f *ssa.Function) functionInfo {
|
|||||||
return info
|
return info
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *compilerContext) localTypeArgsSuffix(f *ssa.Function) string {
|
func (c *compilerContext) canonicalFunctionName(f *ssa.Function) string {
|
||||||
typeArgs := f.TypeArgs()
|
typeArgs := f.TypeArgs()
|
||||||
if len(typeArgs) == 0 {
|
if len(typeArgs) == 0 {
|
||||||
return ""
|
return f.RelString(nil)
|
||||||
}
|
}
|
||||||
var hasLocal bool
|
|
||||||
parts := make([]string, len(typeArgs))
|
parts := make([]string, len(typeArgs))
|
||||||
for i, ta := range typeArgs {
|
for i, ta := range typeArgs {
|
||||||
name, isLocal := c.getTypeCodeName(ta)
|
parts[i], _ = c.getTypeCodeName(ta)
|
||||||
if isLocal {
|
|
||||||
hasLocal = true
|
|
||||||
}
|
|
||||||
parts[i] = name
|
|
||||||
}
|
}
|
||||||
if !hasLocal {
|
return f.Origin().RelString(nil) + "[" + strings.Join(parts, ",") + "]"
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return "$localtype:" + strings.Join(parts, ",")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
// parsePragmas is used by getFunctionInfo to parse function pragmas such as
|
||||||
|
|||||||
Vendored
+15
-9
@@ -17,12 +17,12 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.aliasSize32(ptr %context) unnamed_addr #1 {
|
define hidden i32 @main.aliasSize32(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @"main.aliasSize[main.F]"(ptr undef)
|
%0 = call i32 @"main.aliasSize[basic:float32]"(ptr undef)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr hidden i32 @"main.aliasSize[main.F]"(ptr %context) unnamed_addr #1 {
|
define linkonce_odr hidden i32 @"main.aliasSize[basic:float32]"(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
ret i32 4
|
ret i32 4
|
||||||
}
|
}
|
||||||
@@ -30,15 +30,21 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden i32 @main.aliasSize64(ptr %context) unnamed_addr #1 {
|
define hidden i32 @main.aliasSize64(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i32 @"main.aliasSize[main.F]"(ptr undef)
|
%0 = call i32 @"main.aliasSize[basic:float64]"(ptr undef)
|
||||||
ret i32 %0
|
ret i32 %0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; Function Attrs: nounwind
|
||||||
|
define linkonce_odr hidden i32 @"main.aliasSize[basic:float64]"(ptr %context) unnamed_addr #1 {
|
||||||
|
entry:
|
||||||
|
ret i32 8
|
||||||
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
define hidden void @main.main(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call %"main.Point[float32]" @"main.Add[float32]"(float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, ptr undef)
|
%0 = call %"main.Point[float32]" @"main.Add[basic:float32]"(float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, ptr undef)
|
||||||
%1 = call %"main.Point[int]" @"main.Add[int]"(i32 0, i32 0, i32 0, i32 0, ptr undef)
|
%1 = call %"main.Point[int]" @"main.Add[basic:int]"(i32 0, i32 0, i32 0, i32 0, ptr undef)
|
||||||
%2 = call i32 @main.aliasSize32(ptr undef)
|
%2 = call i32 @main.aliasSize32(ptr undef)
|
||||||
call void @main.checkSize(i32 %2, ptr undef) #4
|
call void @main.checkSize(i32 %2, ptr undef) #4
|
||||||
%3 = call i32 @main.aliasSize64(ptr undef)
|
%3 = call i32 @main.aliasSize64(ptr undef)
|
||||||
@@ -47,7 +53,7 @@ entry:
|
|||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, ptr %context) unnamed_addr #1 {
|
define linkonce_odr hidden %"main.Point[float32]" @"main.Add[basic:float32]"(float %a.X, float %a.Y, float %b.X, float %b.Y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
@@ -110,7 +116,7 @@ declare void @main.checkSize(i32, ptr) #0
|
|||||||
declare void @runtime.nilPanic(ptr) #0
|
declare void @runtime.nilPanic(ptr) #0
|
||||||
|
|
||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define linkonce_odr hidden %"main.Point[int]" @"main.Add[int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 {
|
define linkonce_odr hidden %"main.Point[int]" @"main.Add[basic:int]"(i32 %a.X, i32 %a.Y, i32 %b.X, i32 %b.Y, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%stackalloc = alloca i8, align 1
|
%stackalloc = alloca i8, align 1
|
||||||
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
%a = call align 4 dereferenceable(8) ptr @runtime.alloc(i32 8, ptr nonnull inttoptr (i32 3 to ptr), ptr undef) #4
|
||||||
@@ -170,7 +176,7 @@ declare void @main.checkBool(i1, ptr) #0
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.aliasMethod32(ptr %x.typecode, ptr %x.value, ptr %context) unnamed_addr #1 {
|
define hidden void @main.aliasMethod32(ptr %x.typecode, ptr %x.value, ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
%0 = call i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[main.F]}}.$typeassert"(ptr %x.typecode) #4
|
%0 = call i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[basic:float32]}}.$typeassert"(ptr %x.typecode) #4
|
||||||
br i1 %0, label %typeassert.ok, label %typeassert.next
|
br i1 %0, label %typeassert.ok, label %typeassert.next
|
||||||
|
|
||||||
typeassert.next: ; preds = %typeassert.ok, %entry
|
typeassert.next: ; preds = %typeassert.ok, %entry
|
||||||
@@ -181,7 +187,7 @@ typeassert.ok: ; preds = %entry
|
|||||||
br label %typeassert.next
|
br label %typeassert.next
|
||||||
}
|
}
|
||||||
|
|
||||||
declare i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[main.F]}}.$typeassert"(ptr) #3
|
declare i1 @"interface:{Get:func:{}{named:main.aliasMethodResult[basic:float32]}}.$typeassert"(ptr) #3
|
||||||
|
|
||||||
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #0 = { "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
attributes #1 = { nounwind "target-features"="+bulk-memory,+bulk-memory-opt,+call-indirect-overlong,+mutable-globals,+nontrapping-fptoint,+sign-ext,-multivalue,-reference-types" }
|
||||||
|
|||||||
Vendored
+2
-2
@@ -50,12 +50,12 @@ entry:
|
|||||||
; Function Attrs: nounwind
|
; Function Attrs: nounwind
|
||||||
define hidden void @main.useGeneric(ptr %context) unnamed_addr #1 {
|
define hidden void @main.useGeneric(ptr %context) unnamed_addr #1 {
|
||||||
entry:
|
entry:
|
||||||
call void @"main.noinlineGenericFunc[int8]"(ptr undef)
|
call void @"main.noinlineGenericFunc[basic:int8]"(ptr undef)
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; Function Attrs: noinline nounwind
|
; Function Attrs: noinline nounwind
|
||||||
define linkonce_odr hidden void @"main.noinlineGenericFunc[int8]"(ptr %context) unnamed_addr #4 {
|
define linkonce_odr hidden void @"main.noinlineGenericFunc[basic:int8]"(ptr %context) unnamed_addr #4 {
|
||||||
entry:
|
entry:
|
||||||
ret void
|
ret void
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+4
-4
@@ -48,13 +48,13 @@ issue4931PairB labels: 0
|
|||||||
issue4931MethodA labels: 0
|
issue4931MethodA labels: 0
|
||||||
issue4931MethodB labels: 0
|
issue4931MethodB labels: 0
|
||||||
ok: aliasSize[float32]==32
|
ok: aliasSize[float32]==32
|
||||||
BUG: aliasSize[float64]==64
|
ok: aliasSize[float64]==64
|
||||||
ok: aliasLocal[float32] accepts own
|
ok: aliasLocal[float32] accepts own
|
||||||
ok: aliasLocal[float64] accepts own
|
ok: aliasLocal[float64] accepts own
|
||||||
BUG: aliasLocal[float32] rejects [float64]
|
ok: aliasLocal[float32] rejects [float64]
|
||||||
BUG: aliasLocal[float64] rejects [float32]
|
ok: aliasLocal[float64] rejects [float32]
|
||||||
ok: aliasBox[float32] implements Get() float32
|
ok: aliasBox[float32] implements Get() float32
|
||||||
BUG: aliasBox[float64] implements Get() float64
|
ok: aliasBox[float64] implements Get() float64
|
||||||
ok: aliasMethod[float32] accepts own
|
ok: aliasMethod[float32] accepts own
|
||||||
ok: aliasMethod[float64] accepts own
|
ok: aliasMethod[float64] accepts own
|
||||||
BUG: aliasMethod[float32] rejects [float64]
|
BUG: aliasMethod[float32] rejects [float64]
|
||||||
|
|||||||
Reference in New Issue
Block a user