authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-08-01 19:12:36+02:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-08-01 19:12:36+02:00
logdf5bbb5b95137384384a758145f6eedcca51867c
tree37b34ab6785ddd58152da86a2d56f9f37236bd06
parentf212e3716b7f687d552fdad5f15cd1f736039910
parentbcf65eb51085c0fa1a9380172cdea55811d9fe97
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Merge branch 'sparc-fixes'


8 files changed, 49 insertions(+), 57 deletions(-)

lib/std/Io/RwLock.zig-2
......@@ -284,8 +284,6 @@ test "concurrent access" {
284284}
285285
286286test "lock canceling" {
287 if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347
288
289287 const io = testing.io;
290288
291289 var rl: Io.RwLock = .init;
lib/std/Io/Threaded/test.zig-2
......@@ -149,8 +149,6 @@ test "async with array return type" {
149149}
150150
151151test "cancel blocked read from pipe" {
152 if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347
153
154152 const global = struct {
155153 fn readFromPipe(io: Io, pipe: Io.File) !void {
156154 var buf: [1]u8 = undefined;
lib/std/Io/net/test.zig-2
......@@ -356,8 +356,6 @@ test "decompress compressed DNS name" {
356356}
357357
358358test "cancel accept" {
359 if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347
360
361359 const io = testing.io;
362360 const localhost: net.IpAddress = .{ .ip4 = .loopback(0) };
363361
lib/std/Io/test.zig-4
......@@ -232,8 +232,6 @@ fn count(a: usize, b: usize, result: *usize) void {
232232}
233233
234234test "Group.cancel" {
235 if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347
236
237235 const global = struct {
238236 fn sleep(io: Io, result: *usize) Io.Cancelable!void {
239237 defer result.* = 1;
......@@ -326,8 +324,6 @@ test "Group materializes error.Cancel" {
326324}
327325
328326test "Group task receives cancelation unknowingly" {
329 if (builtin.cpu.arch.isSPARC() and builtin.os.tag == .linux) return error.SkipZigTest; // https://codeberg.org/ziglang/zig/issues/35347
330
331327 const S = struct {
332328 io: Io,
333329 err: ?Io.Cancelable!void,
lib/std/Thread.zig+32-37
......@@ -1146,6 +1146,13 @@ const LinuxThreadImpl = struct {
11461146 parent_tid: i32 = undefined,
11471147 mapped: []align(std.heap.page_size_min) u8,
11481148
1149 // On SPARC, the kernel needs to be able to restore the current register window from the
1150 // stack when returning from a syscall. That presents a bit of a problem in `freeAndExit`
1151 // since we're deallocating the stack! The good news is that, since we do not care about
1152 // the contents of the incoming and local registers at that point, we can just tell the
1153 // kernel that our stack is this undefined global buffer.
1154 var sparc_exit_stack: [192]u8 align(16) = undefined;
1155
11491156 /// Calls `munmap(mapped.ptr, mapped.len)` then `exit(1)` without touching the stack (which lives in `mapped.ptr`).
11501157 /// Ported over from musl libc's pthread detached implementation:
11511158 /// https://github.com/ifduyue/musl/search?q=__unmapself
......@@ -1365,51 +1372,39 @@ const LinuxThreadImpl = struct {
13651372 [len] "{r5}" (self.mapped.len),
13661373 ),
13671374 .sparc => asm volatile (
1368 \\ # See sparc64 comments below.
1369 \\ 1:
1370 \\ cmp %%fp, 0
1371 \\ beq 2f
1372 \\ nop
1373 \\ ba 1b
1374 \\ restore
1375 \\ 2:
1376 \\ mov %%g1, %%o0 // ptr
1377 \\ mov %%g2, %%o1 // len
1378 \\ mov 73, %%g1 // SYS_munmap
1379 \\ t 0x3 // ST_FLUSH_WINDOWS
1380 \\ t 0x10
1381 \\ mov 1, %%g1 // SYS_exit
1382 \\ mov 0, %%o0
1383 \\ t 0x10
1375 \\ // See sparc64 comments below.
1376 \\ t 0x3 // ST_FLUSH_WINDOWS
1377 \\ mov %%g3, %%sp
1378 \\ mov %%g1, %%o0
1379 \\ mov %%g2, %%o1
1380 \\ mov 73, %%g1 // SYS_munmap
1381 \\ t 0x10
1382 \\ mov 1, %%g1 // SYS_exit
1383 \\ mov 0, %%o0
1384 \\ t 0x10
13841385 :
13851386 : [ptr] "{g1}" (@intFromPtr(self.mapped.ptr)),
13861387 [len] "{g2}" (self.mapped.len),
1388 [stack] "{g3}" (&sparc_exit_stack),
13871389 : .{ .memory = true }),
13881390 .sparc64 => asm volatile (
1389 \\ # SPARCs really don't like it when active stack frames
1390 \\ # is unmapped (it will result in a segfault), so we
1391 \\ # force-deactivate it by running `restore` until
1392 \\ # all frames are cleared.
1393 \\ 1:
1394 \\ cmp %%fp, 0
1395 \\ beq 2f
1396 \\ nop
1397 \\ ba 1b
1398 \\ restore
1399 \\ 2:
1400 \\ mov %%g1, %%o0 // ptr
1401 \\ mov %%g2, %%o1 // len
1402 \\ mov 73, %%g1 // SYS_munmap
1403 \\ # Flush register window contents to prevent background
1404 \\ # memory access before unmapping the stack.
1405 \\ flushw
1406 \\ t 0x6d
1407 \\ mov 1, %%g1 // SYS_exit
1408 \\ mov 0, %%o0
1409 \\ t 0x6d
1391 \\ // Ensure that the kernel only has to flush the current register window.
1392 \\ flushw
1393 \\ // Set up a fake stack for the syscall to restore l/i registers from. Local
1394 \\ // and incoming registers must be treated as effectively garbage past this
1395 \\ // instruction!
1396 \\ sub %%g3, 2047, %%sp
1397 \\ mov %%g1, %%o0
1398 \\ mov %%g2, %%o1
1399 \\ mov 73, %%g1 // SYS_munmap
1400 \\ t 0x6d
1401 \\ mov 1, %%g1 // SYS_exit
1402 \\ mov 0, %%o0
1403 \\ t 0x6d
14101404 :
14111405 : [ptr] "{g1}" (@intFromPtr(self.mapped.ptr)),
14121406 [len] "{g2}" (self.mapped.len),
1407 [stack] "{g3}" (&sparc_exit_stack),
14131408 : .{ .memory = true }),
14141409 .loongarch32, .loongarch64 => asm volatile (
14151410 \\ ori $a7, $zero, 215 # SYS_munmap
lib/std/heap/PageAllocator.zig+1
......@@ -24,6 +24,7 @@ pub const vtable: Allocator.VTable = .{
2424/// that don't provide a hint (for security reasons, but it serves our needs
2525/// too).
2626const enable_hints = switch (builtin.target.os.tag) {
27 .linux => !builtin.target.cpu.arch.isSPARC(), // https://bugzilla.kernel.org/show_bug.cgi?id=221820
2728 .openbsd => false,
2829 else => true,
2930};
lib/std/os/linux/sparc.zig+8-5
......@@ -260,13 +260,16 @@ pub fn clone() callconv(.naked) u32 {
260260
261261pub const restore = restore_rt;
262262
263// Need to use C ABI here instead of naked
264// to prevent an infinite loop when calling rt_sigreturn.
265pub fn restore_rt() callconv(.c) void {
266 return asm volatile ("t 0x10"
263pub fn restore_rt() callconv(.naked) noreturn {
264 asm volatile (
265 \\ nop
266 \\ nop
267 );
268 asm volatile (
269 \\ t 0x10
267270 :
268271 : [number] "{g1}" (@backingInt(SYS.rt_sigreturn)),
269 : .{ .memory = true, .xcc = true, .o0 = true, .o1 = true, .o2 = true, .o3 = true, .o4 = true, .o5 = true, .o7 = true });
272 );
270273}
271274
272275pub const VDSO = struct {
lib/std/os/linux/sparc64.zig+8-5
......@@ -259,13 +259,16 @@ pub fn clone() callconv(.naked) u64 {
259259
260260pub const restore = restore_rt;
261261
262// Need to use C ABI here instead of naked
263// to prevent an infinite loop when calling rt_sigreturn.
264pub fn restore_rt() callconv(.c) void {
265 return asm volatile ("t 0x6d"
262pub fn restore_rt() callconv(.naked) noreturn {
263 asm volatile (
264 \\ nop
265 \\ nop
266 );
267 asm volatile (
268 \\ t 0x6d
266269 :
267270 : [number] "{g1}" (@backingInt(SYS.rt_sigreturn)),
268 : .{ .memory = true, .xcc = true, .o0 = true, .o1 = true, .o2 = true, .o3 = true, .o4 = true, .o5 = true, .o7 = true });
271 );
269272}
270273
271274pub const VDSO = struct {