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 }