mirror of
https://github.com/golang/go
synced 2024-11-11 20:01:37 -07:00
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>
This commit is contained in:
parent
79d03ad739
commit
bdbba22404
447
src/reflect/abi_test.go
Normal file
447
src/reflect/abi_test.go
Normal file
@ -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
|
||||
}
|
@ -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{}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user