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