]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile,link: generate PC-value tables with inlining information
authorDavid Lazar <lazard@golang.org>
Fri, 17 Feb 2017 17:28:05 +0000 (12:28 -0500)
committerDavid Lazar <lazard@golang.org>
Fri, 3 Mar 2017 21:29:30 +0000 (21:29 +0000)
In order to generate accurate tracebacks, the runtime needs to know the
inlined call stack for a given PC. This creates two tables per function
for this purpose. The first table is the inlining tree (stored in the
function's funcdata), which has a node containing the file, line, and
function name for every inlined call. The second table is a PC-value
table that maps each PC to a node in the inlining tree (or -1 if the PC
is not the result of inlining).

To give the appearance that inlining hasn't happened, the runtime also
needs the original source position information of inlined AST nodes.
Previously the compiler plastered over the line numbers of inlined AST
nodes with the line number of the call. This meant that the PC-line
table mapped each PC to line number of the outermost call in its inlined
call stack, with no way to access the innermost line number.

Now the compiler retains line numbers of inlined AST nodes and writes
the innermost source position information to the PC-line and PC-file
tables. Some tools and tests expect to see outermost line numbers, so we
provide the OutermostLine function for displaying line info.

To keep track of the inlined call stack for an AST node, we extend the
src.PosBase type with an index into a global inlining tree. Every time
the compiler inlines a call, it creates a node in the global inlining
tree for the call, and writes its index to the PosBase of every inlined
AST node. The parent of this node is the inlining tree index of the
call. -1 signifies no parent.

For each function, the compiler creates a local inlining tree and a
PC-value table mapping each PC to an index in the local tree.  These are
written to an object file, which is read by the linker.  The linker
re-encodes these tables compactly by deduplicating function names and
file names.

This change increases the size of binaries by 4-5%. For example, this is
how the go1 benchmark binary is impacted by this change:

section             old bytes   new bytes   delta
.text               3.49M ± 0%  3.49M ± 0%   +0.06%
.rodata             1.12M ± 0%  1.21M ± 0%   +8.21%
.gopclntab          1.50M ± 0%  1.68M ± 0%  +11.89%
.debug_line          338k ± 0%   435k ± 0%  +28.78%
Total               9.21M ± 0%  9.58M ± 0%   +4.01%

Updates #19348.

Change-Id: Ic4f180c3b516018138236b0c35e0218270d957d3
Reviewed-on: https://go-review.googlesource.com/37231
Run-TryBot: David Lazar <lazard@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
17 files changed:
src/cmd/compile/internal/gc/inl.go
src/cmd/compile/internal/gc/subr.go
src/cmd/compile/internal/gc/util.go
src/cmd/internal/obj/funcdata.go
src/cmd/internal/obj/inl.go [new file with mode: 0644]
src/cmd/internal/obj/link.go
src/cmd/internal/obj/objfile.go
src/cmd/internal/obj/pcln.go
src/cmd/internal/obj/util.go
src/cmd/internal/src/pos.go
src/cmd/internal/src/xpos_test.go
src/cmd/link/internal/ld/link.go
src/cmd/link/internal/ld/objfile.go
src/cmd/link/internal/ld/pcln.go
src/cmd/link/internal/ld/symtab.go
src/runtime/funcdata.h
src/runtime/symtab.go

index 3401fc6c518cd9e5e371482cafd5c2e4f9b4575b..6e3ee91ae300b3000f911fc1885539c0ef158ea1 100644 (file)
@@ -843,12 +843,23 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
        call.Type = n.Type
        call.Typecheck = 1
 
-       // Hide the args from setlno -- the parameters to the inlined
+       // Hide the args from setPos -- the parameters to the inlined
        // call already have good line numbers that should be preserved.
        args := as.Rlist
        as.Rlist.Set(nil)
 
-       setlno(call, n.Pos)
+       // Rewrite the line information for the inlined AST.
+       parent := -1
+       callBase := Ctxt.PosTable.Pos(n.Pos).Base()
+       if callBase != nil {
+               parent = callBase.InliningIndex()
+       }
+       newIndex := Ctxt.InlTree.Add(parent, n.Pos, Linksym(fn.Sym))
+       setpos := &setPos{
+               bases:       make(map[*src.PosBase]*src.PosBase),
+               newInlIndex: newIndex,
+       }
+       setpos.node(call)
 
        as.Rlist.Set(args.Slice())
 
@@ -1024,29 +1035,47 @@ func (subst *inlsubst) node(n *Node) *Node {
        }
 }
 
