]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go
ac238f6dea42a90c375cc32c7d09c8d8e735024e
[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                 val += int(m(42, int64(selectA(i)(one(i), 2))))
174         }
175         return val
176 }
177
178 // ExerciseFuncField calls mostly a1 and m1.
179 //
180 // This is a simplified version of ExerciseFuncConcrete, but accessing the
181 // function values via a struct field.
182 //
183 //go:noinline
184 func ExerciseFuncField(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
185         ops := struct {
186                 a AddFunc
187                 m mult.MultFunc
188         }{}
189
190         val := 0
191         for i := 0; i < iter; i++ {
192                 ops.a = a1
193                 ops.m = m1
194                 if i%10 == 0 {
195                         ops.a = a2
196                         ops.m = m2
197                 }
198
199                 // N.B. Profiles only distinguish calls on a per-line level,
200                 // making the two calls ambiguous. However because the
201                 // function types are mutually exclusive, devirtualization can
202                 // still select the correct callee for each.
203                 //
204                 // If they were not mutually exclusive (for example, two
205                 // AddFunc calls), then we could not definitively select the
206                 // correct callee.
207                 val += int(ops.m(42, int64(ops.a(1, 2))))
208         }
209         return val
210 }
211
212 //go:noinline
213 func AddClosure() AddFunc {
214         // Implicit closure by capturing the receiver.
215         var a Add
216         return a.Add
217 }
218
219 //go:noinline
220 func SubClosure() AddFunc {
221         var s Sub
222         return s.Add
223 }
224
225 // ExerciseFuncClosure calls mostly a1 and m1.
226 //
227 // This is a simplified version of ExerciseFuncConcrete, but we need two
228 // distinct call sites to test two different types of function values.
229 //
230 //go:noinline
231 func ExerciseFuncClosure(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int {
232         val := 0
233         for i := 0; i < iter; i++ {
234                 a := a1
235                 m := m1
236                 if i%10 == 0 {
237                         a = a2
238                         m = m2
239                 }
240
241                 // N.B. Profiles only distinguish calls on a per-line level,
242                 // making the two calls ambiguous. However because the
243                 // function types are mutually exclusive, devirtualization can
244                 // still select the correct callee for each.
245                 //
246                 // If they were not mutually exclusive (for example, two
247                 // AddFunc calls), then we could not definitively select the
248                 // correct callee.
249                 val += int(m(42, int64(a(1, 2))))
250         }
251         return val
252 }