github.com/epfl-dcsl/gotee@v0.0.0-20200909122901-014b35f5e5e9/src/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  	"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  }