compiler,linker: support for DWARF inlined instances

Compiler and linker changes to support DWARF inlined instances,
see https://go.googlesource.com/proposal/+/HEAD/design/22080-dwarf-inlining.md
for design details.

This functionality is gated via the cmd/compile option -gendwarfinl=N,
where N={0,1,2}, where a value of 0 disables dwarf inline generation,
a value of 1 turns on dwarf generation without tracking of formal/local
vars from inlined routines, and a value of 2 enables inlines with
variable tracking.

Updates #22080

Change-Id: I69309b3b815d9fed04aebddc0b8d33d0dbbfad6e
Reviewed-on: https://go-review.googlesource.com/75550
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: David Chase <drchase@google.com>
diff --git a/src/cmd/compile/internal/gc/dwinl.go b/src/cmd/compile/internal/gc/dwinl.go
new file mode 100644
index 0000000..f76bacc
--- /dev/null
+++ b/src/cmd/compile/internal/gc/dwinl.go
@@ -0,0 +1,317 @@
+// 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 {
+	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{
+					DeclFile: pos.Base().SymFilename(),
+					DeclLine: pos.Line(),
+					DeclCol:  pos.Col(),
+				}
+				m[vp] = i
+			}
+			for j := 0; j < len(sl); j++ {
+				vp := varPos{
+					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)
+	}
+}