diff options
Diffstat (limited to 'src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go')
| -rw-r--r-- | src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go | 214 |
1 files changed, 208 insertions, 6 deletions
diff --git a/src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go b/src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go index 4748e19e10..63de3d3c3f 100644 --- a/src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go +++ b/src/cmd/compile/internal/test/testdata/pgo/devirtualize/devirt.go @@ -16,7 +16,11 @@ package devirt // // Dots in the last package path component are escaped in symbol names. Use one // to ensure the escaping doesn't break lookup. -import "example.com/pgo/devirtualize/mult.pkg" +import ( + "fmt" + + "example.com/pgo/devirtualize/mult.pkg" +) var sink int @@ -42,15 +46,46 @@ func (Sub) Add(a, b int) int { return a - b } -// Exercise calls mostly a1 and m1. +// ExerciseIface calls mostly a1 and m1. // //go:noinline -func Exercise(iter int, a1, a2 Adder, m1, m2 mult.Multiplier) { +func ExerciseIface(iter int, a1, a2 Adder, m1, m2 mult.Multiplier) int { + // The call below must evaluate selectA() to determine the receiver to + // use. This should happen exactly once per iteration. Assert that is + // the case to ensure the IR manipulation does not result in over- or + // under-evaluation. + selectI := 0 + selectA := func(gotI int) Adder { + if gotI != selectI { + panic(fmt.Sprintf("selectA not called once per iteration; got i %d want %d", gotI, selectI)) + } + selectI++ + + if gotI%10 == 0 { + return a2 + } + return a1 + } + oneI := 0 + one := func(gotI int) int { + if gotI != oneI { + panic(fmt.Sprintf("one not called once per iteration; got i %d want %d", gotI, oneI)) + } + oneI++ + + // The function value must be evaluated before arguments, so + // selectI must have been incremented already. + if selectI != oneI { + panic(fmt.Sprintf("selectA not called before not called before one; got i %d want %d", selectI, oneI)) + } + + return 1 + } + + val := 0 for i := 0; i < iter; i++ { - a := a1 m := m1 if i%10 == 0 { - a = a2 m = m2 } @@ -63,6 +98,173 @@ func Exercise(iter int, a1, a2 Adder, m1, m2 mult.Multiplier) { // If they were not mutually exclusive (for example, two Add // calls), then we could not definitively select the correct // callee. - sink += m.Multiply(42, a.Add(1, 2)) + val += m.Multiply(42, selectA(i).Add(one(i), 2)) + } + return val +} + +type AddFunc func(int, int) int + +func AddFn(a, b int) int { + for i := 0; i < 1000; i++ { + sink++ + } + return a + b +} + +func SubFn(a, b int) int { + for i := 0; i < 1000; i++ { + sink++ + } + return a - b +} + +// ExerciseFuncConcrete calls mostly a1 and m1. +// +//go:noinline +func ExerciseFuncConcrete(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int { + // The call below must evaluate selectA() to determine the function to + // call. This should happen exactly once per iteration. Assert that is + // the case to ensure the IR manipulation does not result in over- or + // under-evaluation. + selectI := 0 + selectA := func(gotI int) AddFunc { + if gotI != selectI { + panic(fmt.Sprintf("selectA not called once per iteration; got i %d want %d", gotI, selectI)) + } + selectI++ + + if gotI%10 == 0 { + return a2 + } + return a1 + } + oneI := 0 + one := func(gotI int) int { + if gotI != oneI { + panic(fmt.Sprintf("one not called once per iteration; got i %d want %d", gotI, oneI)) + } + oneI++ + + // The function value must be evaluated before arguments, so + // selectI must have been incremented already. + if selectI != oneI { + panic(fmt.Sprintf("selectA not called before not called before one; got i %d want %d", selectI, oneI)) + } + + return 1 + } + + val := 0 + for i := 0; i < iter; i++ { + m := m1 + if i%10 == 0 { + m = m2 + } + + // N.B. Profiles only distinguish calls on a per-line level, + // making the two calls ambiguous. However because the + // function types are mutually exclusive, devirtualization can + // still select the correct callee for each. + // + // If they were not mutually exclusive (for example, two + // AddFunc calls), then we could not definitively select the + // correct callee. + // + // TODO(prattmic): Export data lookup for function value + // callees not implemented, meaning the type is unavailable. + //sink += int(m(42, int64(a(1, 2)))) + + v := selectA(i)(one(i), 2) + val += int(m(42, int64(v))) + } + return val +} + +// ExerciseFuncField calls mostly a1 and m1. +// +// This is a simplified version of ExerciseFuncConcrete, but accessing the +// function values via a struct field. +// +//go:noinline +func ExerciseFuncField(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int { + ops := struct { + a AddFunc + m mult.MultFunc + }{} + + val := 0 + for i := 0; i < iter; i++ { + ops.a = a1 + ops.m = m1 + if i%10 == 0 { + ops.a = a2 + ops.m = m2 + } + + // N.B. Profiles only distinguish calls on a per-line level, + // making the two calls ambiguous. However because the + // function types are mutually exclusive, devirtualization can + // still select the correct callee for each. + // + // If they were not mutually exclusive (for example, two + // AddFunc calls), then we could not definitively select the + // correct callee. + // + // TODO(prattmic): Export data lookup for function value + // callees not implemented, meaning the type is unavailable. + //sink += int(ops.m(42, int64(ops.a(1, 2)))) + + v := ops.a(1, 2) + val += int(ops.m(42, int64(v))) + } + return val +} + +//go:noinline +func AddClosure() AddFunc { + // Implicit closure by capturing the receiver. + var a Add + return a.Add +} + +//go:noinline +func SubClosure() AddFunc { + var s Sub + return s.Add +} + +// ExerciseFuncClosure calls mostly a1 and m1. +// +// This is a simplified version of ExerciseFuncConcrete, but we need two +// distinct call sites to test two different types of function values. +// +//go:noinline +func ExerciseFuncClosure(iter int, a1, a2 AddFunc, m1, m2 mult.MultFunc) int { + val := 0 + for i := 0; i < iter; i++ { + a := a1 + m := m1 + if i%10 == 0 { + a = a2 + m = m2 + } + + // N.B. Profiles only distinguish calls on a per-line level, + // making the two calls ambiguous. However because the + // function types are mutually exclusive, devirtualization can + // still select the correct callee for each. + // + // If they were not mutually exclusive (for example, two + // AddFunc calls), then we could not definitively select the + // correct callee. + // + // TODO(prattmic): Export data lookup for function value + // callees not implemented, meaning the type is unavailable. + //sink += int(m(42, int64(a(1, 2)))) + + v := a(1, 2) + val += int(m(42, int64(v))) } + return val } |
