diff options
author | Michael Anthony Knyszek <mknyszek@google.com> | 2021-03-05 19:47:34 +0000 |
---|---|---|
committer | Michael Knyszek <mknyszek@google.com> | 2021-03-18 21:22:04 +0000 |
commit | bdbba224040a0f079a80368d9956f85a10577045 (patch) | |
tree | be93bffae4e8a9418ec5ccf14f6e98b8cff1bd1c /src/reflect | |
parent | 79d03ad7396c781aa490442a3c853b5cb627298d (diff) | |
download | go-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.go | 447 | ||||
-rw-r--r-- | src/reflect/export_test.go | 15 |
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{} +} |