]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile,cmd/link: introduce generic interface call relocations
authorKeith Randall <khr@golang.org>
Fri, 22 Oct 2021 01:04:55 +0000 (18:04 -0700)
committerKeith Randall <khr@golang.org>
Mon, 25 Oct 2021 20:39:17 +0000 (20:39 +0000)
To capture the fact that a method was called on a generic interface,
so we can make sure the linker doesn't throw away any implementations
that might be the method called.

See the comment in reflect.go for details.

Fixes #49049

Change-Id: I0be74b6e727c1ecefedae072b149f59d539dc1e9
Reviewed-on: https://go-review.googlesource.com/c/go/+/357835
Trust: Keith Randall <khr@golang.org>
Trust: Dan Scales <danscales@google.com>
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Mui <cherryyz@google.com>
Reviewed-by: Dan Scales <danscales@google.com>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
src/cmd/compile/internal/reflectdata/reflect.go
src/cmd/compile/internal/walk/expr.go
src/cmd/internal/objabi/reloctype.go
src/cmd/internal/objabi/reloctype_string.go
src/cmd/link/internal/ld/deadcode.go
test/typeparam/issue49049.go [new file with mode: 0644]

index 27e6188ab72217b59134bd92be8e47cc84bc2416..369ee7542247953ac9e5a477aadd24468d1faf68 100644 (file)
@@ -18,6 +18,7 @@ import (
        "cmd/compile/internal/inline"
        "cmd/compile/internal/ir"
        "cmd/compile/internal/objw"
+       "cmd/compile/internal/staticdata"
        "cmd/compile/internal/typebits"
        "cmd/compile/internal/typecheck"
        "cmd/compile/internal/types"
@@ -1995,8 +1996,33 @@ func MarkUsedIfaceMethod(n *ir.CallExpr) {
        dot := n.X.(*ir.SelectorExpr)
        ityp := dot.X.Type()
        if ityp.HasShape() {
-               base.Fatalf("marking method of shape type used %+v %s", ityp, dot.Sel.Name)
+               // Here we're calling a method on a generic interface. Something like:
+               //
+               // type I[T any] interface { foo() T }
+               // func f[T any](x I[T]) {
+               //     ... = x.foo()
+               // }
+               // f[int](...)
+               // f[string](...)
+               //
+               // In this case, in f we're calling foo on a generic interface.
+               // Which method could that be? Normally we could match the method
+               // both by name and by type. But in this case we don't really know
+               // the type of the method we're calling. It could be func()int
+               // or func()string. So we match on just the function name, instead
+               // of both the name and the type used for the non-generic case below.
+               // TODO: instantiations at least know the shape of the instantiated
+               // type, and the linker could do more complicated matching using
+               // some sort of fuzzy shape matching. For now, only use the name
+               // of the method for matching.
+               r := obj.Addrel(ir.CurFunc.LSym)
+               // We use a separate symbol just to tell the linker the method name.
+               // (The symbol itself is not needed in the final binary.)
+               r.Sym = staticdata.StringSym(src.NoXPos, dot.Sel.Name)
+               r.Type = objabi.R_USEGENERICIFACEMETHOD
+               return
        }
+
        tsym := TypeLinksym(ityp)
        r := obj.Addrel(ir.CurFunc.LSym)
        r.Sym = tsym
index c452cecbed4df970f8f186e31771f53678479636..e5bf6cf0b5e3670a8df0c827f1385ff40251f54d 100644 (file)
@@ -506,17 +506,7 @@ func walkCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
                usemethod(n)
        }
        if n.Op() == ir.OCALLINTER {
-               if n.X.(*ir.SelectorExpr).X.Type().HasShape() {
-                       // There should be an entry in n.KeepAlive to keep the
-                       // dictionary alive (added in ../noder/transformCall).
-                       // The dictionary in turn marks the method as used.
-                       if len(n.KeepAlive) == 0 {
-                               // TODO(khr): this fails for issue44688.go.
-                               //base.Fatalf("KeepAlive of dictionary arg missing")
-                       }
-               } else {
-                       reflectdata.MarkUsedIfaceMethod(n)
-               }
+               reflectdata.MarkUsedIfaceMethod(n)
        }
 
        if n.Op() == ir.OCALLFUNC && n.X.Op() == ir.OCLOSURE {
index 0cc60fbe3b34bfff5250f03653b338a706713194..a9c3030181aead1f6fea67f4ad33469088838c86 100644 (file)
@@ -93,6 +93,11 @@ const (
        // This is a marker relocation (0-sized), for the linker's reachabililty
        // analysis.
        R_USEIFACEMETHOD
+       // Similar to R_USEIFACEMETHOD, except instead of indicating a type +
+       // method offset with Sym+Add, Sym points to a symbol containing the name
+       // of the method being called. See the description in
+       // cmd/compile/internal/reflectdata/reflect.go:MarkUsedIfaceMethod for details.
+       R_USEGENERICIFACEMETHOD
        // R_METHODOFF resolves to a 32-bit offset from the beginning of the section
        // holding the data being relocated to the referenced symbol.
        // It is a variant of R_ADDROFF used when linking from the uncommonType of a
index f2e06a5b21382553cc45bc73bdec3768a9ceca4e..d1b15b5a194f29f0d5faba7dbad5570fdd8044cf 100644 (file)
@@ -32,48 +32,49 @@ func _() {
        _ = x[R_USETYPE-22]
        _ = x[R_USEIFACE-23]
        _ = x[R_USEIFACEMETHOD-24]
-       _ = x[R_METHODOFF-25]
-       _ = x[R_KEEP-26]
-       _ = x[R_POWER_TOC-27]
-       _ = x[R_GOTPCREL-28]
-       _ = x[R_JMPMIPS-29]
-       _ = x[R_DWARFSECREF-30]
-       _ = x[R_DWARFFILEREF-31]
-       _ = x[R_ARM64_TLS_LE-32]
-       _ = x[R_ARM64_TLS_IE-33]
-       _ = x[R_ARM64_GOTPCREL-34]
-       _ = x[R_ARM64_GOT-35]
-       _ = x[R_ARM64_PCREL-36]
-       _ = x[R_ARM64_LDST8-37]
-       _ = x[R_ARM64_LDST16-38]
-       _ = x[R_ARM64_LDST32-39]
-       _ = x[R_ARM64_LDST64-40]
-       _ = x[R_ARM64_LDST128-41]
-       _ = x[R_POWER_TLS_LE-42]
-       _ = x[R_POWER_TLS_IE-43]
-       _ = x[R_POWER_TLS-44]
-       _ = x[R_ADDRPOWER_DS-45]
-       _ = x[R_ADDRPOWER_GOT-46]
-       _ = x[R_ADDRPOWER_PCREL-47]
-       _ = x[R_ADDRPOWER_TOCREL-48]
-       _ = x[R_ADDRPOWER_TOCREL_DS-49]
-       _ = x[R_RISCV_CALL-50]
-       _ = x[R_RISCV_CALL_TRAMP-51]
-       _ = x[R_RISCV_PCREL_ITYPE-52]
-       _ = x[R_RISCV_PCREL_STYPE-53]
-       _ = x[R_RISCV_TLS_IE_ITYPE-54]
-       _ = x[R_RISCV_TLS_IE_STYPE-55]
-       _ = x[R_PCRELDBL-56]
-       _ = x[R_ADDRMIPSU-57]
-       _ = x[R_ADDRMIPSTLS-58]
-       _ = x[R_ADDRCUOFF-59]
-       _ = x[R_WASMIMPORT-60]
-       _ = x[R_XCOFFREF-61]
+       _ = x[R_USEGENERICIFACEMETHOD-25]
+       _ = x[R_METHODOFF-26]
+       _ = x[R_KEEP-27]
+       _ = x[R_POWER_TOC-28]
+       _ = x[R_GOTPCREL-29]
+       _ = x[R_JMPMIPS-30]
+       _ = x[R_DWARFSECREF-31]
+       _ = x[R_DWARFFILEREF-32]
+       _ = x[R_ARM64_TLS_LE-33]
+       _ = x[R_ARM64_TLS_IE-34]
+       _ = x[R_ARM64_GOTPCREL-35]
+       _ = x[R_ARM64_GOT-36]
+       _ = x[R_ARM64_PCREL-37]
+       _ = x[R_ARM64_LDST8-38]
+       _ = x[R_ARM64_LDST16-39]
+       _ = x[R_ARM64_LDST32-40]
+       _ = x[R_ARM64_LDST64-41]
+       _ = x[R_ARM64_LDST128-42]
+       _ = x[R_POWER_TLS_LE-43]
+       _ = x[R_POWER_TLS_IE-44]
+       _ = x[R_POWER_TLS-45]
+       _ = x[R_ADDRPOWER_DS-46]
+       _ = x[R_ADDRPOWER_GOT-47]
+       _ = x[R_ADDRPOWER_PCREL-48]
+       _ = x[R_ADDRPOWER_TOCREL-49]
+       _ = x[R_ADDRPOWER_TOCREL_DS-50]
+       _ = x[R_RISCV_CALL-51]
+       _ = x[R_RISCV_CALL_TRAMP-52]
+       _ = x[R_RISCV_PCREL_ITYPE-53]
+       _ = x[R_RISCV_PCREL_STYPE-54]
+       _ = x[R_RISCV_TLS_IE_ITYPE-55]
+       _ = x[R_RISCV_TLS_IE_STYPE-56]
+       _ = x[R_PCRELDBL-57]
+       _ = x[R_ADDRMIPSU-58]
+       _ = x[R_ADDRMIPSTLS-59]
+       _ = x[R_ADDRCUOFF-60]
+       _ = x[R_WASMIMPORT-61]
+       _ = x[R_XCOFFREF-62]
 }
 
-const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_CALLR_RISCV_CALL_TRAMPR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
+const _RelocType_name = "R_ADDRR_ADDRPOWERR_ADDRARM64R_ADDRMIPSR_ADDROFFR_SIZER_CALLR_CALLARMR_CALLARM64R_CALLINDR_CALLPOWERR_CALLMIPSR_CONSTR_PCRELR_TLS_LER_TLS_IER_GOTOFFR_PLT0R_PLT1R_PLT2R_USEFIELDR_USETYPER_USEIFACER_USEIFACEMETHODR_USEGENERICIFACEMETHODR_METHODOFFR_KEEPR_POWER_TOCR_GOTPCRELR_JMPMIPSR_DWARFSECREFR_DWARFFILEREFR_ARM64_TLS_LER_ARM64_TLS_IER_ARM64_GOTPCRELR_ARM64_GOTR_ARM64_PCRELR_ARM64_LDST8R_ARM64_LDST16R_ARM64_LDST32R_ARM64_LDST64R_ARM64_LDST128R_POWER_TLS_LER_POWER_TLS_IER_POWER_TLSR_ADDRPOWER_DSR_ADDRPOWER_GOTR_ADDRPOWER_PCRELR_ADDRPOWER_TOCRELR_ADDRPOWER_TOCREL_DSR_RISCV_CALLR_RISCV_CALL_TRAMPR_RISCV_PCREL_ITYPER_RISCV_PCREL_STYPER_RISCV_TLS_IE_ITYPER_RISCV_TLS_IE_STYPER_PCRELDBLR_ADDRMIPSUR_ADDRMIPSTLSR_ADDRCUOFFR_WASMIMPORTR_XCOFFREF"
 
-var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 221, 227, 238, 248, 257, 270, 284, 298, 312, 328, 339, 352, 365, 379, 393, 407, 422, 436, 450, 461, 475, 490, 507, 525, 546, 558, 576, 595, 614, 634, 654, 664, 675, 688, 699, 711, 721}
+var _RelocType_index = [...]uint16{0, 6, 17, 28, 38, 47, 53, 59, 68, 79, 88, 99, 109, 116, 123, 131, 139, 147, 153, 159, 165, 175, 184, 194, 210, 233, 244, 250, 261, 271, 280, 293, 307, 321, 335, 351, 362, 375, 388, 402, 416, 430, 445, 459, 473, 484, 498, 513, 530, 548, 569, 581, 599, 618, 637, 657, 677, 687, 698, 711, 722, 734, 744}
 
 func (i RelocType) String() string {
        i -= 1
index 79acd73387aaab38a6309e17382da9dba797d3f9..7b57a85cdec89fafaa3286f223e6e0f1ecbb0120 100644 (file)
@@ -22,10 +22,11 @@ type deadcodePass struct {
        ldr  *loader.Loader
        wq   heap // work queue, using min-heap for better locality
 
-       ifaceMethod     map[methodsig]bool // methods declared in reached interfaces
-       markableMethods []methodref        // methods of reached types
-       reflectSeen     bool               // whether we have seen a reflect method call
-       dynlink         bool
+       ifaceMethod        map[methodsig]bool // methods called from reached interface call sites
+       genericIfaceMethod map[string]bool    // names of methods called from reached generic interface call sites
+       markableMethods    []methodref        // methods of reached types
+       reflectSeen        bool               // whether we have seen a reflect method call
+       dynlink            bool
 
        methodsigstmp []methodsig // scratch buffer for decoding method signatures
 }
@@ -33,6 +34,7 @@ type deadcodePass struct {
 func (d *deadcodePass) init() {
        d.ldr.InitReachable()
        d.ifaceMethod = make(map[methodsig]bool)
+       d.genericIfaceMethod = make(map[string]bool)
        if buildcfg.Experiment.FieldTrack {
                d.ldr.Reachparent = make([]loader.Sym, d.ldr.NSym())
        }
@@ -197,6 +199,13 @@ func (d *deadcodePass) flood() {
                                }
                                d.ifaceMethod[m] = true
                                continue
+                       case objabi.R_USEGENERICIFACEMETHOD:
+                               name := d.decodeGenericIfaceMethod(d.ldr, r.Sym())
+                               if d.ctxt.Debugvlog > 1 {
+                                       d.ctxt.Logf("reached generic iface method: %s\n", name)
+                               }
+                               d.genericIfaceMethod[name] = true
+                               continue // don't mark referenced symbol - it is not needed in the final binary.
                        }
                        rs := r.Sym()
                        if isgotype && usedInIface && d.ldr.IsGoType(rs) && !d.ldr.AttrUsedInIface(rs) {
@@ -352,7 +361,7 @@ func deadcode(ctxt *Link) {
                // in the last pass.
                rem := d.markableMethods[:0]
                for _, m := range d.markableMethods {
-                       if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] {
+                       if (d.reflectSeen && m.isExported()) || d.ifaceMethod[m.m] || d.genericIfaceMethod[m.m.name] {
                                d.markMethod(m)
                        } else {
                                rem = append(rem, m)
@@ -425,6 +434,11 @@ func (d *deadcodePass) decodeIfaceMethod(ldr *loader.Loader, arch *sys.Arch, sym
        return m
 }
 
+// Decode the method name stored in symbol symIdx. The symbol should contain just the bytes of a method name.
+func (d *deadcodePass) decodeGenericIfaceMethod(ldr *loader.Loader, symIdx loader.Sym) string {
+       return string(ldr.Data(symIdx))
+}
+
 func (d *deadcodePass) decodetypeMethods(ldr *loader.Loader, arch *sys.Arch, symIdx loader.Sym, relocs *loader.Relocs) []methodsig {
        p := ldr.Data(symIdx)
        if !decodetypeHasUncommon(arch, p) {
diff --git a/test/typeparam/issue49049.go b/test/typeparam/issue49049.go
new file mode 100644 (file)
index 0000000..f4fdd05
--- /dev/null
@@ -0,0 +1,27 @@
+// run -gcflags=-G=3
+
+// Copyright 2021 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 main
+
+type A[T any] interface {
+       m()
+}
+
+type Z struct {
+       a,b int
+}
+
+func (z *Z) m() {
+}
+
+func test[T any]() {
+       var a A[T] = &Z{}
+       f := a.m
+       f()
+}
+func main() {
+       test[string]()
+}