From 095719aa39f960ad78eb3cf01422606dbee3b3b8 Mon Sep 17 00:00:00 2001 From: Romain Marcadier Date: Thu, 30 Apr 2026 17:58:01 +0200 Subject: [PATCH] purego: add fixed-arity Syscall0-Syscall15 variants to avoid heap allocation When SyscallN is called across module boundaries the variadic args slice always escapes to the heap, even for small call sites. Expose Syscall0 through Syscall15 with explicit named parameters so callers avoid that allocation entirely: no slice is formed at the call site, and the stack-local [maxArgs]uintptr arrays are built entirely inside the wrapper. The implementations follow the same pattern as SyscallN (zero-initialised tmp/floats arrays, go:uintptrescapes, Windows delegation via syscall_syscallN) and share the existing syscall_SyscallN internal path, so no platform-specific assembly is required. Tests are added to TestABI_ArgumentPassing using two new C helpers: stack_0_uintptr (0-arg, returns 42) and stack_15_uintptr (15-arg sum), covering every arity and verifying parity with SyscallN. JJ-Change-Id: kpqxzv --- func_test.go | 111 ++++++++++++ syscall_fixed.go | 342 ++++++++++++++++++++++++++++++++++++ testdata/abitest/abi_test.c | 13 ++ 3 files changed, 466 insertions(+) create mode 100644 syscall_fixed.go diff --git a/func_test.go b/func_test.go index cf7ebc04..29aab21a 100644 --- a/func_test.go +++ b/func_test.go @@ -469,6 +469,117 @@ func TestABI_ArgumentPassing(t *testing.T) { } }) + t.Run("syscall_fixed", func(t *testing.T) { + fn0, err := load.OpenSymbol(lib, "stack_0_uintptr") + if err != nil { + t.Fatalf("OpenSymbol(stack_0_uintptr) failed: %v", err) + } + fn15, err := load.OpenSymbol(lib, "stack_15_uintptr") + if err != nil { + t.Fatalf("OpenSymbol(stack_15_uintptr) failed: %v", err) + } + + // stack_0_uintptr takes no arguments and returns the constant 42. + { + got, _, _ := purego.Syscall0(fn0) + gotN, _, _ := purego.SyscallN(fn0) + if got != 42 { + t.Errorf("Syscall0: got %d, want 42", got) + } + if got != gotN { + t.Errorf("Syscall0 vs SyscallN: %d != %d", got, gotN) + } + } + + // stack_15_uintptr sums its 15 arguments. Calling it with K explicit + // arguments (the remaining slots are zero-initialised) yields sum(1..K). + check := func(name string, got, gotN uintptr, want int) { + t.Helper() + if got != uintptr(want) { + t.Errorf("%s: got %d, want %d", name, got, want) + } + if got != gotN { + t.Errorf("%s vs SyscallN: %d != %d", name, got, gotN) + } + } + + { + got, _, _ := purego.Syscall1(fn15, 1) + gotN, _, _ := purego.SyscallN(fn15, 1) + check("Syscall1", got, gotN, 1) + } + { + got, _, _ := purego.Syscall2(fn15, 1, 2) + gotN, _, _ := purego.SyscallN(fn15, 1, 2) + check("Syscall2", got, gotN, 3) + } + { + got, _, _ := purego.Syscall3(fn15, 1, 2, 3) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3) + check("Syscall3", got, gotN, 6) + } + { + got, _, _ := purego.Syscall4(fn15, 1, 2, 3, 4) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4) + check("Syscall4", got, gotN, 10) + } + { + got, _, _ := purego.Syscall5(fn15, 1, 2, 3, 4, 5) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5) + check("Syscall5", got, gotN, 15) + } + { + got, _, _ := purego.Syscall6(fn15, 1, 2, 3, 4, 5, 6) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6) + check("Syscall6", got, gotN, 21) + } + { + got, _, _ := purego.Syscall7(fn15, 1, 2, 3, 4, 5, 6, 7) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7) + check("Syscall7", got, gotN, 28) + } + { + got, _, _ := purego.Syscall8(fn15, 1, 2, 3, 4, 5, 6, 7, 8) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8) + check("Syscall8", got, gotN, 36) + } + { + got, _, _ := purego.Syscall9(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9) + check("Syscall9", got, gotN, 45) + } + { + got, _, _ := purego.Syscall10(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10) + check("Syscall10", got, gotN, 55) + } + { + got, _, _ := purego.Syscall11(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11) + check("Syscall11", got, gotN, 66) + } + { + got, _, _ := purego.Syscall12(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12) + check("Syscall12", got, gotN, 78) + } + { + got, _, _ := purego.Syscall13(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) + check("Syscall13", got, gotN, 91) + } + { + got, _, _ := purego.Syscall14(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14) + check("Syscall14", got, gotN, 105) + } + { + got, _, _ := purego.Syscall15(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + gotN, _, _ := purego.SyscallN(fn15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) + check("Syscall15", got, gotN, 120) + } + }) + t.Run("32_mixed_int_float", func(t *testing.T) { if unsafe.Sizeof(uintptr(0)) == 4 { t.Skip("requires 64-bit uintptr slots") diff --git a/syscall_fixed.go b/syscall_fixed.go new file mode 100644 index 00000000..a5880b5f --- /dev/null +++ b/syscall_fixed.go @@ -0,0 +1,342 @@ +// SPDX-License-Identifier: Apache-2.0 +// SPDX-FileCopyrightText: 2026 The Ebitengine Authors + +//go:build darwin || freebsd || linux || netbsd || windows + +package purego + +import "runtime" + +// Syscall0 is a fixed-arity variant of [SyscallN] for zero arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall0(fn uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn) + } + var tmp [maxArgs]uintptr + var floats [maxArgs]uintptr + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall1 is a fixed-arity variant of [SyscallN] for one argument. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall1(fn, a1 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1) + } + var tmp [maxArgs]uintptr + tmp[0] = a1 + var floats [maxArgs]uintptr + floats[0] = a1 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall2 is a fixed-arity variant of [SyscallN] for two arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall2(fn, a1, a2 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1] = a1, a2 + var floats [maxArgs]uintptr + floats[0], floats[1] = a1, a2 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall3 is a fixed-arity variant of [SyscallN] for three arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall3(fn, a1, a2, a3 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2] = a1, a2, a3 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2] = a1, a2, a3 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall4 is a fixed-arity variant of [SyscallN] for four arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall4(fn, a1, a2, a3, a4 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3] = a1, a2, a3, a4 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3] = a1, a2, a3, a4 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall5 is a fixed-arity variant of [SyscallN] for five arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall5(fn, a1, a2, a3, a4, a5 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4] = a1, a2, a3, a4, a5 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4] = a1, a2, a3, a4, a5 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall6 is a fixed-arity variant of [SyscallN] for six arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5] = a1, a2, a3, a4, a5, a6 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5] = a1, a2, a3, a4, a5, a6 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall7 is a fixed-arity variant of [SyscallN] for seven arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall7(fn, a1, a2, a3, a4, a5, a6, a7 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6] = a1, a2, a3, a4, a5, a6, a7 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6] = a1, a2, a3, a4, a5, a6, a7 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall8 is a fixed-arity variant of [SyscallN] for eight arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall8(fn, a1, a2, a3, a4, a5, a6, a7, a8 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7] = a1, a2, a3, a4, a5, a6, a7, a8 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7] = a1, a2, a3, a4, a5, a6, a7, a8 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall9 is a fixed-arity variant of [SyscallN] for nine arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8] = a1, a2, a3, a4, a5, a6, a7, a8, a9 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], floats[8] = a1, a2, a3, a4, a5, a6, a7, a8, a9 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall10 is a fixed-arity variant of [SyscallN] for ten arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall10(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], floats[8], floats[9] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall11 is a fixed-arity variant of [SyscallN] for eleven arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall11(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], floats[8], floats[9], floats[10] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall12 is a fixed-arity variant of [SyscallN] for twelve arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall12(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], floats[8], floats[9], floats[10], floats[11] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall13 is a fixed-arity variant of [SyscallN] for thirteen arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall13(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], tmp[12] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], floats[8], floats[9], floats[10], floats[11], floats[12] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall14 is a fixed-arity variant of [SyscallN] for fourteen arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall14(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], floats[8], floats[9], floats[10], floats[11], floats[12], floats[13] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} + +// Syscall15 is a fixed-arity variant of [SyscallN] for fifteen arguments. +// It avoids the heap allocation of a variadic slice when called across module +// boundaries. See [SyscallN] for the full documentation and caveats. +// +//go:uintptrescapes +func Syscall15(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr) (r1, r2, err uintptr) { + if fn == 0 { + panic("purego: fn is nil") + } + if runtime.GOOS == "windows" { + return syscall_syscallN(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15) + } + var tmp [maxArgs]uintptr + tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8], tmp[9], tmp[10], tmp[11], tmp[12], tmp[13], tmp[14] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 + var floats [maxArgs]uintptr + floats[0], floats[1], floats[2], floats[3], floats[4], floats[5], floats[6], floats[7], floats[8], floats[9], floats[10], floats[11], floats[12], floats[13], floats[14] = a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 + s := syscall_SyscallN(fn, tmp[:], floats[:], 0) + defer thePool.Put(s) + return s.a1, s.a2, s.a3 +} diff --git a/testdata/abitest/abi_test.c b/testdata/abitest/abi_test.c index 824e453d..ea84bec2 100644 --- a/testdata/abitest/abi_test.c +++ b/testdata/abitest/abi_test.c @@ -130,6 +130,19 @@ void stack_25_int64_exceeds(char *buf, size_t bufsize, int64_t a1, int64_t a2, i a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15, a16, a17, a18, a19, a20, a21, a22, a23, a24, a25); } +uintptr_t stack_0_uintptr(void) { + return 42; +} + +uintptr_t stack_15_uintptr( + uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, + uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9, uintptr_t a10, + uintptr_t a11, uintptr_t a12, uintptr_t a13, uintptr_t a14, uintptr_t a15 +) { + return a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a10 + + a11 + a12 + a13 + a14 + a15; +} + uintptr_t stack_20_uintptr( uintptr_t a1, uintptr_t a2, uintptr_t a3, uintptr_t a4, uintptr_t a5, uintptr_t a6, uintptr_t a7, uintptr_t a8, uintptr_t a9, uintptr_t a10,