Source file src/cmd/compile/internal/dwarfgen/dwinl.go

     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 dwarfgen
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  
    11  	"cmd/compile/internal/base"
    12  	"cmd/compile/internal/ir"
    13  	"cmd/internal/dwarf"
    14  	"cmd/internal/obj"
    15  	"cmd/internal/src"
    16  )
    17  
    18  // To identify variables by original source position.
    19  type varPos struct {
    20  	DeclName string
    21  	DeclFile string
    22  	DeclLine uint
    23  	DeclCol  uint
    24  }
    25  
    26  // This is the main entry point for collection of raw material to
    27  // drive generation of DWARF "inlined subroutine" DIEs. See proposal
    28  // 22080 for more details and background info.
    29  func assembleInlines(fnsym *obj.LSym, dwVars []*dwarf.Var) dwarf.InlCalls {
    30  	var inlcalls dwarf.InlCalls
    31  
    32  	if base.Debug.DwarfInl != 0 {
    33  		base.Ctxt.Logf("assembling DWARF inlined routine info for %v\n", fnsym.Name)
    34  	}
    35  
    36  	// This maps inline index (from Ctxt.InlTree) to index in inlcalls.Calls
    37  	imap := make(map[int]int)
    38  
    39  	// Walk progs to build up the InlCalls data structure
    40  	var prevpos src.XPos
    41  	for p := fnsym.Func().Text; p != nil; p = p.Link {
    42  		if p.Pos == prevpos {
    43  			continue
    44  		}
    45  		ii := posInlIndex(p.Pos)
    46  		if ii >= 0 {
    47  			insertInlCall(&inlcalls, ii, imap)
    48  		}
    49  		prevpos = p.Pos
    50  	}
    51  
    52  	// This is used to partition DWARF vars by inline index. Vars not
    53  	// produced by the inliner will wind up in the vmap[0] entry.
    54  	vmap := make(map[int32][]*dwarf.Var)
    55  
    56  	// Now walk the dwarf vars and partition them based on whether they
    57  	// were produced by the inliner (dwv.InlIndex > 0) or were original
    58  	// vars/params from the function (dwv.InlIndex == 0).
    59  	for _, dwv := range dwVars {
    60  
    61  		vmap[dwv.InlIndex] = append(vmap[dwv.InlIndex], dwv)
    62  
    63  		// Zero index => var was not produced by an inline
    64  		if dwv.InlIndex == 0 {
    65  			continue
    66  		}
    67  
    68  		// Look up index in our map, then tack the var in question
    69  		// onto the vars list for the correct inlined call.
    70  		ii := int(dwv.InlIndex) - 1
    71  		idx, ok := imap[ii]
    72  		if !ok {
    73  			// We can occasionally encounter a var produced by the
    74  			// inliner for which there is no remaining prog; add a new
    75  			// entry to the call list in this scenario.
    76  			idx = insertInlCall(&inlcalls, ii, imap)
    77  		}
    78  		inlcalls.Calls[idx].InlVars =
    79  			append(inlcalls.Calls[idx].InlVars, dwv)
    80  	}
    81  
    82  	// Post process the map above to assign child indices to vars.
    83  	//
    84  	// A given variable is treated differently depending on whether it
    85  	// is part of the top-level function (ii == 0) or if it was
    86  	// produced as a result of an inline (ii != 0).
    87  	//
    88  	// If a variable was not produced by an inline and its containing
    89  	// function was not inlined, then we just assign an ordering of
    90  	// based on variable name.
    91  	//
    92  	// If a variable was not produced by an inline and its containing
    93  	// function was inlined, then we need to assign a child index
    94  	// based on the order of vars in the abstract function (in
    95  	// addition, those vars that don't appear in the abstract
    96  	// function, such as "~r1", are flagged as such).
    97  	//
    98  	// If a variable was produced by an inline, then we locate it in
    99  	// the pre-inlining decls for the target function and assign child
   100  	// index accordingly.
   101  	for ii, sl := range vmap {
   102  		var m map[varPos]int
   103  		if ii == 0 {
   104  			if !fnsym.WasInlined() {
   105  				for j, v := range sl {
   106  					v.ChildIndex = int32(j)
   107  				}
   108  				continue
   109  			}
   110  			m = makePreinlineDclMap(fnsym)
   111  		} else {
   112  			ifnlsym := base.Ctxt.InlTree.InlinedFunction(int(ii - 1))
   113  			m = makePreinlineDclMap(ifnlsym)
   114  		}
   115  
   116  		// Here we assign child indices to variables based on
   117  		// pre-inlined decls, and set the "IsInAbstract" flag
   118  		// appropriately. In addition: parameter and local variable
   119  		// names are given "middle dot" version numbers as part of the
   120  		// writing them out to export data (see issue 4326). If DWARF
   121  		// inlined routine generation is turned on, we want to undo
   122  		// this versioning, since DWARF variables in question will be
   123  		// parented by the inlined routine and not the top-level
   124  		// caller.
   125  		synthCount := len(m)
   126  		for _, v := range sl {
   127  			canonName := unversion(v.Name)
   128  			vp := varPos{
   129  				DeclName: canonName,
   130  				DeclFile: v.DeclFile,
   131  				DeclLine: v.DeclLine,
   132  				DeclCol:  v.DeclCol,
   133  			}
   134  			synthesized := strings.HasPrefix(v.Name, "~r") || canonName == "_" || strings.HasPrefix(v.Name, "~b")
   135  			if idx, found := m[vp]; found {
   136  				v.ChildIndex = int32(idx)
   137  				v.IsInAbstract = !synthesized
   138  				v.Name = canonName
   139  			} else {
   140  				// Variable can't be found in the pre-inline dcl list.
   141  				// In the top-level case (ii=0) this can happen
   142  				// because a composite variable was split into pieces,
   143  				// and we're looking at a piece. We can also see
   144  				// return temps (~r%d) that were created during
   145  				// lowering, or unnamed params ("_").
   146  				v.ChildIndex = int32(synthCount)
   147  				synthCount++
   148  			}
   149  		}
   150  	}
   151  
   152  	// Make a second pass through the progs to compute PC ranges for
   153  	// the various inlined calls.
   154  	start := int64(-1)
   155  	curii := -1
   156  	var prevp *obj.Prog
   157  	for p := fnsym.Func().Text; p != nil; prevp, p = p, p.Link {
   158  		if prevp != nil && p.Pos == prevp.Pos {
   159  			continue
   160  		}
   161  		ii := posInlIndex(p.Pos)
   162  		if ii == curii {
   163  			continue
   164  		}
   165  		// Close out the current range
   166  		if start != -1 {
   167  			addRange(inlcalls.Calls, start, p.Pc, curii, imap)
   168  		}
   169  		// Begin new range
   170  		start = p.Pc
   171  		curii = ii
   172  	}
   173  	if start != -1 {
   174  		addRange(inlcalls.Calls, start, fnsym.Size, curii, imap)
   175  	}
   176  
   177  	// Issue 33188: if II foo is a child of II bar, then ensure that
   178  	// bar's ranges include the ranges of foo (the loop above will produce
   179  	// disjoint ranges).
   180  	for k, c := range inlcalls.Calls {
   181  		if c.Root {
   182  			unifyCallRanges(inlcalls, k)
   183  		}
   184  	}
   185  
   186  	// Debugging
   187  	if base.Debug.DwarfInl != 0 {
   188  		dumpInlCalls(inlcalls)
   189  		dumpInlVars(dwVars)
   190  	}
   191  
   192  	// Perform a consistency check on inlined routine PC ranges
   193  	// produced by unifyCallRanges above. In particular, complain in
   194  	// cases where you have A -> B -> C (e.g. C is inlined into B, and
   195  	// B is inlined into A) and the ranges for B are not enclosed
   196  	// within the ranges for A, or C within B.
   197  	for k, c := range inlcalls.Calls {
   198  		if c.Root {
   199  			checkInlCall(fnsym.Name, inlcalls, fnsym.Size, k, -1)
   200  		}
   201  	}
   202  
   203  	return inlcalls
   204  }
   205  
   206  // Secondary hook for DWARF inlined subroutine generation. This is called
   207  // late in the compilation when it is determined that we need an
   208  // abstract function DIE for an inlined routine imported from a
   209  // previously compiled package.
   210  func AbstractFunc(fn *obj.LSym) {
   211  	ifn := base.Ctxt.DwFixups.GetPrecursorFunc(fn)
   212  	if ifn == nil {
   213  		base.Ctxt.Diag("failed to locate precursor fn for %v", fn)
   214  		return
   215  	}
   216  	_ = ifn.(*ir.Func)
   217  	if base.Debug.DwarfInl != 0 {
   218  		base.Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
   219  	}
   220  	base.Ctxt.DwarfAbstractFunc(ifn, fn, base.Ctxt.Pkgpath)
   221  }
   222  
   223  // Undo any versioning performed when a name was written
   224  // out as part of export data.
   225  func unversion(name string) string {
   226  	if i := strings.Index(name, "·"); i > 0 {
   227  		name = name[:i]
   228  	}
   229  	return name
   230  }
   231  
   232  // Given a function that was inlined as part of the compilation, dig
   233  // up the pre-inlining DCL list for the function and create a map that
   234  // supports lookup of pre-inline dcl index, based on variable
   235  // position/name. NB: the recipe for computing variable pos/file/line
   236  // needs to be kept in sync with the similar code in gc.createSimpleVars
   237  // and related functions.
   238  func makePreinlineDclMap(fnsym *obj.LSym) map[varPos]int {
   239  	dcl := preInliningDcls(fnsym)
   240  	m := make(map[varPos]int)
   241  	for i, n := range dcl {
   242  		pos := base.Ctxt.InnermostPos(n.Pos())
   243  		vp := varPos{
   244  			DeclName: unversion(n.Sym().Name),
   245  			DeclFile: pos.RelFilename(),
   246  			DeclLine: pos.RelLine(),
   247  			DeclCol:  pos.RelCol(),
   248  		}
   249  		if _, found := m[vp]; found {
   250  			// We can see collisions (variables with the same name/file/line/col) in obfuscated or machine-generated code -- see issue 44378 for an example. Skip duplicates in such cases, since it is unlikely that a human will be debugging such code.
   251  			continue
   252  		}
   253  		m[vp] = i
   254  	}
   255  	return m
   256  }
   257  
   258  func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
   259  	callIdx, found := imap[inlIdx]
   260  	if found {
   261  		return callIdx
   262  	}
   263  
   264  	// Haven't seen this inline yet. Visit parent of inline if there
   265  	// is one. We do this first so that parents appear before their
   266  	// children in the resulting table.
   267  	parCallIdx := -1
   268  	parInlIdx := base.Ctxt.InlTree.Parent(inlIdx)
   269  	if parInlIdx >= 0 {
   270  		parCallIdx = insertInlCall(dwcalls, parInlIdx, imap)
   271  	}
   272  
   273  	// Create new entry for this inline
   274  	inlinedFn := base.Ctxt.InlTree.InlinedFunction(inlIdx)
   275  	callXPos := base.Ctxt.InlTree.CallPos(inlIdx)
   276  	absFnSym := base.Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
   277  	pb := base.Ctxt.PosTable.Pos(callXPos).Base()
   278  	callFileSym := base.Ctxt.Lookup(pb.SymFilename())
   279  	ic := dwarf.InlCall{
   280  		InlIndex:  inlIdx,
   281  		CallFile:  callFileSym,
   282  		CallLine:  uint32(callXPos.Line()),
   283  		AbsFunSym: absFnSym,
   284  		Root:      parCallIdx == -1,
   285  	}
   286  	dwcalls.Calls = append(dwcalls.Calls, ic)
   287  	callIdx = len(dwcalls.Calls) - 1
   288  	imap[inlIdx] = callIdx
   289  
   290  	if parCallIdx != -1 {
   291  		// Add this inline to parent's child list
   292  		dwcalls.Calls[parCallIdx].Children = append(dwcalls.Calls[parCallIdx].Children, callIdx)
   293  	}
   294  
   295  	return callIdx
   296  }
   297  
   298  // Given a src.XPos, return its associated inlining index if it
   299  // corresponds to something created as a result of an inline, or -1 if
   300  // there is no inline info. Note that the index returned will refer to
   301  // the deepest call in the inlined stack, e.g. if you have "A calls B
   302  // calls C calls D" and all three callees are inlined (B, C, and D),
   303  // the index for a node from the inlined body of D will refer to the
   304  // call to D from C. Whew.
   305  func posInlIndex(xpos src.XPos) int {
   306  	pos := base.Ctxt.PosTable.Pos(xpos)
   307  	if b := pos.Base(); b != nil {
   308  		ii := b.InliningIndex()
   309  		if ii >= 0 {
   310  			return ii
   311  		}
   312  	}
   313  	return -1
   314  }
   315  
   316  func addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int) {
   317  	if start == -1 {
   318  		panic("bad range start")
   319  	}
   320  	if end == -1 {
   321  		panic("bad range end")
   322  	}
   323  	if ii == -1 {
   324  		return
   325  	}
   326  	if start == end {
   327  		return
   328  	}
   329  	// Append range to correct inlined call
   330  	callIdx, found := imap[ii]
   331  	if !found {
   332  		base.Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start)
   333  	}
   334  	call := &calls[callIdx]
   335  	call.Ranges = append(call.Ranges, dwarf.Range{Start: start, End: end})
   336  }
   337  
   338  func dumpInlCall(inlcalls dwarf.InlCalls, idx, ilevel int) {
   339  	for i := 0; i < ilevel; i++ {
   340  		base.Ctxt.Logf("  ")
   341  	}
   342  	ic := inlcalls.Calls[idx]
   343  	callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex)
   344  	base.Ctxt.Logf("  %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
   345  	for _, f := range ic.InlVars {
   346  		base.Ctxt.Logf(" %v", f.Name)
   347  	}
   348  	base.Ctxt.Logf(" ) C: (")
   349  	for _, k := range ic.Children {
   350  		base.Ctxt.Logf(" %v", k)
   351  	}
   352  	base.Ctxt.Logf(" ) R:")
   353  	for _, r := range ic.Ranges {
   354  		base.Ctxt.Logf(" [%d,%d)", r.Start, r.End)
   355  	}
   356  	base.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  	for k, c := range inlcalls.Calls {
   365  		if c.Root {
   366  			dumpInlCall(inlcalls, k, 0)
   367  		}
   368  	}
   369  }
   370  
   371  func dumpInlVars(dwvars []*dwarf.Var) {
   372  	for i, dwv := range dwvars {
   373  		typ := "local"
   374  		if dwv.Abbrev == dwarf.DW_ABRV_PARAM_LOCLIST || dwv.Abbrev == dwarf.DW_ABRV_PARAM {
   375  			typ = "param"
   376  		}
   377  		ia := 0
   378  		if dwv.IsInAbstract {
   379  			ia = 1
   380  		}
   381  		base.Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
   382  	}
   383  }
   384  
   385  func rangesContains(par []dwarf.Range, rng dwarf.Range) (bool, string) {
   386  	for _, r := range par {
   387  		if rng.Start >= r.Start && rng.End <= r.End {
   388  			return true, ""
   389  		}
   390  	}
   391  	msg := fmt.Sprintf("range [%d,%d) not contained in {", rng.Start, rng.End)
   392  	for _, r := range par {
   393  		msg += fmt.Sprintf(" [%d,%d)", r.Start, r.End)
   394  	}
   395  	msg += " }"
   396  	return false, msg
   397  }
   398  
   399  func rangesContainsAll(parent, child []dwarf.Range) (bool, string) {
   400  	for _, r := range child {
   401  		c, m := rangesContains(parent, r)
   402  		if !c {
   403  			return false, m
   404  		}
   405  	}
   406  	return true, ""
   407  }
   408  
   409  // checkInlCall verifies that the PC ranges for inline info 'idx' are
   410  // enclosed/contained within the ranges of its parent inline (or if
   411  // this is a root/toplevel inline, checks that the ranges fall within
   412  // the extent of the top level function). A panic is issued if a
   413  // malformed range is found.
   414  func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx, parentIdx int) {
   415  
   416  	// Callee
   417  	ic := inlCalls.Calls[idx]
   418  	callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
   419  	calleeRanges := ic.Ranges
   420  
   421  	// Caller
   422  	caller := funcName
   423  	parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}}
   424  	if parentIdx != -1 {
   425  		pic := inlCalls.Calls[parentIdx]
   426  		caller = base.Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
   427  		parentRanges = pic.Ranges
   428  	}
   429  
   430  	// Callee ranges contained in caller ranges?
   431  	c, m := rangesContainsAll(parentRanges, calleeRanges)
   432  	if !c {
   433  		base.Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
   434  	}
   435  
   436  	// Now visit kids
   437  	for _, k := range ic.Children {
   438  		checkInlCall(funcName, inlCalls, funcSize, k, idx)
   439  	}
   440  }
   441  
   442  // unifyCallRanges ensures that the ranges for a given inline
   443  // transitively include all of the ranges for its child inlines.
   444  func unifyCallRanges(inlcalls dwarf.InlCalls, idx int) {
   445  	ic := &inlcalls.Calls[idx]
   446  	for _, childIdx := range ic.Children {
   447  		// First make sure child ranges are unified.
   448  		unifyCallRanges(inlcalls, childIdx)
   449  
   450  		// Then merge child ranges into ranges for this inline.
   451  		cic := inlcalls.Calls[childIdx]
   452  		ic.Ranges = dwarf.MergeRanges(ic.Ranges, cic.Ranges)
   453  	}
   454  }
   455  

View as plain text