github.com/megatontech/mynoteforgo@v0.0.0-20200507084910-5d0c6ea6e890/源码/cmd/compile/internal/gc/dwinl.go (about)

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