mirror of
https://github.com/golang/go.git
synced 2026-06-28 03:40:37 +00:00
cmd/compile: simplify closure name
Currently, a closure in a function is usually named after the outer function, usually in the form of pkg.outer.funcN. When the containing function is inlined, we attach the inlined caller's name to the closure name, so this may become things like callerpkg.caller.pkg.outer.funcN. With multiple levels of inlining, this name can get pretty long and clutter. This CL change the compiler to use the simple, pre-inlining name for closures. That is, the closure is always named pkg.outer.funcN where outer is the containing function in the source code. This name is not changed during inlining. With inlining, there may be multiple copies of the closure, all with the same name. They are likely to be compiled identically, although technically it is possible for the compiler to optimize them differently based on the context. So we'll use a content hash to distinguish and deduplicate them. With the content-addressable symbol mechanism, the linker is capable of handling multiple symbols with the same name, and use the content hash to distinguish and deduplicate them. A complication is that the compiler is not able to handle multiple symbols with the same name when compiling a package. So we give them temporarily unique suffixes during the compilation (based on the inline call stack), and trim the suffix in the object file and DWARF generation. So their linker symbols remain simple. One caveat is nested closure (i.e. a closure within a closure). Previously, a nested closure is named as topLevelFunc.funcN.M where topLevelFunc.funcN is the outer closure. When the outer closure is inlined, and the inlined caller is not a closure, it is named as caller.topLevelFunc.funcN.funcM (note the extra "func"). This is arguably a bug in the current code, as it decides whether to include the "func" word based on whether the physical containing function is a closure or not, not the source-level function. This CL removes the "caller" part from the name, but does not address the extra "func" word. So when the outer closure is inlined, the inner closure will be named topLevelFunc.funcN.funcM, which differs from the original topLevelFunc.funcN.M. This is not too bad in that the name won't get too long, and still match the source. Fixes #60324. Change-Id: Ia69c35a8f9b1a3b2c27db1a0959c1316be8b1f81 Reviewed-on: https://go-review.googlesource.com/c/go/+/770200 Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Commit-Queue: Cherry Mui <cherryyz@google.com> Reviewed-by: David Chase <drchase@google.com> TryBot-Bypass: Cherry Mui <cherryyz@google.com> Reviewed-by: Alessandro Arzilli <alessandro.arzilli@gmail.com>
This commit is contained in:
parent
1456da550a
commit
409f784bea
18 changed files with 272 additions and 73 deletions
|
|
@ -324,9 +324,9 @@ func CanInline(fn *ir.Func, profile *pgoir.Profile) {
|
|||
// function is inlinable.
|
||||
func noteInlinableFunc(n *ir.Name, fn *ir.Func, cost int32) {
|
||||
if base.Flag.LowerM > 1 {
|
||||
fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n, cost, fn.Type(), fn.Body)
|
||||
fmt.Printf("%v: can inline %v with cost %d as: %v { %v }\n", ir.Line(fn), n.DiagName(), cost, fn.Type(), fn.Body)
|
||||
} else if base.Flag.LowerM != 0 {
|
||||
fmt.Printf("%v: can inline %v\n", ir.Line(fn), n)
|
||||
fmt.Printf("%v: can inline %v\n", ir.Line(fn), n.DiagName())
|
||||
}
|
||||
// JSON optimization log output.
|
||||
if logopt.Enabled() {
|
||||
|
|
@ -1227,9 +1227,9 @@ func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller, closur
|
|||
if base.Flag.LowerM != 0 {
|
||||
if buildcfg.Experiment.NewInliner {
|
||||
fmt.Printf("%v: inlining call to %v with score %d\n",
|
||||
ir.Line(n), fn, score)
|
||||
ir.Line(n), fn.Nname.DiagName(), score)
|
||||
} else {
|
||||
fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn)
|
||||
fmt.Printf("%v: inlining call to %v\n", ir.Line(n), fn.Nname.DiagName())
|
||||
}
|
||||
}
|
||||
if base.Flag.LowerM > 2 {
|
||||
|
|
@ -1239,7 +1239,7 @@ func mkinlcall(callerfn *ir.Func, n *ir.CallExpr, fn *ir.Func, bigCaller, closur
|
|||
res := InlineCall(callerfn, n, fn, inlIndex)
|
||||
|
||||
if res == nil {
|
||||
base.FatalfAt(n.Pos(), "inlining call to %v failed", fn)
|
||||
base.FatalfAt(n.Pos(), "inlining call to %v failed", fn.Nname.DiagName())
|
||||
}
|
||||
|
||||
if base.Flag.LowerM > 2 {
|
||||
|
|
|
|||
|
|
@ -7,11 +7,13 @@ package ir
|
|||
import (
|
||||
"cmd/compile/internal/base"
|
||||
"cmd/compile/internal/types"
|
||||
"cmd/internal/hash"
|
||||
"cmd/internal/obj"
|
||||
"cmd/internal/objabi"
|
||||
"cmd/internal/src"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"strings"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
|
|
@ -430,10 +432,9 @@ func ClosureDebugRuntimeCheck(clo *ClosureExpr) {
|
|||
var globClosgen int32
|
||||
|
||||
// closureName generates a new unique name for a closure within outerfn at pos.
|
||||
func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
|
||||
if outerfn.OClosure != nil && outerfn.OClosure.Func.RangeParent != nil {
|
||||
outerfn = outerfn.OClosure.Func.RangeParent
|
||||
}
|
||||
// gen is an optional counter for the closure name. If it is 0, the counter
|
||||
// will be computed based on outerfn.
|
||||
func closureName(outerfn *Func, pos src.XPos, why Op, gen int) *types.Sym {
|
||||
pkg := types.LocalPkg
|
||||
outer := "glob."
|
||||
var suffix string = "."
|
||||
|
|
@ -451,7 +452,6 @@ func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
|
|||
case ODEFER:
|
||||
suffix = ".deferwrap"
|
||||
}
|
||||
gen := &globClosgen
|
||||
|
||||
// There may be multiple functions named "_". In those
|
||||
// cases, we can't use their individual Closgens as it
|
||||
|
|
@ -459,30 +459,57 @@ func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
|
|||
if !IsBlank(outerfn.Nname) {
|
||||
pkg = outerfn.Sym().Pkg
|
||||
outer = FuncName(outerfn)
|
||||
}
|
||||
|
||||
switch why {
|
||||
case OCLOSURE:
|
||||
gen = &outerfn.funcLitGen
|
||||
case ORANGE:
|
||||
gen = &outerfn.rangeLitGen
|
||||
default:
|
||||
gen = &outerfn.goDeferGen
|
||||
// If this closure was created due to inlining, find the original
|
||||
// outer function's name for the closure (#60324).
|
||||
var inlHash string
|
||||
if inlIndex := base.Ctxt.InnermostPos(pos).Base().InliningIndex(); inlIndex >= 0 {
|
||||
// The compiler doesn't like multiple symbols with the same
|
||||
// name. We make a unique suffix temporarily for the
|
||||
// compiler, and strip it during object file writing, so
|
||||
// it will not be the linker symbol name. For linking,
|
||||
// we use a content hash to disambiguate instead.
|
||||
// We choose the suffix as a hash of the inline call stack.
|
||||
h := hash.New32()
|
||||
io.WriteString(h, outer)
|
||||
base.Ctxt.InlTree.AllParents(inlIndex, func(call obj.InlinedCall) {
|
||||
io.WriteString(h, call.Name+":"+call.Pos.LineNumber()+":"+call.Pos.ColumnNumber())
|
||||
})
|
||||
inlHash = base64.StdEncoding.EncodeToString(h.Sum(nil)[:8])
|
||||
|
||||
outer = base.Ctxt.InlTree.InlinedFuncName(inlIndex)
|
||||
if pkgPath := base.Ctxt.InlTree.InlinedFuncPkg(inlIndex); pkgPath != "" {
|
||||
pkg = types.NewPkg(pkgPath, "")
|
||||
}
|
||||
}
|
||||
|
||||
// If this closure was created due to inlining, then incorporate any
|
||||
// inlined functions' names into the closure's linker symbol name
|
||||
// too (#60324).
|
||||
if inlIndex := base.Ctxt.InnermostPos(pos).Base().InliningIndex(); inlIndex >= 0 {
|
||||
names := []string{outer}
|
||||
base.Ctxt.InlTree.AllParents(inlIndex, func(call obj.InlinedCall) {
|
||||
names = append(names, call.Name)
|
||||
})
|
||||
outer = strings.Join(names, ".")
|
||||
if gen == 0 {
|
||||
p := &globClosgen
|
||||
if !IsBlank(outerfn.Nname) {
|
||||
switch why {
|
||||
case OCLOSURE:
|
||||
p = &outerfn.funcLitGen
|
||||
case ORANGE:
|
||||
p = &outerfn.rangeLitGen
|
||||
default:
|
||||
p = &outerfn.goDeferGen
|
||||
}
|
||||
}
|
||||
*p++
|
||||
gen = int(*p)
|
||||
}
|
||||
|
||||
*gen++
|
||||
return pkg.Lookup(fmt.Sprintf("%s%s%d", outer, suffix, *gen))
|
||||
name := fmt.Sprintf("%s%s%d", outer, suffix, gen)
|
||||
if inlHash != "" {
|
||||
// Attach the inline hash (see the comment above).
|
||||
// If it already has a hash, trim it, so we don't include
|
||||
// two hashes for nested closures. The new hash should be
|
||||
// enough to disambiguate.
|
||||
name = obj.TrimInlineHash(name) + "#" + inlHash + "#"
|
||||
}
|
||||
|
||||
return pkg.Lookup(name)
|
||||
}
|
||||
|
||||
// NewClosureFunc creates a new Func to represent a function literal
|
||||
|
|
@ -500,14 +527,19 @@ func closureName(outerfn *Func, pos src.XPos, why Op) *types.Sym {
|
|||
// why is the reason we're generating this Func. It can be OCLOSURE
|
||||
// (for a normal function literal) or OGO or ODEFER (for wrapping a
|
||||
// call expression that has parameters or results).
|
||||
func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func, pkg *Package) *Func {
|
||||
//
|
||||
// gen is an optional counter for the closure name. If it is 0,
|
||||
// the counter will be computed based on outerfn.
|
||||
func NewClosureFunc(fpos, cpos src.XPos, why Op, typ *types.Type, outerfn *Func, pkg *Package, gen int) *Func {
|
||||
if outerfn == nil {
|
||||
base.FatalfAt(fpos, "outerfn is nil")
|
||||
}
|
||||
|
||||
fn := NewFunc(fpos, fpos, closureName(outerfn, cpos, why), typ)
|
||||
fn := NewFunc(fpos, fpos, closureName(outerfn, cpos, why, gen), typ)
|
||||
fn.SetDupok(outerfn.Dupok()) // if the outer function is dupok, so is the closure
|
||||
|
||||
fn.Linksym().Set(obj.AttrContentAddressable, true)
|
||||
|
||||
clo := &ClosureExpr{Func: fn}
|
||||
clo.op = OCLOSURE
|
||||
clo.pos = cpos
|
||||
|
|
|
|||
|
|
@ -167,6 +167,10 @@ func (*Name) CanBeNtype() {}
|
|||
func (*Name) CanBeAnSSASym() {}
|
||||
func (*Name) CanBeAnSSAAux() {}
|
||||
|
||||
// DiagName returns the symbol name for diagnostics.
|
||||
// XXX should it be part of the formatter?
|
||||
func (n *Name) DiagName() string { return obj.TrimInlineHash(fmt.Sprint(n.Sym())) }
|
||||
|
||||
// Pragma returns the PragmaFlag for p, which must be for an OTYPE.
|
||||
func (n *Name) Pragma() PragmaFlag { return n.pragma }
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,11 @@ type reader struct {
|
|||
|
||||
dict *readerDict
|
||||
|
||||
// funcLitGen is a counter for closure names.
|
||||
funcLitGen int
|
||||
// rangeLitGen is a counter for range func closure names.
|
||||
rangeLitGen int
|
||||
|
||||
// TODO(mdempsky): The state below is all specific to reading
|
||||
// function bodies. It probably makes sense to split it out
|
||||
// separately so that it doesn't take up space in every reader
|
||||
|
|
@ -3307,8 +3312,17 @@ func (r *reader) inlClosureFunc(origPos src.XPos, sig *types.Type, why ir.Op) *i
|
|||
curfn = r.curfn
|
||||
}
|
||||
|
||||
var gen int
|
||||
if why == ir.ORANGE {
|
||||
r.rangeLitGen++
|
||||
gen = r.rangeLitGen
|
||||
} else {
|
||||
r.funcLitGen++
|
||||
gen = r.funcLitGen
|
||||
}
|
||||
|
||||
// TODO(mdempsky): Remove hard-coding of typecheck.Target.
|
||||
return ir.NewClosureFunc(origPos, r.inlPos(origPos), why, sig, curfn, typecheck.Target)
|
||||
return ir.NewClosureFunc(origPos, r.inlPos(origPos), why, sig, curfn, typecheck.Target, gen)
|
||||
}
|
||||
|
||||
func (r *reader) exprList() []ir.Node {
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ func normalizeGoDeferCall(pos src.XPos, op ir.Op, call ir.Node, init *ir.Nodes)
|
|||
}
|
||||
|
||||
// Create a new wrapper function without parameters or results.
|
||||
wrapperFn := ir.NewClosureFunc(pos, pos, op, types.NewSignature(nil, nil, nil), ir.CurFunc, Target)
|
||||
wrapperFn := ir.NewClosureFunc(pos, pos, op, types.NewSignature(nil, nil, nil), ir.CurFunc, Target, 0)
|
||||
wrapperFn.DeclareParams(true)
|
||||
wrapperFn.SetWrapper(true)
|
||||
|
||||
|
|
|
|||
80
src/cmd/compile/testdata/script/closure_name.txt
vendored
Normal file
80
src/cmd/compile/testdata/script/closure_name.txt
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
# Test closure naming with inlining.
|
||||
|
||||
go build -gcflags=-S -o x.exe x.go
|
||||
# original closure name should appear
|
||||
stderr 'adder\.func1'
|
||||
# inlining should not cause the closure to have longer names
|
||||
! stderr 'F1\.adder\.func1'
|
||||
! stderr 'F2\.adder\.func1'
|
||||
# the counter should match the original one, regardless of
|
||||
# how many closures are in the inlined caller
|
||||
! stderr 'adder\.func2'
|
||||
! stderr 'adder\.func3'
|
||||
! stderr 'adder\.func4'
|
||||
# user closure counter and range func counter should not
|
||||
# interfere
|
||||
stderr 'ranger\.func1'
|
||||
stderr 'ranger-range1'
|
||||
stderr 'ranger\.func2'
|
||||
|
||||
go tool nm x.exe
|
||||
# same applies to the nm output
|
||||
stdout 'adder\.func1'
|
||||
! stdout 'F1\.adder\.func1'
|
||||
! stdout 'F2\.adder\.func1'
|
||||
! stdout 'adder\.func2'
|
||||
! stdout 'adder\.func3'
|
||||
! stdout 'adder\.func4'
|
||||
stdout 'ranger\.func1'
|
||||
stdout 'ranger-range1'
|
||||
stdout 'ranger\.func2'
|
||||
# the inline hash should not make into the final binary
|
||||
! stdout 'adder\.func1#'
|
||||
|
||||
-- x.go --
|
||||
package main
|
||||
|
||||
// keep prevents closures being optimized out
|
||||
//
|
||||
//go:noinline
|
||||
func keep(x func()) func() { return x }
|
||||
|
||||
func adder(n int) func(int) int {
|
||||
return func(x int) int { return x + n }
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func F1() func(int) int {
|
||||
// adder is inlined into F1, along with its closure.
|
||||
// The closure should still be named addr.func1.
|
||||
return adder(1)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func F2() func(int) int {
|
||||
keep(func() {})
|
||||
keep(func() {})
|
||||
keep(func() {})
|
||||
// Closures in the outer function should not change
|
||||
// the counter. It should still be addr.func1.
|
||||
return adder(2)
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func iter(yield func(int) bool) {
|
||||
_ = yield(1) && yield(2) && yield(3)
|
||||
}
|
||||
|
||||
func ranger() func(int) int {
|
||||
keep(func() {}) // func1
|
||||
s := 0
|
||||
for x := range iter { // range1
|
||||
s += x
|
||||
}
|
||||
return func(x int) int { return s + x } // func2
|
||||
}
|
||||
|
||||
func main() {
|
||||
x := 1
|
||||
F1()(F2()(ranger()(x)))
|
||||
}
|
||||
|
|
@ -363,7 +363,7 @@ func (ctxt *Link) populateDWARF(curfn Func, s *LSym) {
|
|||
panic("bad startPos")
|
||||
}
|
||||
fnstate := &dwarf.FnState{
|
||||
Name: s.Name,
|
||||
Name: TrimInlineHash(s.Name),
|
||||
Info: info,
|
||||
Loc: loc,
|
||||
Ranges: ranges,
|
||||
|
|
@ -435,7 +435,7 @@ func (ctxt *Link) DwarfAbstractFunc(curfn Func, s *LSym) {
|
|||
scopes, _ := ctxt.DebugInfo(ctxt, s, absfn, curfn)
|
||||
dwctxt := dwCtxt{ctxt}
|
||||
fnstate := dwarf.FnState{
|
||||
Name: s.Name,
|
||||
Name: TrimInlineHash(s.Name),
|
||||
Info: absfn,
|
||||
Absfn: absfn,
|
||||
StartPos: ctxt.InnermostPos(curfn.Pos()),
|
||||
|
|
|
|||
|
|
@ -89,6 +89,14 @@ func (tree *InlTree) InlinedFunction(inlIndex int) *LSym {
|
|||
return tree.nodes[inlIndex].Func
|
||||
}
|
||||
|
||||
func (tree *InlTree) InlinedFuncName(inlIndex int) string {
|
||||
return tree.nodes[inlIndex].Name
|
||||
}
|
||||
|
||||
func (tree *InlTree) InlinedFuncPkg(inlIndex int) string {
|
||||
return tree.nodes[inlIndex].Func.Pkg
|
||||
}
|
||||
|
||||
func (tree *InlTree) CallPos(inlIndex int) src.XPos {
|
||||
return tree.nodes[inlIndex].Pos
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"cmd/internal/objabi"
|
||||
"cmd/internal/sys"
|
||||
"cmp"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"internal/abi"
|
||||
|
|
@ -276,6 +277,22 @@ func (w *writer) writeFile(ctxt *Link, file *FileInfo) {
|
|||
}
|
||||
}
|
||||
|
||||
var inlHashLen = base64.StdEncoding.EncodedLen(8)
|
||||
|
||||
// TrimInlineHash strips the content hash of inlined call stacks from a symbol name.
|
||||
func TrimInlineHash(name string) string {
|
||||
// The inline hash is in the form of #NNNN#, where NNNN is
|
||||
// the base64 encoding of a 8-byte hash value.
|
||||
a, b, ok := strings.Cut(name, "#")
|
||||
if !ok {
|
||||
return name
|
||||
}
|
||||
if len(b) < inlHashLen+1 || b[inlHashLen] != '#' {
|
||||
return name
|
||||
}
|
||||
return a + b[inlHashLen+1:]
|
||||
}
|
||||
|
||||
func (w *writer) StringTable() {
|
||||
w.AddString("")
|
||||
for _, p := range w.ctxt.Imports {
|
||||
|
|
@ -300,7 +317,7 @@ func (w *writer) StringTable() {
|
|||
if strings.HasPrefix(s.Name, `"".`) {
|
||||
w.ctxt.Diag("unqualified symbol name: %v", s.Name)
|
||||
}
|
||||
w.AddString(s.Name)
|
||||
w.AddString(TrimInlineHash(s.Name))
|
||||
})
|
||||
|
||||
// All filenames are in the postable.
|
||||
|
|
@ -373,7 +390,8 @@ func (w *writer) Sym(s *LSym) {
|
|||
name = filepath.ToSlash(name)
|
||||
}
|
||||
align := uint32(s.Align)
|
||||
if s.ContentAddressable() && s.Size != 0 && align == 0 {
|
||||
if s.ContentAddressable() && s.Size != 0 && align == 0 && s.Type != objabi.STEXT && s.Type != objabi.STEXTFIPS {
|
||||
// The linker applies a default alignment for text symbols, so they are okay.
|
||||
// TODO: Check that alignment is set for all symbols.
|
||||
w.ctxt.Diag("%s: is content-addressable but alignment is not set (size is %d)", s.Name, s.Size)
|
||||
}
|
||||
|
|
@ -381,7 +399,7 @@ func (w *writer) Sym(s *LSym) {
|
|||
w.ctxt.Diag("%s: symbol too large (%d bytes > %d bytes)", s.Name, s.Size, MaxSymSize)
|
||||
}
|
||||
o := &w.tmpSym
|
||||
o.SetName(name, w.Writer)
|
||||
o.SetName(TrimInlineHash(name), w.Writer)
|
||||
o.SetABI(abi)
|
||||
o.SetType(uint8(s.Type))
|
||||
o.SetFlag(flag)
|
||||
|
|
@ -416,10 +434,16 @@ func (w *writer) Hash(s *LSym) {
|
|||
// Some of these conditions are duplicated in cmd/link/internal/ld.(*Link).symtab.
|
||||
// TODO: instead of duplicating them, have the compiler decide where symbols go.
|
||||
func contentHashSection(s *LSym) byte {
|
||||
name := s.Name
|
||||
if s.Type == objabi.STEXT {
|
||||
return 't'
|
||||
}
|
||||
if s.Type == objabi.STEXTFIPS {
|
||||
return 'f'
|
||||
}
|
||||
if s.IsPcdata() {
|
||||
return 'P'
|
||||
}
|
||||
name := s.Name
|
||||
if strings.HasPrefix(name, "gcargs.") ||
|
||||
strings.HasPrefix(name, "gclocals.") ||
|
||||
strings.HasPrefix(name, "gclocals·") ||
|
||||
|
|
@ -480,6 +504,14 @@ func (w *writer) contentHash(s *LSym) goobj.HashType {
|
|||
tmp[8] = contentHashSection(s)
|
||||
h.Write(tmp[:9])
|
||||
|
||||
if s.Type == objabi.STEXT || s.Type == objabi.STEXTFIPS {
|
||||
// We don't want to combine irrelevant functions.
|
||||
// Include the name to distinguish, without the temporary
|
||||
// inline hash, so different inlined copies do get a chance
|
||||
// to be combined.
|
||||
io.WriteString(h, TrimInlineHash(s.Name))
|
||||
}
|
||||
|
||||
// The compiler trims trailing zeros _sometimes_. We just do
|
||||
// it always.
|
||||
h.Write(bytes.TrimRight(s.P, "\x00"))
|
||||
|
|
@ -492,9 +524,14 @@ func (w *writer) contentHash(s *LSym) goobj.HashType {
|
|||
h.Write(tmp[:])
|
||||
rs := r.Sym
|
||||
if rs == nil {
|
||||
fmt.Printf("symbol: %s\n", s)
|
||||
fmt.Printf("relocation: %#v\n", r)
|
||||
panic("nil symbol target in relocation")
|
||||
// marker relocation
|
||||
io.WriteString(h, "nil symbol")
|
||||
continue
|
||||
}
|
||||
if rs == s {
|
||||
io.WriteString(h, "self symbol")
|
||||
continue
|
||||
// TODO: mutual recursion?
|
||||
}
|
||||
switch rs.PkgIdx {
|
||||
case goobj.PkgIdxHashed64:
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
package src
|
||||
|
||||
import "fmt"
|
||||
|
||||
// XPos is a more compact representation of Pos.
|
||||
type XPos struct {
|
||||
index int32
|
||||
|
|
@ -93,6 +95,14 @@ func (p XPos) LineNumber() string {
|
|||
return p.lico.lineNumber()
|
||||
}
|
||||
|
||||
// ColumnNumber returns a string for the column number, "?" if it is not known.
|
||||
func (p XPos) ColumnNumber() string {
|
||||
if !p.IsKnown() {
|
||||
return "?"
|
||||
}
|
||||
return fmt.Sprintf("%d", p.lico.Col())
|
||||
}
|
||||
|
||||
// FileIndex returns a smallish non-negative integer corresponding to the
|
||||
// file for this source position. Smallish is relative; it can be thousands
|
||||
// large, but not millions.
|
||||
|
|
|
|||
|
|
@ -1046,7 +1046,7 @@ func AddMachoSym(ldr *loader.Loader, s loader.Sym) {
|
|||
// When dynamically linking, all non-local variables and plugin-exported
|
||||
// symbols need to be exported.
|
||||
func machoShouldExport(ctxt *Link, ldr *loader.Loader, s loader.Sym) bool {
|
||||
if !ctxt.DynlinkingGo() || ldr.AttrLocal(s) {
|
||||
if !ctxt.DynlinkingGo() || ldr.AttrLocal(s) || (ldr.IsContentHashed(s) && ldr.SymType(s).IsText()) {
|
||||
return false
|
||||
}
|
||||
if ctxt.BuildMode == BuildModePlugin && strings.HasPrefix(ldr.SymExtname(s), objabi.PathToPrefix(*flagPluginPath)) {
|
||||
|
|
|
|||
|
|
@ -937,7 +937,8 @@ func (f *peFile) writeSymbols(ctxt *Link) {
|
|||
}
|
||||
}
|
||||
class := IMAGE_SYM_CLASS_EXTERNAL
|
||||
if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) {
|
||||
if ldr.IsFileLocal(s) || ldr.AttrVisibilityHidden(s) || ldr.AttrLocal(s) ||
|
||||
(ldr.IsContentHashed(s) && ldr.SymType(s).IsText()) {
|
||||
class = IMAGE_SYM_CLASS_STATIC
|
||||
}
|
||||
f.writeSymbol(ctxt.Out, ldr, s, name, value, sect, peSymType, uint8(class))
|
||||
|
|
|
|||
|
|
@ -110,9 +110,12 @@ func putelfsym(ctxt *Link, x loader.Sym, typ elf.SymType, curbind elf.SymBind) {
|
|||
// One pass for each binding: elf.STB_LOCAL, elf.STB_GLOBAL,
|
||||
// maybe one day elf.STB_WEAK.
|
||||
bind := elf.STB_GLOBAL
|
||||
if ldr.IsFileLocal(x) && !isStaticTmp(sname) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
|
||||
if ldr.IsFileLocal(x) && !isStaticTmp(sname) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) ||
|
||||
(ldr.IsContentHashed(x) && ldr.SymType(x).IsText()) {
|
||||
// Static tmp is package local, but a package can be shared among multiple DSOs.
|
||||
// They need to have a single view of the static tmp that are writable.
|
||||
// Content-hashed text symbols may have the same name. Mark them local to avoid
|
||||
// collision.
|
||||
bind = elf.STB_LOCAL
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -829,7 +829,7 @@ func (f *xcoffFile) writeSymbolFunc(ctxt *Link, x loader.Sym) []xcoffSym {
|
|||
Nnumaux: 2,
|
||||
}
|
||||
|
||||
if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) {
|
||||
if ldr.IsFileLocal(x) || ldr.AttrVisibilityHidden(x) || ldr.AttrLocal(x) || ldr.IsContentHashed(x) {
|
||||
s.Nsclass = C_HIDEXT
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -816,6 +816,16 @@ func (l *Loader) SymVersion(i Sym) int {
|
|||
return abiToVer(r.Sym(li).ABI(), r.version)
|
||||
}
|
||||
|
||||
func (l *Loader) IsContentHashed(i Sym) bool {
|
||||
if l.IsExternal(i) {
|
||||
return false
|
||||
}
|
||||
r, li := l.toLocal(i)
|
||||
start := uint32(r.ndef + r.nhashed64def)
|
||||
end := start + uint32(r.nhasheddef)
|
||||
return start <= li && li < end
|
||||
}
|
||||
|
||||
func (l *Loader) IsFileLocal(i Sym) bool {
|
||||
return l.SymVersion(i) >= sym.SymVerStatic
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ func main() {
|
|||
y := func(x int) int { // ERROR "func literal does not escape" "can inline main.func13.1"
|
||||
return x + 2
|
||||
}
|
||||
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2" "can inline main.main.func13.func35"
|
||||
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2" "can inline main.func13.func2"
|
||||
return func(x int) int { // ERROR "can inline main.func13.2" "func literal escapes to heap"
|
||||
return x + 1
|
||||
}, 42
|
||||
|
|
@ -118,7 +118,7 @@ func main() {
|
|||
if y(40) != 41 {
|
||||
ppanic("y(40) != 41")
|
||||
}
|
||||
}() // ERROR "func literal does not escape" "inlining call to main.func13" "inlining call to main.main.func13.func35"
|
||||
}() // ERROR "func literal does not escape" "inlining call to main.func13$" "inlining call to main.func13.func2"
|
||||
}
|
||||
|
||||
{
|
||||
|
|
@ -191,17 +191,17 @@ func main() {
|
|||
{
|
||||
x := 42
|
||||
if z := func(y int) int { // ERROR "can inline main.func22"
|
||||
return func() int { // ERROR "can inline main.func22.1" "can inline main.main.func22.func40"
|
||||
return func() int { // ERROR "can inline main.func22.1" "can inline main.func22.func1"
|
||||
return x + y
|
||||
}() // ERROR "inlining call to main.func22.1"
|
||||
}(1); z != 43 { // ERROR "inlining call to main.func22" "inlining call to main.main.func22.func40"
|
||||
}(1); z != 43 { // ERROR "inlining call to main.func22$" "inlining call to main.func22.func1"
|
||||
ppanic("z != 43")
|
||||
}
|
||||
if z := func(y int) int { // ERROR "func literal does not escape" "can inline main.func23"
|
||||
return func() int { // ERROR "can inline main.func23.1" "can inline main.main.func23.func41"
|
||||
return func() int { // ERROR "can inline main.func23.1" "can inline main.func23.func1"
|
||||
return x + y
|
||||
}() // ERROR "inlining call to main.func23.1"
|
||||
}; z(1) != 43 { // ERROR "inlining call to main.func23" "inlining call to main.main.func23.func41"
|
||||
}; z(1) != 43 { // ERROR "inlining call to main.func23$" "inlining call to main.func23.func1"
|
||||
_ = z // prevent simple deadcode elimination after inlining
|
||||
ppanic("z(1) != 43")
|
||||
}
|
||||
|
|
@ -210,10 +210,10 @@ func main() {
|
|||
{
|
||||
a := 1
|
||||
func() { // ERROR "can inline main.func24"
|
||||
func() { // ERROR "can inline main.func24" "can inline main.main.func24.func42"
|
||||
func() { // ERROR "can inline main.func24.1" "can inline main.func24.func1"
|
||||
a = 2
|
||||
}() // ERROR "inlining call to main.func24"
|
||||
}() // ERROR "inlining call to main.func24" "inlining call to main.main.func24.func42"
|
||||
}() // ERROR "inlining call to main.func24.1"
|
||||
}() // ERROR "inlining call to main.func24$" "inlining call to main.func24.func1"
|
||||
if a != 2 {
|
||||
ppanic("a != 2")
|
||||
}
|
||||
|
|
@ -222,13 +222,13 @@ func main() {
|
|||
{
|
||||
b := 2
|
||||
func(b int) { // ERROR "can inline main.func25"
|
||||
func() { // ERROR "can inline main.func25.1" "can inline main.main.func25.func43"
|
||||
func() { // ERROR "can inline main.func25.1" "can inline main.func25.func1"
|
||||
b = 3
|
||||
}() // ERROR "inlining call to main.func25.1"
|
||||
if b != 3 {
|
||||
ppanic("b != 3")
|
||||
}
|
||||
}(b) // ERROR "inlining call to main.func25" "inlining call to main.main.func25.func43"
|
||||
}(b) // ERROR "inlining call to main.func25"
|
||||
if b != 2 {
|
||||
ppanic("b != 2")
|
||||
}
|
||||
|
|
@ -258,13 +258,13 @@ func main() {
|
|||
// revisit those. E.g., func34 and func36 are constructed by the inliner.
|
||||
if r := func(x int) int { // ERROR "can inline main.func27"
|
||||
b := 3
|
||||
return func(y int) int { // ERROR "can inline main.func27.1" "can inline main.main.func27.func45"
|
||||
return func(y int) int { // ERROR "can inline main.func27.1" "can inline main.func27.func1"
|
||||
c := 5
|
||||
return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.main.func27.func45.1" "can inline main.func27.main.func27.1.2" "can inline main.main.func27.main.main.func27.func45.func48"
|
||||
return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.func27.func1.1" "can inline main.func27.func1.func1"
|
||||
return a*x + b*y + c*z
|
||||
}(10) // ERROR "inlining call to main.func27.1.1"
|
||||
}(100) // ERROR "inlining call to main.func27.1" "inlining call to main.func27.main.func27.1.2"
|
||||
}(1000); r != 2350 { // ERROR "inlining call to main.func27" "inlining call to main.main.func27.func45" "inlining call to main.main.func27.main.main.func27.func45.func48"
|
||||
}(100) // ERROR "inlining call to main.func27.1$" "inlining call to main.func27.1.1"
|
||||
}(1000); r != 2350 { // ERROR "inlining call to main.func27$" "inlining call to main.func27.func1$" "inlining call to main.func27.func1.func1"
|
||||
ppanic("r != 2350")
|
||||
}
|
||||
}
|
||||
|
|
@ -273,16 +273,16 @@ func main() {
|
|||
a := 2
|
||||
if r := func(x int) int { // ERROR "can inline main.func28"
|
||||
b := 3
|
||||
return func(y int) int { // ERROR "can inline main.func28.1" "can inline main.main.func28.func46"
|
||||
return func(y int) int { // ERROR "can inline main.func28.1" "can inline main.func28.func1"
|
||||
c := 5
|
||||
func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.main.func28.1.2" "can inline main.main.func28.func46.1" "can inline main.main.func28.main.main.func28.func46.func49"
|
||||
func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.func1.1" "can inline main.func28.func1.func1"
|
||||
a = a * x
|
||||
b = b * y
|
||||
c = c * z
|
||||
}(10) // ERROR "inlining call to main.func28.1.1"
|
||||
return a + c
|
||||
}(100) + b // ERROR "inlining call to main.func28.1" "inlining call to main.func28.main.func28.1.2"
|
||||
}(1000); r != 2350 { // ERROR "inlining call to main.func28" "inlining call to main.main.func28.func46" "inlining call to main.main.func28.main.main.func28.func46.func49"
|
||||
}(100) + b // ERROR "inlining call to main.func28.1$" "inlining call to main.func28.1.1"
|
||||
}(1000); r != 2350 { // ERROR "inlining call to main.func28$" "inlining call to main.func28.func1$" "inlining call to main.func28.func1.func1"
|
||||
ppanic("r != 2350")
|
||||
}
|
||||
if a != 2000 {
|
||||
|
|
|
|||
|
|
@ -7,26 +7,26 @@
|
|||
package codegen
|
||||
|
||||
func main() {
|
||||
// amd64:"LEAQ command-line-arguments\\.main\\.f\\.g\\.h\\.func3"
|
||||
// amd64:"LEAQ command-line-arguments\\.h\\.func1"
|
||||
f(1)()
|
||||
|
||||
// amd64:"LEAQ command-line-arguments\\.main\\.g\\.h\\.func2"
|
||||
// amd64:"LEAQ command-line-arguments\\.h\\.func1"
|
||||
g(2)()
|
||||
|
||||
// amd64:"LEAQ command-line-arguments\\.main\\.h\\.func1"
|
||||
// amd64:"LEAQ command-line-arguments\\.h\\.func1"
|
||||
h(3)()
|
||||
|
||||
// amd64:"LEAQ command-line-arguments\\.main\\.f\\.g\\.h\\.func4"
|
||||
// amd64:"LEAQ command-line-arguments\\.h\\.func1"
|
||||
f(4)()
|
||||
}
|
||||
|
||||
func f(x int) func() {
|
||||
// amd64:"LEAQ command-line-arguments\\.f\\.g\\.h\\.func1"
|
||||
// amd64:"LEAQ command-line-arguments\\.h\\.func1"
|
||||
return g(x)
|
||||
}
|
||||
|
||||
func g(x int) func() {
|
||||
// amd64:"LEAQ command-line-arguments\\.g\\.h\\.func1"
|
||||
// amd64:"LEAQ command-line-arguments\\.h\\.func1"
|
||||
return h(x)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,13 +119,13 @@ func r(z int) int {
|
|||
return x + z
|
||||
}
|
||||
bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2"
|
||||
return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.r.func2.func3"
|
||||
return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func2.func1"
|
||||
return 2*y + x*z
|
||||
}(x) // ERROR "inlining call to r.func2.1"
|
||||
}
|
||||
_, _ = foo, bar // prevent simple deadcode elimination after inlining
|
||||
|
||||
return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.r.func2.func3"
|
||||
return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2$" "inlining call to r.func2.func1"
|
||||
}
|
||||
|
||||
func s0(x int) int { // ERROR "can inline s0"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue