1
2
3
4
5 package devirtualize
6
7 import (
8 "cmd/compile/internal/base"
9 "cmd/compile/internal/ir"
10 "cmd/compile/internal/pgoir"
11 "cmd/compile/internal/typecheck"
12 "cmd/compile/internal/types"
13 "cmd/internal/obj"
14 "cmd/internal/pgo"
15 "cmd/internal/src"
16 "testing"
17 )
18
19 func init() {
20
21
22
23 types.PtrSize = 8
24 types.RegSize = 8
25 types.MaxWidth = 1 << 50
26 typecheck.InitUniverse()
27 base.Ctxt = &obj.Link{}
28 base.Debug.PGODebug = 3
29 }
30
31 func makePos(b *src.PosBase, line, col uint) src.XPos {
32 return base.Ctxt.PosTable.XPos(src.MakePos(b, line, col))
33 }
34
35 type profileBuilder struct {
36 p *pgoir.Profile
37 }
38
39 func newProfileBuilder() *profileBuilder {
40
41
42 return &profileBuilder{
43 p: &pgoir.Profile{
44 WeightedCG: &pgoir.IRGraph{
45 IRNodes: make(map[string]*pgoir.IRNode),
46 },
47 },
48 }
49 }
50
51
52 func (p *profileBuilder) Profile() *pgoir.Profile {
53 return p.p
54 }
55
56
57
58
59 func (p *profileBuilder) NewNode(name string, fn *ir.Func) *pgoir.IRNode {
60 n := &pgoir.IRNode{
61 OutEdges: make(map[pgo.NamedCallEdge]*pgoir.IREdge),
62 }
63 if fn != nil {
64 n.AST = fn
65 } else {
66 n.LinkerSymbolName = name
67 }
68 p.p.WeightedCG.IRNodes[name] = n
69 return n
70 }
71
72
73 func addEdge(caller, callee *pgoir.IRNode, offset int, weight int64) {
74 namedEdge := pgo.NamedCallEdge{
75 CallerName: caller.Name(),
76 CalleeName: callee.Name(),
77 CallSiteOffset: offset,
78 }
79 irEdge := &pgoir.IREdge{
80 Src: caller,
81 Dst: callee,
82 CallSiteOffset: offset,
83 Weight: weight,
84 }
85 caller.OutEdges[namedEdge] = irEdge
86 }
87
88
89
90 func makeStructWithMethod(pkg *types.Pkg, structName, methName string) *ir.Func {
91
92 structType := types.NewStruct(nil)
93
94
95 recv := types.NewField(src.NoXPos, typecheck.Lookup(structName), structType)
96 sig := types.NewSignature(recv, nil, nil)
97 fn := ir.NewFunc(src.NoXPos, src.NoXPos, pkg.Lookup(structName+"."+methName), sig)
98
99
100 structType.SetMethods([]*types.Field{types.NewField(src.NoXPos, typecheck.Lookup(methName), sig)})
101
102 return fn
103 }
104
105 func TestFindHotConcreteInterfaceCallee(t *testing.T) {
106 p := newProfileBuilder()
107
108 pkgFoo := types.NewPkg("example.com/foo", "foo")
109 basePos := src.NewFileBase("foo.go", "/foo.go")
110
111 const (
112
113 callerStart = 42
114
115
116 callOffset = 1
117
118
119 wrongCallOffset = 2
120 )
121
122
123
124
125 fooSig := types.NewSignature(types.FakeRecv(), nil, nil)
126 method := types.NewField(src.NoXPos, typecheck.Lookup("Foo"), fooSig)
127 iface := types.NewInterface([]*types.Field{method})
128
129 callerFn := ir.NewFunc(makePos(basePos, callerStart, 1), src.NoXPos, pkgFoo.Lookup("Caller"), types.NewSignature(nil, nil, nil))
130
131 hotCalleeFn := makeStructWithMethod(pkgFoo, "HotCallee", "Foo")
132 coldCalleeFn := makeStructWithMethod(pkgFoo, "ColdCallee", "Foo")
133 wrongLineCalleeFn := makeStructWithMethod(pkgFoo, "WrongLineCallee", "Foo")
134 wrongMethodCalleeFn := makeStructWithMethod(pkgFoo, "WrongMethodCallee", "Bar")
135
136 callerNode := p.NewNode("example.com/foo.Caller", callerFn)
137 hotCalleeNode := p.NewNode("example.com/foo.HotCallee.Foo", hotCalleeFn)
138 coldCalleeNode := p.NewNode("example.com/foo.ColdCallee.Foo", coldCalleeFn)
139 wrongLineCalleeNode := p.NewNode("example.com/foo.WrongCalleeLine.Foo", wrongLineCalleeFn)
140 wrongMethodCalleeNode := p.NewNode("example.com/foo.WrongCalleeMethod.Foo", wrongMethodCalleeFn)
141
142 hotMissingCalleeNode := p.NewNode("example.com/bar.HotMissingCallee.Foo", nil)
143
144 addEdge(callerNode, wrongLineCalleeNode, wrongCallOffset, 100)
145 addEdge(callerNode, wrongMethodCalleeNode, callOffset, 100)
146 addEdge(callerNode, hotCalleeNode, callOffset, 10)
147 addEdge(callerNode, coldCalleeNode, callOffset, 1)
148
149
150
151
152
153
154 addEdge(callerNode, hotMissingCalleeNode, callOffset, 10)
155
156
157 sel := typecheck.NewMethodExpr(src.NoXPos, iface, typecheck.Lookup("Foo"))
158 call := ir.NewCallExpr(makePos(basePos, callerStart+callOffset, 1), ir.OCALLINTER, sel, nil)
159
160 gotFn, gotWeight := findHotConcreteInterfaceCallee(p.Profile(), callerFn, call)
161 if gotFn != hotCalleeFn {
162 t.Errorf("findHotConcreteInterfaceCallee func got %v want %v", gotFn, hotCalleeFn)
163 }
164 if gotWeight != 10 {
165 t.Errorf("findHotConcreteInterfaceCallee weight got %v want 10", gotWeight)
166 }
167 }
168
169 func TestFindHotConcreteFunctionCallee(t *testing.T) {
170
171
172
173
174 p := newProfileBuilder()
175
176 pkgFoo := types.NewPkg("example.com/foo", "foo")
177 basePos := src.NewFileBase("foo.go", "/foo.go")
178
179 const (
180
181 callerStart = 42
182
183
184 callOffset = 1
185 )
186
187 callerFn := ir.NewFunc(makePos(basePos, callerStart, 1), src.NoXPos, pkgFoo.Lookup("Caller"), types.NewSignature(nil, nil, nil))
188
189
190 hotCalleeFn := ir.NewFunc(src.NoXPos, src.NoXPos, pkgFoo.Lookup("HotCallee"), types.NewSignature(nil, nil, nil))
191
192
193 wrongCalleeFn := ir.NewFunc(src.NoXPos, src.NoXPos, pkgFoo.Lookup("WrongCallee"), types.NewSignature(nil, nil,
194 []*types.Field{
195 types.NewField(src.NoXPos, nil, types.Types[types.TBOOL]),
196 },
197 ))
198
199 callerNode := p.NewNode("example.com/foo.Caller", callerFn)
200 hotCalleeNode := p.NewNode("example.com/foo.HotCallee", hotCalleeFn)
201 wrongCalleeNode := p.NewNode("example.com/foo.WrongCallee", wrongCalleeFn)
202
203 addEdge(callerNode, wrongCalleeNode, callOffset, 100)
204 addEdge(callerNode, hotCalleeNode, callOffset, 10)
205
206
207 name := ir.NewNameAt(src.NoXPos, typecheck.Lookup("fn"), types.NewSignature(nil, nil, nil))
208
209 call := ir.NewCallExpr(makePos(basePos, callerStart+callOffset, 1), ir.OCALL, name, nil)
210
211 gotFn, gotWeight := findHotConcreteFunctionCallee(p.Profile(), callerFn, call)
212 if gotFn != hotCalleeFn {
213 t.Errorf("findHotConcreteFunctionCallee func got %v want %v", gotFn, hotCalleeFn)
214 }
215 if gotWeight != 10 {
216 t.Errorf("findHotConcreteFunctionCallee weight got %v want 10", gotWeight)
217 }
218 }
219
View as plain text