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,