mirror of
https://github.com/golang/go.git
synced 2025-12-08 06:10:04 +00: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
2 changed files with 461 additions and 1 deletions
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
|
package reflect
|
||||||
|
|
||||||
import "unsafe"
|
import (
|
||||||
|
"sync"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
// MakeRO returns a copy of v with the read-only flag set.
|
// MakeRO returns a copy of v with the read-only flag set.
|
||||||
func MakeRO(v Value) Value {
|
func MakeRO(v Value) Value {
|
||||||
|
|
@ -17,6 +20,12 @@ func IsRO(v Value) bool {
|
||||||
return v.flag&flagStickyRO != 0
|
return v.flag&flagStickyRO != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
IntArgRegs = &intArgRegs
|
||||||
|
FloatArgRegs = &floatArgRegs
|
||||||
|
FloatRegSize = &floatRegSize
|
||||||
|
)
|
||||||
|
|
||||||
var CallGC = &callGC
|
var CallGC = &callGC
|
||||||
|
|
||||||
const PtrSize = ptrSize
|
const PtrSize = ptrSize
|
||||||
|
|
@ -122,3 +131,7 @@ func ResolveReflectName(s string) {
|
||||||
type Buffer struct {
|
type Buffer struct {
|
||||||
buf []byte
|
buf []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ClearLayoutCache() {
|
||||||
|
layoutCache = sync.Map{}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue