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  

View as plain text