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  			vp := varPos{
   128  				DeclName: v.Name,
   129  				DeclFile: v.DeclFile,
   130  				DeclLine: v.DeclLine,
   131  				DeclCol:  v.DeclCol,
   132  			}
   133  			synthesized := strings.HasPrefix(v.Name, "~") || v.Name == "_"
   134  			if idx, found := m[vp]; found {
   135  				v.ChildIndex = int32(idx)
   136  				v.IsInAbstract = !synthesized
   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  				v.ChildIndex = int32(synthCount)
   145  				synthCount++
   146  			}
   147  		}
   148  	}
   149  
   150  	// Make a second pass through the progs to compute PC ranges for
   151  	// the various inlined calls.
   152  	start := int64(-1)
   153  	curii := -1
   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  		}
   163  		// Close out the current range
   164  		if start != -1 {
   165  			addRange(inlcalls.Calls, start, p.Pc, curii, imap)
   166  		}
   167  		// Begin new range
   168  		start = p.Pc
   169  		curii = ii
   170  	}
   171  	if start != -1 {
   172  		addRange(inlcalls.Calls, start, fnsym.Size, curii, imap)
   173  	}
   174  
   175  	// Issue 33188: if II foo is a child of II bar, then ensure that
   176  	// bar's ranges include the ranges of foo (the loop above will produce
   177  	// disjoint ranges).
   178  	for k, c := range inlcalls.Calls {
   179  		if c.Root {
   180  			unifyCallRanges(inlcalls, k)
   181  		}
   182  	}
   183  
   184  	// Debugging
   185  	if base.Debug.DwarfInl != 0 {
   186  		dumpInlCalls(inlcalls)
   187  		dumpInlVars(dwVars)
   188  	}
   189  
   190  	// Perform a consistency check on inlined routine PC ranges
   191  	// produced by unifyCallRanges above. In particular, complain in
   192  	// cases where you have A -> B -> C (e.g. C is inlined into B, and
   193  	// B is inlined into A) and the ranges for B are not enclosed
   194  	// within the ranges for A, or C within B.
   195  	for k, c := range inlcalls.Calls {
   196  		if c.Root {
   197  			checkInlCall(fnsym.Name, inlcalls, fnsym.Size, k, -1)
   198  		}
   199  	}
   200  
   201  	return inlcalls
   202  }
   203  
   204  // Secondary hook for DWARF inlined subroutine generation. This is called
   205  // late in the compilation when it is determined that we need an
   206  // abstract function DIE for an inlined routine imported from a
   207  // previously compiled package.
   208  func AbstractFunc(fn *obj.LSym) {
   209  	ifn := base.Ctxt.DwFixups.GetPrecursorFunc(fn)
   210  	if ifn == nil {
   211  		base.Ctxt.Diag("failed to locate precursor fn for %v", fn)
   212  		return
   213  	}
   214  	_ = ifn.(*ir.Func)
   215  	if base.Debug.DwarfInl != 0 {
   216  		base.Ctxt.Logf("DwarfAbstractFunc(%v)\n", fn.Name)
   217  	}
   218  	base.Ctxt.DwarfAbstractFunc(ifn, fn)
   219  }
   220  
   221  // Given a function that was inlined as part of the compilation, dig
   222  // up the pre-inlining DCL list for the function and create a map that
   223  // supports lookup of pre-inline dcl index, based on variable
   224  // position/name. NB: the recipe for computing variable pos/file/line
   225  // needs to be kept in sync with the similar code in gc.createSimpleVars
   226  // and related functions.
   227  func makePreinlineDclMap(fnsym *obj.LSym) map[varPos]int {
   228  	dcl := preInliningDcls(fnsym)
   229  	m := make(map[varPos]int)
   230  	for i, n := range dcl {
   231  		pos := base.Ctxt.InnermostPos(n.Pos())
   232  		vp := varPos{
   233  			DeclName: n.Sym().Name,
   234  			DeclFile: pos.RelFilename(),
   235  			DeclLine: pos.RelLine(),
   236  			DeclCol:  pos.RelCol(),
   237  		}
   238  		if _, found := m[vp]; found {
   239  			// 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.
   240  			continue
   241  		}
   242  		m[vp] = i
   243  	}
   244  	return m
   245  }
   246  
   247  func insertInlCall(dwcalls *dwarf.InlCalls, inlIdx int, imap map[int]int) int {
   248  	callIdx, found := imap[inlIdx]
   249  	if found {
   250  		return callIdx
   251  	}
   252  
   253  	// Haven't seen this inline yet. Visit parent of inline if there
   254  	// is one. We do this first so that parents appear before their
   255  	// children in the resulting table.
   256  	parCallIdx := -1
   257  	parInlIdx := base.Ctxt.InlTree.Parent(inlIdx)
   258  	if parInlIdx >= 0 {
   259  		parCallIdx = insertInlCall(dwcalls, parInlIdx, imap)
   260  	}
   261  
   262  	// Create new entry for this inline
   263  	inlinedFn := base.Ctxt.InlTree.InlinedFunction(inlIdx)
   264  	callXPos := base.Ctxt.InlTree.CallPos(inlIdx)
   265  	callPos := base.Ctxt.InnermostPos(callXPos)
   266  	absFnSym := base.Ctxt.DwFixups.AbsFuncDwarfSym(inlinedFn)
   267  	ic := dwarf.InlCall{
   268  		InlIndex:  inlIdx,
   269  		CallPos:   callPos,
   270  		AbsFunSym: absFnSym,
   271  		Root:      parCallIdx == -1,
   272  	}
   273  	dwcalls.Calls = append(dwcalls.Calls, ic)
   274  	callIdx = len(dwcalls.Calls) - 1
   275  	imap[inlIdx] = callIdx
   276  
   277  	if parCallIdx != -1 {
   278  		// Add this inline to parent's child list
   279  		dwcalls.Calls[parCallIdx].Children = append(dwcalls.Calls[parCallIdx].Children, callIdx)
   280  	}
   281  
   282  	return callIdx
   283  }
   284  
   285  // Given a src.XPos, return its associated inlining index if it
   286  // corresponds to something created as a result of an inline, or -1 if
   287  // there is no inline info. Note that the index returned will refer to
   288  // the deepest call in the inlined stack, e.g. if you have "A calls B
   289  // calls C calls D" and all three callees are inlined (B, C, and D),
   290  // the index for a node from the inlined body of D will refer to the
   291  // call to D from C. Whew.
   292  func posInlIndex(xpos src.XPos) int {
   293  	pos := base.Ctxt.PosTable.Pos(xpos)
   294  	if b := pos.Base(); b != nil {
   295  		ii := b.InliningIndex()
   296  		if ii >= 0 {
   297  			return ii
   298  		}
   299  	}
   300  	return -1
   301  }
   302  
   303  func addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int) {
   304  	if start == -1 {
   305  		panic("bad range start")
   306  	}
   307  	if end == -1 {
   308  		panic("bad range end")
   309  	}
   310  	if ii == -1 {
   311  		return
   312  	}
   313  	if start == end {
   314  		return
   315  	}
   316  	// Append range to correct inlined call
   317  	callIdx, found := imap[ii]
   318  	if !found {
   319  		base.Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start)
   320  	}
   321  	call := &calls[callIdx]
   322  	call.Ranges = append(call.Ranges, dwarf.Range{Start: start, End: end})
   323  }
   324  
   325  func dumpInlCall(inlcalls dwarf.InlCalls, idx, ilevel int) {
   326  	for i := 0; i < ilevel; i++ {
   327  		base.Ctxt.Logf("  ")
   328  	}
   329  	ic := inlcalls.Calls[idx]
   330  	callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex)
   331  	base.Ctxt.Logf("  %d: II:%d (%s) V: (", idx, ic.InlIndex, callee.Name)
   332  	for _, f := range ic.InlVars {
   333  		base.Ctxt.Logf(" %v", f.Name)
   334  	}
   335  	base.Ctxt.Logf(" ) C: (")
   336  	for _, k := range ic.Children {
   337  		base.Ctxt.Logf(" %v", k)
   338  	}
   339  	base.Ctxt.Logf(" ) R:")
   340  	for _, r := range ic.Ranges {
   341  		base.Ctxt.Logf(" [%d,%d)", r.Start, r.End)
   342  	}
   343  	base.Ctxt.Logf("\n")
   344  	for _, k := range ic.Children {
   345  		dumpInlCall(inlcalls, k, ilevel+1)
   346  	}
   347  
   348  }
   349  
   350  func dumpInlCalls(inlcalls dwarf.InlCalls) {
   351  	for k, c := range inlcalls.Calls {
   352  		if c.Root {
   353  			dumpInlCall(inlcalls, k, 0)
   354  		}
   355  	}
   356  }
   357  
   358  func dumpInlVars(dwvars []*dwarf.Var) {
   359  	for i, dwv := range dwvars {
   360  		typ := "local"
   361  		if dwv.Tag == dwarf.DW_TAG_formal_parameter {
   362  			typ = "param"
   363  		}
   364  		ia := 0
   365  		if dwv.IsInAbstract {
   366  			ia = 1
   367  		}
   368  		base.Ctxt.Logf("V%d: %s CI:%d II:%d IA:%d %s\n", i, dwv.Name, dwv.ChildIndex, dwv.InlIndex-1, ia, typ)
   369  	}
   370  }
   371  
   372  func rangesContains(par []dwarf.Range, rng dwarf.Range) (bool, string) {
   373  	for _, r := range par {
   374  		if rng.Start >= r.Start && rng.End <= r.End {
   375  			return true, ""
   376  		}
   377  	}
   378  	msg := fmt.Sprintf("range [%d,%d) not contained in {", rng.Start, rng.End)
   379  	for _, r := range par {
   380  		msg += fmt.Sprintf(" [%d,%d)", r.Start, r.End)
   381  	}
   382  	msg += " }"
   383  	return false, msg
   384  }
   385  
   386  func rangesContainsAll(parent, child []dwarf.Range) (bool, string) {
   387  	for _, r := range child {
   388  		c, m := rangesContains(parent, r)
   389  		if !c {
   390  			return false, m
   391  		}
   392  	}
   393  	return true, ""
   394  }
   395  
   396  // checkInlCall verifies that the PC ranges for inline info 'idx' are
   397  // enclosed/contained within the ranges of its parent inline (or if
   398  // this is a root/toplevel inline, checks that the ranges fall within
   399  // the extent of the top level function). A panic is issued if a
   400  // malformed range is found.
   401  func checkInlCall(funcName string, inlCalls dwarf.InlCalls, funcSize int64, idx, parentIdx int) {
   402  
   403  	// Callee
   404  	ic := inlCalls.Calls[idx]
   405  	callee := base.Ctxt.InlTree.InlinedFunction(ic.InlIndex).Name
   406  	calleeRanges := ic.Ranges
   407  
   408  	// Caller
   409  	caller := funcName
   410  	parentRanges := []dwarf.Range{dwarf.Range{Start: int64(0), End: funcSize}}
   411  	if parentIdx != -1 {
   412  		pic := inlCalls.Calls[parentIdx]
   413  		caller = base.Ctxt.InlTree.InlinedFunction(pic.InlIndex).Name
   414  		parentRanges = pic.Ranges
   415  	}
   416  
   417  	// Callee ranges contained in caller ranges?
   418  	c, m := rangesContainsAll(parentRanges, calleeRanges)
   419  	if !c {
   420  		base.Fatalf("** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n", funcName, caller, callee, idx, m)
   421  	}
   422  
   423  	// Now visit kids
   424  	for _, k := range ic.Children {
   425  		checkInlCall(funcName, inlCalls, funcSize, k, idx)
   426  	}
   427  }
   428  
   429  // unifyCallRanges ensures that the ranges for a given inline
   430  // transitively include all of the ranges for its child inlines.
   431  func unifyCallRanges(inlcalls dwarf.InlCalls, idx int) {
   432  	ic := &inlcalls.Calls[idx]
   433  	for _, childIdx := range ic.Children {
   434  		// First make sure child ranges are unified.
   435  		unifyCallRanges(inlcalls, childIdx)
   436  
   437  		// Then merge child ranges into ranges for this inline.
   438  		cic := inlcalls.Calls[childIdx]
   439  		ic.Ranges = dwarf.MergeRanges(ic.Ranges, cic.Ranges)
   440  	}
   441  }
   442  

View as plain text