...

Source file src/pkg/cmd/compile/internal/gc/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 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 == "_" || strings.HasPrefix(v.Name, "~b")
   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		start := int64(-1)
   151		curii := -1
   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			}
   161			// Close out the current range
   162			if start != -1 {
   163				addRange(inlcalls.Calls, start, p.Pc, curii, imap)
   164			}
   165			// Begin new range
   166			start = p.Pc
   167			curii = ii
   168		}
   169		if start != -1 {
   170			addRange(inlcalls.Calls, start, fnsym.Size, curii, imap)
   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 addRange(calls []dwarf.InlCall, start, end int64, ii int, imap map[int]int) {
   291		if start == -1 {
   292			panic("bad range start")
   293		}
   294		if end == -1 {
   295			panic("bad range end")
   296		}
   297		if ii == -1 {
   298			return
   299		}
   300		if start == end {
   301			return
   302		}
   303		// Append range to correct inlined call
   304		callIdx, found := imap[ii]
   305		if !found {
   306			Fatalf("can't find inlIndex %d in imap for prog at %d\n", ii, start)
   307		}
   308		call := &calls[callIdx]
   309		call.Ranges = append(call.Ranges, dwarf.Range{Start: start, End: end})
   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	}
   358	

View as plain text