// 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. //go:build goexperiment.regabireflect // +build goexperiment.regabireflect package reflect_test import ( "internal/abi" "math/rand" "reflect" "runtime" "testing" "testing/quick" ) // As of early May 2021 this is no longer necessary for amd64, // but it remains in case this is needed for the next register abi port. // TODO (1.18) If enabling register ABI on additional architectures turns out not to need this, remove it. type MagicLastTypeNameForTestingRegisterABI struct{} func TestMethodValueCallABI(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. defer reflect.SetArgRegs(reflect.SetArgRegs(abi.IntArgRegs, abi.FloatArgRegs, abi.EffectiveFloatRegSize)) // This test is simple. Calling a method value involves // pretty much just plumbing whatever arguments in whichever // location through to reflectcall. They're already set up // for us, so there isn't a whole lot to do. Let's just // make sure that we can pass register and stack arguments // through. The exact combination is not super important. makeMethodValue := func(method string) (*StructWithMethods, interface{}) { s := new(StructWithMethods) v := reflect.ValueOf(s).MethodByName(method) return s, v.Interface() } a0 := StructFewRegs{ 10, 11, 12, 13, 20.0, 21.0, 22.0, 23.0, } a1 := [4]uint64{100, 101, 102, 103} a2 := StructFillRegs{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, } s, i := makeMethodValue("AllRegsCall") f0 := i.(func(StructFewRegs, MagicLastTypeNameForTestingRegisterABI) StructFewRegs) r0 := f0(a0, MagicLastTypeNameForTestingRegisterABI{}) if r0 != a0 { t.Errorf("bad method value call: got %#v, want %#v", r0, a0) } if s.Value != 1 { t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 1) } s, i = makeMethodValue("RegsAndStackCall") f1 := i.(func(StructFewRegs, [4]uint64, MagicLastTypeNameForTestingRegisterABI) (StructFewRegs, [4]uint64)) r0, r1 := f1(a0, a1, MagicLastTypeNameForTestingRegisterABI{}) if r0 != a0 { t.Errorf("bad method value call: got %#v, want %#v", r0, a0) } if r1 != a1 { t.Errorf("bad method value call: got %#v, want %#v", r1, a1) } if s.Value != 2 { t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 2) } s, i = makeMethodValue("SpillStructCall") f2 := i.(func(StructFillRegs, MagicLastTypeNameForTestingRegisterABI) StructFillRegs) r2 := f2(a2, MagicLastTypeNameForTestingRegisterABI{}) if r2 != a2 { t.Errorf("bad method value call: got %#v, want %#v", r2, a2) } if s.Value != 3 { t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 3) } s, i = makeMethodValue("ValueRegMethodSpillInt") f3 := i.(func(StructFillRegs, int, MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, int)) r3a, r3b := f3(a2, 42, MagicLastTypeNameForTestingRegisterABI{}) if r3a != a2 { t.Errorf("bad method value call: got %#v, want %#v", r3a, a2) } if r3b != 42 { t.Errorf("bad method value call: got %#v, want %#v", r3b, 42) } if s.Value != 4 { t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 4) } s, i = makeMethodValue("ValueRegMethodSpillPtr") f4 := i.(func(StructFillRegs, *byte, MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, *byte)) vb := byte(10) r4a, r4b := f4(a2, &vb, MagicLastTypeNameForTestingRegisterABI{}) if r4a != a2 { t.Errorf("bad method value call: got %#v, want %#v", r4a, a2) } if r4b != &vb { t.Errorf("bad method value call: got %#v, want %#v", r4b, &vb) } if s.Value != 5 { t.Errorf("bad method value call: failed to set s.Value: got %d, want %d", s.Value, 5) } } type StructWithMethods struct { Value int } type StructFewRegs struct { a0, a1, a2, a3 int f0, f1, f2, f3 float64 } type StructFillRegs struct { a0, a1, a2, a3, a4, a5, a6, a7, a8 int f0, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14 float64 } func (m *StructWithMethods) AllRegsCall(s StructFewRegs, _ MagicLastTypeNameForTestingRegisterABI) StructFewRegs { m.Value = 1 return s } func (m *StructWithMethods) RegsAndStackCall(s StructFewRegs, a [4]uint64, _ MagicLastTypeNameForTestingRegisterABI) (StructFewRegs, [4]uint64) { m.Value = 2 return s, a } func (m *StructWithMethods) SpillStructCall(s StructFillRegs, _ MagicLastTypeNameForTestingRegisterABI) StructFillRegs { m.Value = 3 return s } // When called as a method value, i is passed on the stack. // When called as a method, i is passed in a register. func (m *StructWithMethods) ValueRegMethodSpillInt(s StructFillRegs, i int, _ MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, int) { m.Value = 4 return s, i } // When called as a method value, i is passed on the stack. // When called as a method, i is passed in a register. func (m *StructWithMethods) ValueRegMethodSpillPtr(s StructFillRegs, i *byte, _ MagicLastTypeNameForTestingRegisterABI) (StructFillRegs, *byte) { m.Value = 5 return s, i } func TestReflectCallABI(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. defer reflect.SetArgRegs(reflect.SetArgRegs(abi.IntArgRegs, abi.FloatArgRegs, abi.EffectiveFloatRegSize)) // 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 abiCallTestCases { 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, y, x) } }) } } func TestReflectMakeFuncCallABI(t *testing.T) { // Enable register-based reflect.MakeFunc and ensure we don't // use potentially incorrect cached versions by clearing // the cache before we start and after we're done. defer reflect.SetArgRegs(reflect.SetArgRegs(abi.IntArgRegs, abi.FloatArgRegs, abi.EffectiveFloatRegSize)) // 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)) makeFuncHandler := func(args []reflect.Value) []reflect.Value { if len(args) == 0 { return []reflect.Value{} } return args[:len(args)-1] // The last Value is an empty magic value. } for _, callFn := range abiMakeFuncTestCases { fnTyp := reflect.TypeOf(callFn).In(0) fn := reflect.MakeFunc(fnTyp, makeFuncHandler) callFn := reflect.ValueOf(callFn) t.Run(runtime.FuncForPC(callFn.Pointer()).Name(), func(t *testing.T) { args := []reflect.Value{fn} for i := 0; i < fnTyp.NumIn()-1; /* last one is magic type */ i++ { args = append(args, genValue(t, fnTyp.In(i), r)) } results := callFn.Call(args) for i := range results { x, y := args[i+1].Interface(), results[i].Interface() if reflect.DeepEqual(x, y) { continue } t.Errorf("arg and result %d differ: got %+v, want %+v", i, y, x) } }) } t.Run("OnlyPointerInRegisterGC", func(t *testing.T) { // This test attempts to induce a failure wherein // the last pointer to an object is passed via registers. // If makeFuncStub doesn't successfully store the pointer // to a location visible to the GC, the object should be // freed and then the next GC should notice that an object // was inexplicably revived. var f func(b *uint64, _ MagicLastTypeNameForTestingRegisterABI) *uint64 mkfn := reflect.MakeFunc(reflect.TypeOf(f), func(args []reflect.Value) []reflect.Value { *(args[0].Interface().(*uint64)) = 5 return args[:1] }) fn := mkfn.Interface().(func(*uint64, MagicLastTypeNameForTestingRegisterABI) *uint64) // Call the MakeFunc'd function while trying pass the only pointer // to a new heap-allocated uint64. *reflect.CallGC = true x := fn(new(uint64), MagicLastTypeNameForTestingRegisterABI{}) *reflect.CallGC = false // Check for bad pointers (which should be x if things went wrong). runtime.GC() // Sanity check x. if *x != 5 { t.Fatalf("failed to set value in object") } }) } var abiCallTestCases = []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, passStruct14, passStruct15, pass2Struct1, passEmptyStruct, passStruct10AndSmall, } // Functions for testing reflect function call functionality. //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 passStruct14(a Struct14) Struct14 { return a } //go:registerparams //go:noinline func passStruct15(a Struct15) Struct15 { 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 } // This test case forces a large argument to the stack followed by more // in-register arguments. //go:registerparams //go:noinline func passStruct10AndSmall(a Struct10, b byte, c uint) (Struct10, byte, uint) { return a, b, c } var abiMakeFuncTestCases = []interface{}{ callArgsNone, callArgsInt, callArgsInt8, callArgsInt16, callArgsInt32, callArgsInt64, callArgsUint, callArgsUint8, callArgsUint16, callArgsUint32, callArgsUint64, callArgsFloat32, callArgsFloat64, callArgsComplex64, callArgsComplex128, callArgsManyInt, callArgsManyFloat64, callArgsArray1, callArgsArray, callArgsArray1Mix, callArgsString, // TODO(mknyszek): Test callArgsing interface values. callArgsSlice, callArgsPointer, callArgsStruct1, callArgsStruct2, callArgsStruct3, callArgsStruct4, callArgsStruct5, callArgsStruct6, callArgsStruct7, callArgsStruct8, callArgsStruct9, callArgsStruct10, // TODO(mknyszek): Test callArgsing unsafe.Pointer values. // TODO(mknyszek): Test callArgsing chan values. callArgsStruct11, callArgsStruct12, callArgsStruct13, callArgsStruct14, callArgsStruct15, callArgs2Struct1, callArgsEmptyStruct, } //go:registerparams //go:noinline func callArgsNone(f func(MagicLastTypeNameForTestingRegisterABI)) { f(MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsInt(f func(int, MagicLastTypeNameForTestingRegisterABI) int, a0 int) int { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsInt8(f func(int8, MagicLastTypeNameForTestingRegisterABI) int8, a0 int8) int8 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsInt16(f func(int16, MagicLastTypeNameForTestingRegisterABI) int16, a0 int16) int16 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsInt32(f func(int32, MagicLastTypeNameForTestingRegisterABI) int32, a0 int32) int32 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsInt64(f func(int64, MagicLastTypeNameForTestingRegisterABI) int64, a0 int64) int64 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsUint(f func(uint, MagicLastTypeNameForTestingRegisterABI) uint, a0 uint) uint { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsUint8(f func(uint8, MagicLastTypeNameForTestingRegisterABI) uint8, a0 uint8) uint8 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsUint16(f func(uint16, MagicLastTypeNameForTestingRegisterABI) uint16, a0 uint16) uint16 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsUint32(f func(uint32, MagicLastTypeNameForTestingRegisterABI) uint32, a0 uint32) uint32 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsUint64(f func(uint64, MagicLastTypeNameForTestingRegisterABI) uint64, a0 uint64) uint64 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsFloat32(f func(float32, MagicLastTypeNameForTestingRegisterABI) float32, a0 float32) float32 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsFloat64(f func(float64, MagicLastTypeNameForTestingRegisterABI) float64, a0 float64) float64 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsComplex64(f func(complex64, MagicLastTypeNameForTestingRegisterABI) complex64, a0 complex64) complex64 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsComplex128(f func(complex128, MagicLastTypeNameForTestingRegisterABI) complex128, a0 complex128) complex128 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsArray1(f func([1]uint32, MagicLastTypeNameForTestingRegisterABI) [1]uint32, a0 [1]uint32) [1]uint32 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsArray(f func([2]uintptr, MagicLastTypeNameForTestingRegisterABI) [2]uintptr, a0 [2]uintptr) [2]uintptr { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsArray1Mix(f func(int, [1]uint32, float64, MagicLastTypeNameForTestingRegisterABI) (int, [1]uint32, float64), a0 int, a1 [1]uint32, a2 float64) (int, [1]uint32, float64) { return f(a0, a1, a2, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsString(f func(string, MagicLastTypeNameForTestingRegisterABI) string, a0 string) string { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsSlice(f func([]byte, MagicLastTypeNameForTestingRegisterABI) []byte, a0 []byte) []byte { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsPointer(f func(*byte, MagicLastTypeNameForTestingRegisterABI) *byte, a0 *byte) *byte { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsManyInt(f func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 int, x MagicLastTypeNameForTestingRegisterABI) (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9 int), a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 int) (int, int, int, int, int, int, int, int, int, int) { return f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsManyFloat64(f func(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 float64, x MagicLastTypeNameForTestingRegisterABI) (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18 float64), a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18 float64) (r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18 float64) { return f(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct1(f func(Struct1, MagicLastTypeNameForTestingRegisterABI) Struct1, a0 Struct1) Struct1 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct2(f func(Struct2, MagicLastTypeNameForTestingRegisterABI) Struct2, a0 Struct2) Struct2 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct3(f func(Struct3, MagicLastTypeNameForTestingRegisterABI) Struct3, a0 Struct3) Struct3 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct4(f func(Struct4, MagicLastTypeNameForTestingRegisterABI) Struct4, a0 Struct4) Struct4 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct5(f func(Struct5, MagicLastTypeNameForTestingRegisterABI) Struct5, a0 Struct5) Struct5 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct6(f func(Struct6, MagicLastTypeNameForTestingRegisterABI) Struct6, a0 Struct6) Struct6 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct7(f func(Struct7, MagicLastTypeNameForTestingRegisterABI) Struct7, a0 Struct7) Struct7 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct8(f func(Struct8, MagicLastTypeNameForTestingRegisterABI) Struct8, a0 Struct8) Struct8 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct9(f func(Struct9, MagicLastTypeNameForTestingRegisterABI) Struct9, a0 Struct9) Struct9 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct10(f func(Struct10, MagicLastTypeNameForTestingRegisterABI) Struct10, a0 Struct10) Struct10 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct11(f func(Struct11, MagicLastTypeNameForTestingRegisterABI) Struct11, a0 Struct11) Struct11 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct12(f func(Struct12, MagicLastTypeNameForTestingRegisterABI) Struct12, a0 Struct12) Struct12 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct13(f func(Struct13, MagicLastTypeNameForTestingRegisterABI) Struct13, a0 Struct13) Struct13 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct14(f func(Struct14, MagicLastTypeNameForTestingRegisterABI) Struct14, a0 Struct14) Struct14 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsStruct15(f func(Struct15, MagicLastTypeNameForTestingRegisterABI) Struct15, a0 Struct15) Struct15 { return f(a0, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgs2Struct1(f func(Struct1, Struct1, MagicLastTypeNameForTestingRegisterABI) (Struct1, Struct1), a0, a1 Struct1) (r0, r1 Struct1) { return f(a0, a1, MagicLastTypeNameForTestingRegisterABI{}) } //go:registerparams //go:noinline func callArgsEmptyStruct(f func(int, struct{}, float64, MagicLastTypeNameForTestingRegisterABI) (int, struct{}, float64), a0 int, a1 struct{}, a2 float64) (int, struct{}, float64) { return f(a0, a1, a2, MagicLastTypeNameForTestingRegisterABI{}) } // 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 } // Struct14 tests a non-zero-sized (and otherwise register-assignable) // struct with a field that is a non-zero length array with zero-sized members. type Struct14 struct { A uintptr X [3]struct{} B float64 } // Struct15 tests a non-zero-sized (and otherwise register-assignable) // struct with a struct field that is zero-sized but contains a // non-zero length array with zero-sized members. type Struct15 struct { A uintptr X struct { Y [3]struct{} } B float64 } 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 }