mirror of
https://github.com/golang/go
synced 2024-11-18 16:04:44 -07:00
cmd/compile,link: generate PC-value tables with inlining information
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>
This commit is contained in:
parent
ed70f37e73
commit
699175a11a
@ -843,12 +843,23 @@ func mkinlcall1(n *Node, fn *Node, isddd bool) *Node {
|
|||||||
call.Type = n.Type
|
call.Type = n.Type
|
||||||
call.Typecheck = 1
|
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.
|
// call already have good line numbers that should be preserved.
|
||||||
args := as.Rlist
|
args := as.Rlist
|
||||||
as.Rlist.Set(nil)
|
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())
|
as.Rlist.Set(args.Slice())
|
||||||
|
|
||||||
@ -1024,29 +1035,47 @@ func (subst *inlsubst) node(n *Node) *Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Plaster over linenumbers
|
// setPos is a visitor to update position info with a new inlining index.
|
||||||
func setlnolist(ll Nodes, lno src.XPos) {
|
type setPos struct {
|
||||||
|
bases map[*src.PosBase]*src.PosBase
|
||||||
|
newInlIndex int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *setPos) nodelist(ll Nodes) {
|
||||||
for _, n := range ll.Slice() {
|
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 {
|
if n == nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't clobber names, unless they're freshly synthesized
|
// don't clobber names, unless they're freshly synthesized
|
||||||
if n.Op != ONAME || !n.Pos.IsKnown() {
|
if n.Op != ONAME || !n.Pos.IsKnown() {
|
||||||
n.Pos = lno
|
n.Pos = s.updatedPos(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
setlno(n.Left, lno)
|
s.node(n.Left)
|
||||||
setlno(n.Right, lno)
|
s.node(n.Right)
|
||||||
setlnolist(n.List, lno)
|
s.nodelist(n.List)
|
||||||
setlnolist(n.Rlist, lno)
|
s.nodelist(n.Rlist)
|
||||||
setlnolist(n.Ninit, lno)
|
s.nodelist(n.Ninit)
|
||||||
setlnolist(n.Nbody, lno)
|
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 {
|
func (n *Node) isMethodCalledAsFunction() bool {
|
||||||
|
@ -87,7 +87,7 @@ func hcrash() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func linestr(pos src.XPos) string {
|
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.
|
// lasterror keeps track of the most recently issued error.
|
||||||
|
@ -10,6 +10,8 @@ import (
|
|||||||
"runtime/pprof"
|
"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 {
|
func (n *Node) Line() string {
|
||||||
return linestr(n.Pos)
|
return linestr(n.Pos)
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,10 @@ package obj
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
PCDATA_StackMapIndex = 0
|
PCDATA_StackMapIndex = 0
|
||||||
|
PCDATA_InlTreeIndex = 1
|
||||||
FUNCDATA_ArgsPointerMaps = 0
|
FUNCDATA_ArgsPointerMaps = 0
|
||||||
FUNCDATA_LocalsPointerMaps = 1
|
FUNCDATA_LocalsPointerMaps = 1
|
||||||
|
FUNCDATA_InlTree = 2
|
||||||
|
|
||||||
// ArgsSizeUnknown is set in Func.argsize to mark all functions
|
// ArgsSizeUnknown is set in Func.argsize to mark all functions
|
||||||
// whose argument size is unknown (C vararg functions, and
|
// whose argument size is unknown (C vararg functions, and
|
||||||
|
78
src/cmd/internal/obj/inl.go
Normal file
78
src/cmd/internal/obj/inl.go
Normal file
@ -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)
|
||||||
|
}
|
@ -397,12 +397,14 @@ type Pcln struct {
|
|||||||
Pcsp Pcdata
|
Pcsp Pcdata
|
||||||
Pcfile Pcdata
|
Pcfile Pcdata
|
||||||
Pcline Pcdata
|
Pcline Pcdata
|
||||||
|
Pcinline Pcdata
|
||||||
Pcdata []Pcdata
|
Pcdata []Pcdata
|
||||||
Funcdata []*LSym
|
Funcdata []*LSym
|
||||||
Funcdataoff []int64
|
Funcdataoff []int64
|
||||||
File []*LSym
|
File []*LSym
|
||||||
Lastfile *LSym
|
Lastfile *LSym
|
||||||
Lastindex int
|
Lastindex int
|
||||||
|
InlTree InlTree // per-function inlining tree extracted from the global tree
|
||||||
}
|
}
|
||||||
|
|
||||||
// A SymKind describes the kind of memory represented by a symbol.
|
// A SymKind describes the kind of memory represented by a symbol.
|
||||||
@ -728,6 +730,7 @@ type Link struct {
|
|||||||
Pathname string
|
Pathname string
|
||||||
Hash map[SymVer]*LSym
|
Hash map[SymVer]*LSym
|
||||||
PosTable src.PosTable
|
PosTable src.PosTable
|
||||||
|
InlTree InlTree // global inlining tree used by gc/inl.go
|
||||||
Imports []string
|
Imports []string
|
||||||
Sym_div *LSym
|
Sym_div *LSym
|
||||||
Sym_divu *LSym
|
Sym_divu *LSym
|
||||||
|
@ -94,6 +94,7 @@
|
|||||||
// - pcsp [data block]
|
// - pcsp [data block]
|
||||||
// - pcfile [data block]
|
// - pcfile [data block]
|
||||||
// - pcline [data block]
|
// - pcline [data block]
|
||||||
|
// - pcinline [data block]
|
||||||
// - npcdata [int]
|
// - npcdata [int]
|
||||||
// - pcdata [npcdata data blocks]
|
// - pcdata [npcdata data blocks]
|
||||||
// - nfuncdata [int]
|
// - nfuncdata [int]
|
||||||
@ -101,6 +102,8 @@
|
|||||||
// - funcdatasym [nfuncdata ints]
|
// - funcdatasym [nfuncdata ints]
|
||||||
// - nfile [int]
|
// - nfile [int]
|
||||||
// - file [nfile symref index]
|
// - file [nfile symref index]
|
||||||
|
// - ninlinedcall [int]
|
||||||
|
// - inlinedcall [ninlinedcall int symref int symref]
|
||||||
//
|
//
|
||||||
// The file layout and meaning of type integers are architecture-independent.
|
// 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.Pcsp.P)
|
||||||
data += len(pc.Pcfile.P)
|
data += len(pc.Pcfile.P)
|
||||||
data += len(pc.Pcline.P)
|
data += len(pc.Pcline.P)
|
||||||
|
data += len(pc.Pcinline.P)
|
||||||
for i := 0; i < len(pc.Pcdata); i++ {
|
for i := 0; i < len(pc.Pcdata); i++ {
|
||||||
data += len(pc.Pcdata[i].P)
|
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.Pcsp.P)
|
||||||
w.wr.Write(pc.Pcfile.P)
|
w.wr.Write(pc.Pcfile.P)
|
||||||
w.wr.Write(pc.Pcline.P)
|
w.wr.Write(pc.Pcline.P)
|
||||||
|
w.wr.Write(pc.Pcinline.P)
|
||||||
for i := 0; i < len(pc.Pcdata); i++ {
|
for i := 0; i < len(pc.Pcdata); i++ {
|
||||||
w.wr.Write(pc.Pcdata[i].P)
|
w.wr.Write(pc.Pcdata[i].P)
|
||||||
}
|
}
|
||||||
@ -300,6 +305,11 @@ func (w *objWriter) writeRefs(s *LSym) {
|
|||||||
for _, f := range pc.File {
|
for _, f := range pc.File {
|
||||||
w.writeRef(f, true)
|
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.Pcsp.P)))
|
||||||
w.writeInt(int64(len(pc.Pcfile.P)))
|
w.writeInt(int64(len(pc.Pcfile.P)))
|
||||||
w.writeInt(int64(len(pc.Pcline.P)))
|
w.writeInt(int64(len(pc.Pcline.P)))
|
||||||
|
w.writeInt(int64(len(pc.Pcinline.P)))
|
||||||
w.writeInt(int64(len(pc.Pcdata)))
|
w.writeInt(int64(len(pc.Pcdata)))
|
||||||
for i := 0; i < len(pc.Pcdata); i++ {
|
for i := 0; i < len(pc.Pcdata); i++ {
|
||||||
w.writeInt(int64(len(pc.Pcdata[i].P)))
|
w.writeInt(int64(len(pc.Pcdata[i].P)))
|
||||||
@ -467,6 +478,14 @@ func (w *objWriter) writeSym(s *LSym) {
|
|||||||
for _, f := range pc.File {
|
for _, f := range pc.File {
|
||||||
w.writeRefIndex(f)
|
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) {
|
func (w *objWriter) writeInt(sval int64) {
|
||||||
|
@ -169,6 +169,62 @@ func pctofileline(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg
|
|||||||
return int32(i)
|
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.
|
// pctospadj computes the sp adjustment in effect.
|
||||||
// It is oldval plus any adjustment made by p itself.
|
// It is oldval plus any adjustment made by p itself.
|
||||||
// The adjustment by p takes effect only after p, so we
|
// 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.Pcfile, cursym, "pctofile", pctofileline, pcln)
|
||||||
funcpctab(ctxt, &pcln.Pcline, cursym, "pctoline", pctofileline, nil)
|
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.
|
// tabulate which pc and func data we have.
|
||||||
havepc := make([]uint32, (npcdata+31)/32)
|
havepc := make([]uint32, (npcdata+31)/32)
|
||||||
havefunc := make([]uint32, (nfuncdata+31)/32)
|
havefunc := make([]uint32, (nfuncdata+31)/32)
|
||||||
|
@ -59,7 +59,7 @@ func Getgoextlinkenabled() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Prog) Line() string {
|
func (p *Prog) Line() string {
|
||||||
return p.Ctxt.PosTable.Pos(p.Pos).String()
|
return p.Ctxt.OutermostPos(p.Pos).String()
|
||||||
}
|
}
|
||||||
|
|
||||||
var armCondCode = []string{
|
var armCondCode = []string{
|
||||||
|
@ -63,6 +63,9 @@ func (p Pos) Filename() string { return p.base.Pos().RelFilename() }
|
|||||||
// Base returns the position base.
|
// Base returns the position base.
|
||||||
func (p Pos) Base() *PosBase { return p.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.
|
// RelFilename returns the filename recorded with the position's base.
|
||||||
func (p Pos) RelFilename() string { return p.base.Filename() }
|
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
|
filename string // file name used to open source file, for error messages
|
||||||
absFilename string // absolute file name, for PC-Line tables
|
absFilename string // absolute file name, for PC-Line tables
|
||||||
line uint // relative line number at pos
|
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
|
// NewFileBase returns a new *PosBase for a file with the given (relative and
|
||||||
// absolute) filenames.
|
// absolute) filenames.
|
||||||
func NewFileBase(filename, absFilename string) *PosBase {
|
func NewFileBase(filename, absFilename string) *PosBase {
|
||||||
if filename != "" {
|
if filename != "" {
|
||||||
base := &PosBase{filename: filename, absFilename: absFilename}
|
base := &PosBase{filename: filename, absFilename: absFilename, inl: -1}
|
||||||
base.pos = MakePos(base, 0, 0)
|
base.pos = MakePos(base, 0, 0)
|
||||||
return base
|
return base
|
||||||
}
|
}
|
||||||
@ -132,7 +136,24 @@ func NewFileBase(filename, absFilename string) *PosBase {
|
|||||||
// //line filename:line
|
// //line filename:line
|
||||||
// at position pos.
|
// at position pos.
|
||||||
func NewLinePragmaBase(pos Pos, filename string, line uint) *PosBase {
|
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 := ©
|
||||||
|
base.inl = inlTreeIndex
|
||||||
|
if old == old.pos.base {
|
||||||
|
base.pos.base = base
|
||||||
|
}
|
||||||
|
return base
|
||||||
}
|
}
|
||||||
|
|
||||||
var noPos Pos
|
var noPos Pos
|
||||||
@ -173,6 +194,16 @@ func (b *PosBase) Line() uint {
|
|||||||
return 0
|
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
|
// lico
|
||||||
|
|
||||||
|
@ -65,3 +65,20 @@ func TestSize(t *testing.T) {
|
|||||||
t.Errorf("size = %v; want 8", unsafe.Sizeof(p))
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -240,10 +240,20 @@ type FuncInfo struct {
|
|||||||
Pcsp Pcdata
|
Pcsp Pcdata
|
||||||
Pcfile Pcdata
|
Pcfile Pcdata
|
||||||
Pcline Pcdata
|
Pcline Pcdata
|
||||||
|
Pcinline Pcdata
|
||||||
Pcdata []Pcdata
|
Pcdata []Pcdata
|
||||||
Funcdata []*Symbol
|
Funcdata []*Symbol
|
||||||
Funcdataoff []int64
|
Funcdataoff []int64
|
||||||
File []*Symbol
|
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 {
|
type Pcdata struct {
|
||||||
|
@ -96,6 +96,7 @@ package ld
|
|||||||
// - pcsp [data block]
|
// - pcsp [data block]
|
||||||
// - pcfile [data block]
|
// - pcfile [data block]
|
||||||
// - pcline [data block]
|
// - pcline [data block]
|
||||||
|
// - pcinline [data block]
|
||||||
// - npcdata [int]
|
// - npcdata [int]
|
||||||
// - pcdata [npcdata data blocks]
|
// - pcdata [npcdata data blocks]
|
||||||
// - nfuncdata [int]
|
// - nfuncdata [int]
|
||||||
@ -103,6 +104,8 @@ package ld
|
|||||||
// - funcdatasym [nfuncdata ints]
|
// - funcdatasym [nfuncdata ints]
|
||||||
// - nfile [int]
|
// - nfile [int]
|
||||||
// - file [nfile symref index]
|
// - file [nfile symref index]
|
||||||
|
// - ninlinedcall [int]
|
||||||
|
// - inlinedcall [ninlinedcall int symref int symref]
|
||||||
//
|
//
|
||||||
// The file layout and meaning of type integers are architecture-independent.
|
// The file layout and meaning of type integers are architecture-independent.
|
||||||
//
|
//
|
||||||
@ -374,6 +377,7 @@ overwrite:
|
|||||||
pc.Pcsp.P = r.readData()
|
pc.Pcsp.P = r.readData()
|
||||||
pc.Pcfile.P = r.readData()
|
pc.Pcfile.P = r.readData()
|
||||||
pc.Pcline.P = r.readData()
|
pc.Pcline.P = r.readData()
|
||||||
|
pc.Pcinline.P = r.readData()
|
||||||
n = r.readInt()
|
n = r.readInt()
|
||||||
pc.Pcdata = r.pcdata[:n:n]
|
pc.Pcdata = r.pcdata[:n:n]
|
||||||
if !isdup {
|
if !isdup {
|
||||||
@ -403,6 +407,14 @@ overwrite:
|
|||||||
for i := 0; i < n; i++ {
|
for i := 0; i < n; i++ {
|
||||||
pc.File[i] = r.readSymIndex()
|
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 !dupok {
|
||||||
if s.Attr.OnList() {
|
if s.Attr.OnList() {
|
||||||
|
@ -108,18 +108,23 @@ func ftabaddstring(ctxt *Link, ftab *Symbol, s string) int32 {
|
|||||||
return start
|
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) {
|
func renumberfiles(ctxt *Link, files []*Symbol, d *Pcdata) {
|
||||||
var f *Symbol
|
var f *Symbol
|
||||||
|
|
||||||
// Give files numbers.
|
// Give files numbers.
|
||||||
for i := 0; i < len(files); i++ {
|
for i := 0; i < len(files); i++ {
|
||||||
f = files[i]
|
f = files[i]
|
||||||
if f.Type != obj.SFILEPATH {
|
numberfile(ctxt, f)
|
||||||
ctxt.Filesyms = append(ctxt.Filesyms, f)
|
|
||||||
f.Value = int64(len(ctxt.Filesyms))
|
|
||||||
f.Type = obj.SFILEPATH
|
|
||||||
f.Name = expandGoroot(f.Name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
newval := int32(-1)
|
newval := int32(-1)
|
||||||
@ -226,6 +231,16 @@ func (ctxt *Link) pclntab() {
|
|||||||
setuintxx(ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize))
|
setuintxx(ctxt, ftab, 8, uint64(nfunc), int64(SysArch.PtrSize))
|
||||||
pclntabPclntabOffset = int32(8 + 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
|
nfunc = 0
|
||||||
var last *Symbol
|
var last *Symbol
|
||||||
for _, s := range ctxt.Textp {
|
for _, s := range ctxt.Textp {
|
||||||
@ -242,6 +257,25 @@ func (ctxt *Link) pclntab() {
|
|||||||
pclntabFirstFunc = s
|
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))
|
||||||
funcstart += int32(-len(ftab.P)) & (int32(SysArch.PtrSize) - 1)
|
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))
|
off = int32(setaddr(ctxt, ftab, int64(off), s))
|
||||||
|
|
||||||
// name int32
|
// 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
|
// args int32
|
||||||
// TODO: Move into funcinfo.
|
// 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
|
// pcdata
|
||||||
off = addpctab(ctxt, ftab, off, &pcln.Pcsp)
|
off = addpctab(ctxt, ftab, off, &pcln.Pcsp)
|
||||||
|
|
||||||
|
@ -513,7 +513,10 @@ func (ctxt *Link) symtab() {
|
|||||||
s.Outer = symgofunc
|
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.Type = obj.SGOFUNC
|
||||||
s.Attr |= AttrHidden
|
s.Attr |= AttrHidden
|
||||||
s.Outer = symgofunc
|
s.Outer = symgofunc
|
||||||
|
@ -9,9 +9,11 @@
|
|||||||
// These must agree with symtab.go and ../cmd/internal/obj/funcdata.go.
|
// These must agree with symtab.go and ../cmd/internal/obj/funcdata.go.
|
||||||
|
|
||||||
#define PCDATA_StackMapIndex 0
|
#define PCDATA_StackMapIndex 0
|
||||||
|
#define PCDATA_InlTreeIndex 1
|
||||||
|
|
||||||
#define FUNCDATA_ArgsPointerMaps 0 /* garbage collector blocks */
|
#define FUNCDATA_ArgsPointerMaps 0 /* garbage collector blocks */
|
||||||
#define FUNCDATA_LocalsPointerMaps 1
|
#define FUNCDATA_LocalsPointerMaps 1
|
||||||
|
#define FUNCDATA_InlTree 2
|
||||||
|
|
||||||
// Pseudo-assembly statements.
|
// Pseudo-assembly statements.
|
||||||
|
|
||||||
|
@ -173,8 +173,10 @@ func (f *Func) raw() *_func {
|
|||||||
// See funcdata.h and ../cmd/internal/obj/funcdata.go.
|
// See funcdata.h and ../cmd/internal/obj/funcdata.go.
|
||||||
const (
|
const (
|
||||||
_PCDATA_StackMapIndex = 0
|
_PCDATA_StackMapIndex = 0
|
||||||
|
_PCDATA_InlTreeIndex = 1
|
||||||
_FUNCDATA_ArgsPointerMaps = 0
|
_FUNCDATA_ArgsPointerMaps = 0
|
||||||
_FUNCDATA_LocalsPointerMaps = 1
|
_FUNCDATA_LocalsPointerMaps = 1
|
||||||
|
_FUNCDATA_InlTree = 2
|
||||||
_ArgsSizeUnknown = -0x80000000
|
_ArgsSizeUnknown = -0x80000000
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user