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