1// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: 2022 The Ebitengine Authors
3
4//go:build (386 || arm) && (freebsd || linux || netbsd || windows)
5
6package purego
7
8// CDecl marks a function as being called using the __cdecl calling convention as defined in
9// the [MSDocs] when passed to NewCallback. It must be the first argument to the function.
10// This is only useful on 386 Windows, but it is safe to use on other platforms.
11//
12// [MSDocs]: https://learn.microsoft.com/en-us/cpp/cpp/cdecl?view=msvc-170
13type CDecl struct{}
14
15const (
16 maxArgs = 15
17)
18
19type syscall15Args struct {
20 _ hostLayout
21 fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15 uintptr
22 a16, a17, a18, a19, a20, a21, a22, a23, a24, a25, a26, a27, a28, a29, a30, a31, a32 uintptr
23 f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16 uintptr
24 arm64_r8 uintptr
25}
26
27func (s *syscall15Args) Set(fn uintptr, ints []uintptr, floats []uintptr, r8 uintptr) {
28 s.fn = fn
29 s.a1 = ints[0]
30 s.a2 = ints[1]
31 s.a3 = ints[2]
32 s.a4 = ints[3]
33 s.a5 = ints[4]
34 s.a6 = ints[5]
35 s.a7 = ints[6]
36 s.a8 = ints[7]
37 s.a9 = ints[8]
38 s.a10 = ints[9]
39 s.a11 = ints[10]
40 s.a12 = ints[11]
41 s.a13 = ints[12]
42 s.a14 = ints[13]
43 s.a15 = ints[14]
44 s.f1 = floats[0]
45 s.f2 = floats[1]
46 s.f3 = floats[2]
47 s.f4 = floats[3]
48 s.f5 = floats[4]
49 s.f6 = floats[5]
50 s.f7 = floats[6]
51 s.f8 = floats[7]
52 s.f9 = floats[8]
53 s.f10 = floats[9]
54 s.f11 = floats[10]
55 s.f12 = floats[11]
56 s.f13 = floats[12]
57 s.f14 = floats[13]
58 s.f15 = floats[14]
59 s.arm64_r8 = r8
60}
61
62// SyscallN takes fn, a C function pointer and a list of arguments as uintptr.
63// On 386 and arm, SyscallN supports at most 15 uintptr arguments.
64// It panics when the maximum is exceeded. It returns the result and the libc error code if there is one.
65//
66// In order to call this function properly make sure to follow all the rules specified in [unsafe.Pointer]
67// especially point 4.
68//
69// NOTE: SyscallN does not properly call functions that have both integer and float parameters.
70// Use RegisterFunc instead when the target function mixes integer and floating-point arguments.
71// See discussion comment https://github.com/ebiten/purego/pull/1#issuecomment-1128057607
72// for an explanation of why that is.
73//
74// On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the
75// stack.
76//
77// The pragma go:nosplit is not needed at this function declaration because it uses go:uintptrescapes
78// which forces all the objects that the uintptrs point to onto the heap where a stack split won't affect
79// their memory location.
80//
81//go:uintptrescapes
82func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) {
83 if fn == 0 {
84 panic("purego: fn is nil")
85 }
86 if len(args) > maxArgs {
87 panic("purego: too many arguments to SyscallN")
88 }
89 // add padding so there is no out-of-bounds slicing
90 var tmp [maxArgs]uintptr
91 copy(tmp[:], args)
92 return syscall_syscall15X(fn, 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])
93}
94
95// SyscallSelf is like SyscallN but efficiently prepends self to args without allocation.
96// It is designed for method-like calls where the first argument is always a receiver pointer.
97// It shares SyscallN's argument-placement limits and mixed integer/float restriction.
98// On 386 and arm, the total count including self is limited to 15 uintptr arguments.
99// This avoids the heap allocation that would occur with SyscallN(fn, append([]uintptr{self}, args...)...).
100//
101//go:uintptrescapes
102func SyscallSelf(fn, self uintptr, args ...uintptr) (r1, r2, err uintptr) {
103 if fn == 0 {
104 panic("purego: fn is nil")
105 }
106 if len(args) > maxArgs-1 {
107 panic("purego: too many arguments to SyscallSelf")
108 }
109 // Use a stack-allocated array to prepend self without heap allocation.
110 // self goes in tmp[0], remaining args follow.
111 var tmp [maxArgs]uintptr
112 tmp[0] = self
113 copy(tmp[1:], args)
114 return syscall_syscall15X(fn, 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])
115}