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 obj
6 7 import "github.com/twitchyliquid64/golang-asm/src"
8 9 // InlTree is a collection of inlined calls. The Parent field of an
10 // InlinedCall is the index of another InlinedCall in InlTree.
11 //
12 // The compiler maintains a global inlining tree and adds a node to it
13 // every time a function is inlined. For example, suppose f() calls g()
14 // and g has two calls to h(), and that f, g, and h are inlineable:
15 //
16 // 1 func main() {
17 // 2 f()
18 // 3 }
19 // 4 func f() {
20 // 5 g()
21 // 6 }
22 // 7 func g() {
23 // 8 h()
24 // 9 h()
25 // 10 }
26 // 11 func h() {
27 // 12 println("H")
28 // 13 }
29 //
30 // Assuming the global tree starts empty, inlining will produce the
31 // following tree:
32 //
33 // []InlinedCall{
34 // {Parent: -1, Func: "f", Pos: <line 2>},
35 // {Parent: 0, Func: "g", Pos: <line 5>},
36 // {Parent: 1, Func: "h", Pos: <line 8>},
37 // {Parent: 1, Func: "h", Pos: <line 9>},
38 // }
39 //
40 // The nodes of h inlined into main will have inlining indexes 2 and 3.
41 //
42 // Eventually, the compiler extracts a per-function inlining tree from
43 // the global inlining tree (see pcln.go).
44 type InlTree struct {
45 nodes []InlinedCall
46 }
47 48 // InlinedCall is a node in an InlTree.
49 type InlinedCall struct {
50 Parent int // index of the parent in the InlTree or < 0 if outermost call
51 Pos src.XPos // position of the inlined call
52 Func *LSym // function that was inlined
53 ParentPC int32 // PC of instruction just before inlined body. Only valid in local trees.
54 }
55 56 // Add adds a new call to the tree, returning its index.
57 func (tree *InlTree) Add(parent int, pos src.XPos, func_ *LSym) int {
58 r := len(tree.nodes)
59 call := InlinedCall{
60 Parent: parent,
61 Pos: pos,
62 Func: func_,
63 }
64 tree.nodes = append(tree.nodes, call)
65 return r
66 }
67 68 func (tree *InlTree) Parent(inlIndex int) int {
69 return tree.nodes[inlIndex].Parent
70 }
71 72 func (tree *InlTree) InlinedFunction(inlIndex int) *LSym {
73 return tree.nodes[inlIndex].Func
74 }
75 76 func (tree *InlTree) CallPos(inlIndex int) src.XPos {
77 return tree.nodes[inlIndex].Pos
78 }
79 80 func (tree *InlTree) setParentPC(inlIndex int, pc int32) {
81 tree.nodes[inlIndex].ParentPC = pc
82 }
83 84 // OutermostPos returns the outermost position corresponding to xpos,
85 // which is where xpos was ultimately inlined to. In the example for
86 // InlTree, main() contains inlined AST nodes from h(), but the
87 // outermost position for those nodes is line 2.
88 func (ctxt *Link) OutermostPos(xpos src.XPos) src.Pos {
89 pos := ctxt.InnermostPos(xpos)
90 91 outerxpos := xpos
92 for ix := pos.Base().InliningIndex(); ix >= 0; {
93 call := ctxt.InlTree.nodes[ix]
94 ix = call.Parent
95 outerxpos = call.Pos
96 }
97 return ctxt.PosTable.Pos(outerxpos)
98 }
99 100 // InnermostPos returns the innermost position corresponding to xpos,
101 // that is, the code that is inlined and that inlines nothing else.
102 // In the example for InlTree above, the code for println within h
103 // would have an innermost position with line number 12, whether
104 // h was not inlined, inlined into g, g-then-f, or g-then-f-then-main.
105 // This corresponds to what someone debugging main, f, g, or h might
106 // expect to see while single-stepping.
107 func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos {
108 return ctxt.PosTable.Pos(xpos)
109 }
110 111 // AllPos returns a slice of the positions inlined at xpos, from
112 // innermost (index zero) to outermost. To avoid gratuitous allocation
113 // the result is passed in and extended if necessary.
114 func (ctxt *Link) AllPos(xpos src.XPos, result []src.Pos) []src.Pos {
115 pos := ctxt.InnermostPos(xpos)
116 result = result[:0]
117 result = append(result, ctxt.PosTable.Pos(xpos))
118 for ix := pos.Base().InliningIndex(); ix >= 0; {
119 call := ctxt.InlTree.nodes[ix]
120 ix = call.Parent
121 result = append(result, ctxt.PosTable.Pos(call.Pos))
122 }
123 return result
124 }
125 126 func dumpInlTree(ctxt *Link, tree InlTree) {
127 for i, call := range tree.nodes {
128 pos := ctxt.PosTable.Pos(call.Pos)
129 ctxt.Logf("%0d | %0d | %s (%s) pc=%d\n", i, call.Parent, call.Func, pos, call.ParentPC)
130 }
131 }
132