From 2eca0e42e59fafae839b507148e8a3679d172079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 13:50:39 +0200 Subject: [PATCH 01/10] std.debug: FP-based unwinding is impossible on avr, csky, msp430, and xcore The ABIs do not define a frame pointer register, nor do they define a guaranteed and fixed area on the stack where one might find saved registers such as a frame pointer or return address. --- lib/std/debug.zig | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/std/debug.zig b/lib/std/debug.zig index d9eb0cd907..377028236e 100644 --- a/lib/std/debug.zig +++ b/lib/std/debug.zig @@ -872,10 +872,14 @@ const StackIterator = union(enum) { }; const fp_usability: FpUsability = switch (builtin.target.cpu.arch) { + .avr, + .csky, .mips, .mipsel, .mips64, .mips64el, + .msp430, + .xcore, => .useless, .hexagon, // The PowerPC ABIs don't actually strictly require a backchain pointer; they allow omitting From 0b55393a2f7d6c3ea233eee53067c535f857a62f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 12:27:40 +0200 Subject: [PATCH 02/10] std.debug: add CPU context and DWARF mappings for ve --- lib/std/debug/Dwarf.zig | 3 ++ lib/std/debug/cpu_context.zig | 95 +++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index acd71eb4ed..044232b996 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1438,6 +1438,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { .riscv32, .riscv32be, .riscv64, .riscv64be => 65, .s390x => 65, .sparc, .sparc64 => 32, + .ve => 144, .x86 => 8, .x86_64 => 16, else => null, @@ -1455,6 +1456,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { .riscv32, .riscv32be, .riscv64, .riscv64be => 8, .s390x => 11, .sparc, .sparc64 => 30, + .ve => 9, .x86 => 5, .x86_64 => 6, else => unreachable, @@ -1472,6 +1474,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { .riscv32, .riscv32be, .riscv64, .riscv64be => 2, .s390x => 15, .sparc, .sparc64 => 14, + .ve => 11, .x86 => 4, .x86_64 => 7, else => unreachable, diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index b08ab49778..f989c01e88 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -12,6 +12,7 @@ else switch (native_arch) { .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc, .sparc, .sparc64 => Sparc, .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, + .ve => Ve, .s390x => S390x, .x86 => X86, .x86_64 => X86_64, @@ -1126,6 +1127,100 @@ const S390x = extern struct { } }; +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const Ve = extern struct { + s: [64]u64, + ic: u64, + + pub inline fn current() Ve { + var ctx: Ve = undefined; + asm volatile ( + \\ st %%s0, 0(, %%s8) + \\ st %%s1, 8(, %%s8) + \\ st %%s2, 16(, %%s8) + \\ st %%s3, 24(, %%s8) + \\ st %%s4, 32(, %%s8) + \\ st %%s5, 40(, %%s8) + \\ st %%s6, 48(, %%s8) + \\ st %%s7, 56(, %%s8) + \\ st %%s8, 64(, %%s8) + \\ st %%s9, 72(, %%s8) + \\ st %%s10, 80(, %%s8) + \\ st %%s11, 88(, %%s8) + \\ st %%s12, 96(, %%s8) + \\ st %%s13, 104(, %%s8) + \\ st %%s14, 112(, %%s8) + \\ st %%s15, 120(, %%s8) + \\ st %%s16, 128(, %%s8) + \\ st %%s17, 136(, %%s8) + \\ st %%s18, 144(, %%s8) + \\ st %%s19, 152(, %%s8) + \\ st %%s20, 160(, %%s8) + \\ st %%s21, 168(, %%s8) + \\ st %%s22, 176(, %%s8) + \\ st %%s23, 184(, %%s8) + \\ st %%s24, 192(, %%s8) + \\ st %%s25, 200(, %%s8) + \\ st %%s26, 208(, %%s8) + \\ st %%s27, 216(, %%s8) + \\ st %%s28, 224(, %%s8) + \\ st %%s29, 232(, %%s8) + \\ st %%s30, 240(, %%s8) + \\ st %%s31, 248(, %%s8) + \\ st %%s32, 256(, %%s8) + \\ st %%s33, 264(, %%s8) + \\ st %%s34, 272(, %%s8) + \\ st %%s35, 280(, %%s8) + \\ st %%s36, 288(, %%s8) + \\ st %%s37, 296(, %%s8) + \\ st %%s38, 304(, %%s8) + \\ st %%s39, 312(, %%s8) + \\ st %%s40, 320(, %%s8) + \\ st %%s41, 328(, %%s8) + \\ st %%s42, 336(, %%s8) + \\ st %%s43, 344(, %%s8) + \\ st %%s44, 352(, %%s8) + \\ st %%s45, 360(, %%s8) + \\ st %%s46, 368(, %%s8) + \\ st %%s47, 376(, %%s8) + \\ st %%s48, 384(, %%s8) + \\ st %%s49, 392(, %%s8) + \\ st %%s50, 400(, %%s8) + \\ st %%s51, 408(, %%s8) + \\ st %%s52, 416(, %%s8) + \\ st %%s53, 424(, %%s8) + \\ st %%s54, 432(, %%s8) + \\ st %%s55, 440(, %%s8) + \\ st %%s56, 448(, %%s8) + \\ st %%s57, 456(, %%s8) + \\ st %%s58, 464(, %%s8) + \\ st %%s59, 472(, %%s8) + \\ st %%s60, 480(, %%s8) + \\ st %%s61, 488(, %%s8) + \\ st %%s62, 496(, %%s8) + \\ st %%s63, 504(, %%s8) + \\ br.l 1f + \\1: + \\ st %%lr, 512(, %%s8) + : + : [ctx] "{s8}" (&ctx), + : .{ .s10 = true, .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *Ve, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...63 => return @ptrCast(&ctx.s[register_num]), + 144 => return @ptrCast(&ctx.ic), + + 64...127 => return error.UnsupportedRegister, // v0 - v63 + 128...143 => return error.UnsupportedRegister, // vm0 - vm15 + + else => return error.InvalidRegister, + } + } +}; + /// The native operating system's `ucontext_t` as seen in the third argument to signal handlers. /// /// These are dramatically simplified since we only need general-purpose registers and don't care From 81fe640dd21c1ba2d8406f4e9bb996d32f30df80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 12:57:05 +0200 Subject: [PATCH 03/10] std.debug: add CPU context and DWARF mappings for lanai --- lib/std/debug/Dwarf.zig | 3 ++ lib/std/debug/cpu_context.zig | 55 +++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 044232b996..fd0683d156 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1432,6 +1432,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { .aarch64, .aarch64_be => 32, .arm, .armeb, .thumb, .thumbeb => 15, .hexagon => 76, + .lanai => 2, .loongarch32, .loongarch64 => 64, .mips, .mipsel, .mips64, .mips64el => 66, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67, @@ -1450,6 +1451,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { .aarch64, .aarch64_be => 29, .arm, .armeb, .thumb, .thumbeb => 11, .hexagon => 30, + .lanai => 5, .loongarch32, .loongarch64 => 22, .mips, .mipsel, .mips64, .mips64el => 30, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, @@ -1468,6 +1470,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { .aarch64, .aarch64_be => 31, .arm, .armeb, .thumb, .thumbeb => 13, .hexagon => 29, + .lanai => 4, .loongarch32, .loongarch64 => 3, .mips, .mipsel, .mips64, .mips64el => 29, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index f989c01e88..b3c348df90 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -7,6 +7,7 @@ else switch (native_arch) { .aarch64, .aarch64_be => Aarch64, .arm, .armeb, .thumb, .thumbeb => Arm, .hexagon => Hexagon, + .lanai => Lanai, .loongarch32, .loongarch64 => LoongArch, .mips, .mipsel, .mips64, .mips64el => Mips, .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc, @@ -500,6 +501,60 @@ const Hexagon = extern struct { } }; +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const Lanai = extern struct { + r: [32]u32, + + pub inline fn current() Lanai { + var ctx: Lanai = undefined; + asm volatile ( + \\ st %%r0, 0[r9] + \\ st %%r1, 4[r9] + \\ st %%r2, 8[r9] + \\ st %%r3, 12[r9] + \\ st %%r4, 16[r9] + \\ st %%r5, 20[r9] + \\ st %%r6, 24[r9] + \\ st %%r7, 28[r9] + \\ st %%r8, 32[r9] + \\ st %%r9, 36[r9] + \\ st %%r10, 40[r9] + \\ st %%r11, 44[r9] + \\ st %%r12, 48[r9] + \\ st %%r13, 52[r9] + \\ st %%r14, 56[r9] + \\ st %%r15, 60[r9] + \\ st %%r16, 64[r9] + \\ st %%r17, 68[r9] + \\ st %%r18, 72[r9] + \\ st %%r19, 76[r9] + \\ st %%r20, 80[r9] + \\ st %%r21, 84[r9] + \\ st %%r22, 88[r9] + \\ st %%r23, 92[r9] + \\ st %%r24, 96[r9] + \\ st %%r25, 100[r9] + \\ st %%r26, 104[r9] + \\ st %%r27, 108[r9] + \\ st %%r28, 112[r9] + \\ st %%r29, 116[r9] + \\ st %%r30, 120[r9] + \\ st %%r31, 124[r9] + : + : [ctx] "{r9}" (&ctx), + : .{ .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *Lanai, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...31 => return @ptrCast(&ctx.s[register_num]), + + else => return error.InvalidRegister, + } + } +}; + /// This is an `extern struct` so that inline assembly in `current` can use field offsets. const LoongArch = extern struct { /// The numbered general-purpose registers r0 - r31. r0 must be zero. From de3947608ceb6e4603e3d249fb07183e3a431536 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 13:35:30 +0200 Subject: [PATCH 04/10] std.debug: add CPU context and DWARF mappings for csky --- lib/std/debug/Dwarf.zig | 3 +++ lib/std/debug/SelfInfo/Elf.zig | 3 ++- lib/std/debug/cpu_context.zig | 39 ++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 1 deletion(-) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index fd0683d156..29091f2a53 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1431,6 +1431,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { return switch (arch) { .aarch64, .aarch64_be => 32, .arm, .armeb, .thumb, .thumbeb => 15, + .csky => 64, .hexagon => 76, .lanai => 2, .loongarch32, .loongarch64 => 64, @@ -1450,6 +1451,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { return switch (arch) { .aarch64, .aarch64_be => 29, .arm, .armeb, .thumb, .thumbeb => 11, + .csky => 14, .hexagon => 30, .lanai => 5, .loongarch32, .loongarch64 => 22, @@ -1469,6 +1471,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { return switch (arch) { .aarch64, .aarch64_be => 31, .arm, .armeb, .thumb, .thumbeb => 13, + .csky => 14, .hexagon => 29, .lanai => 4, .loongarch32, .loongarch64 => 3, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index bee04a5eef..4f3fbe9177 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -101,10 +101,11 @@ pub const can_unwind: bool = s: { .x86, .x86_64, }, - // Not supported yet: arc, arm/armeb/thumb/thumbeb, csky, m68k, or1k, xtensa + // Not supported yet: arc, arm/armeb/thumb/thumbeb, m68k, or1k, xtensa .linux => &.{ .aarch64, .aarch64_be, + .csky, .loongarch64, .mips, .mipsel, diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index b3c348df90..eb255d20ac 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -6,6 +6,7 @@ pub const Native = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "CpuCont else switch (native_arch) { .aarch64, .aarch64_be => Aarch64, .arm, .armeb, .thumb, .thumbeb => Arm, + .csky => Csky, .hexagon => Hexagon, .lanai => Lanai, .loongarch32, .loongarch64 => LoongArch, @@ -74,6 +75,13 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { .sp = uc.mcontext.sp, .pc = uc.mcontext.pc, }, + .csky => .{ + .r = uc.mcontext.r0_13 ++ + [_]u32{ uc.mcontext.r14, uc.mcontext.r15 } ++ + uc.mcontext.r16_30 ++ + [_]u32{uc.mcontext.r31}, + .pc = uc.mcontext.pc, + }, .hexagon, .loongarch32, .loongarch64, .mips, .mipsel, .mips64, .mips64el, .or1k => .{ .r = uc.mcontext.r, .pc = uc.mcontext.pc, @@ -433,6 +441,37 @@ const Aarch64 = extern struct { } }; +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const Csky = extern struct { + /// The numbered general-purpose registers r0 - r31. + r: [32]u32, + pc: u32, + + pub inline fn current() Csky { + var ctx: Csky = undefined; + asm volatile ( + \\ stm r0-r31, (t0) + \\ grs t1, 1f + \\1: + \\ st32.w t1, (t0, 128) + : + : [ctx] "{r12}" (&ctx), + : .{ .r13 = true, .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *Csky, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...31 => return @ptrCast(&ctx.r[register_num]), + 64 => return @ptrCast(&ctx.pc), + + 32...63 => return error.UnsupportedRegister, // f0 - f31 + + else => return error.InvalidRegister, + } + } +}; + /// This is an `extern struct` so that inline assembly in `current` can use field offsets. const Hexagon = extern struct { /// The numbered general-purpose registers r0 - r31. From eb36a45ed9fbdf5ab92c3cdb650375a13eaaa648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 15:56:32 +0200 Subject: [PATCH 05/10] std.debug: add CPU context and DWARF mappings for or1k --- lib/std/debug/Dwarf.zig | 3 ++ lib/std/debug/SelfInfo/Elf.zig | 3 +- lib/std/debug/cpu_context.zig | 61 ++++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+), 1 deletion(-) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 29091f2a53..dcf415cf66 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1436,6 +1436,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { .lanai => 2, .loongarch32, .loongarch64 => 64, .mips, .mipsel, .mips64, .mips64el => 66, + .or1k => 35, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67, .riscv32, .riscv32be, .riscv64, .riscv64be => 65, .s390x => 65, @@ -1456,6 +1457,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { .lanai => 5, .loongarch32, .loongarch64 => 22, .mips, .mipsel, .mips64, .mips64el => 30, + .or1k => 2, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, .riscv32, .riscv32be, .riscv64, .riscv64be => 8, .s390x => 11, @@ -1476,6 +1478,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { .lanai => 4, .loongarch32, .loongarch64 => 3, .mips, .mipsel, .mips64, .mips64el => 29, + .or1k => 1, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, .riscv32, .riscv32be, .riscv64, .riscv64be => 2, .s390x => 15, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 4f3fbe9177..44e5e70e3b 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -101,7 +101,7 @@ pub const can_unwind: bool = s: { .x86, .x86_64, }, - // Not supported yet: arc, arm/armeb/thumb/thumbeb, m68k, or1k, xtensa + // Not supported yet: arc, arm/armeb/thumb/thumbeb, m68k, xtensa .linux => &.{ .aarch64, .aarch64_be, @@ -111,6 +111,7 @@ pub const can_unwind: bool = s: { .mipsel, .mips64, .mips64el, + .or1k, .riscv32, .riscv64, .s390x, diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index eb255d20ac..f4141ce33a 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -11,6 +11,7 @@ else switch (native_arch) { .lanai => Lanai, .loongarch32, .loongarch64 => LoongArch, .mips, .mipsel, .mips64, .mips64el => Mips, + .or1k => Or1k, .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc, .sparc, .sparc64 => Sparc, .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv, @@ -818,6 +819,66 @@ const Mips = extern struct { } }; +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const Or1k = extern struct { + /// The numbered general-purpose registers r0 - r31. + r: [32]u32, + pc: u32, + + pub inline fn current() Or1k { + var ctx: Or1k = undefined; + asm volatile ( + \\ l.sw 0(r15), r0 + \\ l.sw 4(r15), r1 + \\ l.sw 8(r15), r2 + \\ l.sw 12(r15), r3 + \\ l.sw 16(r15), r4 + \\ l.sw 20(r15), r5 + \\ l.sw 24(r15), r6 + \\ l.sw 28(r15), r7 + \\ l.sw 32(r15), r8 + \\ l.sw 36(r15), r9 + \\ l.sw 40(r15), r10 + \\ l.sw 44(r15), r11 + \\ l.sw 48(r15), r12 + \\ l.sw 52(r15), r13 + \\ l.sw 56(r15), r14 + \\ l.sw 60(r15), r15 + \\ l.sw 64(r15), r16 + \\ l.sw 68(r15), r17 + \\ l.sw 72(r15), r18 + \\ l.sw 76(r15), r19 + \\ l.sw 80(r15), r20 + \\ l.sw 84(r15), r21 + \\ l.sw 88(r15), r22 + \\ l.sw 92(r15), r23 + \\ l.sw 96(r15), r24 + \\ l.sw 100(r15), r25 + \\ l.sw 104(r15), r26 + \\ l.sw 108(r15), r27 + \\ l.sw 112(r15), r28 + \\ l.sw 116(r15), r29 + \\ l.sw 120(r15), r30 + \\ l.sw 124(r15), r31 + \\ l.jal 1f + \\1: + \\ l.sw 128(r15), r9 + : + : [ctx] "{r15}" (&ctx), + : .{ .r9 = true, .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *Or1k, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...31 => return @ptrCast(&ctx.r[register_num]), + 35 => return @ptrCast(&ctx.pc), + + else => return error.InvalidRegister, + } + } +}; + /// This is an `extern struct` so that inline assembly in `current` can use field offsets. const Powerpc = extern struct { /// The numbered general-purpose registers r0 - r31. From ba9ab3fb6720ff5894ac07741eccaaca44122eb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 17:09:30 +0200 Subject: [PATCH 06/10] std.debug: add CPU context and DWARF mappings for m68k --- lib/std/debug/Dwarf.zig | 3 +++ lib/std/debug/SelfInfo/Elf.zig | 9 +++++--- lib/std/debug/cpu_context.zig | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index dcf415cf66..8aaf85c66b 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1435,6 +1435,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { .hexagon => 76, .lanai => 2, .loongarch32, .loongarch64 => 64, + .m68k => 26, .mips, .mipsel, .mips64, .mips64el => 66, .or1k => 35, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67, @@ -1456,6 +1457,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { .hexagon => 30, .lanai => 5, .loongarch32, .loongarch64 => 22, + .m68k => 14, .mips, .mipsel, .mips64, .mips64el => 30, .or1k => 2, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, @@ -1477,6 +1479,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { .hexagon => 29, .lanai => 4, .loongarch32, .loongarch64 => 3, + .m68k => 15, .mips, .mipsel, .mips64, .mips64el => 29, .or1k => 1, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index 44e5e70e3b..e25f357803 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -94,19 +94,21 @@ pub const can_unwind: bool = s: { // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format. const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) { - // Not supported yet: arm, m68k + // Not supported yet: arm .haiku => &.{ .aarch64, + .m68k, .riscv64, .x86, .x86_64, }, - // Not supported yet: arc, arm/armeb/thumb/thumbeb, m68k, xtensa + // Not supported yet: arc, arm/armeb/thumb/thumbeb, xtensa .linux => &.{ .aarch64, .aarch64_be, .csky, .loongarch64, + .m68k, .mips, .mipsel, .mips64, @@ -133,10 +135,11 @@ pub const can_unwind: bool = s: { .riscv64, .x86_64, }, - // Not supported yet: arm/armeb, m68k, mips64/mips64el + // Not supported yet: arm/armeb, mips64/mips64el .netbsd => &.{ .aarch64, .aarch64_be, + .m68k, .mips, .mipsel, .x86, diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index f4141ce33a..1278e630ff 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -10,6 +10,7 @@ else switch (native_arch) { .hexagon => Hexagon, .lanai => Lanai, .loongarch32, .loongarch64 => LoongArch, + .m68k => M68k, .mips, .mipsel, .mips64, .mips64el => Mips, .or1k => Or1k, .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc, @@ -87,6 +88,11 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { .r = uc.mcontext.r, .pc = uc.mcontext.pc, }, + .m68k => .{ + .d = uc.mcontext.d, + .a = uc.mcontext.a, + .pc = uc.mcontext.pc, + }, .powerpc, .powerpcle, .powerpc64, .powerpc64le => .{ .r = uc.mcontext.r, .pc = uc.mcontext.pc, @@ -697,6 +703,40 @@ const LoongArch = extern struct { } }; +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const M68k = extern struct { + /// The numbered data registers d0 - d7. + d: [8]u32, + /// The numbered address registers a0 - a7. + a: [8]u32, + pc: u32, + + pub inline fn current() M68k { + var ctx: M68k = undefined; + asm volatile ( + \\ movem.l %%d0 - %%a7, (%%a0) + \\ lea.l (%%pc), %%a1 + \\ move.l %%a1, (%%a0, 64) + : + : [ctx] "{a0}" (&ctx), + : .{ .a1 = true, .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *M68k, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...7 => return @ptrCast(&ctx.d[register_num]), + 8...15 => return @ptrCast(&ctx.a[register_num - 8]), + 26 => return @ptrCast(&ctx.pc), + + 16...23 => return error.UnsupportedRegister, // fp0 - fp7 + 24...25 => return error.UnsupportedRegister, // Return columns in GCC...? + + else => return error.InvalidRegister, + } + } +}; + /// This is an `extern struct` so that inline assembly in `current` can use field offsets. const Mips = extern struct { /// The numbered general-purpose registers r0 - r31. r0 must be zero. From 4c81a496e7c979755d9fc74734ab740a828c4491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 17:31:55 +0200 Subject: [PATCH 07/10] std.debug: add CPU context and DWARF mappings for arc --- lib/std/debug/Dwarf.zig | 3 ++ lib/std/debug/SelfInfo/Elf.zig | 3 +- lib/std/debug/cpu_context.zig | 80 +++++++++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 3 deletions(-) diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig index 8aaf85c66b..7229dcdf4c 100644 --- a/lib/std/debug/Dwarf.zig +++ b/lib/std/debug/Dwarf.zig @@ -1430,6 +1430,7 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u16 { pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { return switch (arch) { .aarch64, .aarch64_be => 32, + .arc => 160, .arm, .armeb, .thumb, .thumbeb => 15, .csky => 64, .hexagon => 76, @@ -1452,6 +1453,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { return switch (arch) { .aarch64, .aarch64_be => 29, + .arc => 27, .arm, .armeb, .thumb, .thumbeb => 11, .csky => 14, .hexagon => 30, @@ -1474,6 +1476,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { return switch (arch) { .aarch64, .aarch64_be => 31, + .arc => 28, .arm, .armeb, .thumb, .thumbeb => 13, .csky => 14, .hexagon => 29, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig index e25f357803..5713542510 100644 --- a/lib/std/debug/SelfInfo/Elf.zig +++ b/lib/std/debug/SelfInfo/Elf.zig @@ -102,10 +102,11 @@ pub const can_unwind: bool = s: { .x86, .x86_64, }, - // Not supported yet: arc, arm/armeb/thumb/thumbeb, xtensa + // Not supported yet: arm/armeb/thumb/thumbeb, xtensa .linux => &.{ .aarch64, .aarch64_be, + .arc, .csky, .loongarch64, .m68k, diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index 1278e630ff..d46f9bd132 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -5,6 +5,7 @@ pub const Native = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "CpuCont root.debug.CpuContext else switch (native_arch) { .aarch64, .aarch64_be => Aarch64, + .arc => Arc, .arm, .armeb, .thumb, .thumbeb => Arm, .csky => Csky, .hexagon => Hexagon, @@ -35,7 +36,20 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { const uc: *const signal_ucontext_t = @ptrCast(@alignCast(ctx_ptr)); // Deal with some special cases first. - if (native_arch.isMIPS32() and native_os == .linux) { + if (native_arch == .arc and native_os == .linux) { + var native: Native = .{ + .r = [_]u32{ uc.mcontext.r31, uc.mcontext.r30, 0, uc.mcontext.r28 } ++ + uc.mcontext.r27_26 ++ + uc.mcontext.r25_13 ++ + uc.mcontext.r12_0, + .pcl = uc.mcontext.pcl, + }; + + // I have no idea why the kernel is storing these registers in such a bizarre order... + std.mem.reverse(native.r[0..]); + + return native; + } else if (native_arch.isMIPS32() and native_os == .linux) { // The O32 kABI uses 64-bit fields for some reason. return .{ .r = s: { @@ -327,6 +341,68 @@ const X86_64 = struct { } }; +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const Arc = extern struct { + /// The numbered general-purpose registers r0 - r31. + r: [32]u32, + pcl: u32, + + pub inline fn current() Arc { + var ctx: Arc = undefined; + asm volatile ( + \\ st r0, [r8, 0] + \\ st r1, [r8, 4] + \\ st r2, [r8, 8] + \\ st r3, [r8, 12] + \\ st r4, [r8, 16] + \\ st r5, [r8, 20] + \\ st r6, [r8, 24] + \\ st r7, [r8, 28] + \\ st r8, [r8, 32] + \\ st r9, [r8, 36] + \\ st r10, [r8, 40] + \\ st r11, [r8, 44] + \\ st r12, [r8, 48] + \\ st r13, [r8, 52] + \\ st r14, [r8, 56] + \\ st r15, [r8, 60] + \\ st r16, [r8, 64] + \\ st r17, [r8, 68] + \\ st r18, [r8, 72] + \\ st r19, [r8, 76] + \\ st r20, [r8, 80] + \\ st r21, [r8, 84] + \\ st r22, [r8, 88] + \\ st r23, [r8, 92] + \\ st r24, [r8, 96] + \\ st r25, [r8, 100] + \\ st r26, [r8, 104] + \\ st r27, [r8, 108] + \\ st r28, [r8, 112] + \\ st r29, [r8, 116] + \\ st r30, [r8, 120] + \\ st r31, [r8, 124] + \\ st pcl, [r8, 128] + : + : [ctx] "{r8}" (&ctx), + : .{ .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *Arc, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...31 => return @ptrCast(&ctx.r[register_num]), + 160 => return @ptrCast(&ctx.pcl), + + 32...57 => return error.UnsupportedRegister, // Extension Core Registers + 58...127 => return error.UnsupportedRegister, // Reserved + 128...159 => return error.UnsupportedRegister, // f0 - f31 + + else => return error.InvalidRegister, + } + } +}; + const Arm = struct { /// The numbered general-purpose registers R0 - R15. r: [16]u32, @@ -1517,7 +1593,7 @@ const signal_ucontext_t = switch (native_os) { _count: u32, }, _status32: u32, - pc: u32, + pcl: u32, r31: u32, r27_26: [2]u32, r12_0: [13]u32, From 1f15e265fe0861cbfad4479c69b79b46158d8d16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 18:51:09 +0200 Subject: [PATCH 08/10] std.debug.cpu_context: sort context decls according to switch prongs (NFC) --- lib/std/debug/cpu_context.zig | 572 +++++++++++++++++----------------- 1 file changed, 286 insertions(+), 286 deletions(-) diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index d46f9bd132..5f8889aa9f 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -212,129 +212,63 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native { }; } -const X86 = struct { - /// The first 8 registers here intentionally match the order of registers in the x86 instruction - /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings, - /// among other things. - pub const Gpr = enum { - // zig fmt: off - eax, ecx, edx, ebx, - esp, ebp, esi, edi, - eip, - // zig fmt: on - }; - gprs: std.enums.EnumArray(Gpr, u32), +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const Aarch64 = extern struct { + /// The numbered general-purpose registers X0 - X30. + x: [31]u64, + sp: u64, + pc: u64, - pub inline fn current() X86 { - var ctx: X86 = undefined; + pub inline fn current() Aarch64 { + var ctx: Aarch64 = undefined; asm volatile ( - \\movl %%eax, 0x00(%%edi) - \\movl %%ecx, 0x04(%%edi) - \\movl %%edx, 0x08(%%edi) - \\movl %%ebx, 0x0c(%%edi) - \\movl %%esp, 0x10(%%edi) - \\movl %%ebp, 0x14(%%edi) - \\movl %%esi, 0x18(%%edi) - \\movl %%edi, 0x1c(%%edi) - \\call 1f - \\1: - \\popl 0x20(%%edi) + \\ stp x0, x1, [x0, #0x000] + \\ stp x2, x3, [x0, #0x010] + \\ stp x4, x5, [x0, #0x020] + \\ stp x6, x7, [x0, #0x030] + \\ stp x8, x9, [x0, #0x040] + \\ stp x10, x11, [x0, #0x050] + \\ stp x12, x13, [x0, #0x060] + \\ stp x14, x15, [x0, #0x070] + \\ stp x16, x17, [x0, #0x080] + \\ stp x18, x19, [x0, #0x090] + \\ stp x20, x21, [x0, #0x0a0] + \\ stp x22, x23, [x0, #0x0b0] + \\ stp x24, x25, [x0, #0x0c0] + \\ stp x26, x27, [x0, #0x0d0] + \\ stp x28, x29, [x0, #0x0e0] + \\ str x30, [x0, #0x0f0] + \\ mov x1, sp + \\ str x1, [x0, #0x0f8] + \\ adr x1, . + \\ str x1, [x0, #0x100] + \\ ldr x1, [x0, #0x008] : - : [gprs] "{edi}" (&ctx.gprs.values), + : [ctx] "{x0}" (&ctx), : .{ .memory = true }); return ctx; } - pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 { - // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1 - // § 2.4.2 "DWARF Register Number Mapping" + pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 { + // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names" switch (register_num) { - // The order of `Gpr` intentionally matches DWARF's mappings. - // - // x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind - // information is from `__eh_frame`). This deviation is not considered here, because - // x86-macos is a deprecated target which is not supported by the Zig Standard Library. - 0...8 => return @ptrCast(&ctx.gprs.values[register_num]), + 0...30 => return @ptrCast(&ctx.x[register_num]), + 31 => return @ptrCast(&ctx.sp), + 32 => return @ptrCast(&ctx.pc), - 9 => return error.UnsupportedRegister, // eflags - 11...18 => return error.UnsupportedRegister, // st0 - st7 - 21...28 => return error.UnsupportedRegister, // xmm0 - xmm7 - 29...36 => return error.UnsupportedRegister, // mm0 - mm7 - 39 => return error.UnsupportedRegister, // mxcsr - 40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs - 48 => return error.UnsupportedRegister, // tr - 49 => return error.UnsupportedRegister, // ldtr - 93...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512) - - else => return error.InvalidRegister, - } - } -}; - -const X86_64 = struct { - /// The order here intentionally matches the order of the DWARF register mappings. It's unclear - /// where those mappings actually originated from---the ordering of the first 4 registers seems - /// quite unusual---but it is currently convenient for us to match DWARF. - pub const Gpr = enum { - // zig fmt: off - rax, rdx, rcx, rbx, - rsi, rdi, rbp, rsp, - r8, r9, r10, r11, - r12, r13, r14, r15, - rip, - // zig fmt: on - }; - gprs: std.enums.EnumArray(Gpr, u64), - - pub inline fn current() X86_64 { - var ctx: X86_64 = undefined; - asm volatile ( - \\movq %%rax, 0x00(%%rdi) - \\movq %%rdx, 0x08(%%rdi) - \\movq %%rcx, 0x10(%%rdi) - \\movq %%rbx, 0x18(%%rdi) - \\movq %%rsi, 0x20(%%rdi) - \\movq %%rdi, 0x28(%%rdi) - \\movq %%rbp, 0x30(%%rdi) - \\movq %%rsp, 0x38(%%rdi) - \\movq %%r8, 0x40(%%rdi) - \\movq %%r9, 0x48(%%rdi) - \\movq %%r10, 0x50(%%rdi) - \\movq %%r11, 0x58(%%rdi) - \\movq %%r12, 0x60(%%rdi) - \\movq %%r13, 0x68(%%rdi) - \\movq %%r14, 0x70(%%rdi) - \\movq %%r15, 0x78(%%rdi) - \\leaq (%%rip), %%rax - \\movq %%rax, 0x80(%%rdi) - \\movq 0x00(%%rdi), %%rax - : - : [gprs] "{rdi}" (&ctx.gprs.values), - : .{ .memory = true }); - return ctx; - } - - pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 { - // System V Application Binary Interface AMD64 Architecture Processor Supplement - // § 3.6.2 "DWARF Register Number Mapping" - switch (register_num) { - // The order of `Gpr` intentionally matches DWARF's mappings. - 0...16 => return @ptrCast(&ctx.gprs.values[register_num]), - - 17...32 => return error.UnsupportedRegister, // xmm0 - xmm15 - 33...40 => return error.UnsupportedRegister, // st0 - st7 - 41...48 => return error.UnsupportedRegister, // mm0 - mm7 - 49 => return error.UnsupportedRegister, // rflags - 50...55 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs - 58...59 => return error.UnsupportedRegister, // fs.base, gs.base - 62 => return error.UnsupportedRegister, // tr - 63 => return error.UnsupportedRegister, // ldtr - 64 => return error.UnsupportedRegister, // mxcsr - 65 => return error.UnsupportedRegister, // fcw - 66 => return error.UnsupportedRegister, // fsw - 67...82 => return error.UnsupportedRegister, // xmm16 - xmm31 (AVX-512) - 118...125 => return error.UnsupportedRegister, // k0 - k7 (AVX-512) - 130...145 => return error.UnsupportedRegister, // r16 - r31 (APX) + 33 => return error.UnsupportedRegister, // ELR_mode + 34 => return error.UnsupportedRegister, // RA_SIGN_STATE + 35 => return error.UnsupportedRegister, // TPIDRRO_ELO + 36 => return error.UnsupportedRegister, // TPIDR_ELO + 37 => return error.UnsupportedRegister, // TPIDR_EL1 + 38 => return error.UnsupportedRegister, // TPIDR_EL2 + 39 => return error.UnsupportedRegister, // TPIDR_EL3 + 40...45 => return error.UnsupportedRegister, // Reserved + 46 => return error.UnsupportedRegister, // VG + 47 => return error.UnsupportedRegister, // FFR + 48...63 => return error.UnsupportedRegister, // P0 - P15 + 64...95 => return error.UnsupportedRegister, // V0 - V31 + 96...127 => return error.UnsupportedRegister, // Z0 - Z31 else => return error.InvalidRegister, } @@ -410,11 +344,11 @@ const Arm = struct { pub inline fn current() Arm { var ctx: Arm = undefined; asm volatile ( - \\// For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm. - \\stm r0, {r0-r12} - \\str r13, [r0, #0x34] - \\str r14, [r0, #0x38] - \\str r15, [r0, #0x3c] + \\ // For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm. + \\ stm r0, {r0-r12} + \\ str r13, [r0, #0x34] + \\ str r14, [r0, #0x38] + \\ str r15, [r0, #0x3c] : : [r] "{r0}" (&ctx.r), : .{ .memory = true }); @@ -461,69 +395,6 @@ const Arm = struct { } }; -/// This is an `extern struct` so that inline assembly in `current` can use field offsets. -const Aarch64 = extern struct { - /// The numbered general-purpose registers X0 - X30. - x: [31]u64, - sp: u64, - pc: u64, - - pub inline fn current() Aarch64 { - var ctx: Aarch64 = undefined; - asm volatile ( - \\stp x0, x1, [x0, #0x000] - \\stp x2, x3, [x0, #0x010] - \\stp x4, x5, [x0, #0x020] - \\stp x6, x7, [x0, #0x030] - \\stp x8, x9, [x0, #0x040] - \\stp x10, x11, [x0, #0x050] - \\stp x12, x13, [x0, #0x060] - \\stp x14, x15, [x0, #0x070] - \\stp x16, x17, [x0, #0x080] - \\stp x18, x19, [x0, #0x090] - \\stp x20, x21, [x0, #0x0a0] - \\stp x22, x23, [x0, #0x0b0] - \\stp x24, x25, [x0, #0x0c0] - \\stp x26, x27, [x0, #0x0d0] - \\stp x28, x29, [x0, #0x0e0] - \\str x30, [x0, #0x0f0] - \\mov x1, sp - \\str x1, [x0, #0x0f8] - \\adr x1, . - \\str x1, [x0, #0x100] - \\ldr x1, [x0, #0x008] - : - : [gprs] "{x0}" (&ctx), - : .{ .memory = true }); - return ctx; - } - - pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 { - // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names" - switch (register_num) { - 0...30 => return @ptrCast(&ctx.x[register_num]), - 31 => return @ptrCast(&ctx.sp), - 32 => return @ptrCast(&ctx.pc), - - 33 => return error.UnsupportedRegister, // ELR_mode - 34 => return error.UnsupportedRegister, // RA_SIGN_STATE - 35 => return error.UnsupportedRegister, // TPIDRRO_ELO - 36 => return error.UnsupportedRegister, // TPIDR_ELO - 37 => return error.UnsupportedRegister, // TPIDR_EL1 - 38 => return error.UnsupportedRegister, // TPIDR_EL2 - 39 => return error.UnsupportedRegister, // TPIDR_EL3 - 40...45 => return error.UnsupportedRegister, // Reserved - 46 => return error.UnsupportedRegister, // VG - 47 => return error.UnsupportedRegister, // FFR - 48...63 => return error.UnsupportedRegister, // P0 - P15 - 64...95 => return error.UnsupportedRegister, // V0 - V31 - 96...127 => return error.UnsupportedRegister, // Z0 - Z31 - - else => return error.InvalidRegister, - } - } -}; - /// This is an `extern struct` so that inline assembly in `current` can use field offsets. const Csky = extern struct { /// The numbered general-purpose registers r0 - r31. @@ -600,7 +471,7 @@ const Hexagon = extern struct { \\ memw(r0 + #128) = r1 \\ r1 = memw(r0 + #4) : - : [gprs] "{r0}" (&ctx), + : [ctx] "{r0}" (&ctx), : .{ .memory = true }); return ctx; } @@ -762,7 +633,7 @@ const LoongArch = extern struct { \\ st.w $ra, $t0, 128 \\ ld.w $ra, $t0, 4 : - : [gprs] "{$r12}" (&ctx), + : [ctx] "{$r12}" (&ctx), : .{ .memory = true }); return ctx; } @@ -908,7 +779,7 @@ const Mips = extern struct { \\ lw $ra, 124($t4) \\ .set pop : - : [gprs] "{$12}" (&ctx), + : [ctx] "{$12}" (&ctx), : .{ .memory = true }); return ctx; } @@ -1087,7 +958,7 @@ const Powerpc = extern struct { \\ stw 8, 128(10) \\ lwz 8, 32(10) : - : [gprs] "{r10}" (&ctx), + : [ctx] "{r10}" (&ctx), : .{ .lr = true, .memory = true }); return ctx; } @@ -1151,104 +1022,6 @@ const Powerpc = extern struct { } }; -/// This is an `extern struct` so that inline assembly in `current` can use field offsets. -const Sparc = extern struct { - g: [8]Gpr, - o: [8]Gpr, - l: [8]Gpr, - i: [8]Gpr, - pc: Gpr, - - pub const Gpr = if (native_arch == .sparc64) u64 else u32; - - pub inline fn current() Sparc { - flushWindows(); - - var ctx: Sparc = undefined; - asm volatile (if (Gpr == u64) - \\ stx %g0, [%l0 + 0] - \\ stx %g1, [%l0 + 8] - \\ stx %g2, [%l0 + 16] - \\ stx %g3, [%l0 + 24] - \\ stx %g4, [%l0 + 32] - \\ stx %g5, [%l0 + 40] - \\ stx %g6, [%l0 + 48] - \\ stx %g7, [%l0 + 56] - \\ stx %o0, [%l0 + 64] - \\ stx %o1, [%l0 + 72] - \\ stx %o2, [%l0 + 80] - \\ stx %o3, [%l0 + 88] - \\ stx %o4, [%l0 + 96] - \\ stx %o5, [%l0 + 104] - \\ stx %o6, [%l0 + 112] - \\ stx %o7, [%l0 + 120] - \\ stx %l0, [%l0 + 128] - \\ stx %l1, [%l0 + 136] - \\ stx %l2, [%l0 + 144] - \\ stx %l3, [%l0 + 152] - \\ stx %l4, [%l0 + 160] - \\ stx %l5, [%l0 + 168] - \\ stx %l6, [%l0 + 176] - \\ stx %l7, [%l0 + 184] - \\ stx %i0, [%l0 + 192] - \\ stx %i1, [%l0 + 200] - \\ stx %i2, [%l0 + 208] - \\ stx %i3, [%l0 + 216] - \\ stx %i4, [%l0 + 224] - \\ stx %i5, [%l0 + 232] - \\ stx %i6, [%l0 + 240] - \\ stx %i7, [%l0 + 248] - \\ call 1f - \\ stx %o7, [%l0 + 256] - \\1: - else - \\ std %g0, [%l0 + 0] - \\ std %g2, [%l0 + 8] - \\ std %g4, [%l0 + 16] - \\ std %g6, [%l0 + 24] - \\ std %o0, [%l0 + 32] - \\ std %o2, [%l0 + 40] - \\ std %o4, [%l0 + 48] - \\ std %o6, [%l0 + 56] - \\ std %l0, [%l0 + 64] - \\ std %l2, [%l0 + 72] - \\ std %l4, [%l0 + 80] - \\ std %l6, [%l0 + 88] - \\ std %i0, [%l0 + 96] - \\ std %i2, [%l0 + 104] - \\ std %i4, [%l0 + 112] - \\ std %i6, [%l0 + 120] - \\ call 1f - \\ st %o7, [%l0 + 128] - \\1: - : - : [gprs] "{l0}" (&ctx), - : .{ .o7 = true, .memory = true }); - return ctx; - } - - noinline fn flushWindows() void { - // Flush all register windows except the current one (hence `noinline`). This ensures that - // we actually see meaningful data on the stack when we walk the frame chain. - if (comptime builtin.target.cpu.has(.sparc, .v9)) - asm volatile ("flushw" ::: .{ .memory = true }) - else - asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS - } - - pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 { - switch (register_num) { - 0...7 => return @ptrCast(&ctx.g[register_num]), - 8...15 => return @ptrCast(&ctx.o[register_num - 8]), - 16...23 => return @ptrCast(&ctx.l[register_num - 16]), - 24...31 => return @ptrCast(&ctx.i[register_num - 24]), - 32 => return @ptrCast(&ctx.pc), - - else => return error.InvalidRegister, - } - } -}; - /// This is an `extern struct` so that inline assembly in `current` can use field offsets. const Riscv = extern struct { /// The numbered general-purpose registers r0 - r31. r0 must be zero. @@ -1334,7 +1107,7 @@ const Riscv = extern struct { \\ sw ra, 128(t0) \\ lw ra, 4(t0) : - : [gprs] "{t0}" (&ctx), + : [ctx] "{t0}" (&ctx), : .{ .memory = true }); return ctx; } @@ -1376,7 +1149,7 @@ const S390x = extern struct { \\ lg %%r0, 0(%%r2) \\ lg %%r1, 8(%%r2) : - : [gprs] "{r2}" (&ctx), + : [ctx] "{r2}" (&ctx), : .{ .memory = true }); return ctx; } @@ -1398,6 +1171,104 @@ const S390x = extern struct { } }; +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const Sparc = extern struct { + g: [8]Gpr, + o: [8]Gpr, + l: [8]Gpr, + i: [8]Gpr, + pc: Gpr, + + pub const Gpr = if (native_arch == .sparc64) u64 else u32; + + pub inline fn current() Sparc { + flushWindows(); + + var ctx: Sparc = undefined; + asm volatile (if (Gpr == u64) + \\ stx %g0, [%l0 + 0] + \\ stx %g1, [%l0 + 8] + \\ stx %g2, [%l0 + 16] + \\ stx %g3, [%l0 + 24] + \\ stx %g4, [%l0 + 32] + \\ stx %g5, [%l0 + 40] + \\ stx %g6, [%l0 + 48] + \\ stx %g7, [%l0 + 56] + \\ stx %o0, [%l0 + 64] + \\ stx %o1, [%l0 + 72] + \\ stx %o2, [%l0 + 80] + \\ stx %o3, [%l0 + 88] + \\ stx %o4, [%l0 + 96] + \\ stx %o5, [%l0 + 104] + \\ stx %o6, [%l0 + 112] + \\ stx %o7, [%l0 + 120] + \\ stx %l0, [%l0 + 128] + \\ stx %l1, [%l0 + 136] + \\ stx %l2, [%l0 + 144] + \\ stx %l3, [%l0 + 152] + \\ stx %l4, [%l0 + 160] + \\ stx %l5, [%l0 + 168] + \\ stx %l6, [%l0 + 176] + \\ stx %l7, [%l0 + 184] + \\ stx %i0, [%l0 + 192] + \\ stx %i1, [%l0 + 200] + \\ stx %i2, [%l0 + 208] + \\ stx %i3, [%l0 + 216] + \\ stx %i4, [%l0 + 224] + \\ stx %i5, [%l0 + 232] + \\ stx %i6, [%l0 + 240] + \\ stx %i7, [%l0 + 248] + \\ call 1f + \\ stx %o7, [%l0 + 256] + \\1: + else + \\ std %g0, [%l0 + 0] + \\ std %g2, [%l0 + 8] + \\ std %g4, [%l0 + 16] + \\ std %g6, [%l0 + 24] + \\ std %o0, [%l0 + 32] + \\ std %o2, [%l0 + 40] + \\ std %o4, [%l0 + 48] + \\ std %o6, [%l0 + 56] + \\ std %l0, [%l0 + 64] + \\ std %l2, [%l0 + 72] + \\ std %l4, [%l0 + 80] + \\ std %l6, [%l0 + 88] + \\ std %i0, [%l0 + 96] + \\ std %i2, [%l0 + 104] + \\ std %i4, [%l0 + 112] + \\ std %i6, [%l0 + 120] + \\ call 1f + \\ st %o7, [%l0 + 128] + \\1: + : + : [ctx] "{l0}" (&ctx), + : .{ .o7 = true, .memory = true }); + return ctx; + } + + noinline fn flushWindows() void { + // Flush all register windows except the current one (hence `noinline`). This ensures that + // we actually see meaningful data on the stack when we walk the frame chain. + if (comptime builtin.target.cpu.has(.sparc, .v9)) + asm volatile ("flushw" ::: .{ .memory = true }) + else + asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS + } + + pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...7 => return @ptrCast(&ctx.g[register_num]), + 8...15 => return @ptrCast(&ctx.o[register_num - 8]), + 16...23 => return @ptrCast(&ctx.l[register_num - 16]), + 24...31 => return @ptrCast(&ctx.i[register_num - 24]), + 32 => return @ptrCast(&ctx.pc), + + else => return error.InvalidRegister, + } + } +}; + /// This is an `extern struct` so that inline assembly in `current` can use field offsets. const Ve = extern struct { s: [64]u64, @@ -1492,6 +1363,135 @@ const Ve = extern struct { } }; +const X86 = struct { + /// The first 8 registers here intentionally match the order of registers in the x86 instruction + /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings, + /// among other things. + pub const Gpr = enum { + // zig fmt: off + eax, ecx, edx, ebx, + esp, ebp, esi, edi, + eip, + // zig fmt: on + }; + gprs: std.enums.EnumArray(Gpr, u32), + + pub inline fn current() X86 { + var ctx: X86 = undefined; + asm volatile ( + \\ movl %%eax, 0x00(%%edi) + \\ movl %%ecx, 0x04(%%edi) + \\ movl %%edx, 0x08(%%edi) + \\ movl %%ebx, 0x0c(%%edi) + \\ movl %%esp, 0x10(%%edi) + \\ movl %%ebp, 0x14(%%edi) + \\ movl %%esi, 0x18(%%edi) + \\ movl %%edi, 0x1c(%%edi) + \\ call 1f + \\1: + \\ popl 0x20(%%edi) + : + : [gprs] "{edi}" (&ctx.gprs.values), + : .{ .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 { + // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1 + // § 2.4.2 "DWARF Register Number Mapping" + switch (register_num) { + // The order of `Gpr` intentionally matches DWARF's mappings. + // + // x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind + // information is from `__eh_frame`). This deviation is not considered here, because + // x86-macos is a deprecated target which is not supported by the Zig Standard Library. + 0...8 => return @ptrCast(&ctx.gprs.values[register_num]), + + 9 => return error.UnsupportedRegister, // eflags + 11...18 => return error.UnsupportedRegister, // st0 - st7 + 21...28 => return error.UnsupportedRegister, // xmm0 - xmm7 + 29...36 => return error.UnsupportedRegister, // mm0 - mm7 + 39 => return error.UnsupportedRegister, // mxcsr + 40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs + 48 => return error.UnsupportedRegister, // tr + 49 => return error.UnsupportedRegister, // ldtr + 93...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512) + + else => return error.InvalidRegister, + } + } +}; + +const X86_64 = struct { + /// The order here intentionally matches the order of the DWARF register mappings. It's unclear + /// where those mappings actually originated from---the ordering of the first 4 registers seems + /// quite unusual---but it is currently convenient for us to match DWARF. + pub const Gpr = enum { + // zig fmt: off + rax, rdx, rcx, rbx, + rsi, rdi, rbp, rsp, + r8, r9, r10, r11, + r12, r13, r14, r15, + rip, + // zig fmt: on + }; + gprs: std.enums.EnumArray(Gpr, u64), + + pub inline fn current() X86_64 { + var ctx: X86_64 = undefined; + asm volatile ( + \\ movq %%rax, 0x00(%%rdi) + \\ movq %%rdx, 0x08(%%rdi) + \\ movq %%rcx, 0x10(%%rdi) + \\ movq %%rbx, 0x18(%%rdi) + \\ movq %%rsi, 0x20(%%rdi) + \\ movq %%rdi, 0x28(%%rdi) + \\ movq %%rbp, 0x30(%%rdi) + \\ movq %%rsp, 0x38(%%rdi) + \\ movq %%r8, 0x40(%%rdi) + \\ movq %%r9, 0x48(%%rdi) + \\ movq %%r10, 0x50(%%rdi) + \\ movq %%r11, 0x58(%%rdi) + \\ movq %%r12, 0x60(%%rdi) + \\ movq %%r13, 0x68(%%rdi) + \\ movq %%r14, 0x70(%%rdi) + \\ movq %%r15, 0x78(%%rdi) + \\ leaq (%%rip), %%rax + \\ movq %%rax, 0x80(%%rdi) + \\ movq 0x00(%%rdi), %%rax + : + : [gprs] "{rdi}" (&ctx.gprs.values), + : .{ .memory = true }); + return ctx; + } + + pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 { + // System V Application Binary Interface AMD64 Architecture Processor Supplement + // § 3.6.2 "DWARF Register Number Mapping" + switch (register_num) { + // The order of `Gpr` intentionally matches DWARF's mappings. + 0...16 => return @ptrCast(&ctx.gprs.values[register_num]), + + 17...32 => return error.UnsupportedRegister, // xmm0 - xmm15 + 33...40 => return error.UnsupportedRegister, // st0 - st7 + 41...48 => return error.UnsupportedRegister, // mm0 - mm7 + 49 => return error.UnsupportedRegister, // rflags + 50...55 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs + 58...59 => return error.UnsupportedRegister, // fs.base, gs.base + 62 => return error.UnsupportedRegister, // tr + 63 => return error.UnsupportedRegister, // ldtr + 64 => return error.UnsupportedRegister, // mxcsr + 65 => return error.UnsupportedRegister, // fcw + 66 => return error.UnsupportedRegister, // fsw + 67...82 => return error.UnsupportedRegister, // xmm16 - xmm31 (AVX-512) + 118...125 => return error.UnsupportedRegister, // k0 - k7 (AVX-512) + 130...145 => return error.UnsupportedRegister, // r16 - r31 (APX) + + else => return error.InvalidRegister, + } + } +}; + /// The native operating system's `ucontext_t` as seen in the third argument to signal handlers. /// /// These are dramatically simplified since we only need general-purpose registers and don't care From 727942bc03a2cd74d10c80894816456f9f66e977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Thu, 16 Oct 2025 18:54:38 +0200 Subject: [PATCH 09/10] std.debug.cpu_context: let the compiler deal with clobbers Otherwise we might be restoring registers we don't even need to. --- lib/std/debug/cpu_context.zig | 29 ++++++++--------------------- 1 file changed, 8 insertions(+), 21 deletions(-) diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index 5f8889aa9f..ac20c407e0 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -242,10 +242,9 @@ const Aarch64 = extern struct { \\ str x1, [x0, #0x0f8] \\ adr x1, . \\ str x1, [x0, #0x100] - \\ ldr x1, [x0, #0x008] : : [ctx] "{x0}" (&ctx), - : .{ .memory = true }); + : .{ .x1 = true, .memory = true }); return ctx; } @@ -469,10 +468,9 @@ const Hexagon = extern struct { \\ memw(r0 + #124) = r31 \\ r1 = pc \\ memw(r0 + #128) = r1 - \\ r1 = memw(r0 + #4) : : [ctx] "{r0}" (&ctx), - : .{ .memory = true }); + : .{ .r1 = true, .memory = true }); return ctx; } @@ -594,7 +592,6 @@ const LoongArch = extern struct { \\ bl 1f \\1: \\ st.d $ra, $t0, 256 - \\ ld.d $ra, $t0, 8 else \\ st.w $zero, $t0, 0 \\ st.w $ra, $t0, 4 @@ -631,10 +628,9 @@ const LoongArch = extern struct { \\ bl 1f \\1: \\ st.w $ra, $t0, 128 - \\ ld.w $ra, $t0, 4 : : [ctx] "{$r12}" (&ctx), - : .{ .memory = true }); + : .{ .r1 = true, .memory = true }); return ctx; } @@ -734,7 +730,6 @@ const Mips = extern struct { \\ bal 1f \\1: \\ sd $ra, 256($t0) - \\ ld $ra, 248($t0) \\ .set pop else \\ .set push @@ -776,11 +771,10 @@ const Mips = extern struct { \\ bal 1f \\1: \\ sw $ra, 128($t4) - \\ lw $ra, 124($t4) \\ .set pop : : [ctx] "{$12}" (&ctx), - : .{ .memory = true }); + : .{ .r31 = true, .memory = true }); return ctx; } @@ -916,7 +910,6 @@ const Powerpc = extern struct { \\1: \\ mflr 8 \\ std 8, 256(10) - \\ ld 8, 64(10) else \\ stw 0, 0(10) \\ stw 1, 4(10) @@ -956,10 +949,9 @@ const Powerpc = extern struct { \\1: \\ mflr 8 \\ stw 8, 128(10) - \\ lwz 8, 32(10) : : [ctx] "{r10}" (&ctx), - : .{ .lr = true, .memory = true }); + : .{ .r8 = true, .lr = true, .memory = true }); return ctx; } @@ -1068,7 +1060,6 @@ const Riscv = extern struct { \\ jal ra, 1f \\1: \\ sd ra, 256(t0) - \\ ld ra, 8(t0) else \\ sw zero, 0(t0) \\ sw ra, 4(t0) @@ -1105,10 +1096,9 @@ const Riscv = extern struct { \\ jal ra, 1f \\1: \\ sw ra, 128(t0) - \\ lw ra, 4(t0) : : [ctx] "{t0}" (&ctx), - : .{ .memory = true }); + : .{ .x1 = true, .memory = true }); return ctx; } @@ -1146,11 +1136,9 @@ const S390x = extern struct { \\ stm %%r0, %%r1, 128(%%r2) \\ larl %%r0, . \\ stg %%r0, 136(%%r2) - \\ lg %%r0, 0(%%r2) - \\ lg %%r1, 8(%%r2) : : [ctx] "{r2}" (&ctx), - : .{ .memory = true }); + : .{ .r0 = true, .r1 = true, .memory = true }); return ctx; } @@ -1458,10 +1446,9 @@ const X86_64 = struct { \\ movq %%r15, 0x78(%%rdi) \\ leaq (%%rip), %%rax \\ movq %%rax, 0x80(%%rdi) - \\ movq 0x00(%%rdi), %%rax : : [gprs] "{rdi}" (&ctx.gprs.values), - : .{ .memory = true }); + : .{ .rax = true, .memory = true }); return ctx; } From 9fd7f38600a61b665978db6203ce019b6fe3054b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20R=C3=B8nne=20Petersen?= Date: Fri, 17 Oct 2025 21:01:09 +0200 Subject: [PATCH 10/10] std.debug.cpu_context.Sparc: fix bad use of call delay slot --- lib/std/debug/cpu_context.zig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig index ac20c407e0..caed9e3289 100644 --- a/lib/std/debug/cpu_context.zig +++ b/lib/std/debug/cpu_context.zig @@ -1207,8 +1207,8 @@ const Sparc = extern struct { \\ stx %i6, [%l0 + 240] \\ stx %i7, [%l0 + 248] \\ call 1f - \\ stx %o7, [%l0 + 256] \\1: + \\ stx %o7, [%l0 + 256] else \\ std %g0, [%l0 + 0] \\ std %g2, [%l0 + 8] @@ -1227,8 +1227,8 @@ const Sparc = extern struct { \\ std %i4, [%l0 + 112] \\ std %i6, [%l0 + 120] \\ call 1f - \\ st %o7, [%l0 + 128] \\1: + \\ st %o7, [%l0 + 128] : : [ctx] "{l0}" (&ctx), : .{ .o7 = true, .memory = true });