Home | History | Annotate | Download | only in gc
      1 // Copyright 2017 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 package gc
      6 
      7 import (
      8 	"cmd/internal/dwarf"
      9 	"cmd/internal/obj"
     10 	"cmd/internal/src"
     11 	"sort"
     12 	"strings"
     13 )
     14 
     15 // To identify variables by original source position.
     16 type varPos struct {
     17 	DeclName string
     18 	DeclFile string
     19 	DeclLine uint
     20 	DeclCol  uint
     21 }
     22 
     23 // This is the main entry point for collection of raw material to
     24 // drive generation of DWARF "inlined subroutine" DIEs. See proposal
     25 // 22080 for more details and background info.
     26 func assembleInlines(fnsym *obj.LSym, fn *Node, dwVars []*dwarf.Var) dwarf.InlCalls {
     27 	var inlcalls dwarf.InlCalls
     28 
     29 	if Debug_gendwarfinl != 0 {
     30 		Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name)
     31 	}
     32 
     33 	// This maps inline index (from Ctxt.InlTree) to index in inlcalls.Calls
     34 	imap := make(map[int]int)
     35 
     36 	// Walk progs to build up the InlCalls data structure
     37 	var prevpos src.XPos
     38 	for p := fnsym.Func.Text; p != nil; p = p.Link {
     39 		if p.Pos == prevpos {
     40 			continue
     41 		}
     42 		ii := posInlIndex(p.Pos)
     43 		if ii >= 0 {
     44 			insertInlCall(&inlcalls, ii, imap)
     45 		}
     46 		prevpos = p.Pos
     47 	}
     48 
     49 	// This is used to partition DWARF vars by inline index. Vars not
     50 	// produced by the inliner will wind up in the vmap[0] entry.
     51 	vmap := make(map[int32][]*dwarf.Var)
     52 
     53 	// Now walk the dwarf vars and partition them based on whether they
     54 	// were produced by the inliner (dwv.InlIndex > 0) or were original
     55 	// vars/params from the function (dwv.InlIndex == 0).
     56 	for _, dwv := range dwVars {
     57 
     58 		vmap[dwv.InlIndex] = append(vmap[dwv.InlIndex], dwv)
     59 
     60 		// Zero index => var was not produced by an inline
     61 		if dwv.InlIndex == 0 {
     62 			continue
     63 		}
     64 
     65 		// Look up index in our map, then tack the var in question
     66 		// onto the vars list for the correct inlined call.
     67 		ii := int(dwv.InlIndex) - 1
     68 		idx, ok := imap[ii]
     69 		if !ok {
     70 			// We can occasionally encounter a var produced by the
     71 			// inliner for which there is no remaining prog; add a new
     72 			// entry to the call list in this scenario.
     73 			idx = insertInlCall(&inlcalls, ii, imap)
     74 		}
     75 		inlcalls.Calls[idx].InlVars =
     76 			append(inlcalls.Calls[idx].InlVars, dwv)
     77 	}
     78 
     79 	// Post process the map above to assign child indices to vars.
     80 	//
     81 	// A given variable is treated differently depending on whether it
     82 	// is part of the top-level function (ii == 0) or if it was
     83 	// produced as a result of an inline (ii != 0).
     84 	//
     85 	// If a variable was not produced by an inline and its containing
     86 	// function was not inlined, then we just assign an ordering of
     87 	// based on variable name.
     88 	//
     89 	// If a variable was not produced by an inline and its containing
     90 	// function was inlined, then we need to assign a child index
     91 	// based on the order of vars in the abstract function (in
     92 	// addition, those vars that don't appear in the abstract
     93 	// function, such as "~r1", are flagged as such).
     94 	//
     95 	// If a variable was produced by an inline, then we locate it in
     96 	// the pre-inlining decls for the target function and assign child
     97 	// index accordingly.
     98 	for ii, sl := range vmap {
     99 		sort.Sort(byClassThenName(sl))
    100 		var m map[varPos]int
    101 		if ii == 0 {
    102 			if !fnsym.WasInlined() {
    103 				for j := 0; j < len(sl); j++ {
    104 					sl[j].ChildIndex = int32(j)
    105 				}
    106 				continue
    107 			}
    108 			m = makePreinlineDclMap(fnsym)
    109 		} else {
    110 			ifnlsym := Ctxt.InlTree.InlinedFunction(int(ii - 1))
    111 			m = makePreinlineDclMap(ifnlsym)
    112 		}
    113 
    114 		// Here we assign child indices to variables based on
    115 		// pre-inlined decls, and set the "IsInAbstract" flag
    116 		// appropriately. In addition: parameter and local variable
    117 		// names are given "middle dot" version numbers as part of the
    118 		// writing them out to export data (see issue 4326). If DWARF
    119 		// inlined routine generation is turned on, we want to undo
    120 		// this versioning, since DWARF variables in question will be
    121 		// parented by the inlined routine and not the top-level
    122 		// caller.
    123 		synthCount := len(m)
    124 		for j := 0; j < len(sl); j++ {
    125 			canonName := unversion(sl[j].Name)
    126 			vp := varPos{
    127 				DeclName: canonName,
    128 				DeclFile: sl[j].DeclFile,
    129 				DeclLine: sl[j].DeclLine,
    130 				DeclCol:  sl[j].DeclCol,
    131 			}
    132 			synthesized := strings.HasPrefix(sl[j].Name, "~r") || canonName == "_"
    133 			if idx, found := m[vp]; found {
    134 				sl[j].ChildIndex = int32(idx)
    135 				sl[j].IsInAbstract = !synthesized
    136 				sl[j].Name = canonName
    137 			} else {
    138 				// Variable can't be found in the pre-inline dcl list.
    139 				// In the top-level case (ii=0) this can happen
    140 				// because a composite variable was split into pieces,
    141 				// and we're looking at a piece. We can also see
    142 				// return temps (~r%d) that were created during
    143 				// lowering, or unnamed params ("_").
    144 				sl[j].ChildIndex = int32(synthCount)
    145 				synthCount += 1
    146 			}
    147 		}
    148 	}
    149 
    150 	// Make a second pass through the progs to compute PC ranges for
    151 	// the various inlined calls.
    152 	curii := -1
    153 	var crange *dwarf.Range
    154 	var prevp *obj.Prog
    155 	for p := fnsym.Func.Text; p != nil; prevp, p = p, p.Link {
    156 		if prevp != nil && p.Pos == prevp.Pos {
    157 			continue
    158 		}
    159 		ii := posInlIndex(p.Pos)
    160 		if ii == curii {
    161 			continue
    162 		} else {
    163 			// Close out the current range
    164 			endRange(crange, prevp)
    165 
    166 			// Begin new range
    167 			crange = beginRange(inlcalls.Calls, p, ii, imap)
    168 			curii = ii
    169 		}
    170 	}
    171 	if prevp != nil {
    172 		endRange(crange, prevp)
    173 	}
    174 
    175 	// Debugging
    176 	if Debug_gendwarfinl != 0 {
    177 		dumpInlCalls(inlcalls)
    178 		dumpInlVars(dwVars)
    179 	}
    180 
    181 	return inlcalls
    182 }
    183 
    184 // Secondary hook for DWARF inlined subroutine generation. This is called
    185 // late in the compilation when it is determined that we need an
    186 // abstract function DIE for an inlined routine imported from a
    187 // previously compiled package.
    188 func genAbstractFunc(fn *obj.LSym) {
    189 	ifn := Ctxt.DwFixups.GetPrecursorFunc(fn)
    190 	if ifn == nil {
    191 		Ctxt.Diag("failed to locate precursor fn for %v", fn)
    192 		return
    193 	}
    194 	if Debug_gendwarfinl != 0 {
    195 		Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
    196 	}
    197 	Ctxt.DwarfAbstractFunc(ifn, fn, myimportpath)
    198 }
    199 
    200 // Undo any versioning performed when a name was written
    201 // out as part of export data.
    202 func unversion(name string) string {
    203 	if i := strings.Index(name, ""); i > 0 {
    204 		name = name[:i]
    205 	}
    206 	return name
    207 }
    208 
    209 // Given a function that was inlined as part of the compilation, dig
    210 // up the pre-inlining DCL list for the function and create a map that
    211 // supports lookup of pre-inline dcl index, based on variable
    212 // position/name.
    213 func makePreinlineDclMap(fnsym *obj.LSym) map[varPos]int {
    214 	dcl := preInliningDcls(fnsym)
    215 	m := make(map[varPos]int)
    216 	for i := 0; i < len(dcl); i++ {
    217 		n := dcl[i]
    218 		pos := Ctxt.InnermostPos(n.Pos)
    219 		vp := varPos{
    220 			DeclName: unversion(n.Sym.Name),
    221 			DeclFile: pos.Base().SymFilename(),
    222 			DeclLine: pos.Line(),
    223 			DeclCol:  pos.Col(),
    224 		}
    225 		if _, found := m[vp]; found {
    226 			Fatalf("child dcl collision on symbol %s within %v\n", n.Sym.Name, fnsym.Name)
    227 		}
    228 		m[vp] = i
    229 	}
    230 	return m
    231 }
    232 
    233 func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
    234 	callIdx, found := imap[inlIdx]
    235 	if found {
    236 		return callIdx
    237 	}
    238 
    239 	// Haven't seen this inline yet. Visit parent of inline if there
    240 	// is one. We do this first so that parents appear before their
    241 	// children in the resulting table.
    242 	parCallIdx := -1
    243 	parInlIdx := Ctxt.InlTree.Parent(inlIdx)
    244 	if parInlIdx >= 0 {
    245 		parCallIdx = insertInlCall(dwcalls, parInlIdx, imap)
    246 	}
    247 
    248 	// Create new entry for this inline
    249 	inlinedFn := Ctxt.InlTree.InlinedFunction(int(inlIdx))
    250 	callXPos := Ctxt.InlTree.CallPos(int(inlIdx))
    251 	absFnSym := Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
    252 	pb := Ctxt.PosTable.Pos(callXPos).Base()
    253 	callFileSym := Ctxt.Lookup(pb.SymFilename())
    254 	ic := dwarf.InlCall{
    255 		InlIndex:  inlIdx,
    256 		CallFile:  callFileSym,
    257 		CallLine:  uint32(callXPos.Line()),
    258 		AbsFunSym: absFnSym,
    259 		Root:      parCallIdx == -1,
    260 	}
    261 	dwcalls.Calls = append(dwcalls.Calls, ic)
    262 	callIdx = len(dwcalls.Calls) - 1
    263 	imap[inlIdx] = callIdx
    264 
    265 	if parCallIdx != -1 {
    266 		// Add this inline to parent's child list
    267 		dwcalls.Calls[parCallIdx].Children = append(dwcalls.Calls[parCallIdx].Children, callIdx)
    268 	}
    269 
    270 	return callIdx
    271 }
    272 
    273 // Given a src.XPos, return its associated inlining index if it
    274 // corresponds to something created as a result of an inline, or -1 if
    275 // there is no inline info. Note that the index returned will refer to
    276 // the deepest call in the inlined stack, e.g. if you have "A calls B
    277 // calls C calls D" and all three callees are inlined (B, C, and D),
    278 // the index for a node from the inlined body of D will refer to the
    279 // call to D from C. Whew.
    280 func posInlIndex(xpos src.XPos) int {
    281 	pos := Ctxt.PosTable.Pos(xpos)
    282 	if b := pos.Base(); b != nil {
    283 		ii := b.InliningIndex()
    284 		if ii >= 0 {
    285 			return ii
    286 		}
    287 	}
    288 	return -1
    289 }
    290 
    291 func endRange(crange *dwarf.Range, p *obj.Prog) {
    292 	if crange == nil {
    293 		return
    294 	}
    295 	crange.End = p.Pc
    296 }
    297 
    298 func beginRange(calls []dwarf.InlCall, p *obj.Prog, ii int, imap map[int]int) *dwarf.Range {
    299 	if ii == -1 {
    300 		return nil
    301 	}
    302 	callIdx, found := imap[ii]
    303 	if !found {
    304 		Fatalf("internal error: can't find inlIndex %d in imap for prog at %d\n", ii, p.Pc)
    305 	}
    306 	call := &calls[callIdx]
    307 
    308 	// Set up range and append to correct inlined call
    309 	call.Ranges = append(call.Ranges, dwarf.Range{Start: p.Pc, End: -1})
    310 	return &call.Ranges[len(call.Ranges)-1]
    311 }
    312 
    313 func cmpDwarfVar(a, b *dwarf.Var) bool {
    314 	// named before artificial
    315 	aart := 0
    316 	if strings.HasPrefix(a.Name, "~r") {
    317 		aart = 1
    318 	}
    319 	bart := 0
    320 	if strings.HasPrefix(b.Name, "~r") {
    321 		bart = 1
    322 	}
    323 	if aart != bart {
    324 		return aart < bart
    325 	}
    326 
    327 	// otherwise sort by name
    328 	return a.Name < b.Name
    329 }
    330 
    331 // byClassThenName implements sort.Interface for []*dwarf.Var using cmpDwarfVar.
    332 type byClassThenName []*dwarf.Var
    333 
    334 func (s byClassThenName) Len() int           { return len(s) }
    335 func (s byClassThenName) Less(i, j int) bool { return cmpDwarfVar(s[i], s[j]) }
    336 func (s byClassThenName) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
    337 
    338 func dumpInlCall(inlcalls dwarf.InlCalls, idx, ilevel int) {
    339 	for i := 0; i < ilevel; i += 1 {
    340 		Ctxt.Logf("  ")
    341 	}
    342 	ic := inlcalls.Calls[idx]
    343 	callee := Ctxt.InlTree.InlinedFunction(ic.InlIndex)
    344 	Ctxt.Logf("  %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
    345 	for _, f := range ic.InlVars {
    346 		Ctxt.Logf(" %v", f.Name)
    347 	}
    348 	Ctxt.Logf(" ) C: (")
    349 	for _, k := range ic.Children {
    350 		Ctxt.Logf(" %v", k)
    351 	}
    352 	Ctxt.Logf(" ) R:")
    353 	for _, r := range ic.Ranges {
    354 		Ctxt.Logf(" [%d,%d)", r.Start, r.End)
    355 	}
    356 	Ctxt.Logf("\n")
    357 	for _, k := range ic.Children {
    358 		dumpInlCall(inlcalls, k, ilevel+1)
    359 	}
    360 
    361 }
    362 
    363 func dumpInlCalls(inlcalls dwarf.InlCalls) {
    364 	n := len(inlcalls.Calls)
    365 	for k := 0; k < n; k += 1 {
    366 		if inlcalls.Calls[k].Root {
    367 			dumpInlCall(inlcalls, k, 0)
    368 		}
    369 	}
    370 }
    371 
    372 func dumpInlVars(dwvars []*dwarf.Var) {
    373 	for i, dwv := range dwvars {
    374 		typ := "local"
    375 		if dwv.Abbrev == dwarf.DW_ABRV_PARAM_LOCLIST || dwv.Abbrev == dwarf.DW_ABRV_PARAM {
    376 			typ = "param"
    377 		}
    378 		ia := 0
    379 		if dwv.IsInAbstract {
    380 			ia = 1
    381 		}
    382 		Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
    383 	}
    384 }
    385