2017-10-06 11:32:28 -04:00
|
|
|
// 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 gc
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"cmd/internal/dwarf"
|
|
|
|
|
"cmd/internal/obj"
|
|
|
|
|
"cmd/internal/src"
|
|
|
|
|
"sort"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// To identify variables by original source position.
|
|
|
|
|
type varPos struct {
|
2017-12-06 20:10:51 -05:00
|
|
|
DeclName string
|
2017-10-06 11:32:28 -04:00
|
|
|
DeclFile string
|
|
|
|
|
DeclLine uint
|
|
|
|
|
DeclCol uint
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is the main entry point for collection of raw material to
|
|
|
|
|
// drive generation of DWARF "inlined subroutine" DIEs. See proposal
|
|
|
|
|
// 22080 for more details and background info.
|
|
|
|
|
func assembleInlines(fnsym *obj.LSym, fn *Node, dwVars []*dwarf.Var) dwarf.InlCalls {
|
|
|
|
|
var inlcalls dwarf.InlCalls
|
|
|
|
|
|
|
|
|
|
if Debug_gendwarfinl != 0 {
|
|
|
|
|
Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This maps inline index (from Ctxt.InlTree) to index in inlcalls.Calls
|
|
|
|
|
imap := make(map[int]int)
|
|
|
|
|
|
|
|
|
|
// Walk progs to build up the InlCalls data structure
|
|
|
|
|
var prevpos src.XPos
|
|
|
|
|
for p := fnsym.Func.Text; p != nil; p = p.Link {
|
|
|
|
|
if p.Pos == prevpos {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ii := posInlIndex(p.Pos)
|
|
|
|
|
if ii >= 0 {
|
|
|
|
|
insertInlCall(&inlcalls, ii, imap)
|
|
|
|
|
}
|
|
|
|
|
prevpos = p.Pos
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This is used to partition DWARF vars by inline index. Vars not
|
|
|
|
|
// produced by the inliner will wind up in the vmap[0] entry.
|
|
|
|
|
vmap := make(map[int32][]*dwarf.Var)
|
|
|
|
|
|
|
|
|
|
// Now walk the dwarf vars and partition them based on whether they
|
|
|
|
|
// were produced by the inliner (dwv.InlIndex > 0) or were original
|
|
|
|
|
// vars/params from the function (dwv.InlIndex == 0).
|
|
|
|
|
for _, dwv := range dwVars {
|
|
|
|
|
|
|
|
|
|
vmap[dwv.InlIndex] = append(vmap[dwv.InlIndex], dwv)
|
|
|
|
|
|
|
|
|
|
// Zero index => var was not produced by an inline
|
|
|
|
|
if dwv.InlIndex == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Look up index in our map, then tack the var in question
|
|
|
|
|
// onto the vars list for the correct inlined call.
|
|
|
|
|
ii := int(dwv.InlIndex) - 1
|
|
|
|
|
idx, ok := imap[ii]
|
|
|
|
|
if !ok {
|
|
|
|
|
// We can occasionally encounter a var produced by the
|
|
|
|
|
// inliner for which there is no remaining prog; add a new
|
|
|
|
|
// entry to the call list in this scenario.
|
|
|
|
|
idx = insertInlCall(&inlcalls, ii, imap)
|
|
|
|
|
}
|
|
|
|
|
inlcalls.Calls[idx].InlVars =
|
|
|
|
|
append(inlcalls.Calls[idx].InlVars, dwv)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Post process the map above to assign child indices to vars. For
|
|
|
|
|
// variables that weren't produced by an inline, sort them
|
|
|
|
|
// according to class and name and assign indices that way. For
|
|
|
|
|
// vars produced by an inline, assign child index by looking up
|
|
|
|
|
// the var name in the origin pre-optimization dcl list for the
|
|
|
|
|
// inlined function.
|
|
|
|
|
for ii, sl := range vmap {
|
|
|
|
|
if ii == 0 {
|
|
|
|
|
sort.Sort(byClassThenName(sl))
|
|
|
|
|
for j := 0; j < len(sl); j++ {
|
|
|
|
|
sl[j].ChildIndex = int32(j)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Assign child index based on pre-inlined decls
|
|
|
|
|
ifnlsym := Ctxt.InlTree.InlinedFunction(int(ii - 1))
|
|
|
|
|
dcl, _ := preInliningDcls(ifnlsym)
|
|
|
|
|
m := make(map[varPos]int)
|
|
|
|
|
for i := 0; i < len(dcl); i++ {
|
|
|
|
|
n := dcl[i]
|
|
|
|
|
pos := Ctxt.InnermostPos(n.Pos)
|
|
|
|
|
vp := varPos{
|
2017-12-06 20:10:51 -05:00
|
|
|
DeclName: n.Sym.Name,
|
2017-10-06 11:32:28 -04:00
|
|
|
DeclFile: pos.Base().SymFilename(),
|
|
|
|
|
DeclLine: pos.Line(),
|
|
|
|
|
DeclCol: pos.Col(),
|
|
|
|
|
}
|
2017-12-06 20:10:51 -05:00
|
|
|
if _, found := m[vp]; found {
|
|
|
|
|
Fatalf("child dcl collision on symbol %s within %v\n", n.Sym.Name, fnsym.Name)
|
|
|
|
|
}
|
2017-10-06 11:32:28 -04:00
|
|
|
m[vp] = i
|
|
|
|
|
}
|
|
|
|
|
for j := 0; j < len(sl); j++ {
|
|
|
|
|
vp := varPos{
|
2017-12-06 20:10:51 -05:00
|
|
|
DeclName: sl[j].Name,
|
2017-10-06 11:32:28 -04:00
|
|
|
DeclFile: sl[j].DeclFile,
|
|
|
|
|
DeclLine: sl[j].DeclLine,
|
|
|
|
|
DeclCol: sl[j].DeclCol,
|
|
|
|
|
}
|
|
|
|
|
if idx, found := m[vp]; found {
|
|
|
|
|
sl[j].ChildIndex = int32(idx)
|
|
|
|
|
} else {
|
|
|
|
|
Fatalf("unexpected: can't find var %s in preInliningDcls for %v\n", sl[j].Name, Ctxt.InlTree.InlinedFunction(int(ii-1)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Make a second pass through the progs to compute PC ranges
|
|
|
|
|
// for the various inlined calls.
|
|
|
|
|
curii := -1
|
|
|
|
|
var crange *dwarf.Range
|
|
|
|
|
var prevp *obj.Prog
|
|
|
|
|
for p := fnsym.Func.Text; p != nil; prevp, p = p, p.Link {
|
|
|
|
|
if prevp != nil && p.Pos == prevp.Pos {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
ii := posInlIndex(p.Pos)
|
|
|
|
|
if ii == curii {
|
|
|
|
|
continue
|
|
|
|
|
} else {
|
|
|
|
|
// Close out the current range
|
|
|
|
|
endRange(crange, prevp)
|
|
|
|
|
|
|
|
|
|
// Begin new range
|
|
|
|
|
crange = beginRange(inlcalls.Calls, p, ii, imap)
|
|
|
|
|
curii = ii
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if prevp != nil {
|
|
|
|
|
endRange(crange, prevp)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Debugging
|
|
|
|
|
if Debug_gendwarfinl != 0 {
|
|
|
|
|
dumpInlCalls(inlcalls)
|
|
|
|
|
dumpInlVars(dwVars)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return inlcalls
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Secondary hook for DWARF inlined subroutine generation. This is called
|
|
|
|
|
// late in the compilation when it is determined that we need an
|
|
|
|
|
// abstract function DIE for an inlined routine imported from a
|
|
|
|
|
// previously compiled package.
|
|
|
|
|
func genAbstractFunc(fn *obj.LSym) {
|
|
|
|
|
ifn := Ctxt.DwFixups.GetPrecursorFunc(fn)
|
|
|
|
|
if ifn == nil {
|
|
|
|
|
Ctxt.Diag("failed to locate precursor fn for %v", fn)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if Debug_gendwarfinl != 0 {
|
|
|
|
|
Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
|
|
|
|
|
}
|
|
|
|
|
Ctxt.DwarfAbstractFunc(ifn, fn, myimportpath)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
|
|
|
|
|
callIdx, found := imap[inlIdx]
|
|
|
|
|
if found {
|
|
|
|
|
return callIdx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Haven't seen this inline yet. Visit parent of inline if there
|
|
|
|
|
// is one. We do this first so that parents appear before their
|
|
|
|
|
// children in the resulting table.
|
|
|
|
|
parCallIdx := -1
|
|
|
|
|
parInlIdx := Ctxt.InlTree.Parent(inlIdx)
|
|
|
|
|
if parInlIdx >= 0 {
|
|
|
|
|
parCallIdx = insertInlCall(dwcalls, parInlIdx, imap)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Create new entry for this inline
|
|
|
|
|
inlinedFn := Ctxt.InlTree.InlinedFunction(int(inlIdx))
|
|
|
|
|
callXPos := Ctxt.InlTree.CallPos(int(inlIdx))
|
|
|
|
|
absFnSym := Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
|
|
|
|
|
pb := Ctxt.PosTable.Pos(callXPos).Base()
|
|
|
|
|
callFileSym := Ctxt.Lookup(pb.SymFilename())
|
|
|
|
|
ic := dwarf.InlCall{
|
|
|
|
|
InlIndex: inlIdx,
|
|
|
|
|
CallFile: callFileSym,
|
|
|
|
|
CallLine: uint32(callXPos.Line()),
|
|
|
|
|
AbsFunSym: absFnSym,
|
|
|
|
|
Root: parCallIdx == -1,
|
|
|
|
|
}
|
|
|
|
|
dwcalls.Calls = append(dwcalls.Calls, ic)
|
|
|
|
|
callIdx = len(dwcalls.Calls) - 1
|
|
|
|
|
imap[inlIdx] = callIdx
|
|
|
|
|
|
|
|
|
|
if parCallIdx != -1 {
|
|
|
|
|
// Add this inline to parent's child list
|
|
|
|
|
dwcalls.Calls[parCallIdx].Children = append(dwcalls.Calls[parCallIdx].Children, callIdx)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return callIdx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Given a src.XPos, return its associated inlining index if it
|
|
|
|
|
// corresponds to something created as a result of an inline, or -1 if
|
|
|
|
|
// there is no inline info. Note that the index returned will refer to
|
|
|
|
|
// the deepest call in the inlined stack, e.g. if you have "A calls B
|
|
|
|
|
// calls C calls D" and all three callees are inlined (B, C, and D),
|
|
|
|
|
// the index for a node from the inlined body of D will refer to the
|
|
|
|
|
// call to D from C. Whew.
|
|
|
|
|
func posInlIndex(xpos src.XPos) int {
|
|
|
|
|
pos := Ctxt.PosTable.Pos(xpos)
|
|
|
|
|
if b := pos.Base(); b != nil {
|
|
|
|
|
ii := b.InliningIndex()
|
|
|
|
|
if ii >= 0 {
|
|
|
|
|
return ii
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return -1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func endRange(crange *dwarf.Range, p *obj.Prog) {
|
|
|
|
|
if crange == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
crange.End = p.Pc
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func beginRange(calls []dwarf.InlCall, p *obj.Prog, ii int, imap map[int]int) *dwarf.Range {
|
|
|
|
|
if ii == -1 {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
callIdx, found := imap[ii]
|
|
|
|
|
if !found {
|
|
|
|
|
Fatalf("internal error: can't find inlIndex %d in imap for prog at %d\n", ii, p.Pc)
|
|
|
|
|
}
|
|
|
|
|
call := &calls[callIdx]
|
|
|
|
|
|
|
|
|
|
// Set up range and append to correct inlined call
|
|
|
|
|
call.Ranges = append(call.Ranges, dwarf.Range{Start: p.Pc, End: -1})
|
|
|
|
|
return &call.Ranges[len(call.Ranges)-1]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func cmpDwarfVar(a, b *dwarf.Var) bool {
|
|
|
|
|
// named before artificial
|
|
|
|
|
aart := 0
|
|
|
|
|
if strings.HasPrefix(a.Name, "~r") {
|
|
|
|
|
aart = 1
|
|
|
|
|
}
|
|
|
|
|
bart := 0
|
|
|
|
|
if strings.HasPrefix(b.Name, "~r") {
|
|
|
|
|
bart = 1
|
|
|
|
|
}
|
|
|
|
|
if aart != bart {
|
|
|
|
|
return aart < bart
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// otherwise sort by name
|
|
|
|
|
return a.Name < b.Name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// byClassThenName implements sort.Interface for []*dwarf.Var using cmpDwarfVar.
|
|
|
|
|
type byClassThenName []*dwarf.Var
|
|
|
|
|
|
|
|
|
|
func (s byClassThenName) Len() int { return len(s) }
|
|
|
|
|
func (s byClassThenName) Less(i, j int) bool { return cmpDwarfVar(s[i], s[j]) }
|
|
|
|
|
func (s byClassThenName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
|
|
|
|
|
|
|
|
|
func dumpInlCall(inlcalls dwarf.InlCalls, idx, ilevel int) {
|
|
|
|
|
for i := 0; i < ilevel; i += 1 {
|
|
|
|
|
Ctxt.Logf(" ")
|
|
|
|
|
}
|
|
|
|
|
ic := inlcalls.Calls[idx]
|
|
|
|
|
callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex)
|
|
|
|
|
Ctxt.Logf(" %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
|
|
|
|
|
for _, f := range ic.InlVars {
|
|
|
|
|
Ctxt.Logf(" %v", f.Name)
|
|
|
|
|
}
|
|
|
|
|
Ctxt.Logf(" ) C: (")
|
|
|
|
|
for _, k := range ic.Children {
|
|
|
|
|
Ctxt.Logf(" %v", k)
|
|
|
|
|
}
|
|
|
|
|
Ctxt.Logf(" ) R:")
|
|
|
|
|
for _, r := range ic.Ranges {
|
|
|
|
|
Ctxt.Logf(" [%d,%d)", r.Start, r.End)
|
|
|
|
|
}
|
|
|
|
|
Ctxt.Logf("\n")
|
|
|
|
|
for _, k := range ic.Children {
|
|
|
|
|
dumpInlCall(inlcalls, k, ilevel+1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dumpInlCalls(inlcalls dwarf.InlCalls) {
|
|
|
|
|
n := len(inlcalls.Calls)
|
|
|
|
|
for k := 0; k < n; k += 1 {
|
|
|
|
|
if inlcalls.Calls[k].Root {
|
|
|
|
|
dumpInlCall(inlcalls, k, 0)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func dumpInlVars(dwvars []*dwarf.Var) {
|
|
|
|
|
for i, dwv := range dwvars {
|
|
|
|
|
typ := "local"
|
|
|
|
|
if dwv.Abbrev == dwarf.DW_ABRV_PARAM_LOCLIST || dwv.Abbrev == dwarf.DW_ABRV_PARAM {
|
|
|
|
|
typ = "param"
|
|
|
|
|
}
|
|
|
|
|
Ctxt.Logf("V%d: %s CI:%d II:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, typ)
|
|
|
|
|
}
|
|
|
|
|
}
|