detached ca3432a

purego: fix struct-return stack-argument undercount (#476)

RegisterFunc's preliminary argument count reserved a register for the
hidden struct-return pointer with a bare ints++. That fails to model the
spill: when the integer registers are already full, the pointer is
prepended as the first integer argument and pushes a regular argument
onto the stack, so a stack slot must be counted instead. Without it, the
"too many stack arguments" guard can pass while the actual call writes
one past the end of the fixed-size sysargs array. The count now spills
the same way an ordinary integer argument does.

The bug was pre-existing on amd64 and reachable on amd64 and loong64.
Windows/amd64 was unaffected because its guard uses the ints+floats+stack
sum, and arm64 is unaffected because it returns the pointer in the
dedicated R8 register.

This also consolidates how the struct-return convention is selected.
Replace the amd64StructReturnInMemory helper in func.go, and the
runtime.GOARCH branches that guarded it, with a per-architecture
structReturnInMemory function defined once in each struct_<arch>.go file
and selected by the filename build tag, matching how getStruct,
addStruct, and setStruct are already dispatched. arm64 keeps a separate
branch because it uses R8 rather than an ordinary argument.

Add TestABI_StructReturnHiddenPointer, which registers maxArgs integer
arguments plus a large struct return and asserts that the registration
is rejected.

Closes #477

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
commit
ca3432a0c92683e33afa2d39bf783ffb6524bfa8
author
Hajime Hoshi · · 2026-07-13 01:53 UTC
committer
GitHub · · 2026-07-13 01:53 UTC
parent
5f49e7c
signature
GPG UnverifiedB5690EEEBB952194 The key is unknown
11 files · +135 -30
A export_test.go +14 -0
@@ -0,0 +1,14 @@
1+// SPDX-License-Identifier: Apache-2.0
2+// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
3+
4+//go:build darwin || freebsd || linux || netbsd || windows
5+
6+package purego
7+
8+// MaxArgs re-exports maxArgs for external tests.
9+const MaxArgs = maxArgs
10+
11+// StructReturnInMemory re-exports structReturnInMemory for external tests.
12+func StructReturnInMemory(size uintptr) bool {
13+ return structReturnInMemory(size)
14+}
@@ -0,0 +1,14 @@
1+// SPDX-License-Identifier: Apache-2.0
2+// SPDX-FileCopyrightText: 2026 The Ebitengine Authors
3+
4+//go:build darwin || freebsd || linux || netbsd || windows
5+
6+package purego
7+
8+// MaxArgs re-exports maxArgs for external tests.
9+const MaxArgs = maxArgs
10+
11+// StructReturnInMemory re-exports structReturnInMemory for external tests.
12+func StructReturnInMemory(size uintptr) bool {
13+ return structReturnInMemory(size)
14+}
M func.go +13 -29
@@ -199,10 +199,16 @@ func RegisterFunc(fptr any, cfn uintptr) {
199199 ensureStructSupported()
200200 outType := ty.Out(0)
201201 checkStructFieldsSupported(outType)
202- if runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size()) {
203- // on amd64 a struct returned in memory is allocated by the caller
204- // and its pointer is passed as a hidden first argument.
205- ints++
202+ if structReturnInMemory(outType.Size()) {
203+ // A struct returned in memory is allocated by the caller and its
204+ // pointer is passed as a hidden first integer argument. When the
205+ // integer registers are already full, prepending it spills a
206+ // regular argument onto the stack.
207+ if ints < numOfIntegerRegisters() {
208+ ints++
209+ } else {
210+ stack++
211+ }
206212 }
207213 }
208214
@@ -286,9 +292,9 @@ func RegisterFunc(fptr any, cfn uintptr) {
286292 var arm64_r8 uintptr
287293 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
288294 outType := ty.Out(0)
289- amd64InMemory := runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size())
290- otherInMemory := (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && outType.Size() > maxRegAllocStructSize
291- if amd64InMemory || otherInMemory {
295+ if structReturnInMemory(outType.Size()) {
296+ // The caller allocates the return value and passes its pointer
297+ // as a hidden first integer argument.
292298 val := reflect.New(outType)
293299 keepAlive = append(keepAlive, val)
294300 addInt(val.Pointer())
@@ -533,28 +539,6 @@ func ensureCallbackStructSupported() {
533539 // iOS (GOOS=ios) shares it with macOS (GOOS=darwin).
534540 const isDarwin = runtime.GOOS == "darwin" || runtime.GOOS == "ios"
535541
536-// amd64StructReturnInMemory reports whether a struct return value of the given
537-// size is returned through a caller-allocated hidden pointer (true) rather than
538-// in registers (false). It must only be consulted on amd64.
539-func amd64StructReturnInMemory(size uintptr) bool {
540- if size == 0 {
541- return false
542- }
543- if runtime.GOOS == "windows" {
544- // The Win64 ABI returns aggregates of exactly 1, 2, 4, or 8 bytes in
545- // RAX. Every other size is returned through a caller-allocated hidden
546- // pointer that the callee also returns in RAX.
547- switch size {
548- case 1, 2, 4, 8:
549- return false
550- default:
551- return true
552- }
553- }
554- // The System V ABI returns aggregates of up to two eightbytes in registers.
555- return size > maxRegAllocStructSize
556-}
557-
558542 func roundUpTo8(val uintptr) uintptr {
559543 return (val + align8ByteMask) &^ align8ByteMask
560544 }
@@ -199,10 +199,16 @@ func RegisterFunc(fptr any, cfn uintptr) {
199 ensureStructSupported()199 ensureStructSupported()
200 outType := ty.Out(0)200 outType := ty.Out(0)
201 checkStructFieldsSupported(outType)201 checkStructFieldsSupported(outType)
202- if runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size()) {202+ if structReturnInMemory(outType.Size()) {
203- // on amd64 a struct returned in memory is allocated by the caller203+ // A struct returned in memory is allocated by the caller and its
204- // and its pointer is passed as a hidden first argument.204+ // pointer is passed as a hidden first integer argument. When the
205- ints++205+ // integer registers are already full, prepending it spills a
206+ // regular argument onto the stack.
207+ if ints < numOfIntegerRegisters() {
208+ ints++
209+ } else {
210+ stack++
211+ }
206 }212 }
207 }213 }
208 214
@@ -286,9 +292,9 @@ func RegisterFunc(fptr any, cfn uintptr) {
286 var arm64_r8 uintptr292 var arm64_r8 uintptr
287 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {293 if ty.NumOut() == 1 && ty.Out(0).Kind() == reflect.Struct {
288 outType := ty.Out(0)294 outType := ty.Out(0)
289- amd64InMemory := runtime.GOARCH == "amd64" && amd64StructReturnInMemory(outType.Size())295+ if structReturnInMemory(outType.Size()) {
290- otherInMemory := (runtime.GOARCH == "loong64" || runtime.GOARCH == "ppc64le" || runtime.GOARCH == "riscv64" || runtime.GOARCH == "s390x") && outType.Size() > maxRegAllocStructSize296+ // The caller allocates the return value and passes its pointer
291- if amd64InMemory || otherInMemory {297+ // as a hidden first integer argument.
292 val := reflect.New(outType)298 val := reflect.New(outType)
293 keepAlive = append(keepAlive, val)299 keepAlive = append(keepAlive, val)
294 addInt(val.Pointer())300 addInt(val.Pointer())
@@ -533,28 +539,6 @@ func ensureCallbackStructSupported() {
533 // iOS (GOOS=ios) shares it with macOS (GOOS=darwin).539 // iOS (GOOS=ios) shares it with macOS (GOOS=darwin).
534 const isDarwin = runtime.GOOS == "darwin" || runtime.GOOS == "ios"540 const isDarwin = runtime.GOOS == "darwin" || runtime.GOOS == "ios"
535 541
536-// amd64StructReturnInMemory reports whether a struct return value of the given
537-// size is returned through a caller-allocated hidden pointer (true) rather than
538-// in registers (false). It must only be consulted on amd64.
539-func amd64StructReturnInMemory(size uintptr) bool {
540- if size == 0 {
541- return false
542- }
543- if runtime.GOOS == "windows" {
544- // The Win64 ABI returns aggregates of exactly 1, 2, 4, or 8 bytes in
545- // RAX. Every other size is returned through a caller-allocated hidden
546- // pointer that the callee also returns in RAX.
547- switch size {
548- case 1, 2, 4, 8:
549- return false
550- default:
551- return true
552- }
553- }
554- // The System V ABI returns aggregates of up to two eightbytes in registers.
555- return size > maxRegAllocStructSize
556-}
557-
558 func roundUpTo8(val uintptr) uintptr {542 func roundUpTo8(val uintptr) uintptr {
559 return (val + align8ByteMask) &^ align8ByteMask543 return (val + align8ByteMask) &^ align8ByteMask
560 }544 }
M func_test.go +36 -0
@@ -10,6 +10,7 @@ import (
1010 "os"
1111 "os/exec"
1212 "path/filepath"
13+ "reflect"
1314 "runtime"
1415 "strings"
1516 "sync"
@@ -597,6 +598,41 @@ func TestABI_TooManyArguments(t *testing.T) {
597598 })
598599 }
599600
601+func TestABI_StructReturnHiddenPointer(t *testing.T) {
602+ // A struct returned in memory is passed a hidden pointer as the first
603+ // integer argument, so maxArgs integer arguments plus it need one more slot
604+ // than sysargs holds and RegisterFunc must reject the registration.
605+ type bigStruct struct{ A, B, C uint64 } // larger than two eightbytes
606+
607+ if !purego.StructReturnInMemory(reflect.TypeFor[bigStruct]().Size()) {
608+ t.Skipf("GOARCH=%s does not return large structs via a hidden integer argument", runtime.GOARCH)
609+ }
610+
611+ in := make([]reflect.Type, purego.MaxArgs)
612+ for i := range in {
613+ in[i] = reflect.TypeFor[uintptr]()
614+ }
615+ fnType := reflect.FuncOf(in, []reflect.Type{reflect.TypeFor[bigStruct]()}, false)
616+ fptr := reflect.New(fnType)
617+
618+ defer func() {
619+ switch r := recover(); {
620+ case r == nil:
621+ t.Fatal("RegisterFunc accepted the call; the hidden struct-return pointer was not counted against the stack limit")
622+ case strings.Contains(fmt.Sprint(r), "too many stack arguments"):
623+ // Expected: the guard fired.
624+ case strings.Contains(fmt.Sprint(r), "only supported on"):
625+ t.Skipf("struct returns are unsupported on this platform: %v", r)
626+ default:
627+ t.Fatalf("unexpected panic: %v", r)
628+ }
629+ }()
630+
631+ // A non-zero cfn passes the nil check; the panic fires during the preflight
632+ // argument count, before the function is ever called.
633+ purego.RegisterFunc(fptr.Interface(), uintptr(1))
634+}
635+
600636 func buildSharedLib(tb testing.TB, compilerEnv, libFile string, sources ...string) error {
601637 tb.Helper()
602638 // When PUREGO_TEST_PREBUILT_LIBDIR is set, the shared library has been
@@ -10,6 +10,7 @@ import (
10 "os"10 "os"
11 "os/exec"11 "os/exec"
12 "path/filepath"12 "path/filepath"
13+ "reflect"
13 "runtime"14 "runtime"
14 "strings"15 "strings"
15 "sync"16 "sync"
@@ -597,6 +598,41 @@ func TestABI_TooManyArguments(t *testing.T) {
597 })598 })
598 }599 }
599 600
601+func TestABI_StructReturnHiddenPointer(t *testing.T) {
602+ // A struct returned in memory is passed a hidden pointer as the first
603+ // integer argument, so maxArgs integer arguments plus it need one more slot
604+ // than sysargs holds and RegisterFunc must reject the registration.
605+ type bigStruct struct{ A, B, C uint64 } // larger than two eightbytes
606+
607+ if !purego.StructReturnInMemory(reflect.TypeFor[bigStruct]().Size()) {
608+ t.Skipf("GOARCH=%s does not return large structs via a hidden integer argument", runtime.GOARCH)
609+ }
610+
611+ in := make([]reflect.Type, purego.MaxArgs)
612+ for i := range in {
613+ in[i] = reflect.TypeFor[uintptr]()
614+ }
615+ fnType := reflect.FuncOf(in, []reflect.Type{reflect.TypeFor[bigStruct]()}, false)
616+ fptr := reflect.New(fnType)
617+
618+ defer func() {
619+ switch r := recover(); {
620+ case r == nil:
621+ t.Fatal("RegisterFunc accepted the call; the hidden struct-return pointer was not counted against the stack limit")
622+ case strings.Contains(fmt.Sprint(r), "too many stack arguments"):
623+ // Expected: the guard fired.
624+ case strings.Contains(fmt.Sprint(r), "only supported on"):
625+ t.Skipf("struct returns are unsupported on this platform: %v", r)
626+ default:
627+ t.Fatalf("unexpected panic: %v", r)
628+ }
629+ }()
630+
631+ // A non-zero cfn passes the nil check; the panic fires during the preflight
632+ // argument count, before the function is ever called.
633+ purego.RegisterFunc(fptr.Interface(), uintptr(1))
634+}
635+
600 func buildSharedLib(tb testing.TB, compilerEnv, libFile string, sources ...string) error {636 func buildSharedLib(tb testing.TB, compilerEnv, libFile string, sources ...string) error {
601 tb.Helper()637 tb.Helper()
602 // When PUREGO_TEST_PREBUILT_LIBDIR is set, the shared library has been638 // When PUREGO_TEST_PREBUILT_LIBDIR is set, the shared library has been
M struct_amd64.go +23 -1
@@ -10,13 +10,35 @@ import (
1010 "unsafe"
1111 )
1212
13+// structReturnInMemory reports whether a struct return value of the given size
14+// is returned through a caller-allocated hidden pointer passed as the first
15+// integer argument (true) rather than in registers (false).
16+func structReturnInMemory(size uintptr) bool {
17+ if size == 0 {
18+ return false
19+ }
20+ if runtime.GOOS == "windows" {
21+ // The Win64 ABI returns aggregates of exactly 1, 2, 4, or 8 bytes in
22+ // RAX. Every other size is returned through a caller-allocated hidden
23+ // pointer that the callee also returns in RAX.
24+ switch size {
25+ case 1, 2, 4, 8:
26+ return false
27+ default:
28+ return true
29+ }
30+ }
31+ // The System V ABI returns aggregates of up to two eightbytes in registers.
32+ return size > maxRegAllocStructSize
33+}
34+
1335 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
1436 outSize := outType.Size()
1537 if runtime.GOOS == "windows" {
1638 switch {
1739 case outSize == 0:
1840 return reflect.New(outType).Elem()
19- case amd64StructReturnInMemory(outSize):
41+ case structReturnInMemory(outSize):
2042 // Returned through the caller-allocated hidden pointer, which the
2143 // callee also returns in RAX.
2244 return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
@@ -10,13 +10,35 @@ import (
10 "unsafe"10 "unsafe"
11 )11 )
12 12
13+// structReturnInMemory reports whether a struct return value of the given size
14+// is returned through a caller-allocated hidden pointer passed as the first
15+// integer argument (true) rather than in registers (false).
16+func structReturnInMemory(size uintptr) bool {
17+ if size == 0 {
18+ return false
19+ }
20+ if runtime.GOOS == "windows" {
21+ // The Win64 ABI returns aggregates of exactly 1, 2, 4, or 8 bytes in
22+ // RAX. Every other size is returned through a caller-allocated hidden
23+ // pointer that the callee also returns in RAX.
24+ switch size {
25+ case 1, 2, 4, 8:
26+ return false
27+ default:
28+ return true
29+ }
30+ }
31+ // The System V ABI returns aggregates of up to two eightbytes in registers.
32+ return size > maxRegAllocStructSize
33+}
34+
13 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {35 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
14 outSize := outType.Size()36 outSize := outType.Size()
15 if runtime.GOOS == "windows" {37 if runtime.GOOS == "windows" {
16 switch {38 switch {
17 case outSize == 0:39 case outSize == 0:
18 return reflect.New(outType).Elem()40 return reflect.New(outType).Elem()
19- case amd64StructReturnInMemory(outSize):41+ case structReturnInMemory(outSize):
20 // Returned through the caller-allocated hidden pointer, which the42 // Returned through the caller-allocated hidden pointer, which the
21 // callee also returns in RAX.43 // callee also returns in RAX.
22 return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()44 return reflect.NewAt(outType, *(*unsafe.Pointer)(unsafe.Pointer(&syscall.a1))).Elem()
M struct_arm.go +7 -0
@@ -28,6 +28,13 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
2828 return keepAlive
2929 }
3030
31+// structReturnInMemory always reports false on arm: an indirect struct return
32+// is recovered from the pointer the callee leaves in a1 (see getStruct) rather
33+// than through a caller-allocated hidden first argument.
34+func structReturnInMemory(size uintptr) bool {
35+ return false
36+}
37+
3138 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
3239 outSize := outType.Size()
3340 if outSize == 0 {
@@ -28,6 +28,13 @@ func addStruct(v reflect.Value, numInts, numFloats, numStack *int, addInt, addFl
28 return keepAlive28 return keepAlive
29 }29 }
30 30
31+// structReturnInMemory always reports false on arm: an indirect struct return
32+// is recovered from the pointer the callee leaves in a1 (see getStruct) rather
33+// than through a caller-allocated hidden first argument.
34+func structReturnInMemory(size uintptr) bool {
35+ return false
36+}
37+
31 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {38 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
32 outSize := outType.Size()39 outSize := outType.Size()
33 if outSize == 0 {40 if outSize == 0 {
M struct_arm64.go +7 -0
@@ -14,6 +14,13 @@ import (
1414 "github.com/ebitengine/purego/internal/strings"
1515 )
1616
17+// structReturnInMemory always reports false on arm64: a struct returned in
18+// memory is passed through the dedicated indirect result register (R8), not as
19+// an ordinary integer argument, so it is handled separately.
20+func structReturnInMemory(size uintptr) bool {
21+ return false
22+}
23+
1724 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
1825 outSize := outType.Size()
1926 switch {
@@ -14,6 +14,13 @@ import (
14 "github.com/ebitengine/purego/internal/strings"14 "github.com/ebitengine/purego/internal/strings"
15 )15 )
16 16
17+// structReturnInMemory always reports false on arm64: a struct returned in
18+// memory is passed through the dedicated indirect result register (R8), not as
19+// an ordinary integer argument, so it is handled separately.
20+func structReturnInMemory(size uintptr) bool {
21+ return false
22+}
23+
17 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {24 func getStruct(outType reflect.Type, syscall syscallArgs) (v reflect.Value) {
18 outSize := outType.Size()25 outSize := outType.Size()
19 switch {26 switch {
M struct_loong64.go +7 -0
@@ -72,6 +72,13 @@ func loong64Classify(t reflect.Type) (leaves []loong64Leaf, useFP bool) {
7272 }
7373 }
7474
75+// structReturnInMemory reports whether a struct return value of the given size
76+// is returned through a caller-allocated hidden pointer passed as the first
77+// integer argument. Aggregates larger than two eightbytes are returned in memory.
78+func structReturnInMemory(size uintptr) bool {
79+ return size > maxRegAllocStructSize
80+}
81+
7582 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
7683 outSize := outType.Size()
7784 if outSize == 0 {
@@ -72,6 +72,13 @@ func loong64Classify(t reflect.Type) (leaves []loong64Leaf, useFP bool) {
72 }72 }
73 }73 }
74 74
75+// structReturnInMemory reports whether a struct return value of the given size
76+// is returned through a caller-allocated hidden pointer passed as the first
77+// integer argument. Aggregates larger than two eightbytes are returned in memory.
78+func structReturnInMemory(size uintptr) bool {
79+ return size > maxRegAllocStructSize
80+}
81+
75 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {82 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
76 outSize := outType.Size()83 outSize := outType.Size()
77 if outSize == 0 {84 if outSize == 0 {
M struct_other.go +7 -0
@@ -22,6 +22,13 @@ func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
2222 panic("purego: struct returns are not supported on this architecture")
2323 }
2424
25+// structReturnInMemory reports whether a struct return value of the given size
26+// is returned through a caller-allocated hidden pointer. Structs are unsupported
27+// on this architecture, so it always reports false.
28+func structReturnInMemory(size uintptr) bool {
29+ return false
30+}
31+
2532 // shouldBundleStackArgs always returns false because C-style stack argument
2633 // bundling is only needed on Darwin ARM64.
2734 func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
@@ -22,6 +22,13 @@ func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
22 panic("purego: struct returns are not supported on this architecture")22 panic("purego: struct returns are not supported on this architecture")
23 }23 }
24 24
25+// structReturnInMemory reports whether a struct return value of the given size
26+// is returned through a caller-allocated hidden pointer. Structs are unsupported
27+// on this architecture, so it always reports false.
28+func structReturnInMemory(size uintptr) bool {
29+ return false
30+}
31+
25 // shouldBundleStackArgs always returns false because C-style stack argument32 // shouldBundleStackArgs always returns false because C-style stack argument
26 // bundling is only needed on Darwin ARM64.33 // bundling is only needed on Darwin ARM64.
27 func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {34 func shouldBundleStackArgs(v reflect.Value, numInts, numFloats int) bool {
M struct_ppc64le.go +7 -0
@@ -8,6 +8,13 @@ import (
88 "unsafe"
99 )
1010
11+// structReturnInMemory reports whether a struct return value of the given size
12+// is returned through a caller-allocated hidden pointer passed as the first
13+// integer argument. Aggregates larger than two eightbytes are returned in memory.
14+func structReturnInMemory(size uintptr) bool {
15+ return size > maxRegAllocStructSize
16+}
17+
1118 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
1219 outSize := outType.Size()
1320
@@ -8,6 +8,13 @@ import (
8 "unsafe"8 "unsafe"
9 )9 )
10 10
11+// structReturnInMemory reports whether a struct return value of the given size
12+// is returned through a caller-allocated hidden pointer passed as the first
13+// integer argument. Aggregates larger than two eightbytes are returned in memory.
14+func structReturnInMemory(size uintptr) bool {
15+ return size > maxRegAllocStructSize
16+}
17+
11 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {18 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
12 outSize := outType.Size()19 outSize := outType.Size()
13 20
M struct_riscv64.go +7 -0
@@ -8,6 +8,13 @@ import (
88 "unsafe"
99 )
1010
11+// structReturnInMemory reports whether a struct return value of the given size
12+// is returned through a caller-allocated hidden pointer passed as the first
13+// integer argument. Aggregates larger than two eightbytes are returned in memory.
14+func structReturnInMemory(size uintptr) bool {
15+ return size > maxRegAllocStructSize
16+}
17+
1118 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
1219 outSize := outType.Size()
1320
@@ -8,6 +8,13 @@ import (
8 "unsafe"8 "unsafe"
9 )9 )
10 10
11+// structReturnInMemory reports whether a struct return value of the given size
12+// is returned through a caller-allocated hidden pointer passed as the first
13+// integer argument. Aggregates larger than two eightbytes are returned in memory.
14+func structReturnInMemory(size uintptr) bool {
15+ return size > maxRegAllocStructSize
16+}
17+
11 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {18 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
12 outSize := outType.Size()19 outSize := outType.Size()
13 20
M struct_s390x.go +7 -0
@@ -8,6 +8,13 @@ import (
88 "unsafe"
99 )
1010
11+// structReturnInMemory reports whether a struct return value of the given size
12+// is returned through a caller-allocated hidden pointer passed as the first
13+// integer argument. Aggregates larger than two eightbytes are returned in memory.
14+func structReturnInMemory(size uintptr) bool {
15+ return size > maxRegAllocStructSize
16+}
17+
1118 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
1219 outSize := outType.Size()
1320
@@ -8,6 +8,13 @@ import (
8 "unsafe"8 "unsafe"
9 )9 )
10 10
11+// structReturnInMemory reports whether a struct return value of the given size
12+// is returned through a caller-allocated hidden pointer passed as the first
13+// integer argument. Aggregates larger than two eightbytes are returned in memory.
14+func structReturnInMemory(size uintptr) bool {
15+ return size > maxRegAllocStructSize
16+}
17+
11 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {18 func getStruct(outType reflect.Type, syscall syscallArgs) reflect.Value {
12 outSize := outType.Size()19 outSize := outType.Size()
13 20

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 102

↑↓ moveenter openesc close