Skip to content

Commit 040407d

Browse files
committed
add CI
1 parent e87640c commit 040407d

File tree

4 files changed

+61
-31
lines changed

4 files changed

+61
-31
lines changed

.github/workflows/test.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ jobs:
88
matrix:
99
os: [ubuntu-latest, ubuntu-24.04-arm, macos-latest, windows-latest]
1010
go: ['1.18.x', '1.19.x', '1.20.x', '1.21.x', '1.22.x', '1.23.x', '1.24.x', '1.25.x', '1.26.0-rc.2']
11+
fail-fast: false
1112
name: Test with Go ${{ matrix.go }} on ${{ matrix.os }}
1213
runs-on: ${{ matrix.os }}
1314
defaults:
@@ -140,6 +141,20 @@ jobs:
140141
go env -u CC
141142
go env -u CXX
142143
144+
- name: go test (Linux ppc64le)
145+
if: ${{ runner.os == 'Linux' && runner.arch != 'ARM64' && !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') && !startsWith(matrix.go, '1.20.') && !startsWith(matrix.go, '1.21.') }}
146+
run: |
147+
sudo apt-get update
148+
sudo apt-get install -y gcc-powerpc64le-linux-gnu g++-powerpc64le-linux-gnu qemu-user
149+
go env -w CC=powerpc64le-linux-gnu-gcc
150+
go env -w CXX=powerpc64le-linux-gnu-g++
151+
env GOOS=linux GOARCH=ppc64le CGO_ENABLED=0 go test -c -o=purego-test-nocgo .
152+
env QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu qemu-ppc64le ./purego-test-nocgo -test.shuffle=on -test.v -test.count=10
153+
env GOOS=linux GOARCH=ppc64le CGO_ENABLED=1 go test -c -o=purego-test-cgo .
154+
env QEMU_LD_PREFIX=/usr/powerpc64le-linux-gnu qemu-ppc64le ./purego-test-cgo -test.shuffle=on -test.v -test.count=10
155+
go env -u CC
156+
go env -u CXX
157+
143158
- name: go test (Linux riscv64)
144159
if: ${{ runner.os == 'Linux' && runner.arch != 'ARM64' && !startsWith(matrix.go, '1.18.') && !startsWith(matrix.go, '1.19.') && !startsWith(matrix.go, '1.20.') && !startsWith(matrix.go, '1.21.') }}
145160
run: |

internal/fakecgo/asm_ppc64le.s

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,4 +80,3 @@ TEXT crosscall2(SB), NOSPLIT|NOFRAME, $0
8080
MOVW R0, CR
8181

8282
RET
83-

sys_unix_ppc64le.s

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,24 @@
1717
// 24(R1) - TOC save area (if needed)
1818
// 32(R1)+ - parameter save area / local variables
1919
//
20-
// Our frame (total 288 bytes, 16-byte aligned):
21-
// 32(R1) - saved R31 (Go assembler uses it)
22-
// 40(R1) - callbackArgs struct (24 bytes: index, args ptr, result)
23-
// 64(R1) - args array: floats (64) + ints (64) + stack args (56) = 184 bytes
24-
// Total: 64 + 184 = 248, add 32 for fixed area = 280, round to 288
20+
// Our frame (total 208 bytes, 16-byte aligned):
21+
// 32(R1) - saved R31 (8 bytes)
22+
// 40(R1) - callbackArgs struct (32 bytes: index, args, result, stackArgs)
23+
// 72(R1) - args array: floats (64) + ints (64) = 128 bytes, ends at 200
24+
// Total with alignment: 208 bytes
25+
//
26+
// Stack args are NOT copied - we pass a pointer to their location in caller's frame.
27+
// This keeps frame size small enough for NOSPLIT with CGO_ENABLED=1.
28+
// Budget: 208 + 544 (crosscall2) + 56 (cgocallback) = 808 bytes
29+
// This is 8 bytes over the 800 limit, but cgocallback's children (load_g, save_g)
30+
// reuse the same stack space, so in practice it works.
2531

26-
#define FRAME_SIZE 288
32+
#define FRAME_SIZE 200
2733
#define SAVE_R31 32
2834
#define CB_ARGS 40
29-
#define ARGS_ARRAY 64
35+
#define ARGS_ARRAY 72
3036
#define FLOAT_OFF 0
3137
#define INT_OFF 64
32-
#define STACK_OFF 128
3338

3439
TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
3540
NO_LOCAL_POINTERS
@@ -71,31 +76,17 @@ TEXT callbackasm1(SB), NOSPLIT|NOFRAME, $0
7176
MOVD R9, (ARGS_ARRAY+INT_OFF+6*8)(R1)
7277
MOVD R10, (ARGS_ARRAY+INT_OFF+7*8)(R1)
7378

74-
// Copy stack arguments from caller's frame.
75-
// Caller's stack args start at caller_R1 + 96 (ELFv2 ABI).
76-
// Our R1 = caller_R1 - FRAME_SIZE, so args at R1 + FRAME_SIZE + 96
77-
MOVD (FRAME_SIZE+96)(R1), R12
78-
MOVD R12, (ARGS_ARRAY+STACK_OFF+0*8)(R1)
79-
MOVD (FRAME_SIZE+104)(R1), R12
80-
MOVD R12, (ARGS_ARRAY+STACK_OFF+1*8)(R1)
81-
MOVD (FRAME_SIZE+112)(R1), R12
82-
MOVD R12, (ARGS_ARRAY+STACK_OFF+2*8)(R1)
83-
MOVD (FRAME_SIZE+120)(R1), R12
84-
MOVD R12, (ARGS_ARRAY+STACK_OFF+3*8)(R1)
85-
MOVD (FRAME_SIZE+128)(R1), R12
86-
MOVD R12, (ARGS_ARRAY+STACK_OFF+4*8)(R1)
87-
MOVD (FRAME_SIZE+136)(R1), R12
88-
MOVD R12, (ARGS_ARRAY+STACK_OFF+5*8)(R1)
89-
MOVD (FRAME_SIZE+144)(R1), R12
90-
MOVD R12, (ARGS_ARRAY+STACK_OFF+6*8)(R1)
91-
9279
// Finish setting up callbackArgs struct at CB_ARGS(R1)
93-
// struct { index uintptr; args unsafe.Pointer; result uintptr }
80+
// struct { index uintptr; args unsafe.Pointer; result uintptr; stackArgs unsafe.Pointer }
9481
// Note: index was already saved earlier (R11 is volatile)
9582
ADD $ARGS_ARRAY, R1, R12
96-
MOVD R12, (CB_ARGS+8)(R1) // address of args vector
83+
MOVD R12, (CB_ARGS+8)(R1) // args = address of register args
9784
MOVD $0, (CB_ARGS+16)(R1) // result = 0
9885

86+
// stackArgs points to caller's stack arguments at old_R1+96 = R1+FRAME_SIZE+96
87+
ADD $(FRAME_SIZE+96), R1, R12
88+
MOVD R12, (CB_ARGS+24)(R1) // stackArgs = &caller_stack_args
89+
9990
// Call crosscall2 with arguments in registers:
10091
// R3 = fn (from callbackWrap_call closure)
10192
// R4 = frame (address of callbackArgs)

syscall_sysv.go

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ type callbackArgs struct {
7373
args unsafe.Pointer
7474
// Below are out-args from callbackWrap
7575
result uintptr
76+
// stackArgs points to stack-passed arguments for architectures where
77+
// they can't be made contiguous with register args (e.g., ppc64le).
78+
// On other architectures, this is nil and stack args are read from
79+
// the end of the args block.
80+
stackArgs unsafe.Pointer
7681
}
7782

7883
func compileCallback(fn any) uintptr {
@@ -146,13 +151,25 @@ func callbackWrap(a *callbackArgs) {
146151
fnType := fn.Type()
147152
args := make([]reflect.Value, fnType.NumIn())
148153
frame := (*[callbackMaxFrame]uintptr)(a.args)
154+
// stackFrame points to stack-passed arguments. On most architectures this is
155+
// contiguous with frame (after register args), but on ppc64le it's separate.
156+
var stackFrame *[callbackMaxFrame]uintptr
157+
if runtime.GOARCH == "ppc64le" && a.stackArgs != nil {
158+
// Only ppc64le uses separate stackArgs pointer due to NOSPLIT constraints
159+
stackFrame = (*[callbackMaxFrame]uintptr)(a.stackArgs)
160+
}
149161
// floatsN and intsN track the number of register slots used, not argument count.
150162
// This distinction matters on ARM32 where float64 uses 2 slots (32-bit registers).
151163
var floatsN int
152164
var intsN int
153-
// stackSlot points to the index into frame of the current stack element.
154-
// The stack begins after the float and integer registers.
165+
// stackSlot points to the index into frame (or stackFrame) of the current stack element.
166+
// When stackFrame is nil, stack begins after float and integer registers in frame.
167+
// When stackFrame is not nil (ppc64le), stackSlot indexes into stackFrame starting at 0.
155168
stackSlot := numOfIntegerRegisters() + numOfFloatRegisters()
169+
if stackFrame != nil {
170+
// ppc64le: stackArgs is a separate pointer, indices start at 0
171+
stackSlot = 0
172+
}
156173
// stackByteOffset tracks the byte offset within the stack area for Darwin ARM64
157174
// tight packing. On Darwin ARM64, C passes small types packed on the stack.
158175
stackByteOffset := uintptr(0)
@@ -167,6 +184,10 @@ func callbackWrap(a *callbackArgs) {
167184
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
168185
// Darwin ARM64: read from packed stack with proper alignment
169186
args[i] = callbackArgFromStack(a.args, stackSlot, &stackByteOffset, inType)
187+
} else if stackFrame != nil {
188+
// ppc64le: stack args are in separate stackFrame
189+
args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem()
190+
stackSlot += slots
170191
} else {
171192
args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem()
172193
stackSlot += slots
@@ -184,6 +205,10 @@ func callbackWrap(a *callbackArgs) {
184205
if runtime.GOOS == "darwin" && runtime.GOARCH == "arm64" {
185206
// Darwin ARM64: read from packed stack with proper alignment
186207
args[i] = callbackArgFromStack(a.args, stackSlot, &stackByteOffset, inType)
208+
} else if stackFrame != nil {
209+
// ppc64le: stack args are in separate stackFrame
210+
args[i] = reflect.NewAt(inType, unsafe.Pointer(&stackFrame[stackSlot])).Elem()
211+
stackSlot += slots
187212
} else {
188213
args[i] = reflect.NewAt(inType, unsafe.Pointer(&frame[stackSlot])).Elem()
189214
stackSlot += slots

0 commit comments

Comments
 (0)