]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go
63de3d3c3f43a0ce64b372079d33c753757573b6
[gostls13.git] / src / cmd / compile / internal / test / testdata / pgo / devirtualize / devirt.go
1 // Copyright 2023 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 // WARNING: Please avoid updating this file. If this file needs to be updated,
6 // then a new devirt.pprof file should be generated:
7 //
8 //      $ cd $GOROOT/src/cmd/compile/internal/test/testdata/pgo/devirtualize/
9 //      $ go mod init example.com/pgo/devirtualize
10 //      $ go test -bench=. -cpuprofile ./devirt.pprof
11
12 package devirt
13
14 // Devirtualization of callees from transitive dependencies should work even if
15 // they aren't directly referenced in the package. See #61577.
16 //
17 // Dots in the last package path component are escaped in symbol names. Use one
18 // to ensure the escaping doesn't break lookup.
19 import (
20         "fmt"
21
22         "example.com/pgo/devirtualize/mult.pkg"
23 )
24
25 var sink int
26
27 type Adder interface {
28         Add(a, b int) int
29 }
30
31 type Add struct{}
32
33 func (Add) Add(a, b int) int {
34         for i := 0; i < 1000; i++ {
35                 sink++
36         }
37         return a + b
38 }
39
40 type Sub struct{}
41
42 func (Sub) Add(a, b int) int {
43         for i := 0; i < 1000; i++ {
44                 sink++
45         }
46         return a - b
47 }
48
49 // ExerciseIface calls mostly a1 and m1.
50 //
51 //go:noinline
52 func ExerciseIface(iter int, a1, a2 Adder, m1, m2 mult.Multiplier) int {
53         // The call below must evaluate selectA() to determine the receiver to
54         // use. This should happen exactly once per iteration. Assert that is
55         // the case to ensure the IR manipulation does not result in over- or
56         // under-evaluation.
57         selectI := 0
58         selectA := func(gotI int) Adder {
59                 if gotI != selectI {
60                         panic(fmt.Sprintf("selectA not called once per iteration; got i %d want %d", gotI, selectI))
61                 }
62                 selectI++
63
64                 if gotI%10 == 0 {
65                         return a2
66                 }
67                 return a1
68         }
69         oneI := 0
70         one := func(gotI int) int {
71                 if gotI != oneI {
72                         panic(fmt.Sprintf("one not called once per iteration; got i %d want %d", gotI, oneI))
73                 }
74                 oneI++
75
76                 // The function value must be evaluated before arguments, so
77                 // selectI must have been incremented already.
78                 if selectI != oneI {
79                         panic(fmt.Sprintf("selectA not called before not called before one; got i %d want %d", selectI, oneI))
80                 }
81
82                 return 1
83         }
84
85         val := 0
86         for i := 0; i < iter; i++ {
87                 m := m1
88                 if i%10 == 0 {
89                         m = m2
90                 }
91
92                 // N.B. Profiles only distinguish calls on a per-line level,
93                 // making the two calls ambiguous. However because the
94                 // interfaces and implementations are mutually exclusive,
95                 // devirtualization can still select the correct callee for
96                 // each.
97                 //
98                 // If they were not mutually exclusive (for example, two Add
99                 // calls), then we could not definitively select the correct
100                 // callee.
101                 val += m.Multiply(42, selectA(i).Add(one(i), 2))
102         }
103         return val
104 }
105
106 type AddFunc func(int, int) int
107
108 func AddFn(a, b int) int {
109         for i := 0; i < 1000; i++ {
110                 sink++
111         }
112         return a + b
113 }
114
115 func SubFn(a, b int) int {
116         for i := 0; i < 1000; i++ {
117                 sink++
118         }
119         return a - b
120 }
121
122 // ExerciseFuncConcrete calls mostly a1 and m1.
123 //
124 //go:noinline
125 func ExerciseFuncConcrete(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
126         // The call below must evaluate selectA() to determine the function to
127         // call. This should happen exactly once per iteration. Assert that is
128         // the case to ensure the IR manipulation does not result in over- or
129         // under-evaluation.
130         selectI := 0
131         selectA := func(gotI int) AddFunc {
132                 if gotI != selectI {
133                         panic(fmt.Sprintf("selectA not called once per iteration; got i %d want %d", gotI, selectI))
134                 }
135                 selectI++
136
137                 if gotI%10 == 0 {
138                         return a2
139                 }
140                 return a1
141         }
142         oneI := 0
143         one := func(gotI int) int {
144                 if gotI != oneI {
145                         panic(fmt.Sprintf("one not called once per iteration; got i %d want %d", gotI, oneI))
146                 }
147                 oneI++
148
149                 // The function value must be evaluated before arguments, so
150                 // selectI must have been incremented already.
151                 if selectI != oneI {
152                         panic(fmt.Sprintf("selectA not called before not called before one; got i %d want %d", selectI, oneI))
153                 }
154
155                 return 1
156         }
157
158         val := 0
159         for i := 0; i < iter; i++ {
160                 m := m1
161                 if i%10 == 0 {
162                         m = m2
163                 }
164
165                 // N.B. Profiles only distinguish calls on a per-line level,
166                 // making the two calls ambiguous. However because the
167                 // function types are mutually exclusive, devirtualization can
168                 // still select the correct callee for each.
169                 //
170                 // If they were not mutually exclusive (for example, two
171                 // AddFunc calls), then we could not definitively select the
172                 // correct callee.
173                 //
174                 // TODO(prattmic): Export data lookup for function value
175                 // callees not implemented, meaning the type is unavailable.
176                 //sink += int(m(42, int64(a(1, 2))))
177
178                 v := selectA(i)(one(i), 2)
179                 val += int(m(42, int64(v)))
180         }
181         return val
182 }
183
184 // ExerciseFuncField calls mostly a1 and m1.
185 //
186 // This is a simplified version of ExerciseFuncConcrete, but accessing the
187 // function values via a struct field.
188 //
189 //go:noinline
190 func ExerciseFuncField(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
191         ops := struct {
192                 a AddFunc
193                 m mult.MultFunc
194         }{}
195
196         val := 0
197         for i := 0; i < iter; i++ {
198                 ops.a = a1
199                 ops.m = m1
200                 if i%10 == 0 {
201                         ops.a = a2
202                         ops.m = m2
203                 }
204
205                 // N.B. Profiles only distinguish calls on a per-line level,
206                 // making the two calls ambiguous. However because the
207                 // function types are mutually exclusive, devirtualization can
208                 // still select the correct callee for each.
209                 //
210                 // If they were not mutually exclusive (for example, two
211                 // AddFunc calls), then we could not definitively select the
212                 // correct callee.
213                 //
214                 // TODO(prattmic): Export data lookup for function value
215                 // callees not implemented, meaning the type is unavailable.
216                 //sink += int(ops.m(42, int64(ops.a(1, 2))))
217
218                 v := ops.a(1, 2)
219                 val += int(ops.m(42, int64(v)))
220         }
221         return val
222 }
223
224 //go:noinline
225 func AddClosure() AddFunc {
226         // Implicit closure by capturing the receiver.
227         var a Add
228         return a.Add
229 }
230
231 //go:noinline
232 func SubClosure() AddFunc {
233         var s Sub
234         return s.Add
235 }
236
237 // ExerciseFuncClosure calls mostly a1 and m1.
238 //
239 // This is a simplified version of ExerciseFuncConcrete, but we need two
240 // distinct call sites to test two different types of function values.
241 //
242 //go:noinline
243 func ExerciseFuncClosure(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
244         val := 0
245         for i := 0; i < iter; i++ {
246                 a := a1
247                 m := m1
248                 if i%10 == 0 {
249                         a = a2
250                         m = m2
251                 }
252
253                 // N.B. Profiles only distinguish calls on a per-line level,
254                 // making the two calls ambiguous. However because the
255                 // function types are mutually exclusive, devirtualization can
256                 // still select the correct callee for each.
257                 //
258                 // If they were not mutually exclusive (for example, two
259                 // AddFunc calls), then we could not definitively select the
260                 // correct callee.
261                 //
262                 // TODO(prattmic): Export data lookup for function value
263                 // callees not implemented, meaning the type is unavailable.
264                 //sink += int(m(42, int64(a(1, 2))))
265
266                 v := a(1, 2)
267                 val += int(m(42, int64(v)))
268         }
269         return val
270 }