summaryrefslogtreecommitdiffstats
path: root/src/cmd/internal/obj/inl.go
blob: 6874471891d927de752ab1622319018972a7bd65 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package obj

import "cmd/internal/src"

// InlTree is a collection of inlined calls. The Parent field of an
// InlinedCall is the index of another InlinedCall in InlTree.
//
// The compiler maintains a global inlining tree and adds a node to it
// every time a function is inlined. For example, suppose f() calls g()
// and g has two calls to h(), and that f, g, and h are inlineable:
//
//	 1 func main() {
//	 2     f()
//	 3 }
//	 4 func f() {
//	 5     g()
//	 6 }
//	 7 func g() {
//	 8     h()
//	 9     h()
//	10 }
//	11 func h() {
//	12     println("H")
//	13 }
//
// Assuming the global tree starts empty, inlining will produce the
// following tree:
//
//	[]InlinedCall{
//	  {Parent: -1, Func: "f", Pos: <line 2>},
//	  {Parent:  0, Func: "g", Pos: <line 5>},
//	  {Parent:  1, Func: "h", Pos: <line 8>},
//	  {Parent:  1, Func: "h", Pos: <line 9>},
//	}
//
// The nodes of h inlined into main will have inlining indexes 2 and 3.
//
// Eventually, the compiler extracts a per-function inlining tree from
// the global inlining tree (see pcln.go).
type InlTree struct {
	nodes []InlinedCall
}

// InlinedCall is a node in an InlTree.
type InlinedCall struct {
	Parent   int      // index of the parent in the InlTree or < 0 if outermost call
	Pos      src.XPos // position of the inlined call
	Func     *LSym    // function that was inlined
	Name     string   // bare name of the function (w/o package prefix)
	ParentPC int32    // PC of instruction just before inlined body. Only valid in local trees.
}

// Add adds a new call to the tree, returning its index.
func (tree *InlTree) Add(parent int, pos src.XPos, func_ *LSym, name string) int {
	r := len(tree.nodes)
	call := InlinedCall{
		Parent: parent,
		Pos:    pos,
		Func:   func_,
		Name:   name,
	}
	tree.nodes = append(tree.nodes, call)
	return r
}

// AllParents invokes do on each InlinedCall in the inlining call
// stack, from outermost to innermost.
//
// That is, if inlIndex corresponds to f inlining g inlining h,
// AllParents invokes do with the call for inlining g into f, and then
// inlining h into g.
func (tree *InlTree) AllParents(inlIndex int, do func(InlinedCall)) {
	if inlIndex >= 0 {
		call := tree.nodes[inlIndex]
		tree.AllParents(call.Parent, do)
		do(call)
	}
}

func (tree *InlTree) Parent(inlIndex int) int {
	return tree.nodes[inlIndex].Parent
}

func (tree *InlTree) InlinedFunction(inlIndex int) *LSym {
	return tree.nodes[inlIndex].Func
}

func (tree *InlTree) CallPos(inlIndex int) src.XPos {
	return tree.nodes[inlIndex].Pos
}

func (tree *InlTree) setParentPC(inlIndex int, pc int32) {
	tree.nodes[inlIndex].ParentPC = pc
}

// OutermostPos returns the outermost position corresponding to xpos,
// which is where xpos was ultimately inlined to. In the example for
// InlTree, main() contains inlined AST nodes from h(), but the
// outermost position for those nodes is line 2.
func (ctxt *Link) OutermostPos(xpos src.XPos) src.Pos {
	pos := ctxt.InnermostPos(xpos)

	outerxpos := xpos
	for ix := pos.Base().InliningIndex(); ix >= 0; {
		call := ctxt.InlTree.nodes[ix]
		ix = call.Parent
		outerxpos = call.Pos
	}
	return ctxt.PosTable.Pos(outerxpos)
}

// InnermostPos returns the innermost position corresponding to xpos,
// that is, the code that is inlined and that inlines nothing else.
// In the example for InlTree above, the code for println within h
// would have an innermost position with line number 12, whether
// h was not inlined, inlined into g, g-then-f, or g-then-f-then-main.
// This corresponds to what someone debugging main, f, g, or h might
// expect to see while single-stepping.
func (ctxt *Link) InnermostPos(xpos src.XPos) src.Pos {
	return ctxt.PosTable.Pos(xpos)
}

// AllPos invokes do with every position in the inlining call stack for xpos,
// from outermost to innermost. That is, xpos corresponds to f inlining g inlining h,
// AllPos invokes do with the position in f, then the position in g, then the position in h.
func (ctxt *Link) AllPos(xpos src.XPos, do func(src.Pos)) {
	pos := ctxt.InnermostPos(xpos)
	ctxt.InlTree.AllParents(pos.Base().InliningIndex(), func(call InlinedCall) {
		do(ctxt.InnermostPos(call.Pos))
	})
	do(pos)
}

func dumpInlTree(ctxt *Link, tree InlTree) {
	for i, call := range tree.nodes {
		pos := ctxt.PosTable.Pos(call.Pos)
		ctxt.Logf("%0d | %0d | %s (%s) pc=%d\n", i, call.Parent, call.Func, pos, call.ParentPC)
	}
}