fakecgo: add s390x support for CGO_ENABLED=0 (#422)
- commit
- d60df56996e903cedc3502eac480026a2c469429
- author
- George Adams · · 2026-07-02 02:47 UTC
- committer
- GitHub · · 2026-07-02 02:47 UTC
- parent
- 5da5389
- signature
- GPG Unverified
B5690EEEBB952194The key is unknown - refs
- tag: v0.11.0-alpha.6
6 files · +235 -19
M .github/workflows/test.yml +10 -5
| @@ -10,7 +10,7 @@ jobs: | |||
| 10 | 10 | strategy: | |
| 11 | 11 | matrix: | |
| 12 | 12 | os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest, windows-11-arm] | |
| 13 | - | go: ['1.25.x', '1.26.x'] | |
| 13 | + | go: ['1.25.x', '1.26.x', '1.27.0-rc.1'] | |
| 14 | 14 | name: Test with Go ${{ matrix.go }} on ${{ matrix.os }} | |
| 15 | 15 | runs-on: ${{ matrix.os }} | |
| 16 | 16 | defaults: | |
| @@ -75,13 +75,13 @@ jobs: | |||
| 75 | 75 | ||
| 76 | 76 | - name: go build (Linux minor architectures) | |
| 77 | 77 | # Test them only on the latest Go to reduce CI time. | |
| 78 | - | # s390x cannot be tested here as it requires Cgo. | |
| 79 | - | if: startsWith(matrix.go, '1.26.') | |
| 78 | + | if: startsWith(matrix.go, '1.27.') | |
| 80 | 79 | run: | | |
| 81 | 80 | # Check cross-compiling Linux binaries for minor architectures. | |
| 82 | 81 | env GOOS=linux GOARCH=loong64 go build -v ./... | |
| 83 | 82 | env GOOS=linux GOARCH=ppc64le go build -v ./... | |
| 84 | 83 | env GOOS=linux GOARCH=riscv64 go build -v ./... | |
| 84 | + | env GOOS=linux GOARCH=s390x go build -v ./... | |
| 85 | 85 | ||
| 86 | 86 | - name: go build (Linux mips, Cgo) | |
| 87 | 87 | # mips builds only via the Cgo fallback (CGO_ENABLED=1). See #460. | |
| @@ -191,7 +191,7 @@ jobs: | |||
| 191 | 191 | minor-arches: | |
| 192 | 192 | strategy: | |
| 193 | 193 | matrix: | |
| 194 | - | go: ['1.25.x', '1.26.x'] | |
| 194 | + | go: ['1.25.x', '1.26.x', '1.27.0-rc.1'] | |
| 195 | 195 | name: Test with Go ${{ matrix.go }} on Linux minor architectures | |
| 196 | 196 | runs-on: ubuntu-latest | |
| 197 | 197 | defaults: | |
| @@ -278,6 +278,11 @@ jobs: | |||
| 278 | 278 | run: | | |
| 279 | 279 | go env -w CC=s390x-linux-gnu-gcc | |
| 280 | 280 | go env -w CXX=s390x-linux-gnu-g++ | |
| 281 | + | # CGO_ENABLED=0 is only supported on Go 1.27+ for s390x. | |
| 282 | + | if [[ "${{ matrix.go }}" != 1.25.* && "${{ matrix.go }}" != 1.26.* ]]; then | |
| 283 | + | env GOOS=linux GOARCH=s390x CGO_ENABLED=0 go test -c -o=purego-test-nocgo . | |
| 284 | + | env QEMU_LD_PREFIX=/usr/s390x-linux-gnu qemu-s390x ./purego-test-nocgo -test.shuffle=on -test.v -test.count=10 | |
| 285 | + | fi | |
| 281 | 286 | env GOOS=linux GOARCH=s390x CGO_ENABLED=1 go test -c -o=purego-test-cgo . | |
| 282 | 287 | env QEMU_LD_PREFIX=/usr/s390x-linux-gnu qemu-s390x ./purego-test-cgo -test.shuffle=on -test.v -test.count=10 | |
| 283 | 288 | go env -u CC | |
| @@ -326,7 +331,7 @@ jobs: | |||
| 326 | 331 | strategy: | |
| 327 | 332 | fail-fast: false | |
| 328 | 333 | matrix: | |
| 329 | - | go: ['1.25.x', '1.26.x'] | |
| 334 | + | go: ['1.25.x', '1.26.x', '1.27.0-rc.1'] | |
| 330 | 335 | name: Test with Go ${{ matrix.go }} on Android amd64 | |
| 331 | 336 | runs-on: ubuntu-latest | |
| 332 | 337 | defaults: | |
| @@ -10,7 +10,7 @@ jobs: | |||||
| 10 | strategy: | 10 | strategy: | ||
| 11 | matrix: | 11 | matrix: | ||
| 12 | os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest, windows-11-arm] | 12 | os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest, windows-11-arm] | ||
| 13 | - | go: ['1.25.x', '1.26.x'] | 13 | + | go: ['1.25.x', '1.26.x', '1.27.0-rc.1'] |
| 14 | name: Test with Go ${{ matrix.go }} on ${{ matrix.os }} | 14 | name: Test with Go ${{ matrix.go }} on ${{ matrix.os }} | ||
| 15 | runs-on: ${{ matrix.os }} | 15 | runs-on: ${{ matrix.os }} | ||
| 16 | defaults: | 16 | defaults: | ||
| @@ -75,13 +75,13 @@ jobs: | |||||
| 75 | 75 | ||||
| 76 | - name: go build (Linux minor architectures) | 76 | - name: go build (Linux minor architectures) | ||
| 77 | # Test them only on the latest Go to reduce CI time. | 77 | # Test them only on the latest Go to reduce CI time. | ||
| 78 | - | # s390x cannot be tested here as it requires Cgo. | 78 | + | if: startsWith(matrix.go, '1.27.') |
| 79 | - | if: startsWith(matrix.go, '1.26.') | |||
| 80 | run: | | 79 | run: | | ||
| 81 | # Check cross-compiling Linux binaries for minor architectures. | 80 | # Check cross-compiling Linux binaries for minor architectures. | ||
| 82 | env GOOS=linux GOARCH=loong64 go build -v ./... | 81 | env GOOS=linux GOARCH=loong64 go build -v ./... | ||
| 83 | env GOOS=linux GOARCH=ppc64le go build -v ./... | 82 | env GOOS=linux GOARCH=ppc64le go build -v ./... | ||
| 84 | env GOOS=linux GOARCH=riscv64 go build -v ./... | 83 | env GOOS=linux GOARCH=riscv64 go build -v ./... | ||
| 84 | + | env GOOS=linux GOARCH=s390x go build -v ./... | |||
| 85 | 85 | ||||
| 86 | - name: go build (Linux mips, Cgo) | 86 | - name: go build (Linux mips, Cgo) | ||
| 87 | # mips builds only via the Cgo fallback (CGO_ENABLED=1). See #460. | 87 | # mips builds only via the Cgo fallback (CGO_ENABLED=1). See #460. | ||
| @@ -191,7 +191,7 @@ jobs: | |||||
| 191 | minor-arches: | 191 | minor-arches: | ||
| 192 | strategy: | 192 | strategy: | ||
| 193 | matrix: | 193 | matrix: | ||
| 194 | - | go: ['1.25.x', '1.26.x'] | 194 | + | go: ['1.25.x', '1.26.x', '1.27.0-rc.1'] |
| 195 | name: Test with Go ${{ matrix.go }} on Linux minor architectures | 195 | name: Test with Go ${{ matrix.go }} on Linux minor architectures | ||
| 196 | runs-on: ubuntu-latest | 196 | runs-on: ubuntu-latest | ||
| 197 | defaults: | 197 | defaults: | ||
| @@ -278,6 +278,11 @@ jobs: | |||||
| 278 | run: | | 278 | run: | | ||
| 279 | go env -w CC=s390x-linux-gnu-gcc | 279 | go env -w CC=s390x-linux-gnu-gcc | ||
| 280 | go env -w CXX=s390x-linux-gnu-g++ | 280 | go env -w CXX=s390x-linux-gnu-g++ | ||
| 281 | + | # CGO_ENABLED=0 is only supported on Go 1.27+ for s390x. | |||
| 282 | + | if [[ "${{ matrix.go }}" != 1.25.* && "${{ matrix.go }}" != 1.26.* ]]; then | |||
| 283 | + | env GOOS=linux GOARCH=s390x CGO_ENABLED=0 go test -c -o=purego-test-nocgo . | |||
| 284 | + | env QEMU_LD_PREFIX=/usr/s390x-linux-gnu qemu-s390x ./purego-test-nocgo -test.shuffle=on -test.v -test.count=10 | |||
| 285 | + | fi | |||
| 281 | env GOOS=linux GOARCH=s390x CGO_ENABLED=1 go test -c -o=purego-test-cgo . | 286 | env GOOS=linux GOARCH=s390x CGO_ENABLED=1 go test -c -o=purego-test-cgo . | ||
| 282 | env QEMU_LD_PREFIX=/usr/s390x-linux-gnu qemu-s390x ./purego-test-cgo -test.shuffle=on -test.v -test.count=10 | 287 | env QEMU_LD_PREFIX=/usr/s390x-linux-gnu qemu-s390x ./purego-test-cgo -test.shuffle=on -test.v -test.count=10 | ||
| 283 | go env -u CC | 288 | go env -u CC | ||
| @@ -326,7 +331,7 @@ jobs: | |||||
| 326 | strategy: | 331 | strategy: | ||
| 327 | fail-fast: false | 332 | fail-fast: false | ||
| 328 | matrix: | 333 | matrix: | ||
| 329 | - | go: ['1.25.x', '1.26.x'] | 334 | + | go: ['1.25.x', '1.26.x', '1.27.0-rc.1'] |
| 330 | name: Test with Go ${{ matrix.go }} on Android amd64 | 335 | name: Test with Go ${{ matrix.go }} on Android amd64 | ||
| 331 | runs-on: ubuntu-latest | 336 | runs-on: ubuntu-latest | ||
| 332 | defaults: | 337 | defaults: | ||
M README.md +5 -4
| @@ -42,9 +42,9 @@ Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs o | |||
| 42 | 42 | ||
| 43 | 43 | - **Android**: 386<sup>1,3</sup>, arm<sup>1,3</sup> | |
| 44 | 44 | - **FreeBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup> | |
| 45 | - | - **Linux**: 386<sup>3</sup>, arm<sup>3</sup>, loong64<sup>3</sup>, ppc64le<sup>3</sup>, riscv64<sup>3</sup>, s390x<sup>1,3</sup> | |
| 45 | + | - **Linux**: 386<sup>3</sup>, arm<sup>3</sup>, loong64<sup>3</sup>, ppc64le<sup>3</sup>, riscv64<sup>3</sup>, s390x<sup>3, 5</sup> | |
| 46 | 46 | - **NetBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup> | |
| 47 | - | - **Windows**: 386<sup>3,5</sup>, arm<sup>3,5,6</sup> | |
| 47 | + | - **Windows**: 386<sup>3,6</sup>, arm<sup>3,6,7</sup> | |
| 48 | 48 | ||
| 49 | 49 | #### Support Notes | |
| 50 | 50 | ||
| @@ -52,8 +52,9 @@ Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs o | |||
| 52 | 52 | 2. These architectures support passing structs by value as arguments and return values when calling C functions, but not in callbacks created with `NewCallback` | |
| 53 | 53 | 3. These architectures do not support passing structs by value as arguments or return values | |
| 54 | 54 | 4. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=0 | |
| 55 | - | 5. These architectures only support `SyscallN` and `NewCallback` | |
| 56 | - | 6. These architectures are no longer supported as of Go 1.26 | |
| 55 | + | 5. These architectures require CGO_ENABLED=1 to compile in versions before Go 1.27, but will be supported without Cgo in Go 1.27 and later | |
| 56 | + | 6. These architectures only support `SyscallN` and `NewCallback` | |
| 57 | + | 7. These architectures are no longer supported as of Go 1.26 | |
| 57 | 58 | ||
| 58 | 59 | ## Example | |
| 59 | 60 | ||
| @@ -42,9 +42,9 @@ Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs o | |||||
| 42 | 42 | ||||
| 43 | - **Android**: 386<sup>1,3</sup>, arm<sup>1,3</sup> | 43 | - **Android**: 386<sup>1,3</sup>, arm<sup>1,3</sup> | ||
| 44 | - **FreeBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup> | 44 | - **FreeBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup> | ||
| 45 | - | - **Linux**: 386<sup>3</sup>, arm<sup>3</sup>, loong64<sup>3</sup>, ppc64le<sup>3</sup>, riscv64<sup>3</sup>, s390x<sup>1,3</sup> | 45 | + | - **Linux**: 386<sup>3</sup>, arm<sup>3</sup>, loong64<sup>3</sup>, ppc64le<sup>3</sup>, riscv64<sup>3</sup>, s390x<sup>3, 5</sup> |
| 46 | - **NetBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup> | 46 | - **NetBSD**: amd64<sup>3,4</sup>, arm64<sup>3,4</sup> | ||
| 47 | - | - **Windows**: 386<sup>3,5</sup>, arm<sup>3,5,6</sup> | 47 | + | - **Windows**: 386<sup>3,6</sup>, arm<sup>3,6,7</sup> |
| 48 | 48 | ||||
| 49 | #### Support Notes | 49 | #### Support Notes | ||
| 50 | 50 | ||||
| @@ -52,8 +52,9 @@ Tier 2 platforms are supported by PureGo on a best-effort basis. Critical bugs o | |||||
| 52 | 2. These architectures support passing structs by value as arguments and return values when calling C functions, but not in callbacks created with `NewCallback` | 52 | 2. These architectures support passing structs by value as arguments and return values when calling C functions, but not in callbacks created with `NewCallback` | ||
| 53 | 3. These architectures do not support passing structs by value as arguments or return values | 53 | 3. These architectures do not support passing structs by value as arguments or return values | ||
| 54 | 4. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=0 | 54 | 4. These architectures require the special flag `-gcflags="github.com/ebitengine/purego/internal/fakecgo=-std"` to compile with CGO_ENABLED=0 | ||
| 55 | - | 5. These architectures only support `SyscallN` and `NewCallback` | 55 | + | 5. These architectures require CGO_ENABLED=1 to compile in versions before Go 1.27, but will be supported without Cgo in Go 1.27 and later |
| 56 | - | 6. These architectures are no longer supported as of Go 1.26 | 56 | + | 6. These architectures only support `SyscallN` and `NewCallback` |
| 57 | + | 7. These architectures are no longer supported as of Go 1.26 | |||
| 57 | 58 | ||||
| 58 | ## Example | 59 | ## Example | ||
| 59 | 60 | ||||
A internal/fakecgo/trampolines_linux_s390x.s +51 -0
| @@ -0,0 +1,51 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build go1.27 && !cgo | |
| 5 | + | ||
| 6 | + | #include "textflag.h" | |
| 7 | + | ||
| 8 | + | TEXT _cgo_purego_setegid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 9 | + | MOVD ·x_cgo_purego_setegid_call(SB), R1 | |
| 10 | + | MOVD (R1), R1 | |
| 11 | + | BR R1 | |
| 12 | + | ||
| 13 | + | TEXT _cgo_purego_seteuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 14 | + | MOVD ·x_cgo_purego_seteuid_call(SB), R1 | |
| 15 | + | MOVD (R1), R1 | |
| 16 | + | BR R1 | |
| 17 | + | ||
| 18 | + | TEXT _cgo_purego_setgid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 19 | + | MOVD ·x_cgo_purego_setgid_call(SB), R1 | |
| 20 | + | MOVD (R1), R1 | |
| 21 | + | BR R1 | |
| 22 | + | ||
| 23 | + | TEXT _cgo_purego_setregid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 24 | + | MOVD ·x_cgo_purego_setregid_call(SB), R1 | |
| 25 | + | MOVD (R1), R1 | |
| 26 | + | BR R1 | |
| 27 | + | ||
| 28 | + | TEXT _cgo_purego_setresgid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 29 | + | MOVD ·x_cgo_purego_setresgid_call(SB), R1 | |
| 30 | + | MOVD (R1), R1 | |
| 31 | + | BR R1 | |
| 32 | + | ||
| 33 | + | TEXT _cgo_purego_setresuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 34 | + | MOVD ·x_cgo_purego_setresuid_call(SB), R1 | |
| 35 | + | MOVD (R1), R1 | |
| 36 | + | BR R1 | |
| 37 | + | ||
| 38 | + | TEXT _cgo_purego_setreuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 39 | + | MOVD ·x_cgo_purego_setreuid_call(SB), R1 | |
| 40 | + | MOVD (R1), R1 | |
| 41 | + | BR R1 | |
| 42 | + | ||
| 43 | + | TEXT _cgo_purego_setuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 44 | + | MOVD ·x_cgo_purego_setuid_call(SB), R1 | |
| 45 | + | MOVD (R1), R1 | |
| 46 | + | BR R1 | |
| 47 | + | ||
| 48 | + | TEXT _cgo_purego_setgroups_trampoline(SB), NOSPLIT|NOFRAME, $0 | |
| 49 | + | MOVD ·x_cgo_purego_setgroups_call(SB), R1 | |
| 50 | + | MOVD (R1), R1 | |
| 51 | + | BR R1 | |
| @@ -0,0 +1,51 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build go1.27 && !cgo | |||
| 5 | + | ||||
| 6 | + | #include "textflag.h" | |||
| 7 | + | ||||
| 8 | + | TEXT _cgo_purego_setegid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 9 | + | MOVD ·x_cgo_purego_setegid_call(SB), R1 | |||
| 10 | + | MOVD (R1), R1 | |||
| 11 | + | BR R1 | |||
| 12 | + | ||||
| 13 | + | TEXT _cgo_purego_seteuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 14 | + | MOVD ·x_cgo_purego_seteuid_call(SB), R1 | |||
| 15 | + | MOVD (R1), R1 | |||
| 16 | + | BR R1 | |||
| 17 | + | ||||
| 18 | + | TEXT _cgo_purego_setgid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 19 | + | MOVD ·x_cgo_purego_setgid_call(SB), R1 | |||
| 20 | + | MOVD (R1), R1 | |||
| 21 | + | BR R1 | |||
| 22 | + | ||||
| 23 | + | TEXT _cgo_purego_setregid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 24 | + | MOVD ·x_cgo_purego_setregid_call(SB), R1 | |||
| 25 | + | MOVD (R1), R1 | |||
| 26 | + | BR R1 | |||
| 27 | + | ||||
| 28 | + | TEXT _cgo_purego_setresgid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 29 | + | MOVD ·x_cgo_purego_setresgid_call(SB), R1 | |||
| 30 | + | MOVD (R1), R1 | |||
| 31 | + | BR R1 | |||
| 32 | + | ||||
| 33 | + | TEXT _cgo_purego_setresuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 34 | + | MOVD ·x_cgo_purego_setresuid_call(SB), R1 | |||
| 35 | + | MOVD (R1), R1 | |||
| 36 | + | BR R1 | |||
| 37 | + | ||||
| 38 | + | TEXT _cgo_purego_setreuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 39 | + | MOVD ·x_cgo_purego_setreuid_call(SB), R1 | |||
| 40 | + | MOVD (R1), R1 | |||
| 41 | + | BR R1 | |||
| 42 | + | ||||
| 43 | + | TEXT _cgo_purego_setuid_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 44 | + | MOVD ·x_cgo_purego_setuid_call(SB), R1 | |||
| 45 | + | MOVD (R1), R1 | |||
| 46 | + | BR R1 | |||
| 47 | + | ||||
| 48 | + | TEXT _cgo_purego_setgroups_trampoline(SB), NOSPLIT|NOFRAME, $0 | |||
| 49 | + | MOVD ·x_cgo_purego_setgroups_call(SB), R1 | |||
| 50 | + | MOVD (R1), R1 | |||
| 51 | + | BR R1 | |||
A internal/fakecgo/trampolines_s390x.s +158 -0
| @@ -0,0 +1,158 @@ | |||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | + | // SPDX-FileCopyrightText: 2026 The Ebitengine Authors | |
| 3 | + | ||
| 4 | + | //go:build go1.27 && !cgo && linux | |
| 5 | + | ||
| 6 | + | #include "textflag.h" | |
| 7 | + | #include "go_asm.h" | |
| 8 | + | ||
| 9 | + | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |
| 10 | + | // Note that C arguments are passed in R2-R6, which matches Go ABIInternal for the first five arguments. | |
| 11 | + | // R1 is used as a temporary register. | |
| 12 | + | ||
| 13 | + | TEXT x_cgo_init_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |
| 14 | + | MOVD R15, R1 | |
| 15 | + | SUB $192, R15 | |
| 16 | + | MOVD R1, 0(R15) // backchain | |
| 17 | + | MOVD R14, 160(R15) // save R14 | |
| 18 | + | MOVD R9, 168(R15) // save R9 (Go runtime needs this preserved) | |
| 19 | + | ||
| 20 | + | MOVD ·x_cgo_init_call(SB), R1 | |
| 21 | + | MOVD (R1), R1 | |
| 22 | + | BL R1 | |
| 23 | + | ||
| 24 | + | MOVD 168(R15), R9 | |
| 25 | + | MOVD 160(R15), R14 | |
| 26 | + | ADD $192, R15 | |
| 27 | + | BR R14 | |
| 28 | + | ||
| 29 | + | TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |
| 30 | + | MOVD R15, R1 | |
| 31 | + | SUB $176, R15 | |
| 32 | + | MOVD R1, 0(R15) // backchain | |
| 33 | + | MOVD R14, 152(R15) // save R14 | |
| 34 | + | ||
| 35 | + | MOVD ·x_cgo_thread_start_call(SB), R1 | |
| 36 | + | MOVD (R1), R1 | |
| 37 | + | BL R1 | |
| 38 | + | ||
| 39 | + | MOVD 152(R15), R14 | |
| 40 | + | ADD $176, R15 | |
| 41 | + | BR R14 | |
| 42 | + | ||
| 43 | + | TEXT x_cgo_setenv_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |
| 44 | + | MOVD R15, R1 | |
| 45 | + | SUB $176, R15 | |
| 46 | + | MOVD R1, 0(R15) // backchain | |
| 47 | + | MOVD R14, 152(R15) // save R14 | |
| 48 | + | ||
| 49 | + | MOVD ·x_cgo_setenv_call(SB), R1 | |
| 50 | + | MOVD (R1), R1 | |
| 51 | + | BL R1 | |
| 52 | + | ||
| 53 | + | MOVD 152(R15), R14 | |
| 54 | + | ADD $176, R15 | |
| 55 | + | BR R14 | |
| 56 | + | ||
| 57 | + | TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |
| 58 | + | MOVD R15, R1 | |
| 59 | + | SUB $176, R15 | |
| 60 | + | MOVD R1, 0(R15) // backchain | |
| 61 | + | MOVD R14, 152(R15) // save R14 | |
| 62 | + | ||
| 63 | + | MOVD ·x_cgo_unsetenv_call(SB), R1 | |
| 64 | + | MOVD (R1), R1 | |
| 65 | + | BL R1 | |
| 66 | + | ||
| 67 | + | MOVD 152(R15), R14 | |
| 68 | + | ADD $176, R15 | |
| 69 | + | BR R14 | |
| 70 | + | ||
| 71 | + | // These just tail-call into Go functions | |
| 72 | + | TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |
| 73 | + | BR ·x_cgo_notify_runtime_init_done(SB) | |
| 74 | + | ||
| 75 | + | TEXT x_cgo_bindm_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |
| 76 | + | BR ·x_cgo_bindm(SB) | |
| 77 | + | ||
| 78 | + | // setg_trampoline(setg uintptr, g uintptr) - called from Go | |
| 79 | + | TEXT ·setg_trampoline(SB), NOSPLIT|NOFRAME, $0-16 | |
| 80 | + | MOVD 8(R15), R1 // setg function pointer | |
| 81 | + | MOVD 16(R15), R2 // g pointer -> C arg | |
| 82 | + | ||
| 83 | + | MOVD R14, R0 | |
| 84 | + | MOVD R15, R3 | |
| 85 | + | SUB $160, R15 | |
| 86 | + | MOVD R3, 0(R15) | |
| 87 | + | MOVD R0, 112(R15) | |
| 88 | + | MOVD R2, 120(R15) // save newg before call | |
| 89 | + | ||
| 90 | + | BL R1 // call setg_gcc | |
| 91 | + | ||
| 92 | + | // Assign g directly instead of calling runtime·load_g | |
| 93 | + | // setg_gcc has already stored newg into TLS; put it in the g register too. | |
| 94 | + | MOVD 120(R15), g | |
| 95 | + | ||
| 96 | + | MOVD 112(R15), R14 | |
| 97 | + | ADD $160, R15 | |
| 98 | + | BR R14 | |
| 99 | + | ||
| 100 | + | TEXT threadentry_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |
| 101 | + | STMG R6, R15, 48(R15) // C save area | |
| 102 | + | MOVD R15, R1 | |
| 103 | + | SUB $176, R15 | |
| 104 | + | MOVD R1, 0(R15) // backchain | |
| 105 | + | ||
| 106 | + | MOVD ·threadentry_call(SB), R1 | |
| 107 | + | MOVD (R1), R1 | |
| 108 | + | BL R1 | |
| 109 | + | ||
| 110 | + | ADD $176, R15 | |
| 111 | + | LMG 48(R15), R6, R15 | |
| 112 | + | RET | |
| 113 | + | ||
| 114 | + | TEXT ·call5(SB), NOSPLIT|NOFRAME, $0-56 | |
| 115 | + | // Load Go args before modifying R15 | |
| 116 | + | MOVD 8(R15), R1 // fn | |
| 117 | + | MOVD 16(R15), R7 // a1 | |
| 118 | + | MOVD 24(R15), R8 // a2 | |
| 119 | + | MOVD 32(R15), R9 // a3 | |
| 120 | + | MOVD 40(R15), R10 // a4 | |
| 121 | + | MOVD 48(R15), R11 // a5 | |
| 122 | + | ||
| 123 | + | // Save state | |
| 124 | + | MOVD R15, R0 // original R15 | |
| 125 | + | MOVD R12, R6 // Go's R12 | |
| 126 | + | ADD $-128, R15 | |
| 127 | + | ||
| 128 | + | // Set up C frame with backchain | |
| 129 | + | MOVD R0, 0(R15) // backchain -> original R15 | |
| 130 | + | MOVD R0, R3 // R3 = original R15 (can't use R0 as base!) | |
| 131 | + | MOVD 0(R3), R7 // save 0(original R15) | |
| 132 | + | MOVD $0, 0(R3) // terminate backchain | |
| 133 | + | ||
| 134 | + | // Save context | |
| 135 | + | MOVD R14, 8(R15) | |
| 136 | + | MOVD R6, 16(R15) // R12 | |
| 137 | + | MOVD R0, 24(R15) // original R15 | |
| 138 | + | MOVD R7, 32(R15) // saved backchain | |
| 139 | + | ||
| 140 | + | // Set up C args (reload a1 since R7 was clobbered) | |
| 141 | + | MOVD 16(R3), R2 // a1 (use R3 as base, not R0!) | |
| 142 | + | MOVD R8, R3 // a2 | |
| 143 | + | MOVD R9, R4 // a3 | |
| 144 | + | MOVD R10, R5 // a4 | |
| 145 | + | MOVD R11, R6 // a5 | |
| 146 | + | ||
| 147 | + | BL R1 | |
| 148 | + | ||
| 149 | + | // Store result and restore | |
| 150 | + | MOVD 24(R15), R3 // original R15 | |
| 151 | + | MOVD R2, 56(R3) // return value | |
| 152 | + | MOVD 32(R15), R7 | |
| 153 | + | MOVD R7, 0(R3) // restore backchain | |
| 154 | + | ||
| 155 | + | MOVD 8(R15), R14 | |
| 156 | + | MOVD 16(R15), R12 | |
| 157 | + | MOVD 24(R15), R15 | |
| 158 | + | BR R14 | |
| @@ -0,0 +1,158 @@ | |||||
| 1 | + | // SPDX-License-Identifier: Apache-2.0 | |||
| 2 | + | // SPDX-FileCopyrightText: 2026 The Ebitengine Authors | |||
| 3 | + | ||||
| 4 | + | //go:build go1.27 && !cgo && linux | |||
| 5 | + | ||||
| 6 | + | #include "textflag.h" | |||
| 7 | + | #include "go_asm.h" | |||
| 8 | + | ||||
| 9 | + | // these trampolines map the gcc ABI to Go ABI and then calls into the Go equivalent functions. | |||
| 10 | + | // Note that C arguments are passed in R2-R6, which matches Go ABIInternal for the first five arguments. | |||
| 11 | + | // R1 is used as a temporary register. | |||
| 12 | + | ||||
| 13 | + | TEXT x_cgo_init_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |||
| 14 | + | MOVD R15, R1 | |||
| 15 | + | SUB $192, R15 | |||
| 16 | + | MOVD R1, 0(R15) // backchain | |||
| 17 | + | MOVD R14, 160(R15) // save R14 | |||
| 18 | + | MOVD R9, 168(R15) // save R9 (Go runtime needs this preserved) | |||
| 19 | + | ||||
| 20 | + | MOVD ·x_cgo_init_call(SB), R1 | |||
| 21 | + | MOVD (R1), R1 | |||
| 22 | + | BL R1 | |||
| 23 | + | ||||
| 24 | + | MOVD 168(R15), R9 | |||
| 25 | + | MOVD 160(R15), R14 | |||
| 26 | + | ADD $192, R15 | |||
| 27 | + | BR R14 | |||
| 28 | + | ||||
| 29 | + | TEXT x_cgo_thread_start_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |||
| 30 | + | MOVD R15, R1 | |||
| 31 | + | SUB $176, R15 | |||
| 32 | + | MOVD R1, 0(R15) // backchain | |||
| 33 | + | MOVD R14, 152(R15) // save R14 | |||
| 34 | + | ||||
| 35 | + | MOVD ·x_cgo_thread_start_call(SB), R1 | |||
| 36 | + | MOVD (R1), R1 | |||
| 37 | + | BL R1 | |||
| 38 | + | ||||
| 39 | + | MOVD 152(R15), R14 | |||
| 40 | + | ADD $176, R15 | |||
| 41 | + | BR R14 | |||
| 42 | + | ||||
| 43 | + | TEXT x_cgo_setenv_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |||
| 44 | + | MOVD R15, R1 | |||
| 45 | + | SUB $176, R15 | |||
| 46 | + | MOVD R1, 0(R15) // backchain | |||
| 47 | + | MOVD R14, 152(R15) // save R14 | |||
| 48 | + | ||||
| 49 | + | MOVD ·x_cgo_setenv_call(SB), R1 | |||
| 50 | + | MOVD (R1), R1 | |||
| 51 | + | BL R1 | |||
| 52 | + | ||||
| 53 | + | MOVD 152(R15), R14 | |||
| 54 | + | ADD $176, R15 | |||
| 55 | + | BR R14 | |||
| 56 | + | ||||
| 57 | + | TEXT x_cgo_unsetenv_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |||
| 58 | + | MOVD R15, R1 | |||
| 59 | + | SUB $176, R15 | |||
| 60 | + | MOVD R1, 0(R15) // backchain | |||
| 61 | + | MOVD R14, 152(R15) // save R14 | |||
| 62 | + | ||||
| 63 | + | MOVD ·x_cgo_unsetenv_call(SB), R1 | |||
| 64 | + | MOVD (R1), R1 | |||
| 65 | + | BL R1 | |||
| 66 | + | ||||
| 67 | + | MOVD 152(R15), R14 | |||
| 68 | + | ADD $176, R15 | |||
| 69 | + | BR R14 | |||
| 70 | + | ||||
| 71 | + | // These just tail-call into Go functions | |||
| 72 | + | TEXT x_cgo_notify_runtime_init_done_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |||
| 73 | + | BR ·x_cgo_notify_runtime_init_done(SB) | |||
| 74 | + | ||||
| 75 | + | TEXT x_cgo_bindm_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |||
| 76 | + | BR ·x_cgo_bindm(SB) | |||
| 77 | + | ||||
| 78 | + | // setg_trampoline(setg uintptr, g uintptr) - called from Go | |||
| 79 | + | TEXT ·setg_trampoline(SB), NOSPLIT|NOFRAME, $0-16 | |||
| 80 | + | MOVD 8(R15), R1 // setg function pointer | |||
| 81 | + | MOVD 16(R15), R2 // g pointer -> C arg | |||
| 82 | + | ||||
| 83 | + | MOVD R14, R0 | |||
| 84 | + | MOVD R15, R3 | |||
| 85 | + | SUB $160, R15 | |||
| 86 | + | MOVD R3, 0(R15) | |||
| 87 | + | MOVD R0, 112(R15) | |||
| 88 | + | MOVD R2, 120(R15) // save newg before call | |||
| 89 | + | ||||
| 90 | + | BL R1 // call setg_gcc | |||
| 91 | + | ||||
| 92 | + | // Assign g directly instead of calling runtime·load_g | |||
| 93 | + | // setg_gcc has already stored newg into TLS; put it in the g register too. | |||
| 94 | + | MOVD 120(R15), g | |||
| 95 | + | ||||
| 96 | + | MOVD 112(R15), R14 | |||
| 97 | + | ADD $160, R15 | |||
| 98 | + | BR R14 | |||
| 99 | + | ||||
| 100 | + | TEXT threadentry_trampoline(SB), NOSPLIT|NOFRAME, $0-0 | |||
| 101 | + | STMG R6, R15, 48(R15) // C save area | |||
| 102 | + | MOVD R15, R1 | |||
| 103 | + | SUB $176, R15 | |||
| 104 | + | MOVD R1, 0(R15) // backchain | |||
| 105 | + | ||||
| 106 | + | MOVD ·threadentry_call(SB), R1 | |||
| 107 | + | MOVD (R1), R1 | |||
| 108 | + | BL R1 | |||
| 109 | + | ||||
| 110 | + | ADD $176, R15 | |||
| 111 | + | LMG 48(R15), R6, R15 | |||
| 112 | + | RET | |||
| 113 | + | ||||
| 114 | + | TEXT ·call5(SB), NOSPLIT|NOFRAME, $0-56 | |||
| 115 | + | // Load Go args before modifying R15 | |||
| 116 | + | MOVD 8(R15), R1 // fn | |||
| 117 | + | MOVD 16(R15), R7 // a1 | |||
| 118 | + | MOVD 24(R15), R8 // a2 | |||
| 119 | + | MOVD 32(R15), R9 // a3 | |||
| 120 | + | MOVD 40(R15), R10 // a4 | |||
| 121 | + | MOVD 48(R15), R11 // a5 | |||
| 122 | + | ||||
| 123 | + | // Save state | |||
| 124 | + | MOVD R15, R0 // original R15 | |||
| 125 | + | MOVD R12, R6 // Go's R12 | |||
| 126 | + | ADD $-128, R15 | |||
| 127 | + | ||||
| 128 | + | // Set up C frame with backchain | |||
| 129 | + | MOVD R0, 0(R15) // backchain -> original R15 | |||
| 130 | + | MOVD R0, R3 // R3 = original R15 (can't use R0 as base!) | |||
| 131 | + | MOVD 0(R3), R7 // save 0(original R15) | |||
| 132 | + | MOVD $0, 0(R3) // terminate backchain | |||
| 133 | + | ||||
| 134 | + | // Save context | |||
| 135 | + | MOVD R14, 8(R15) | |||
| 136 | + | MOVD R6, 16(R15) // R12 | |||
| 137 | + | MOVD R0, 24(R15) // original R15 | |||
| 138 | + | MOVD R7, 32(R15) // saved backchain | |||
| 139 | + | ||||
| 140 | + | // Set up C args (reload a1 since R7 was clobbered) | |||
| 141 | + | MOVD 16(R3), R2 // a1 (use R3 as base, not R0!) | |||
| 142 | + | MOVD R8, R3 // a2 | |||
| 143 | + | MOVD R9, R4 // a3 | |||
| 144 | + | MOVD R10, R5 // a4 | |||
| 145 | + | MOVD R11, R6 // a5 | |||
| 146 | + | ||||
| 147 | + | BL R1 | |||
| 148 | + | ||||
| 149 | + | // Store result and restore | |||
| 150 | + | MOVD 24(R15), R3 // original R15 | |||
| 151 | + | MOVD R2, 56(R3) // return value | |||
| 152 | + | MOVD 32(R15), R7 | |||
| 153 | + | MOVD R7, 0(R3) // restore backchain | |||
| 154 | + | ||||
| 155 | + | MOVD 8(R15), R14 | |||
| 156 | + | MOVD 16(R15), R12 | |||
| 157 | + | MOVD 24(R15), R15 | |||
| 158 | + | BR R14 | |||
M sys_unix_s390x.s +10 -8
| @@ -18,22 +18,24 @@ | |||
| 18 | 18 | // | |
| 19 | 19 | // S390X uses R2-R6 for integer arguments (5 registers) and F0,F2,F4,F6 for floats (4 registers). | |
| 20 | 20 | // | |
| 21 | - | // Our frame layout (total 264 bytes, 8-byte aligned): | |
| 21 | + | // Our frame layout (total 200 bytes, 8-byte aligned). To stay under the | |
| 22 | + | // 800-byte nosplit limit on Go 1.27 the callbackArgs struct is stored in the | |
| 23 | + | // caller's free linkage slot (old_R15+16..48) instead of our own frame: | |
| 22 | 24 | // 0(R15) - back chain | |
| 23 | 25 | // 48(R15) - saved R6-R15 (done by STMG) | |
| 24 | - | // 160(R15) - callbackArgs struct (32 bytes: index, args, result, stackArgs) | |
| 25 | - | // 192(R15) - args array start | |
| 26 | + | // 128(R15) - args array start | |
| 26 | 27 | // | |
| 27 | 28 | // Args array layout: | |
| 28 | 29 | // - floats F0,F2,F4,F6 (32 bytes) | |
| 29 | 30 | // - ints R2-R6 (40 bytes) | |
| 30 | - | // Total args array: 72 bytes, ends at 264 | |
| 31 | + | // Total args array: 72 bytes, ends at 200 | |
| 31 | 32 | // | |
| 33 | + | // callbackArgs struct lives at old_R15+16 (32 bytes: index, args, result, stackArgs) | |
| 32 | 34 | // Stack args in caller's frame start at old_R15+160 | |
| 33 | 35 | ||
| 34 | - | #define FRAME_SIZE 264 | |
| 35 | - | #define CB_ARGS 160 | |
| 36 | - | #define ARGS_ARRAY 192 | |
| 36 | + | #define FRAME_SIZE 200 | |
| 37 | + | #define ARGS_ARRAY 128 | |
| 38 | + | #define CB_ARGS (FRAME_SIZE+16) | |
| 37 | 39 | #define FLOAT_OFF 0 | |
| 38 | 40 | #define INT_OFF 32 | |
| 39 | 41 | ||
| @@ -71,7 +73,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||
| 71 | 73 | MOVD R5, (ARGS_ARRAY+INT_OFF+3*8)(R15) | |
| 72 | 74 | ||
| 73 | 75 | // R6 (5th int arg) was saved at 48(old_R15) by STMG | |
| 74 | - | // old_R15 = current R15 + FRAME_SIZE, so R6 is at 48+FRAME_SIZE(R15) = 312(R15) | |
| 76 | + | // old_R15 = current R15 + FRAME_SIZE | |
| 75 | 77 | MOVD (48+FRAME_SIZE)(R15), R1 | |
| 76 | 78 | MOVD R1, (ARGS_ARRAY+INT_OFF+4*8)(R15) | |
| 77 | 79 | ||
| @@ -18,22 +18,24 @@ | |||||
| 18 | // | 18 | // | ||
| 19 | // S390X uses R2-R6 for integer arguments (5 registers) and F0,F2,F4,F6 for floats (4 registers). | 19 | // S390X uses R2-R6 for integer arguments (5 registers) and F0,F2,F4,F6 for floats (4 registers). | ||
| 20 | // | 20 | // | ||
| 21 | - | // Our frame layout (total 264 bytes, 8-byte aligned): | 21 | + | // Our frame layout (total 200 bytes, 8-byte aligned). To stay under the |
| 22 | + | // 800-byte nosplit limit on Go 1.27 the callbackArgs struct is stored in the | |||
| 23 | + | // caller's free linkage slot (old_R15+16..48) instead of our own frame: | |||
| 22 | // 0(R15) - back chain | 24 | // 0(R15) - back chain | ||
| 23 | // 48(R15) - saved R6-R15 (done by STMG) | 25 | // 48(R15) - saved R6-R15 (done by STMG) | ||
| 24 | - | // 160(R15) - callbackArgs struct (32 bytes: index, args, result, stackArgs) | 26 | + | // 128(R15) - args array start |
| 25 | - | // 192(R15) - args array start | |||
| 26 | // | 27 | // | ||
| 27 | // Args array layout: | 28 | // Args array layout: | ||
| 28 | // - floats F0,F2,F4,F6 (32 bytes) | 29 | // - floats F0,F2,F4,F6 (32 bytes) | ||
| 29 | // - ints R2-R6 (40 bytes) | 30 | // - ints R2-R6 (40 bytes) | ||
| 30 | - | // Total args array: 72 bytes, ends at 264 | 31 | + | // Total args array: 72 bytes, ends at 200 |
| 31 | // | 32 | // | ||
| 33 | + | // callbackArgs struct lives at old_R15+16 (32 bytes: index, args, result, stackArgs) | |||
| 32 | // Stack args in caller's frame start at old_R15+160 | 34 | // Stack args in caller's frame start at old_R15+160 | ||
| 33 | 35 | ||||
| 34 | - | #define FRAME_SIZE 264 | 36 | + | #define FRAME_SIZE 200 |
| 35 | - | #define CB_ARGS 160 | 37 | + | #define ARGS_ARRAY 128 |
| 36 | - | #define ARGS_ARRAY 192 | 38 | + | #define CB_ARGS (FRAME_SIZE+16) |
| 37 | #define FLOAT_OFF 0 | 39 | #define FLOAT_OFF 0 | ||
| 38 | #define INT_OFF 32 | 40 | #define INT_OFF 32 | ||
| 39 | 41 | ||||
| @@ -71,7 +73,7 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0 | |||||
| 71 | MOVD R5, (ARGS_ARRAY+INT_OFF+3*8)(R15) | 73 | MOVD R5, (ARGS_ARRAY+INT_OFF+3*8)(R15) | ||
| 72 | 74 | ||||
| 73 | // R6 (5th int arg) was saved at 48(old_R15) by STMG | 75 | // R6 (5th int arg) was saved at 48(old_R15) by STMG | ||
| 74 | - | // old_R15 = current R15 + FRAME_SIZE, so R6 is at 48+FRAME_SIZE(R15) = 312(R15) | 76 | + | // old_R15 = current R15 + FRAME_SIZE |
| 75 | MOVD (48+FRAME_SIZE)(R15), R1 | 77 | MOVD (48+FRAME_SIZE)(R15), R1 | ||
| 76 | MOVD R1, (ARGS_ARRAY+INT_OFF+4*8)(R15) | 78 | MOVD R1, (ARGS_ARRAY+INT_OFF+4*8)(R15) | ||
| 77 | 79 | ||||
M syscall_unix.go +1 -2
| @@ -1,8 +1,7 @@ | |||
| 1 | 1 | // SPDX-License-Identifier: Apache-2.0 | |
| 2 | 2 | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | |
| 3 | 3 | ||
| 4 | - | // TODO: remove s390x cgo dependency once golang/go#77449 is resolved | |
| 5 | - | //go:build darwin || freebsd || (linux && (386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64 || (cgo && s390x))) || netbsd | |
| 4 | + | //go:build darwin || freebsd || (linux && (386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64 || (s390x && (cgo || go1.27)))) || netbsd | |
| 6 | 5 | ||
| 7 | 6 | package purego | |
| 8 | 7 | ||
| @@ -1,8 +1,7 @@ | |||||
| 1 | // SPDX-License-Identifier: Apache-2.0 | 1 | // SPDX-License-Identifier: Apache-2.0 | ||
| 2 | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | 2 | // SPDX-FileCopyrightText: 2022 The Ebitengine Authors | ||
| 3 | 3 | ||||
| 4 | - | // TODO: remove s390x cgo dependency once golang/go#77449 is resolved | 4 | + | //go:build darwin || freebsd || (linux && (386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64 || (s390x && (cgo || go1.27)))) || netbsd |
| 5 | - | //go:build darwin || freebsd || (linux && (386 || amd64 || arm || arm64 || loong64 || ppc64le || riscv64 || (cgo && s390x))) || netbsd | |||
| 6 | 5 | ||||
| 7 | package purego | 6 | package purego | ||
| 8 | 7 | ||||