github.com/yukk001/go1.10.8@v0.0.0-20190813125351-6df2d3982e20/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 }