Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 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 { |
Than McIntosh | 692f2e9 | 2017-12-06 20:10:51 -0500 | [diff] [blame] | 17 | DeclName string |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 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 | |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 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. |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 98 | for ii, sl := range vmap { |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 99 | sort.Sort(byClassThenName(sl)) |
| 100 | var m map[varPos]int |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 101 | if ii == 0 { |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 102 | if !fnsym.WasInlined() { |
| 103 | for j := 0; j < len(sl); j++ { |
| 104 | sl[j].ChildIndex = int32(j) |
| 105 | } |
| 106 | continue |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 107 | } |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 108 | m = makePreinlineDclMap(fnsym) |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 109 | } else { |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 110 | ifnlsym := Ctxt.InlTree.InlinedFunction(int(ii - 1)) |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 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, |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 131 | } |
Than McIntosh | 841d865 | 2017-12-20 09:54:13 -0500 | [diff] [blame] | 132 | synthesized := strings.HasPrefix(sl[j].Name, "~r") || canonName == "_" |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 133 | if idx, found := m[vp]; found { |
| 134 | sl[j].ChildIndex = int32(idx) |
Than McIntosh | 841d865 | 2017-12-20 09:54:13 -0500 | [diff] [blame] | 135 | sl[j].IsInAbstract = !synthesized |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 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 |
Than McIntosh | 841d865 | 2017-12-20 09:54:13 -0500 | [diff] [blame] | 143 | // lowering, or unnamed params ("_"). |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 144 | sl[j].ChildIndex = int32(synthCount) |
| 145 | synthCount += 1 |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 146 | } |
| 147 | } |
| 148 | } |
| 149 | |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 150 | // Make a second pass through the progs to compute PC ranges for |
| 151 | // the various inlined calls. |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 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 | |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 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 | |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 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 | } |
Than McIntosh | fdecaa8 | 2017-12-11 15:53:31 -0500 | [diff] [blame] | 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) |
Than McIntosh | 4435fcf | 2017-10-06 11:32:28 -0400 | [diff] [blame] | 383 | } |
| 384 | } |