Source file 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 } 253