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