-// Plaster over linenumbers
-func setlnolist(ll Nodes, lno src.XPos) {
+// setPos is a visitor to update position info with a new inlining index.
+type setPos struct {
+       bases       map[*src.PosBase]*src.PosBase
+       newInlIndex int
+}
+
+func (s *setPos) nodelist(ll Nodes) {
        for _, n := range ll.Slice() {
-               setlno(n, lno)
+               s.node(n)
        }
 }
 
-func setlno(n *Node, lno src.XPos) {
+func (s *setPos) node(n *Node) {
        if n == nil {
                return
        }
 
        // don't clobber names, unless they're freshly synthesized
        if n.Op != ONAME || !n.Pos.IsKnown() {
-               n.Pos = lno
+               n.Pos = s.updatedPos(n)
        }
 
-       setlno(n.Left, lno)
-       setlno(n.Right, lno)
-       setlnolist(n.List, lno)
-       setlnolist(n.Rlist, lno)
-       setlnolist(n.Ninit, lno)
-       setlnolist(n.Nbody, lno)
+       s.node(n.Left)
+       s.node(n.Right)
+       s.nodelist(n.List)
+       s.nodelist(n.Rlist)
+       s.nodelist(n.Ninit)
+       s.nodelist(n.Nbody)
+}
+
+func (s *setPos) updatedPos(n *Node) src.XPos {
+       pos := Ctxt.PosTable.Pos(n.Pos)
+       oldbase := pos.Base() // can be nil
+       newbase := s.bases[oldbase]
+       if newbase == nil {
+               newbase = src.NewInliningBase(oldbase, s.newInlIndex)
+               pos.SetBase(newbase)
+               s.bases[oldbase] = newbase
+       }
+       pos.SetBase(newbase)
+       return Ctxt.PosTable.XPos(pos)
 }
 
 func (n *Node) isMethodCalledAsFunction() bool {
index adc487be4c507e2e50b4299232a117b7a3223d4f..c1c4d04217c5a0ae75d9f8d8c9205e28c2cfecd8 100644 (file)
@@ -87,7 +87,7 @@ func hcrash() {
 }
 
 func linestr(pos src.XPos) string {
-       return Ctxt.PosTable.Pos(pos).String()
+       return Ctxt.OutermostPos(pos).String()
 }
 
 // lasterror keeps track of the most recently issued error.
index 50f636e9d1ad0c020cd435b71e6b6e063bea3882..947d8a8516ebb170862230ceb1e81dbb4a562bad 100644 (file)
@@ -10,6 +10,8 @@ import (
        "runtime/pprof"
 )
 
+// Line returns n's position as a string. If n has been inlined,
+// it uses the outermost position where n has been inlined.
 func (n *Node) Line() string {
        return linestr(n.Pos)
 }
index d5f891096bb53075b9d292360240a0c2f8111839..af1dbc271ddc94b9ecf82556d5df6640236205ef 100644 (file)
@@ -12,8 +12,10 @@ package obj
 
 const (
        PCDATA_StackMapIndex       = 0
+       PCDATA_InlTreeIndex        = 1
        FUNCDATA_ArgsPointerMaps   = 0
        FUNCDATA_LocalsPointerMaps = 1
+       FUNCDATA_InlTree           = 2
 
        // ArgsSizeUnknown is set in Func.argsize to mark all functions
        // whose argument size is unknown (C vararg functions, and
diff --git a/src/cmd/internal/obj/inl.go b/src/cmd/internal/obj/inl.go
new file mode 100644 (file)
index 0000000..f5e0695
--- /dev/null
@@ -0,0 +1,78 @@
+// Copyright 2017 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 obj
+
+import "cmd/internal/src"
+
+// InlTree s a collection of inlined calls. The Parent field of an
+// InlinedCall is the index of another InlinedCall in InlTree.
+//
+// The compiler maintains a global inlining tree and adds a node to it
+// every time a function is inlined. For example, suppose f() calls g()
+// and g has two calls to h(), and that f, g, and h are inlineable:
+//
+//  1 func main() {
+//  2     f()
+//  3 }
+//  4 func f() {
+//  5     g()
+//  6 }
+//  7 func g() {
+//  8     h()
+//  9     h()
+// 10 }
+//
+// Assuming the global tree starts empty, inlining will produce the
+// following tree:
+//
+//   []InlinedCall{
+//     {Parent: -1, Func: "f", Pos: <line 2>},
+//     {Parent:  0, Func: "g", Pos: <line 5>},
+//     {Parent:  1, Func: "h", Pos: <line 8>},
+//     {Parent:  1, Func: "h", Pos: <line 9>},
+//   }
+//
+// The nodes of h inlined into main will have inlining indexes 2 and 3.
+//
+// Eventually, the compiler extracts a per-function inlining tree from
+// the global inlining tree (see pcln.go).
+type InlTree struct {
+       nodes []InlinedCall
+}
+
+// InlinedCall is a node in an InlTree.
+type InlinedCall struct {
+       Parent int      // index of the parent in the InlTree or < 0 if outermost call
+       Pos    src.XPos // position of the inlined call
+       Func   *LSym    // function that was inlined
+}
+
+// Add adds a new call to the tree, returning its index.
+func (tree *InlTree) Add(parent int, pos src.XPos, func_ *LSym) int {
+       r := len(tree.nodes)
+       call := InlinedCall{
+               Parent: parent,
+               Pos:    pos,
+               Func:   func_,
+       }
+       tree.nodes = append(tree.nodes, call)
+       return r
+}
+
+// OutermostPos returns the outermost position corresponding to xpos,
+// which is where xpos was ultimately inlined to. In the example for
+// InlTree, main() contains inlined AST nodes from h(), but the
+// outermost position for those nodes is line 2.
+func (ctxt *Link) OutermostPos(xpos src.XPos) src.Pos {
+       pos := ctxt.PosTable.Pos(xpos)
+
+       outerxpos := xpos
+       for ix := pos.Base().InliningIndex(); ix >= 0; {
+               call := ctxt.InlTree.nodes[ix]
+               ix = call.Parent
+               outerxpos = call.Pos
+       }
+       return ctxt.PosTable.Pos(outerxpos)
+}
index 9dd0c1447846a36c259b745c39f2fbcc9728aa8b..2b066cef34d6d2e568ca5bb0227e2c89f43ace9a 100644 (file)
@@ -397,12 +397,14 @@ type Pcln struct {
        Pcsp        Pcdata
        Pcfile      Pcdata
        Pcline      Pcdata
+       Pcinline    Pcdata
        Pcdata      []Pcdata
        Funcdata    []*LSym
        Funcdataoff []int64
        File        []*LSym
        Lastfile    *LSym
        Lastindex   int
+       InlTree     InlTree // per-function inlining tree extracted from the global tree
 }
 
 // A SymKind describes the kind of memory represented by a symbol.
@@ -728,6 +730,7 @@ type Link struct {
        Pathname      string
        Hash          map[SymVer]*LSym
        PosTable      src.PosTable
+       InlTree       InlTree // global inlining tree used by gc/inl.go
        Imports       []string
        Sym_div       *LSym
        Sym_divu      *LSym
index be8b2b40c9d47939a6c585f931c64425bf4568a7..3be34d9fbeaa3345f583e31f6682e27277725feb 100644 (file)
@@ -94,6 +94,7 @@
 //     - pcsp [data block]
 //     - pcfile [data block]
 //     - pcline [data block]
+//     - pcinline [data block]
 //     - npcdata [int]
 //     - pcdata [npcdata data blocks]
 //     - nfuncdata [int]
 //     - funcdatasym [nfuncdata ints]
 //     - nfile [int]
 //     - file [nfile symref index]
+//     - ninlinedcall [int]
+//     - inlinedcall [ninlinedcall int symref int symref]
 //
 // The file layout and meaning of type integers are architecture-independent.
 //
@@ -156,6 +159,7 @@ func (w *objWriter) addLengths(s *LSym) {
        data += len(pc.Pcsp.P)
        data += len(pc.Pcfile.P)
        data += len(pc.Pcline.P)
+       data += len(pc.Pcinline.P)
        for i := 0; i < len(pc.Pcdata); i++ {
                data += len(pc.Pcdata[i].P)
        }
@@ -227,6 +231,7 @@ func WriteObjFile(ctxt *Link, b *bufio.Writer) {
                w.wr.Write(pc.Pcsp.P)
                w.wr.Write(pc.Pcfile.P)
                w.wr.Write(pc.Pcline.P)
+               w.wr.Write(pc.Pcinline.P)
                for i := 0; i < len(pc.Pcdata); i++ {
                        w.wr.Write(pc.Pcdata[i].P)
                }
@@ -300,6 +305,11 @@ func (w *objWriter) writeRefs(s *LSym) {
                for _, f := range pc.File {
                        w.writeRef(f, true)
                }
+               for _, call := range pc.InlTree.nodes {
+                       w.writeRef(call.Func, false)
+                       f, _ := linkgetlineFromPos(w.ctxt, call.Pos)
+                       w.writeRef(f, true)
+               }
        }
 }
 
@@ -452,6 +462,7 @@ func (w *objWriter) writeSym(s *LSym) {
        w.writeInt(int64(len(pc.Pcsp.P)))
        w.writeInt(int64(len(pc.Pcfile.P)))
        w.writeInt(int64(len(pc.Pcline.P)))
+       w.writeInt(int64(len(pc.Pcinline.P)))
        w.writeInt(int64(len(pc.Pcdata)))
        for i := 0; i < len(pc.Pcdata); i++ {
                w.writeInt(int64(len(pc.Pcdata[i].P)))
@@ -467,6 +478,14 @@ func (w *objWriter) writeSym(s *LSym) {
        for _, f := range pc.File {
                w.writeRefIndex(f)
        }
+       w.writeInt(int64(len(pc.InlTree.nodes)))
+       for _, call := range pc.InlTree.nodes {
+               w.writeInt(int64(call.Parent))
+               f, l := linkgetlineFromPos(w.ctxt, call.Pos)
+               w.writeRefIndex(f)
+               w.writeInt(int64(l))
+               w.writeRefIndex(call.Func)
+       }
 }
 
 func (w *objWriter) writeInt(sval int64) {
index 517550c72d4dcbd701f3eed40153d4b18111f111..8db7802d0cc983b16ccd0b2648a978b8ae6add8a 100644 (file)
@@ -169,6 +169,62 @@ func pctofileline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg
        return int32(i)
 }
 
+// pcinlineState holds the state used to create a function's inlining
+// tree and the PC-value table that maps PCs to nodes in that tree.
+type pcinlineState struct {
+       globalToLocal map[int]int
+       localTree     InlTree
+}
+
+// addBranch adds a branch from the global inlining tree in ctxt to
+// the function's local inlining tree, returning the index in the local tree.
+func (s *pcinlineState) addBranch(ctxt *Link, globalIndex int) int {
+       if globalIndex < 0 {
+               return -1
+       }
+
+       localIndex, ok := s.globalToLocal[globalIndex]
+       if ok {
+               return localIndex
+       }
+
+       // Since tracebacks don't include column information, we could
+       // use one node for multiple calls of the same function on the
+       // same line (e.g., f(x) + f(y)). For now, we use one node for
+       // each inlined call.
+       call := ctxt.InlTree.nodes[globalIndex]
+       call.Parent = s.addBranch(ctxt, call.Parent)
+       localIndex = len(s.localTree.nodes)
+       s.localTree.nodes = append(s.localTree.nodes, call)
+       s.globalToLocal[globalIndex] = localIndex
+       return localIndex
+}
+
+// pctoinline computes the index into the local inlining tree to use at p.
+// If p is not the result of inlining, pctoinline returns -1. Because p.Pos
+// applies to p, phase == 0 (before p) takes care of the update.
+func (s *pcinlineState) pctoinline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 {
+       if phase == 1 {
+               return oldval
+       }
+
+       posBase := ctxt.PosTable.Pos(p.Pos).Base()
+       if posBase == nil {
+               return -1
+       }
+
+       globalIndex := posBase.InliningIndex()
+       if globalIndex < 0 {
+               return -1
+       }
+
+       if s.globalToLocal == nil {
+               s.globalToLocal = make(map[int]int)
+       }
+
+       return int32(s.addBranch(ctxt, globalIndex))
+}
+
 // pctospadj computes the sp adjustment in effect.
 // It is oldval plus any adjustment made by p itself.
 // The adjustment by p takes effect only after p, so we
@@ -238,6 +294,10 @@ func linkpcln(ctxt *Link, cursym *LSym) {
        funcpctab(ctxt, &pcln.Pcfile, cursym, "pctofile", pctofileline, pcln)
        funcpctab(ctxt, &pcln.Pcline, cursym, "pctoline", pctofileline, nil)
 
+       pcinlineState := new(pcinlineState)
+       funcpctab(ctxt, &pcln.Pcinline, cursym, "pctoinline", pcinlineState.pctoinline, nil)
+       pcln.InlTree = pcinlineState.localTree
+
        // tabulate which pc and func data we have.
        havepc := make([]uint32, (npcdata+31)/32)
        havefunc := make([]uint32, (nfuncdata+31)/32)
index d0d050ab08864d3f9a8baa086d3ca8f429bb1a08..1e48f8061637c6038b5009a6fa89668be2510f62 100644 (file)
@@ -59,7 +59,7 @@ func Getgoextlinkenabled() string {
 }
 
 func (p *Prog) Line() string {
-       return p.Ctxt.PosTable.Pos(p.Pos).String()
+       return p.Ctxt.OutermostPos(p.Pos).String()
 }
 
 var armCondCode = []string{
index ad124990e811e1e54c6c92e7047c17045238cb30..35e213a6c508e9e0db23fba2b612734e81e71e6d 100644 (file)
@@ -63,6 +63,9 @@ func (p Pos) Filename() string { return p.base.Pos().RelFilename() }
 // Base returns the position base.
 func (p Pos) Base() *PosBase { return p.base }
 
+// SetBase sets the position base.
+func (p *Pos) SetBase(base *PosBase) { p.base = base }
+
 // RelFilename returns the filename recorded with the position's base.
 func (p Pos) RelFilename() string { return p.base.Filename() }
 
@@ -115,13 +118,14 @@ type PosBase struct {
        filename    string // file name used to open source file, for error messages
        absFilename string // absolute file name, for PC-Line tables
        line        uint   // relative line number at pos
+       inl         int    // inlining index (see cmd/internal/obj/inl.go)
 }
 
 // NewFileBase returns a new *PosBase for a file with the given (relative and
 // absolute) filenames.
 func NewFileBase(filename, absFilename string) *PosBase {
        if filename != "" {
-               base := &PosBase{filename: filename, absFilename: absFilename}
+               base := &PosBase{filename: filename, absFilename: absFilename, inl: -1}
                base.pos = MakePos(base, 0, 0)
                return base
        }
@@ -132,7 +136,24 @@ func NewFileBase(filename, absFilename string) *PosBase {
 //      //line filename:line
 // at position pos.
 func NewLinePragmaBase(pos Pos, filename string, line uint) *PosBase {
-       return &PosBase{pos, filename, filename, line - 1}
+       return &PosBase{pos, filename, filename, line - 1, -1}
+}
+
+// NewInliningBase returns a copy of the old PosBase with the given inlining
+// index. If old == nil, the resulting PosBase has no filename.
+func NewInliningBase(old *PosBase, inlTreeIndex int) *PosBase {
+       if old == nil {
+               base := &PosBase{inl: inlTreeIndex}
+               base.pos = MakePos(base, 0, 0)
+               return base
+       }
+       copy := *old
+       base := &copy
+       base.inl = inlTreeIndex
+       if old == old.pos.base {
+               base.pos.base = base
+       }
+       return base
 }
 
 var noPos Pos
@@ -173,6 +194,16 @@ func (b *PosBase) Line() uint {
        return 0
 }
 
+// InliningIndex returns the index into the global inlining
+// tree recorded with the base. If b == nil or the base has
+// not been inlined, the result is < 0.
+func (b *PosBase) InliningIndex() int {
+       if b != nil {
+               return b.inl
+       }
+       return -1
+}
+
 // ----------------------------------------------------------------------------
 // lico
 
index 3f5b848b3c1bf5d4b2d34a5b790cbf9270e58d24..4cfeedcd0567eb4261f124f135f8a1254153047e 100644 (file)
@@ -65,3 +65,20 @@ func TestSize(t *testing.T) {
                t.Errorf("size = %v; want 8", unsafe.Sizeof(p))
        }
 }
+
+func TestSetBase(t *testing.T) {
+       var tab PosTable
+       b1 := NewFileBase("b1", "b1")
+       orig := MakePos(b1, 42, 7)
+       xpos := tab.XPos(orig)
+
+       pos := tab.Pos(xpos)
+       new := NewInliningBase(b1, 2)
+       pos.SetBase(new)
+       xpos = tab.XPos(pos)
+
+       pos = tab.Pos(xpos)
+       if inl := pos.Base().InliningIndex(); inl != 2 {
+               t.Fatalf("wrong inlining index: %d", inl)
+       }
+}
index ffe0873fc8684c0765f7c6d1d883b0cb536b80e8..1c61835da6bd370c363c1ad361ad4a655cf4d3d6 100644 (file)
@@ -240,10 +240,20 @@ type FuncInfo struct {
        Pcsp        Pcdata
        Pcfile      Pcdata
        Pcline      Pcdata
+       Pcinline    Pcdata
        Pcdata      []Pcdata
        Funcdata    []*Symbol
        Funcdataoff []int64
        File        []*Symbol
+       InlTree     []InlinedCall
+}
+
+// InlinedCall is a node in a local inlining tree (FuncInfo.InlTree).
+type InlinedCall struct {
+       Parent int32   // index of parent in InlTree
+       File   *Symbol // file of the inlined call
+       Line   int32   // line number of the inlined call
+       Func   *Symbol // function that was inlined
 }
 
 type Pcdata struct {
index 7626a4fbc18cf599ad98efc416a3a153dfb68708..c036acbd77082231a76a83a4d50d307c9c4c6dad 100644 (file)
@@ -96,6 +96,7 @@ package ld
 //     - pcsp [data block]
 //     - pcfile [data block]
 //     - pcline [data block]
+//     - pcinline [data block]
 //     - npcdata [int]
 //     - pcdata [npcdata data blocks]
 //     - nfuncdata [int]
@@ -103,6 +104,8 @@ package ld
 //     - funcdatasym [nfuncdata ints]
 //     - nfile [int]
 //     - file [nfile symref index]
+//     - ninlinedcall [int]
+//     - inlinedcall [ninlinedcall int symref int symref]
 //
 // The file layout and meaning of type integers are architecture-independent.
 //
@@ -374,6 +377,7 @@ overwrite:
                pc.Pcsp.P = r.readData()
                pc.Pcfile.P = r.readData()
                pc.Pcline.P = r.readData()
+               pc.Pcinline.P = r.readData()
                n = r.readInt()
                pc.Pcdata = r.pcdata[:n:n]
                if !isdup {
@@ -403,6 +407,14 @@ overwrite:
                for i := 0; i < n; i++ {
                        pc.File[i] = r.readSymIndex()
                }
+               n = r.readInt()
+               pc.InlTree = make([]InlinedCall, n)
+               for i := 0; i < n; i++ {
+                       pc.InlTree[i].Parent = r.readInt32()
+                       pc.InlTree[i].File = r.readSymIndex()
+                       pc.InlTree[i].Line = r.readInt32()
+                       pc.InlTree[i].Func = r.readSymIndex()
+               }
 
                if !dupok {
                        if s.Attr.OnList() {
index 479425f2111ef6191778e136049b55921bc966e4..a1e28c1536b75db6802acf24c4855d8ce0801746 100644 (file)
@@ -108,18 +108,23 @@ func ftabaddstring(ctxt *Link, ftab *Symbol, s string) int32 {
        return start
 }
 
+// numberfile assigns a file number to the file if it hasn't been assigned already.
+func numberfile(ctxt *Link, file *Symbol) {
+       if file.Type != obj.SFILEPATH {
+               ctxt.Filesyms = append(ctxt.Filesyms, file)
+               file.Value = int64(len(ctxt.Filesyms))
+               file.Type = obj.SFILEPATH
+               file.Name = expandGoroot(file.Name)
+       }
+}
+
 func renumberfiles(ctxt *Link, files []*Symbol, d *Pcdata) {
        var f *Symbol
 
        // Give files numbers.
        for i := 0; i < len(files); i++ {
                f = files[i]
-               if f.Type != obj.SFILEPATH {
-                       ctxt.Filesyms = append(ctxt.Filesyms, f)
-                       f.Value = int64(len(ctxt.Filesyms))
-                       f.Type = obj.SFILEPATH
-                       f.Name = expandGoroot(f.Name)
-               }
+               numberfile(ctxt, f)
        }
 
        newval := int32(-1)
@@ -226,6 +231,16 @@ func (ctxt *Link) pclntab() {
        setuintxx(ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize))
        pclntabPclntabOffset = int32(8 + SysArch.PtrSize)
 
+       funcnameoff := make(map[string]int32)
+       nameToOffset := func(name string) int32 {
+               nameoff, ok := funcnameoff[name]
+               if !ok {
+                       nameoff = ftabaddstring(ctxt, ftab, name)
+                       funcnameoff[name] = nameoff
+               }
+               return nameoff
+       }
+
        nfunc = 0
        var last *Symbol
        for _, s := range ctxt.Textp {
@@ -242,6 +257,25 @@ func (ctxt *Link) pclntab() {
                        pclntabFirstFunc = s
                }
 
+               if len(pcln.InlTree) > 0 {
+                       if len(pcln.Pcdata) <= obj.PCDATA_InlTreeIndex {
+                               // Create inlining pcdata table.
+                               pcdata := make([]Pcdata, obj.PCDATA_InlTreeIndex+1)
+                               copy(pcdata, pcln.Pcdata)
+                               pcln.Pcdata = pcdata
+                       }
+
+                       if len(pcln.Funcdataoff) <= obj.FUNCDATA_InlTree {
+                               // Create inline tree funcdata.
+                               funcdata := make([]*Symbol, obj.FUNCDATA_InlTree+1)
+                               funcdataoff := make([]int64, obj.FUNCDATA_InlTree+1)
+                               copy(funcdata, pcln.Funcdata)
+                               copy(funcdataoff, pcln.Funcdataoff)
+                               pcln.Funcdata = funcdata
+                               pcln.Funcdataoff = funcdataoff
+                       }
+               }
+
                funcstart := int32(len(ftab.P))
                funcstart += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1)
 
@@ -264,7 +298,8 @@ func (ctxt *Link) pclntab() {
                off = int32(setaddr(ctxt, ftab, int64(off), s))
 
                // name int32
-               off = int32(setuint32(ctxt, ftab, int64(off), uint32(ftabaddstring(ctxt, ftab, s.Name))))
+               nameoff := nameToOffset(s.Name)
+               off = int32(setuint32(ctxt, ftab, int64(off), uint32(nameoff)))
 
                // args int32
                // TODO: Move into funcinfo.
@@ -295,6 +330,30 @@ func (ctxt *Link) pclntab() {
                        }
                }
 
+               if len(pcln.InlTree) > 0 {
+                       inlTreeSym := ctxt.Syms.Lookup("inltree."+s.Name, 0)
+                       inlTreeSym.Type = obj.SRODATA
+                       inlTreeSym.Attr |= AttrReachable | AttrDuplicateOK
+
+                       for i, call := range pcln.InlTree {
+                               // Usually, call.File is already numbered since the file
+                               // shows up in the Pcfile table. However, two inlined calls
+                               // might overlap exactly so that only the innermost file
+                               // appears in the Pcfile table. In that case, this assigns
+                               // the outer file a number.
+                               numberfile(ctxt, call.File)
+                               nameoff := nameToOffset(call.Func.Name)
+
+                               setuint32(ctxt, inlTreeSym, int64(i*16+0), uint32(call.Parent))
+                               setuint32(ctxt, inlTreeSym, int64(i*16+4), uint32(call.File.Value))
+                               setuint32(ctxt, inlTreeSym, int64(i*16+8), uint32(call.Line))
+                               setuint32(ctxt, inlTreeSym, int64(i*16+12), uint32(nameoff))
+                       }
+
+                       pcln.Funcdata[obj.FUNCDATA_InlTree] = inlTreeSym
+                       pcln.Pcdata[obj.PCDATA_InlTreeIndex] = pcln.Pcinline
+               }
+
                // pcdata
                off = addpctab(ctxt, ftab, off, &pcln.Pcsp)
 
index dd0e5407e828c46b5aaa8ca8e46ce4350fa70155..87de67bbdcbac87d9d3022e834dab2df32f433d6 100644 (file)
@@ -513,7 +513,10 @@ func (ctxt *Link) symtab() {
                                s.Outer = symgofunc
                        }
 
-               case strings.HasPrefix(s.Name, "gcargs."), strings.HasPrefix(s.Name, "gclocals."), strings.HasPrefix(s.Name, "gclocals·"):
+               case strings.HasPrefix(s.Name, "gcargs."),
+                       strings.HasPrefix(s.Name, "gclocals."),
+                       strings.HasPrefix(s.Name, "gclocals·"),
+                       strings.HasPrefix(s.Name, "inltree."):
                        s.Type = obj.SGOFUNC
                        s.Attr |= AttrHidden
                        s.Outer = symgofunc
index c77e7b094f1e4f950d889a6ea9af3267736b72e8..27d5a2f6b975d968b4d7f1569a851220e884e643 100644 (file)
@@ -9,9 +9,11 @@
 // These must agree with symtab.go and ../cmd/internal/obj/funcdata.go.
 
 #define PCDATA_StackMapIndex 0
+#define PCDATA_InlTreeIndex 1
 
 #define FUNCDATA_ArgsPointerMaps 0 /* garbage collector blocks */
 #define FUNCDATA_LocalsPointerMaps 1
+#define FUNCDATA_InlTree 2
 
 // Pseudo-assembly statements.
 
index ad8a8a1b8d278387c0c49abe8093fa65f48a1d87..94bdc92853bf94a2f233232bfc38d7efbd085541 100644 (file)
@@ -173,8 +173,10 @@ func (f *Func) raw() *_func {
 // See funcdata.h and ../cmd/internal/obj/funcdata.go.
 const (
        _PCDATA_StackMapIndex       = 0
+       _PCDATA_InlTreeIndex        = 1
        _FUNCDATA_ArgsPointerMaps   = 0
        _FUNCDATA_LocalsPointerMaps = 1
+       _FUNCDATA_InlTree           = 2
        _ArgsSizeUnknown            = -0x80000000
 )