purego: use go:nosplit and map to proper implementation (#6)
This commit also adds windows support to purego
- commit
- cce0ae8e7ec8f56ec142abf1fff514dbc7a2fd54
- author
- TotallyGamerJet · · 2022-06-13 03:09 UTC
- committer
- GitHub · · 2022-06-13 03:09 UTC
- parent
- 119eb37
- signature
- GPG Unverified
4AEE18F83AFDEB23The key is unknown
3 files · +72 -28
M syscall.go +18 -28
| @@ -1,15 +1,27 @@ | |||
| 1 | 1 | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | 2 | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | |
| 3 | 3 | ||
| 4 | - | //go:build darwin | |
| 5 | - | // +build darwin | |
| 4 | + | //go:build darwin || windows | |
| 5 | + | // +build darwin windows | |
| 6 | 6 | ||
| 7 | 7 | package purego | |
| 8 | 8 | ||
| 9 | - | import "unsafe" | |
| 10 | - | ||
| 11 | 9 | const maxArgs = 9 | |
| 12 | 10 | ||
| 11 | + | // f matches argument numbers to a Syscall implementation that can take that many | |
| 12 | + | var f = map[int]func(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr){ | |
| 13 | + | 0: syscall_syscallX3, | |
| 14 | + | 1: syscall_syscallX3, | |
| 15 | + | 2: syscall_syscallX3, | |
| 16 | + | 3: syscall_syscallX3, | |
| 17 | + | 4: syscall_syscallX6, | |
| 18 | + | 5: syscall_syscallX6, | |
| 19 | + | 6: syscall_syscallX6, | |
| 20 | + | 7: syscall_syscall9X, | |
| 21 | + | 8: syscall_syscall9X, | |
| 22 | + | 9: syscall_syscall9X, | |
| 23 | + | } | |
| 24 | + | ||
| 13 | 25 | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | |
| 14 | 26 | // There is an internal maximum number of arguments that SyscallN can take. It panics | |
| 15 | 27 | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | |
| @@ -20,6 +32,7 @@ const maxArgs = 9 | |||
| 20 | 32 | // | |
| 21 | 33 | // On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the | |
| 22 | 34 | // stack. | |
| 35 | + | //go:nosplit | |
| 23 | 36 | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |
| 24 | 37 | if len(args) > maxArgs { | |
| 25 | 38 | panic("too many arguments to SyscallN") | |
| @@ -27,28 +40,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||
| 27 | 40 | // add padding so there is no out-of-bounds slicing | |
| 28 | 41 | var tmp [maxArgs]uintptr | |
| 29 | 42 | copy(tmp[:], args) | |
| 30 | - | if len(args) <= 6 { | |
| 31 | - | // use the 6 argument version because | |
| 32 | - | // gl.GenFramebuffersEXT would fail with the 9 version | |
| 33 | - | // See https://github.com/hajimehoshi/ebiten/issues/2102#issuecomment-1134679352 | |
| 34 | - | return syscall_syscall6X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]) | |
| 35 | - | } | |
| 36 | - | return syscall_syscall9X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) | |
| 37 | - | } | |
| 38 | - | ||
| 39 | - | func callc(fn uintptr, args unsafe.Pointer) { | |
| 40 | - | runtime_entersyscall() | |
| 41 | - | runtime_libcCall(unsafe.Pointer(fn), args) | |
| 42 | - | runtime_exitsyscall() | |
| 43 | - | } | |
| 44 | - | ||
| 45 | - | var syscall9XABI0 uintptr | |
| 46 | - | ||
| 47 | - | func syscall9X() // implemented in assembly | |
| 48 | - | ||
| 49 | - | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |
| 50 | - | args := struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr }{ | |
| 51 | - | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | |
| 52 | - | callc(syscall9XABI0, unsafe.Pointer(&args)) | |
| 53 | - | return args.r1, args.r2, args.err | |
| 43 | + | return f[len(args)](fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) | |
| 54 | 44 | } | |
| @@ -1,15 +1,27 @@ | |||||
| 1 | // SPDX-License-Identifier: Apache-2.0 | 1 | // SPDX-License-Identifier: Apache-2.0 | ||
| 2 | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | 2 | // SPDX-FileCopyrightText: 2022 The Ebiten Authors | ||
| 3 | 3 | ||||
| 4 | - | //go:build darwin | 4 | + | //go:build darwin || windows |
| 5 | - | // +build darwin | 5 | + | // +build darwin windows |
| 6 | 6 | ||||
| 7 | package purego | 7 | package purego | ||
| 8 | 8 | ||||
| 9 | - | import "unsafe" | |||
| 10 | - | ||||
| 11 | const maxArgs = 9 | 9 | const maxArgs = 9 | ||
| 12 | 10 | ||||
| 11 | + | // f matches argument numbers to a Syscall implementation that can take that many | |||
| 12 | + | var f = map[int]func(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr){ | |||
| 13 | + | 0: syscall_syscallX3, | |||
| 14 | + | 1: syscall_syscallX3, | |||
| 15 | + | 2: syscall_syscallX3, | |||
| 16 | + | 3: syscall_syscallX3, | |||
| 17 | + | 4: syscall_syscallX6, | |||
| 18 | + | 5: syscall_syscallX6, | |||
| 19 | + | 6: syscall_syscallX6, | |||
| 20 | + | 7: syscall_syscall9X, | |||
| 21 | + | 8: syscall_syscall9X, | |||
| 22 | + | 9: syscall_syscall9X, | |||
| 23 | + | } | |||
| 24 | + | ||||
| 13 | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | 25 | // SyscallN takes fn, a C function pointer and a list of arguments as uintptr. | ||
| 14 | // There is an internal maximum number of arguments that SyscallN can take. It panics | 26 | // There is an internal maximum number of arguments that SyscallN can take. It panics | ||
| 15 | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | 27 | // when the maximum is exceeded. It returns the result and the libc error code if there is one. | ||
| @@ -20,6 +32,7 @@ const maxArgs = 9 | |||||
| 20 | // | 32 | // | ||
| 21 | // On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the | 33 | // On amd64, if there are more than 8 floats the 9th and so on will be placed incorrectly on the | ||
| 22 | // stack. | 34 | // stack. | ||
| 35 | + | //go:nosplit | |||
| 23 | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | 36 | func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | ||
| 24 | if len(args) > maxArgs { | 37 | if len(args) > maxArgs { | ||
| 25 | panic("too many arguments to SyscallN") | 38 | panic("too many arguments to SyscallN") | ||
| @@ -27,28 +40,5 @@ func SyscallN(fn uintptr, args ...uintptr) (r1, r2, err uintptr) { | |||||
| 27 | // add padding so there is no out-of-bounds slicing | 40 | // add padding so there is no out-of-bounds slicing | ||
| 28 | var tmp [maxArgs]uintptr | 41 | var tmp [maxArgs]uintptr | ||
| 29 | copy(tmp[:], args) | 42 | copy(tmp[:], args) | ||
| 30 | - | if len(args) <= 6 { | 43 | + | return f[len(args)](fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) |
| 31 | - | // use the 6 argument version because | |||
| 32 | - | // gl.GenFramebuffersEXT would fail with the 9 version | |||
| 33 | - | // See https://github.com/hajimehoshi/ebiten/issues/2102#issuecomment-1134679352 | |||
| 34 | - | return syscall_syscall6X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5]) | |||
| 35 | - | } | |||
| 36 | - | return syscall_syscall9X(fn, tmp[0], tmp[1], tmp[2], tmp[3], tmp[4], tmp[5], tmp[6], tmp[7], tmp[8]) | |||
| 37 | - | } | |||
| 38 | - | ||||
| 39 | - | func callc(fn uintptr, args unsafe.Pointer) { | |||
| 40 | - | runtime_entersyscall() | |||
| 41 | - | runtime_libcCall(unsafe.Pointer(fn), args) | |||
| 42 | - | runtime_exitsyscall() | |||
| 43 | - | } | |||
| 44 | - | ||||
| 45 | - | var syscall9XABI0 uintptr | |||
| 46 | - | ||||
| 47 | - | func syscall9X() // implemented in assembly | |||
| 48 | - | ||||
| 49 | - | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |||
| 50 | - | args := struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr }{ | |||
| 51 | - | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | |||
| 52 | - | callc(syscall9XABI0, unsafe.Pointer(&args)) | |||
| 53 | - | return args.r1, args.r2, args.err | |||
| 54 | } | 44 | } | ||
A syscall_darwin.go +31 -0
| @@ -0,0 +1,31 @@ | |||
| 1 | + | package purego | |
| 2 | + | ||
| 3 | + | import "unsafe" | |
| 4 | + | ||
| 5 | + | func callc(fn uintptr, args unsafe.Pointer) { | |
| 6 | + | runtime_entersyscall() | |
| 7 | + | runtime_libcCall(unsafe.Pointer(fn), args) | |
| 8 | + | runtime_exitsyscall() | |
| 9 | + | } | |
| 10 | + | ||
| 11 | + | //go:nosplit | |
| 12 | + | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 13 | + | return syscall_syscall6X(fn, a1, a2, a3, 0, 0, 0) | |
| 14 | + | } | |
| 15 | + | ||
| 16 | + | //go:nosplit | |
| 17 | + | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 18 | + | return syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6) | |
| 19 | + | } | |
| 20 | + | ||
| 21 | + | var syscall9XABI0 uintptr | |
| 22 | + | ||
| 23 | + | func syscall9X() // implemented in assembly | |
| 24 | + | ||
| 25 | + | //go:nosplit | |
| 26 | + | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |
| 27 | + | args := struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr }{ | |
| 28 | + | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | |
| 29 | + | callc(syscall9XABI0, unsafe.Pointer(&args)) | |
| 30 | + | return args.r1, args.r2, args.err | |
| 31 | + | } | |
| @@ -0,0 +1,31 @@ | |||||
| 1 | + | package purego | |||
| 2 | + | ||||
| 3 | + | import "unsafe" | |||
| 4 | + | ||||
| 5 | + | func callc(fn uintptr, args unsafe.Pointer) { | |||
| 6 | + | runtime_entersyscall() | |||
| 7 | + | runtime_libcCall(unsafe.Pointer(fn), args) | |||
| 8 | + | runtime_exitsyscall() | |||
| 9 | + | } | |||
| 10 | + | ||||
| 11 | + | //go:nosplit | |||
| 12 | + | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 13 | + | return syscall_syscall6X(fn, a1, a2, a3, 0, 0, 0) | |||
| 14 | + | } | |||
| 15 | + | ||||
| 16 | + | //go:nosplit | |||
| 17 | + | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 18 | + | return syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6) | |||
| 19 | + | } | |||
| 20 | + | ||||
| 21 | + | var syscall9XABI0 uintptr | |||
| 22 | + | ||||
| 23 | + | func syscall9X() // implemented in assembly | |||
| 24 | + | ||||
| 25 | + | //go:nosplit | |||
| 26 | + | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |||
| 27 | + | args := struct{ fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err uintptr }{ | |||
| 28 | + | fn, a1, a2, a3, a4, a5, a6, a7, a8, a9, r1, r2, err} | |||
| 29 | + | callc(syscall9XABI0, unsafe.Pointer(&args)) | |||
| 30 | + | return args.r1, args.r2, args.err | |||
| 31 | + | } | |||
A syscall_windows.go +23 -0
| @@ -0,0 +1,23 @@ | |||
| 1 | + | package purego | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "syscall" | |
| 5 | + | ) | |
| 6 | + | ||
| 7 | + | //go:nosplit | |
| 8 | + | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 9 | + | r1, r2, errno := syscall.Syscall(fn, 3, a1, a2, a3) | |
| 10 | + | return r1, r2, uintptr(errno) | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | //go:nosplit | |
| 14 | + | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |
| 15 | + | r1, r2, errno := syscall.Syscall6(fn, 6, a1, a2, a3, a4, a5, a6) | |
| 16 | + | return r1, r2, uintptr(errno) | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | //go:nosplit | |
| 20 | + | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |
| 21 | + | r1, r2, errno := syscall.Syscall9(fn, 9, a1, a2, a3, a4, a5, a6, a7, a8, a9) | |
| 22 | + | return r1, r2, uintptr(errno) | |
| 23 | + | } | |
| @@ -0,0 +1,23 @@ | |||||
| 1 | + | package purego | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "syscall" | |||
| 5 | + | ) | |||
| 6 | + | ||||
| 7 | + | //go:nosplit | |||
| 8 | + | func syscall_syscallX3(fn, a1, a2, a3, _, _, _, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 9 | + | r1, r2, errno := syscall.Syscall(fn, 3, a1, a2, a3) | |||
| 10 | + | return r1, r2, uintptr(errno) | |||
| 11 | + | } | |||
| 12 | + | ||||
| 13 | + | //go:nosplit | |||
| 14 | + | func syscall_syscallX6(fn, a1, a2, a3, a4, a5, a6, _, _, _ uintptr) (r1, r2, err uintptr) { | |||
| 15 | + | r1, r2, errno := syscall.Syscall6(fn, 6, a1, a2, a3, a4, a5, a6) | |||
| 16 | + | return r1, r2, uintptr(errno) | |||
| 17 | + | } | |||
| 18 | + | ||||
| 19 | + | //go:nosplit | |||
| 20 | + | func syscall_syscall9X(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2, err uintptr) { | |||
| 21 | + | r1, r2, errno := syscall.Syscall9(fn, 9, a1, a2, a3, a4, a5, a6, a7, a8, a9) | |||
| 22 | + | return r1, r2, uintptr(errno) | |||
| 23 | + | } | |||