2017-10-06 11:32:28 -04:00
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
2020-12-23 00:58:27 -05:00
package dwarfgen
2017-10-06 11:32:28 -04:00
import (
2020-12-23 00:58:27 -05:00
"fmt"
"strings"
2020-11-19 20:49:23 -05:00
"cmd/compile/internal/base"
2020-11-28 07:31:18 -05:00
"cmd/compile/internal/ir"
2017-10-06 11:32:28 -04:00
"cmd/internal/dwarf"
"cmd/internal/obj"
"cmd/internal/src"
)
// To identify variables by original source position.
type varPos struct {
2017-12-06 20:10:51 -05:00
DeclName string
2017-10-06 11:32:28 -04:00
DeclFile string
DeclLine uint
DeclCol uint
}
// This is the main entry point for collection of raw material to
// drive generation of DWARF "inlined subroutine" DIEs. See proposal
// 22080 for more details and background info.
2018-03-01 15:36:04 +00:00
func assembleInlines ( fnsym * obj . LSym , dwVars [ ] * dwarf . Var ) dwarf . InlCalls {
2017-10-06 11:32:28 -04:00
var inlcalls dwarf . InlCalls
2020-11-19 20:49:23 -05:00
if base . Debug . DwarfInl != 0 {
base . Ctxt . Logf ( "assembling DWARF inlined routine info for %v\n" , fnsym . Name )
2017-10-06 11:32:28 -04:00
}
// This maps inline index (from Ctxt.InlTree) to index in inlcalls.Calls
imap := make ( map [ int ] int )
// Walk progs to build up the InlCalls data structure
var prevpos src . XPos
2020-07-19 00:30:12 -04:00
for p := fnsym . Func ( ) . Text ; p != nil ; p = p . Link {
2017-10-06 11:32:28 -04:00
if p . Pos == prevpos {
continue
}
ii := posInlIndex ( p . Pos )
if ii >= 0 {
insertInlCall ( & inlcalls , ii , imap )
}
prevpos = p . Pos
}
// This is used to partition DWARF vars by inline index. Vars not
// produced by the inliner will wind up in the vmap[0] entry.
vmap := make ( map [ int32 ] [ ] * dwarf . Var )
// Now walk the dwarf vars and partition them based on whether they
// were produced by the inliner (dwv.InlIndex > 0) or were original
// vars/params from the function (dwv.InlIndex == 0).
for _ , dwv := range dwVars {
vmap [ dwv . InlIndex ] = append ( vmap [ dwv . InlIndex ] , dwv )
// Zero index => var was not produced by an inline
if dwv . InlIndex == 0 {
continue
}
// Look up index in our map, then tack the var in question
// onto the vars list for the correct inlined call.
ii := int ( dwv . InlIndex ) - 1
idx , ok := imap [ ii ]
if ! ok {
// We can occasionally encounter a var produced by the
// inliner for which there is no remaining prog; add a new
// entry to the call list in this scenario.
idx = insertInlCall ( & inlcalls , ii , imap )
}
inlcalls . Calls [ idx ] . InlVars =
append ( inlcalls . Calls [ idx ] . InlVars , dwv )
}
2017-12-11 15:53:31 -05:00
// Post process the map above to assign child indices to vars.
//
// A given variable is treated differently depending on whether it
// is part of the top-level function (ii == 0) or if it was
// produced as a result of an inline (ii != 0).
//
// If a variable was not produced by an inline and its containing
// function was not inlined, then we just assign an ordering of
// based on variable name.
//
// If a variable was not produced by an inline and its containing
// function was inlined, then we need to assign a child index
// based on the order of vars in the abstract function (in
// addition, those vars that don't appear in the abstract
// function, such as "~r1", are flagged as such).
//
// If a variable was produced by an inline, then we locate it in
// the pre-inlining decls for the target function and assign child
// index accordingly.
2017-10-06 11:32:28 -04:00
for ii , sl := range vmap {
2017-12-11 15:53:31 -05:00
var m map [ varPos ] int
2017-10-06 11:32:28 -04:00
if ii == 0 {
2017-12-11 15:53:31 -05:00
if ! fnsym . WasInlined ( ) {
2018-03-14 10:50:49 +00:00
for j , v := range sl {
v . ChildIndex = int32 ( j )
2017-12-11 15:53:31 -05:00
}
continue
2017-10-06 11:32:28 -04:00
}
2017-12-11 15:53:31 -05:00
m = makePreinlineDclMap ( fnsym )
2017-10-06 11:32:28 -04:00
} else {
2020-11-19 20:49:23 -05:00
ifnlsym := base . Ctxt . InlTree . InlinedFunction ( int ( ii - 1 ) )
2017-12-11 15:53:31 -05:00
m = makePreinlineDclMap ( ifnlsym )
}
// Here we assign child indices to variables based on
// pre-inlined decls, and set the "IsInAbstract" flag
// appropriately. In addition: parameter and local variable
// names are given "middle dot" version numbers as part of the
// writing them out to export data (see issue 4326). If DWARF
// inlined routine generation is turned on, we want to undo
// this versioning, since DWARF variables in question will be
// parented by the inlined routine and not the top-level
// caller.
synthCount := len ( m )
2018-03-14 10:50:49 +00:00
for _ , v := range sl {
canonName := unversion ( v . Name )
2017-12-11 15:53:31 -05:00
vp := varPos {
DeclName : canonName ,
2018-03-14 10:50:49 +00:00
DeclFile : v . DeclFile ,
DeclLine : v . DeclLine ,
DeclCol : v . DeclCol ,
2017-10-06 11:32:28 -04:00
}
2019-03-22 08:14:45 -04:00
synthesized := strings . HasPrefix ( v . Name , "~r" ) || canonName == "_" || strings . HasPrefix ( v . Name , "~b" )
2017-12-11 15:53:31 -05:00
if idx , found := m [ vp ] ; found {
2018-03-14 10:50:49 +00:00
v . ChildIndex = int32 ( idx )
v . IsInAbstract = ! synthesized
v . Name = canonName
2017-12-11 15:53:31 -05:00
} else {
// Variable can't be found in the pre-inline dcl list.
// In the top-level case (ii=0) this can happen
// because a composite variable was split into pieces,
// and we're looking at a piece. We can also see
// return temps (~r%d) that were created during
2017-12-20 09:54:13 -05:00
// lowering, or unnamed params ("_").
2018-03-14 10:50:49 +00:00
v . ChildIndex = int32 ( synthCount )
2018-06-11 09:11:29 +01:00
synthCount ++
2017-10-06 11:32:28 -04:00
}
}
}
2017-12-11 15:53:31 -05:00
// Make a second pass through the progs to compute PC ranges for
// the various inlined calls.
2019-01-15 14:50:09 -08:00
start := int64 ( - 1 )
2017-10-06 11:32:28 -04:00
curii := - 1
var prevp * obj . Prog
2020-07-19 00:30:12 -04:00
for p := fnsym . Func ( ) . Text ; p != nil ; prevp , p = p , p . Link {
2017-10-06 11:32:28 -04:00
if prevp != nil && p . Pos == prevp . Pos {
continue
}
ii := posInlIndex ( p . Pos )
if ii == curii {
continue
}
2019-01-15 14:50:09 -08:00
// Close out the current range
if start != - 1 {
addRange ( inlcalls . Calls , start , p . Pc , curii , imap )
}
// Begin new range
start = p . Pc
curii = ii
2017-10-06 11:32:28 -04:00
}
2019-01-15 14:50:09 -08:00
if start != - 1 {
addRange ( inlcalls . Calls , start , fnsym . Size , curii , imap )
2017-10-06 11:32:28 -04:00
}
2020-08-17 14:17:07 -04:00
// Issue 33188: if II foo is a child of II bar, then ensure that
// bar's ranges include the ranges of foo (the loop above will produce
// disjoint ranges).
for k , c := range inlcalls . Calls {
if c . Root {
unifyCallRanges ( inlcalls , k )
}
}
2017-10-06 11:32:28 -04:00
// Debugging
2020-11-19 20:49:23 -05:00
if base . Debug . DwarfInl != 0 {
2017-10-06 11:32:28 -04:00
dumpInlCalls ( inlcalls )
dumpInlVars ( dwVars )
}
2020-08-17 14:17:07 -04:00
// Perform a consistency check on inlined routine PC ranges
// produced by unifyCallRanges above. In particular, complain in
// cases where you have A -> B -> C (e.g. C is inlined into B, and
// B is inlined into A) and the ranges for B are not enclosed
// within the ranges for A, or C within B.
for k , c := range inlcalls . Calls {
if c . Root {
checkInlCall ( fnsym . Name , inlcalls , fnsym . Size , k , - 1 )
}
}
2017-10-06 11:32:28 -04:00
return inlcalls
}
// Secondary hook for DWARF inlined subroutine generation. This is called
// late in the compilation when it is determined that we need an
// abstract function DIE for an inlined routine imported from a
// previously compiled package.
2020-12-23 00:58:27 -05:00
func AbstractFunc ( fn * obj . LSym ) {
2020-11-19 20:49:23 -05:00
ifn := base . Ctxt . DwFixups . GetPrecursorFunc ( fn )
2017-10-06 11:32:28 -04:00
if ifn == nil {
2020-11-19 20:49:23 -05:00
base . Ctxt . Diag ( "failed to locate precursor fn for %v" , fn )
2017-10-06 11:32:28 -04:00
return
}
2020-11-28 07:31:18 -05:00
_ = ifn . ( * ir . Func )
2020-11-19 20:49:23 -05:00
if base . Debug . DwarfInl != 0 {
base . Ctxt . Logf ( "DwarfAbstractFunc(%v)\n" , fn . Name )
2017-10-06 11:32:28 -04:00
}
2020-11-19 20:49:23 -05:00
base . Ctxt . DwarfAbstractFunc ( ifn , fn , base . Ctxt . Pkgpath )
2017-10-06 11:32:28 -04:00
}
2017-12-11 15:53:31 -05:00
// Undo any versioning performed when a name was written
// out as part of export data.
func unversion ( name string ) string {
if i := strings . Index ( name , "·" ) ; i > 0 {
name = name [ : i ]
}
return name
}
// Given a function that was inlined as part of the compilation, dig
// up the pre-inlining DCL list for the function and create a map that
// supports lookup of pre-inline dcl index, based on variable
2018-03-20 12:36:37 -04:00
// position/name. NB: the recipe for computing variable pos/file/line
// needs to be kept in sync with the similar code in gc.createSimpleVars
// and related functions.
2017-12-11 15:53:31 -05:00
func makePreinlineDclMap ( fnsym * obj . LSym ) map [ varPos ] int {
dcl := preInliningDcls ( fnsym )
m := make ( map [ varPos ] int )
2018-03-14 10:50:49 +00:00
for i , n := range dcl {
2020-11-22 09:59:15 -05:00
pos := base . Ctxt . InnermostPos ( n . Pos ( ) )
2017-12-11 15:53:31 -05:00
vp := varPos {
2020-11-22 09:59:15 -05:00
DeclName : unversion ( n . Sym ( ) . Name ) ,
2018-03-20 12:36:37 -04:00
DeclFile : pos . RelFilename ( ) ,
DeclLine : pos . RelLine ( ) ,
2021-08-18 14:42:12 -07:00
DeclCol : pos . RelCol ( ) ,
2017-12-11 15:53:31 -05:00
}
if _ , found := m [ vp ] ; found {
2021-02-19 10:09:15 -05:00
// 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.
continue
2017-12-11 15:53:31 -05:00
}
m [ vp ] = i
}
return m
}
2017-10-06 11:32:28 -04:00
func insertInlCall ( dwcalls * dwarf . InlCalls , inlIdx int , imap map [ int ] int ) int {
callIdx , found := imap [ inlIdx ]
if found {
return callIdx
}
// Haven't seen this inline yet. Visit parent of inline if there
// is one. We do this first so that parents appear before their
// children in the resulting table.
parCallIdx := - 1
2020-11-19 20:49:23 -05:00
parInlIdx := base . Ctxt . InlTree . Parent ( inlIdx )
2017-10-06 11:32:28 -04:00
if parInlIdx >= 0 {
parCallIdx = insertInlCall ( dwcalls , parInlIdx , imap )
}
// Create new entry for this inline
2020-11-19 20:49:23 -05:00
inlinedFn := base . Ctxt . InlTree . InlinedFunction ( inlIdx )
callXPos := base . Ctxt . InlTree . CallPos ( inlIdx )
absFnSym := base . Ctxt . DwFixups . AbsFuncDwarfSym ( inlinedFn )
pb := base . Ctxt . PosTable . Pos ( callXPos ) . Base ( )
callFileSym := base . Ctxt . Lookup ( pb . SymFilename ( ) )
2017-10-06 11:32:28 -04:00
ic := dwarf . InlCall {
InlIndex : inlIdx ,
CallFile : callFileSym ,
CallLine : uint32 ( callXPos . Line ( ) ) ,
AbsFunSym : absFnSym ,
Root : parCallIdx == - 1 ,
}
dwcalls . Calls = append ( dwcalls . Calls , ic )
callIdx = len ( dwcalls . Calls ) - 1
imap [ inlIdx ] = callIdx
if parCallIdx != - 1 {
// Add this inline to parent's child list
dwcalls . Calls [ parCallIdx ] . Children = append ( dwcalls . Calls [ parCallIdx ] . Children , callIdx )
}
return callIdx
}
// Given a src.XPos, return its associated inlining index if it
// corresponds to something created as a result of an inline, or -1 if
// there is no inline info. Note that the index returned will refer to
// the deepest call in the inlined stack, e.g. if you have "A calls B
// calls C calls D" and all three callees are inlined (B, C, and D),
// the index for a node from the inlined body of D will refer to the
// call to D from C. Whew.
func posInlIndex ( xpos src . XPos ) int {
2020-11-19 20:49:23 -05:00
pos := base . Ctxt . PosTable . Pos ( xpos )
2017-10-06 11:32:28 -04:00
if b := pos . Base ( ) ; b != nil {
ii := b . InliningIndex ( )
if ii >= 0 {
return ii
}
}
return - 1
}
2019-01-15 14:50:09 -08:00
func addRange ( calls [ ] dwarf . InlCall , start , end int64 , ii int , imap map [ int ] int ) {
if start == - 1 {
panic ( "bad range start" )
}
if end == - 1 {
panic ( "bad range end" )
2017-10-06 11:32:28 -04:00
}
if ii == - 1 {
2019-01-15 14:50:09 -08:00
return
2017-10-06 11:32:28 -04:00
}
2019-01-15 14:50:09 -08:00
if start == end {
return
}
// Append range to correct inlined call
2017-10-06 11:32:28 -04:00
callIdx , found := imap [ ii ]
if ! found {
2020-11-19 20:49:23 -05:00
base . Fatalf ( "can't find inlIndex %d in imap for prog at %d\n" , ii , start )
2017-10-06 11:32:28 -04:00
}
call := & calls [ callIdx ]
2019-01-15 14:50:09 -08:00
call . Ranges = append ( call . Ranges , dwarf . Range { Start : start , End : end } )
2017-10-06 11:32:28 -04:00
}
func dumpInlCall ( inlcalls dwarf . InlCalls , idx , ilevel int ) {
2018-03-14 10:50:49 +00:00
for i := 0 ; i < ilevel ; i ++ {
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( " " )
2017-10-06 11:32:28 -04:00
}
ic := inlcalls . Calls [ idx ]
2020-11-19 20:49:23 -05:00
callee := base . Ctxt . InlTree . InlinedFunction ( ic . InlIndex )
base . Ctxt . Logf ( " %d: II:%d (%s) V: (" , idx , ic . InlIndex , callee . Name )
2017-10-06 11:32:28 -04:00
for _ , f := range ic . InlVars {
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( " %v" , f . Name )
2017-10-06 11:32:28 -04:00
}
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( " ) C: (" )
2017-10-06 11:32:28 -04:00
for _ , k := range ic . Children {
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( " %v" , k )
2017-10-06 11:32:28 -04:00
}
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( " ) R:" )
2017-10-06 11:32:28 -04:00
for _ , r := range ic . Ranges {
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( " [%d,%d)" , r . Start , r . End )
2017-10-06 11:32:28 -04:00
}
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( "\n" )
2017-10-06 11:32:28 -04:00
for _ , k := range ic . Children {
dumpInlCall ( inlcalls , k , ilevel + 1 )
}
}
func dumpInlCalls ( inlcalls dwarf . InlCalls ) {
2018-03-14 10:50:49 +00:00
for k , c := range inlcalls . Calls {
if c . Root {
2017-10-06 11:32:28 -04:00
dumpInlCall ( inlcalls , k , 0 )
}
}
}
func dumpInlVars ( dwvars [ ] * dwarf . Var ) {
for i , dwv := range dwvars {
typ := "local"
if dwv . Abbrev == dwarf . DW_ABRV_PARAM_LOCLIST || dwv . Abbrev == dwarf . DW_ABRV_PARAM {
typ = "param"
}
2017-12-11 15:53:31 -05:00
ia := 0
if dwv . IsInAbstract {
ia = 1
}
2020-11-19 20:49:23 -05:00
base . Ctxt . Logf ( "V%d: %s CI:%d II:%d IA:%d %s\n" , i , dwv . Name , dwv . ChildIndex , dwv . InlIndex - 1 , ia , typ )
2017-10-06 11:32:28 -04:00
}
}
2020-08-17 14:17:07 -04:00
func rangesContains ( par [ ] dwarf . Range , rng dwarf . Range ) ( bool , string ) {
for _ , r := range par {
if rng . Start >= r . Start && rng . End <= r . End {
return true , ""
}
}
msg := fmt . Sprintf ( "range [%d,%d) not contained in {" , rng . Start , rng . End )
for _ , r := range par {
msg += fmt . Sprintf ( " [%d,%d)" , r . Start , r . End )
}
msg += " }"
return false , msg
}
func rangesContainsAll ( parent , child [ ] dwarf . Range ) ( bool , string ) {
for _ , r := range child {
c , m := rangesContains ( parent , r )
if ! c {
return false , m
}
}
return true , ""
}
// checkInlCall verifies that the PC ranges for inline info 'idx' are
// enclosed/contained within the ranges of its parent inline (or if
// this is a root/toplevel inline, checks that the ranges fall within
// the extent of the top level function). A panic is issued if a
// malformed range is found.
func checkInlCall ( funcName string , inlCalls dwarf . InlCalls , funcSize int64 , idx , parentIdx int ) {
// Callee
ic := inlCalls . Calls [ idx ]
2020-11-19 20:49:23 -05:00
callee := base . Ctxt . InlTree . InlinedFunction ( ic . InlIndex ) . Name
2020-08-17 14:17:07 -04:00
calleeRanges := ic . Ranges
// Caller
caller := funcName
parentRanges := [ ] dwarf . Range { dwarf . Range { Start : int64 ( 0 ) , End : funcSize } }
if parentIdx != - 1 {
pic := inlCalls . Calls [ parentIdx ]
2020-11-19 20:49:23 -05:00
caller = base . Ctxt . InlTree . InlinedFunction ( pic . InlIndex ) . Name
2020-08-17 14:17:07 -04:00
parentRanges = pic . Ranges
}
// Callee ranges contained in caller ranges?
c , m := rangesContainsAll ( parentRanges , calleeRanges )
if ! c {
2020-11-19 20:49:23 -05:00
base . Fatalf ( "** malformed inlined routine range in %s: caller %s callee %s II=%d %s\n" , funcName , caller , callee , idx , m )
2020-08-17 14:17:07 -04:00
}
// Now visit kids
for _ , k := range ic . Children {
checkInlCall ( funcName , inlCalls , funcSize , k , idx )
}
}
// unifyCallRanges ensures that the ranges for a given inline
// transitively include all of the ranges for its child inlines.
func unifyCallRanges ( inlcalls dwarf . InlCalls , idx int ) {
ic := & inlcalls . Calls [ idx ]
for _ , childIdx := range ic . Children {
// First make sure child ranges are unified.
unifyCallRanges ( inlcalls , childIdx )
// Then merge child ranges into ranges for this inline.
cic := inlcalls . Calls [ childIdx ]
ic . Ranges = dwarf . MergeRanges ( ic . Ranges , cic . Ranges )
}
}