aboutsummaryrefslogtreecommitdiff
path: root/src/reflect
diff options
context:
space:
mode:
authorMichael Anthony Knyszek <mknyszek@google.com>2021-03-05 19:47:34 +0000
committerMichael Knyszek <mknyszek@google.com>2021-03-18 21:22:04 +0000
commitbdbba224040a0f079a80368d9956f85a10577045 (patch)
treebe93bffae4e8a9418ec5ccf14f6e98b8cff1bd1c /src/reflect
parent79d03ad7396c781aa490442a3c853b5cb627298d (diff)
downloadgo-bdbba224040a0f079a80368d9956f85a10577045.tar.gz
go-bdbba224040a0f079a80368d9956f85a10577045.zip
reflect: add tests for reflect.Value.Call for the new ABI
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>
Diffstat (limited to 'src/reflect')
-rw-r--r--src/reflect/abi_test.go447
-rw-r--r--src/reflect/export_test.go15
2 files changed, 461 insertions, 1 deletions
diff --git a/src/reflect/abi_test.go b/src/reflect/abi_test.go
new file mode 100644
index 0000000000..418896ee87
--- /dev/null
+++ b/src/reflect/abi_test.go
@@ -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
+}
diff --git a/src/reflect/export_test.go b/src/reflect/export_test.go
index ddcfca9dee..3a5ed5af3c 100644
--- a/src/reflect/export_test.go
+++ b/src/reflect/export_test.go
@@ -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{}
+}