blob: e4eae3e87f634974097b51ea1216bee76e5b8b7a [file] [log] [blame]
Than McIntosh4435fcf2017-10-06 11:32:28 -04001// 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
5package gc
6
7import (
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.
16type varPos struct {
Than McIntosh692f2e92017-12-06 20:10:51 -050017 DeclName string
Than McIntosh4435fcf2017-10-06 11:32:28 -040018 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.
26func 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 McIntoshfdecaa82017-12-11 15:53:31 -050079 // 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 McIntosh4435fcf2017-10-06 11:32:28 -040098 for ii, sl := range vmap {
Than McIntoshfdecaa82017-12-11 15:53:31 -050099 sort.Sort(byClassThenName(sl))
100 var m map[varPos]int
Than McIntosh4435fcf2017-10-06 11:32:28 -0400101 if ii == 0 {
Than McIntoshfdecaa82017-12-11 15:53:31 -0500102 if !fnsym.WasInlined() {
103 for j := 0; j < len(sl); j++ {
104 sl[j].ChildIndex = int32(j)
105 }
106 continue
Than McIntosh4435fcf2017-10-06 11:32:28 -0400107 }
Than McIntoshfdecaa82017-12-11 15:53:31 -0500108 m = makePreinlineDclMap(fnsym)
Than McIntosh4435fcf2017-10-06 11:32:28 -0400109 } else {
Than McIntosh4435fcf2017-10-06 11:32:28 -0400110 ifnlsym := Ctxt.InlTree.InlinedFunction(int(ii - 1))
Than McIntoshfdecaa82017-12-11 15:53:31 -0500111 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 McIntosh4435fcf2017-10-06 11:32:28 -0400131 }
Than McIntosh841d8652017-12-20 09:54:13 -0500132 synthesized := strings.HasPrefix(sl[j].Name, "~r") || canonName == "_"
Than McIntoshfdecaa82017-12-11 15:53:31 -0500133 if idx, found := m[vp]; found {
134 sl[j].ChildIndex = int32(idx)
Than McIntosh841d8652017-12-20 09:54:13 -0500135 sl[j].IsInAbstract = !synthesized
Than McIntoshfdecaa82017-12-11 15:53:31 -0500136 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 McIntosh841d8652017-12-20 09:54:13 -0500143 // lowering, or unnamed params ("_").
Than McIntoshfdecaa82017-12-11 15:53:31 -0500144 sl[j].ChildIndex = int32(synthCount)
145 synthCount += 1
Than McIntosh4435fcf2017-10-06 11:32:28 -0400146 }
147 }
148 }
149
Than McIntoshfdecaa82017-12-11 15:53:31 -0500150 // Make a second pass through the progs to compute PC ranges for
151 // the various inlined calls.
Than McIntosh4435fcf2017-10-06 11:32:28 -0400152 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.
188func 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 McIntoshfdecaa82017-12-11 15:53:31 -0500200// Undo any versioning performed when a name was written
201// out as part of export data.
202func 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.
213func 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 McIntosh4435fcf2017-10-06 11:32:28 -0400233func 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.
280func 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
291func endRange(crange *dwarf.Range, p *obj.Prog) {
292 if crange == nil {
293 return
294 }
295 crange.End = p.Pc
296}
297
298func 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
313func 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.
332type byClassThenName []*dwarf.Var
333
334func (s byClassThenName) Len() int { return len(s) }
335func (s byClassThenName) Less(i, j int) bool { return cmpDwarfVar(s[i], s[j]) }
336func (s byClassThenName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
337
338func 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
363func 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
372func 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 McIntoshfdecaa82017-12-11 15:53:31 -0500378 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 McIntosh4435fcf2017-10-06 11:32:28 -0400383 }
384}