]> Cypherpunks repositories - gostls13.git/commitdiff
reflect: add tests for reflect.Value.Call for the new ABI
authorMichael Anthony Knyszek <mknyszek@google.com>
Fri, 5 Mar 2021 19:47:34 +0000 (19:47 +0000)
committerMichael Knyszek <mknyszek@google.com>
Thu, 18 Mar 2021 21:22:04 +0000 (21:22 +0000)
This change adds tests for reflect.Value.Call for calling functions
using the new register-based ABI.

For #40724.

Change-Id: Ia9afd43e26dd80c7e36dd135a5b71acce8074801
Reviewed-on: https://go-review.googlesource.com/c/go/+/299269
Trust: Michael Knyszek <mknyszek@google.com>
Run-TryBot: Michael Knyszek <mknyszek@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
src/reflect/abi_test.go [new file with mode: 0644]
src/reflect/export_test.go

diff --git a/src/reflect/abi_test.go b/src/reflect/abi_test.go
new file mode 100644 (file)
index 0000000..418896e
--- /dev/null
@@ -0,0 +1,447 @@
+// Copyright 2021 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build goexperiment.regabi
+//go:build goexperiment.regabi
+
+package reflect_test
+
+import (
+       "internal/abi"
+       "math/rand"
+       "reflect"
+       "runtime"
+       "testing"
+       "testing/quick"
+)
+
+func TestReflectValueCallABI(t *testing.T) {
+       // Enable register-based reflect.Call and ensure we don't
+       // use potentially incorrect cached versions by clearing
+       // the cache before we start and after we're done.
+       var oldRegs struct {
+               ints, floats int
+               floatSize    uintptr
+       }
+       oldRegs.ints = *reflect.IntArgRegs
+       oldRegs.floats = *reflect.FloatArgRegs
+       oldRegs.floatSize = *reflect.FloatRegSize
+       *reflect.IntArgRegs = abi.IntArgRegs
+       *reflect.FloatArgRegs = abi.FloatArgRegs
+       *reflect.FloatRegSize = uintptr(abi.EffectiveFloatRegSize)
+       reflect.ClearLayoutCache()
+       defer func() {
+               *reflect.IntArgRegs = oldRegs.ints
+               *reflect.FloatArgRegs = oldRegs.floats
+               *reflect.FloatRegSize = oldRegs.floatSize
+               reflect.ClearLayoutCache()
+       }()
+
+       // Execute the functions defined below which all have the
+       // same form and perform the same function: pass all arguments
+       // to return values. The purpose is to test the call boundary
+       // and make sure it works.
+       r := rand.New(rand.NewSource(genValueRandSeed))
+       for _, fn := range []interface{}{
+               passNone,
+               passInt,
+               passInt8,
+               passInt16,
+               passInt32,
+               passInt64,
+               passUint,
+               passUint8,
+               passUint16,
+               passUint32,
+               passUint64,
+               passFloat32,
+               passFloat64,
+               passComplex64,
+               passComplex128,
+               passManyInt,
+               passManyFloat64,
+               passArray1,
+               passArray,
+               passArray1Mix,
+               passString,
+               // TODO(mknyszek): Test passing interface values.
+               passSlice,
+               passPointer,
+               passStruct1,
+               passStruct2,
+               passStruct3,
+               passStruct4,
+               passStruct5,
+               passStruct6,
+               passStruct7,
+               passStruct8,
+               passStruct9,
+               passStruct10,
+               // TODO(mknyszek): Test passing unsafe.Pointer values.
+               // TODO(mknyszek): Test passing chan values.
+               passStruct11,
+               passStruct12,
+               passStruct13,
+               pass2Struct1,
+               passEmptyStruct,
+       } {
+               fn := reflect.ValueOf(fn)
+               t.Run(runtime.FuncForPC(fn.Pointer()).Name(), func(t *testing.T) {
+                       typ := fn.Type()
+                       if typ.Kind() != reflect.Func {
+                               t.Fatalf("test case is not a function, has type: %s", typ.String())
+                       }
+                       if typ.NumIn() != typ.NumOut() {
+                               t.Fatalf("test case has different number of inputs and outputs: %d in, %d out", typ.NumIn(), typ.NumOut())
+                       }
+                       var args []reflect.Value
+                       for i := 0; i < typ.NumIn(); i++ {
+                               args = append(args, genValue(t, typ.In(i), r))
+                       }
+                       results := fn.Call(args)
+                       for i := range results {
+                               x, y := args[i].Interface(), results[i].Interface()
+                               if reflect.DeepEqual(x, y) {
+                                       continue
+                               }
+                               t.Errorf("arg and result %d differ: got %+v, want %+v", i, x, y)
+                       }
+               })
+       }
+}
+
+// Functions for testing reflect.Value.Call.
+
+//go:registerparams
+//go:noinline
+func passNone() {}
+
+//go:registerparams
+//go:noinline
+func passInt(a int) int {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passInt8(a int8) int8 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passInt16(a int16) int16 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passInt32(a int32) int32 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passInt64(a int64) int64 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passUint(a uint) uint {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passUint8(a uint8) uint8 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passUint16(a uint16) uint16 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passUint32(a uint32) uint32 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passUint64(a uint64) uint64 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passFloat32(a float32) float32 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passFloat64(a float64) float64 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passComplex64(a complex64) complex64 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passComplex128(a complex128) complex128 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passArray1(a [1]uint32) [1]uint32 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passArray(a [2]uintptr) [2]uintptr {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passArray1Mix(a int, b [1]uint32, c float64) (int, [1]uint32, float64) {
+       return a, b, c
+}
+
+//go:registerparams
+//go:noinline
+func passString(a string) string {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passSlice(a []byte) []byte {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passPointer(a *byte) *byte {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passManyInt(a, b, c, d, e, f, g, h, i, j int) (int, int, int, int, int, int, int, int, int, int) {
+       return a, b, c, d, e, f, g, h, i, j
+}
+
+//go:registerparams
+//go:noinline
+func passManyFloat64(a, b, c, d, e, f, g, h, i, j, l, m, n, o, p, q, r, s, t float64) (float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64, float64) {
+       return a, b, c, d, e, f, g, h, i, j, l, m, n, o, p, q, r, s, t
+}
+
+//go:registerparams
+//go:noinline
+func passStruct1(a Struct1) Struct1 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct2(a Struct2) Struct2 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct3(a Struct3) Struct3 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct4(a Struct4) Struct4 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct5(a Struct5) Struct5 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct6(a Struct6) Struct6 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct7(a Struct7) Struct7 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct8(a Struct8) Struct8 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct9(a Struct9) Struct9 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct10(a Struct10) Struct10 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct11(a Struct11) Struct11 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct12(a Struct12) Struct12 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func passStruct13(a Struct13) Struct13 {
+       return a
+}
+
+//go:registerparams
+//go:noinline
+func pass2Struct1(a, b Struct1) (x, y Struct1) {
+       return a, b
+}
+
+//go:registerparams
+//go:noinline
+func passEmptyStruct(a int, b struct{}, c float64) (int, struct{}, float64) {
+       return a, b, c
+}
+
+// Struct1 is a simple integer-only aggregate struct.
+type Struct1 struct {
+       A, B, C uint
+}
+
+// Struct2 is Struct1 but with an array-typed field that will
+// force it to get passed on the stack.
+type Struct2 struct {
+       A, B, C uint
+       D       [2]uint32
+}
+
+// Struct3 is Struct2 but with an anonymous array-typed field.
+// This should act identically to Struct2.
+type Struct3 struct {
+       A, B, C uint
+       D       [2]uint32
+}
+
+// Struct4 has byte-length fields that should
+// each use up a whole registers.
+type Struct4 struct {
+       A, B int8
+       C, D uint8
+       E    bool
+}
+
+// Struct5 is a relatively large struct
+// with both integer and floating point values.
+type Struct5 struct {
+       A             uint16
+       B             int16
+       C, D          uint32
+       E             int32
+       F, G, H, I, J float32
+}
+
+// Struct6 has a nested struct.
+type Struct6 struct {
+       Struct1
+}
+
+// Struct7 is a struct with a nested array-typed field
+// that cannot be passed in registers as a result.
+type Struct7 struct {
+       Struct1
+       Struct2
+}
+
+// Struct8 is large aggregate struct type that may be
+// passed in registers.
+type Struct8 struct {
+       Struct5
+       Struct1
+}
+
+// Struct9 is a type that has an array type nested
+// 2 layers deep, and as a result needs to be passed
+// on the stack.
+type Struct9 struct {
+       Struct1
+       Struct7
+}
+
+// Struct10 is a struct type that is too large to be
+// passed in registers.
+type Struct10 struct {
+       Struct5
+       Struct8
+}
+
+// Struct11 is a struct type that has several reference
+// types in it.
+type Struct11 struct {
+       X map[string]int
+}
+
+// Struct12 has Struct11 embedded into it to test more
+// paths.
+type Struct12 struct {
+       A int
+       Struct11
+}
+
+// Struct13 tests an empty field.
+type Struct13 struct {
+       A int
+       X struct{}
+       B int
+}
+
+const genValueRandSeed = 0
+
+// genValue generates a pseudorandom reflect.Value with type t.
+// The reflect.Value produced by this function is always the same
+// for the same type.
+func genValue(t *testing.T, typ reflect.Type, r *rand.Rand) reflect.Value {
+       // Re-seed and reset the PRNG because we want each value with the
+       // same type to be the same random value.
+       r.Seed(genValueRandSeed)
+       v, ok := quick.Value(typ, r)
+       if !ok {
+               t.Fatal("failed to generate value")
+       }
+       return v
+}
index ddcfca9dee45c22d06cb8fdd11086a95d899eea4..3a5ed5af3c6aa97f5f168acd62221f1188120d06 100644 (file)
@@ -4,7 +4,10 @@
 
 package reflect
 
-import "unsafe"
+import (
+       "sync"
+       "unsafe"
+)
 
 // MakeRO returns a copy of v with the read-only flag set.
 func MakeRO(v Value) Value {
@@ -17,6 +20,12 @@ func IsRO(v Value) bool {
        return v.flag&flagStickyRO != 0
 }
 
+var (
+       IntArgRegs   = &intArgRegs
+       FloatArgRegs = &floatArgRegs
+       FloatRegSize = &floatRegSize
+)
+
 var CallGC = &callGC
 
 const PtrSize = ptrSize
@@ -122,3 +131,7 @@ func ResolveReflectName(s string) {
 type Buffer struct {
        buf []byte
 }
+
+func ClearLayoutCache() {
+       layoutCache = sync.Map{}
+}