authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2025-10-18 11:12:56+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2025-10-18 11:12:56+02:00
log631915ad961c83b8a2e4e36f11a278dae054372e
tree75a28dd93d57aba7c0d86d6e205b65cd151a7fec
parent35d079051467cc5edbb80ce48cbaeb5bddc92850
parent9fd7f38600a61b665978db6203ce019b6fe3054b
signaturebadge-check Signed by PGP key B5690EEEBB952194

Merge pull request #25600 from alexrp/std-debug-more-arches

`std.debug`: add CPU contexts and DWARF mappings for more architectures

4 files changed, 660 insertions(+), 279 deletions(-)

lib/std/debug.zig+4
...@@ -872,10 +872,14 @@ const StackIterator = union(enum) {...@@ -872,10 +872,14 @@ const StackIterator = union(enum) {
872 };872 };
873873
874 const fp_usability: FpUsability = switch (builtin.target.cpu.arch) {874 const fp_usability: FpUsability = switch (builtin.target.cpu.arch) {
875 .avr,
876 .csky,
875 .mips,877 .mips,
876 .mipsel,878 .mipsel,
877 .mips64,879 .mips64,
878 .mips64el,880 .mips64el,
881 .msp430,
882 .xcore,
879 => .useless,883 => .useless,
880 .hexagon,884 .hexagon,
881 // The PowerPC ABIs don't actually strictly require a backchain pointer; they allow omitting885 // The PowerPC ABIs don't actually strictly require a backchain pointer; they allow omitting
lib/std/debug/Dwarf.zig+18
...@@ -1430,14 +1430,20 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u16 {...@@ -1430,14 +1430,20 @@ pub fn compactUnwindToDwarfRegNumber(unwind_reg_number: u3) !u16 {
1430pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {1430pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
1431 return switch (arch) {1431 return switch (arch) {
1432 .aarch64, .aarch64_be => 32,1432 .aarch64, .aarch64_be => 32,
1433 .arc => 160,
1433 .arm, .armeb, .thumb, .thumbeb => 15,1434 .arm, .armeb, .thumb, .thumbeb => 15,
1435 .csky => 64,
1434 .hexagon => 76,1436 .hexagon => 76,
1437 .lanai => 2,
1435 .loongarch32, .loongarch64 => 64,1438 .loongarch32, .loongarch64 => 64,
1439 .m68k => 26,
1436 .mips, .mipsel, .mips64, .mips64el => 66,1440 .mips, .mipsel, .mips64, .mips64el => 66,
1441 .or1k => 35,
1437 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67,1442 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67,
1438 .riscv32, .riscv32be, .riscv64, .riscv64be => 65,1443 .riscv32, .riscv32be, .riscv64, .riscv64be => 65,
1439 .s390x => 65,1444 .s390x => 65,
1440 .sparc, .sparc64 => 32,1445 .sparc, .sparc64 => 32,
1446 .ve => 144,
1441 .x86 => 8,1447 .x86 => 8,
1442 .x86_64 => 16,1448 .x86_64 => 16,
1443 else => null,1449 else => null,
...@@ -1447,14 +1453,20 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {...@@ -1447,14 +1453,20 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
1447pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {1453pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
1448 return switch (arch) {1454 return switch (arch) {
1449 .aarch64, .aarch64_be => 29,1455 .aarch64, .aarch64_be => 29,
1456 .arc => 27,
1450 .arm, .armeb, .thumb, .thumbeb => 11,1457 .arm, .armeb, .thumb, .thumbeb => 11,
1458 .csky => 14,
1451 .hexagon => 30,1459 .hexagon => 30,
1460 .lanai => 5,
1452 .loongarch32, .loongarch64 => 22,1461 .loongarch32, .loongarch64 => 22,
1462 .m68k => 14,
1453 .mips, .mipsel, .mips64, .mips64el => 30,1463 .mips, .mipsel, .mips64, .mips64el => 30,
1464 .or1k => 2,
1454 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,1465 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,
1455 .riscv32, .riscv32be, .riscv64, .riscv64be => 8,1466 .riscv32, .riscv32be, .riscv64, .riscv64be => 8,
1456 .s390x => 11,1467 .s390x => 11,
1457 .sparc, .sparc64 => 30,1468 .sparc, .sparc64 => 30,
1469 .ve => 9,
1458 .x86 => 5,1470 .x86 => 5,
1459 .x86_64 => 6,1471 .x86_64 => 6,
1460 else => unreachable,1472 else => unreachable,
...@@ -1464,14 +1476,20 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {...@@ -1464,14 +1476,20 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
1464pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {1476pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {
1465 return switch (arch) {1477 return switch (arch) {
1466 .aarch64, .aarch64_be => 31,1478 .aarch64, .aarch64_be => 31,
1479 .arc => 28,
1467 .arm, .armeb, .thumb, .thumbeb => 13,1480 .arm, .armeb, .thumb, .thumbeb => 13,
1481 .csky => 14,
1468 .hexagon => 29,1482 .hexagon => 29,
1483 .lanai => 4,
1469 .loongarch32, .loongarch64 => 3,1484 .loongarch32, .loongarch64 => 3,
1485 .m68k => 15,
1470 .mips, .mipsel, .mips64, .mips64el => 29,1486 .mips, .mipsel, .mips64, .mips64el => 29,
1487 .or1k => 1,
1471 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,1488 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,
1472 .riscv32, .riscv32be, .riscv64, .riscv64be => 2,1489 .riscv32, .riscv32be, .riscv64, .riscv64be => 2,
1473 .s390x => 15,1490 .s390x => 15,
1474 .sparc, .sparc64 => 14,1491 .sparc, .sparc64 => 14,
1492 .ve => 11,
1475 .x86 => 4,1493 .x86 => 4,
1476 .x86_64 => 7,1494 .x86_64 => 7,
1477 else => unreachable,1495 else => unreachable,
lib/std/debug/SelfInfo/Elf.zig+9-3
...@@ -94,22 +94,27 @@ pub const can_unwind: bool = s: {...@@ -94,22 +94,27 @@ pub const can_unwind: bool = s: {
94 // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through94 // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through
95 // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format.95 // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format.
96 const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {96 const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {
97 // Not supported yet: arm, m68k97 // Not supported yet: arm
98 .haiku => &.{98 .haiku => &.{
99 .aarch64,99 .aarch64,
100 .m68k,
100 .riscv64,101 .riscv64,
101 .x86,102 .x86,
102 .x86_64,103 .x86_64,
103 },104 },
104 // Not supported yet: arc, arm/armeb/thumb/thumbeb, csky, m68k, or1k, xtensa105 // Not supported yet: arm/armeb/thumb/thumbeb, xtensa
105 .linux => &.{106 .linux => &.{
106 .aarch64,107 .aarch64,
107 .aarch64_be,108 .aarch64_be,
109 .arc,
110 .csky,
108 .loongarch64,111 .loongarch64,
112 .m68k,
109 .mips,113 .mips,
110 .mipsel,114 .mipsel,
111 .mips64,115 .mips64,
112 .mips64el,116 .mips64el,
117 .or1k,
113 .riscv32,118 .riscv32,
114 .riscv64,119 .riscv64,
115 .s390x,120 .s390x,
...@@ -131,10 +136,11 @@ pub const can_unwind: bool = s: {...@@ -131,10 +136,11 @@ pub const can_unwind: bool = s: {
131 .riscv64,136 .riscv64,
132 .x86_64,137 .x86_64,
133 },138 },
134 // Not supported yet: arm/armeb, m68k, mips64/mips64el139 // Not supported yet: arm/armeb, mips64/mips64el
135 .netbsd => &.{140 .netbsd => &.{
136 .aarch64,141 .aarch64,
137 .aarch64_be,142 .aarch64_be,
143 .m68k,
138 .mips,144 .mips,
139 .mipsel,145 .mipsel,
140 .x86,146 .x86,
lib/std/debug/cpu_context.zig+629-276
...@@ -5,13 +5,19 @@ pub const Native = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "CpuCont...@@ -5,13 +5,19 @@ pub const Native = if (@hasDecl(root, "debug") and @hasDecl(root.debug, "CpuCont
5 root.debug.CpuContext5 root.debug.CpuContext
6else switch (native_arch) {6else switch (native_arch) {
7 .aarch64, .aarch64_be => Aarch64,7 .aarch64, .aarch64_be => Aarch64,
8 .arc => Arc,
8 .arm, .armeb, .thumb, .thumbeb => Arm,9 .arm, .armeb, .thumb, .thumbeb => Arm,
10 .csky => Csky,
9 .hexagon => Hexagon,11 .hexagon => Hexagon,
12 .lanai => Lanai,
10 .loongarch32, .loongarch64 => LoongArch,13 .loongarch32, .loongarch64 => LoongArch,
14 .m68k => M68k,
11 .mips, .mipsel, .mips64, .mips64el => Mips,15 .mips, .mipsel, .mips64, .mips64el => Mips,
16 .or1k => Or1k,
12 .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc,17 .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc,
13 .sparc, .sparc64 => Sparc,18 .sparc, .sparc64 => Sparc,
14 .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv,19 .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv,
20 .ve => Ve,
15 .s390x => S390x,21 .s390x => S390x,
16 .x86 => X86,22 .x86 => X86,
17 .x86_64 => X86_64,23 .x86_64 => X86_64,
...@@ -30,7 +36,20 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {...@@ -30,7 +36,20 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
30 const uc: *const signal_ucontext_t = @ptrCast(@alignCast(ctx_ptr));36 const uc: *const signal_ucontext_t = @ptrCast(@alignCast(ctx_ptr));
3137
32 // Deal with some special cases first.38 // Deal with some special cases first.
33 if (native_arch.isMIPS32() and native_os == .linux) {39 if (native_arch == .arc and native_os == .linux) {
40 var native: Native = .{
41 .r = [_]u32{ uc.mcontext.r31, uc.mcontext.r30, 0, uc.mcontext.r28 } ++
42 uc.mcontext.r27_26 ++
43 uc.mcontext.r25_13 ++
44 uc.mcontext.r12_0,
45 .pcl = uc.mcontext.pcl,
46 };
47
48 // I have no idea why the kernel is storing these registers in such a bizarre order...
49 std.mem.reverse(native.r[0..]);
50
51 return native;
52 } else if (native_arch.isMIPS32() and native_os == .linux) {
34 // The O32 kABI uses 64-bit fields for some reason.53 // The O32 kABI uses 64-bit fields for some reason.
35 return .{54 return .{
36 .r = s: {55 .r = s: {
...@@ -72,10 +91,22 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {...@@ -72,10 +91,22 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
72 .sp = uc.mcontext.sp,91 .sp = uc.mcontext.sp,
73 .pc = uc.mcontext.pc,92 .pc = uc.mcontext.pc,
74 },93 },
94 .csky => .{
95 .r = uc.mcontext.r0_13 ++
96 [_]u32{ uc.mcontext.r14, uc.mcontext.r15 } ++
97 uc.mcontext.r16_30 ++
98 [_]u32{uc.mcontext.r31},
99 .pc = uc.mcontext.pc,
100 },
75 .hexagon, .loongarch32, .loongarch64, .mips, .mipsel, .mips64, .mips64el, .or1k => .{101 .hexagon, .loongarch32, .loongarch64, .mips, .mipsel, .mips64, .mips64el, .or1k => .{
76 .r = uc.mcontext.r,102 .r = uc.mcontext.r,
77 .pc = uc.mcontext.pc,103 .pc = uc.mcontext.pc,
78 },104 },
105 .m68k => .{
106 .d = uc.mcontext.d,
107 .a = uc.mcontext.a,
108 .pc = uc.mcontext.pc,
109 },
79 .powerpc, .powerpcle, .powerpc64, .powerpc64le => .{110 .powerpc, .powerpcle, .powerpc64, .powerpc64le => .{
80 .r = uc.mcontext.r,111 .r = uc.mcontext.r,
81 .pc = uc.mcontext.pc,112 .pc = uc.mcontext.pc,
...@@ -181,129 +212,124 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {...@@ -181,129 +212,124 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {
181 };212 };
182}213}
183214
184const X86 = struct {215/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
185 /// The first 8 registers here intentionally match the order of registers in the x86 instruction216const Aarch64 = extern struct {
186 /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,217 /// The numbered general-purpose registers X0 - X30.
187 /// among other things.218 x: [31]u64,
188 pub const Gpr = enum {219 sp: u64,
189 // zig fmt: off220 pc: u64,
190 eax, ecx, edx, ebx,
191 esp, ebp, esi, edi,
192 eip,
193 // zig fmt: on
194 };
195 gprs: std.enums.EnumArray(Gpr, u32),
196221
197 pub inline fn current() X86 {222 pub inline fn current() Aarch64 {
198 var ctx: X86 = undefined;223 var ctx: Aarch64 = undefined;
199 asm volatile (224 asm volatile (
200 \\movl %%eax, 0x00(%%edi)225 \\ stp x0, x1, [x0, #0x000]
201 \\movl %%ecx, 0x04(%%edi)226 \\ stp x2, x3, [x0, #0x010]
202 \\movl %%edx, 0x08(%%edi)227 \\ stp x4, x5, [x0, #0x020]
203 \\movl %%ebx, 0x0c(%%edi)228 \\ stp x6, x7, [x0, #0x030]
204 \\movl %%esp, 0x10(%%edi)229 \\ stp x8, x9, [x0, #0x040]
205 \\movl %%ebp, 0x14(%%edi)230 \\ stp x10, x11, [x0, #0x050]
206 \\movl %%esi, 0x18(%%edi)231 \\ stp x12, x13, [x0, #0x060]
207 \\movl %%edi, 0x1c(%%edi)232 \\ stp x14, x15, [x0, #0x070]
208 \\call 1f233 \\ stp x16, x17, [x0, #0x080]
209 \\1:234 \\ stp x18, x19, [x0, #0x090]
210 \\popl 0x20(%%edi)235 \\ stp x20, x21, [x0, #0x0a0]
236 \\ stp x22, x23, [x0, #0x0b0]
237 \\ stp x24, x25, [x0, #0x0c0]
238 \\ stp x26, x27, [x0, #0x0d0]
239 \\ stp x28, x29, [x0, #0x0e0]
240 \\ str x30, [x0, #0x0f0]
241 \\ mov x1, sp
242 \\ str x1, [x0, #0x0f8]
243 \\ adr x1, .
244 \\ str x1, [x0, #0x100]
211 :245 :
212 : [gprs] "{edi}" (&ctx.gprs.values),246 : [ctx] "{x0}" (&ctx),
213 : .{ .memory = true });247 : .{ .x1 = true, .memory = true });
214 return ctx;248 return ctx;
215 }249 }
216250
217 pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 {251 pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {
218 // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1252 // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"
219 // § 2.4.2 "DWARF Register Number Mapping"
220 switch (register_num) {253 switch (register_num) {
221 // The order of `Gpr` intentionally matches DWARF's mappings.254 0...30 => return @ptrCast(&ctx.x[register_num]),
222 //255 31 => return @ptrCast(&ctx.sp),
223 // x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind256 32 => return @ptrCast(&ctx.pc),
224 // information is from `__eh_frame`). This deviation is not considered here, because
225 // x86-macos is a deprecated target which is not supported by the Zig Standard Library.
226 0...8 => return @ptrCast(&ctx.gprs.values[register_num]),
227257
228 9 => return error.UnsupportedRegister, // eflags258 33 => return error.UnsupportedRegister, // ELR_mode
229 11...18 => return error.UnsupportedRegister, // st0 - st7259 34 => return error.UnsupportedRegister, // RA_SIGN_STATE
230 21...28 => return error.UnsupportedRegister, // xmm0 - xmm7260 35 => return error.UnsupportedRegister, // TPIDRRO_ELO
231 29...36 => return error.UnsupportedRegister, // mm0 - mm7261 36 => return error.UnsupportedRegister, // TPIDR_ELO
232 39 => return error.UnsupportedRegister, // mxcsr262 37 => return error.UnsupportedRegister, // TPIDR_EL1
233 40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs263 38 => return error.UnsupportedRegister, // TPIDR_EL2
234 48 => return error.UnsupportedRegister, // tr264 39 => return error.UnsupportedRegister, // TPIDR_EL3
235 49 => return error.UnsupportedRegister, // ldtr265 40...45 => return error.UnsupportedRegister, // Reserved
236 93...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)266 46 => return error.UnsupportedRegister, // VG
267 47 => return error.UnsupportedRegister, // FFR
268 48...63 => return error.UnsupportedRegister, // P0 - P15
269 64...95 => return error.UnsupportedRegister, // V0 - V31
270 96...127 => return error.UnsupportedRegister, // Z0 - Z31
237271
238 else => return error.InvalidRegister,272 else => return error.InvalidRegister,
239 }273 }
240 }274 }
241};275};
242276
243const X86_64 = struct {277/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
244 /// The order here intentionally matches the order of the DWARF register mappings. It's unclear278const Arc = extern struct {
245 /// where those mappings actually originated from---the ordering of the first 4 registers seems279 /// The numbered general-purpose registers r0 - r31.
246 /// quite unusual---but it is currently convenient for us to match DWARF.280 r: [32]u32,
247 pub const Gpr = enum {281 pcl: u32,
248 // zig fmt: off
249 rax, rdx, rcx, rbx,
250 rsi, rdi, rbp, rsp,
251 r8, r9, r10, r11,
252 r12, r13, r14, r15,
253 rip,
254 // zig fmt: on
255 };
256 gprs: std.enums.EnumArray(Gpr, u64),
257282
258 pub inline fn current() X86_64 {283 pub inline fn current() Arc {
259 var ctx: X86_64 = undefined;284 var ctx: Arc = undefined;
260 asm volatile (285 asm volatile (
261 \\movq %%rax, 0x00(%%rdi)286 \\ st r0, [r8, 0]
262 \\movq %%rdx, 0x08(%%rdi)287 \\ st r1, [r8, 4]
263 \\movq %%rcx, 0x10(%%rdi)288 \\ st r2, [r8, 8]
264 \\movq %%rbx, 0x18(%%rdi)289 \\ st r3, [r8, 12]
265 \\movq %%rsi, 0x20(%%rdi)290 \\ st r4, [r8, 16]
266 \\movq %%rdi, 0x28(%%rdi)291 \\ st r5, [r8, 20]
267 \\movq %%rbp, 0x30(%%rdi)292 \\ st r6, [r8, 24]
268 \\movq %%rsp, 0x38(%%rdi)293 \\ st r7, [r8, 28]
269 \\movq %%r8, 0x40(%%rdi)294 \\ st r8, [r8, 32]
270 \\movq %%r9, 0x48(%%rdi)295 \\ st r9, [r8, 36]
271 \\movq %%r10, 0x50(%%rdi)296 \\ st r10, [r8, 40]
272 \\movq %%r11, 0x58(%%rdi)297 \\ st r11, [r8, 44]
273 \\movq %%r12, 0x60(%%rdi)298 \\ st r12, [r8, 48]
274 \\movq %%r13, 0x68(%%rdi)299 \\ st r13, [r8, 52]
275 \\movq %%r14, 0x70(%%rdi)300 \\ st r14, [r8, 56]
276 \\movq %%r15, 0x78(%%rdi)301 \\ st r15, [r8, 60]
277 \\leaq (%%rip), %%rax302 \\ st r16, [r8, 64]
278 \\movq %%rax, 0x80(%%rdi)303 \\ st r17, [r8, 68]
279 \\movq 0x00(%%rdi), %%rax304 \\ st r18, [r8, 72]
305 \\ st r19, [r8, 76]
306 \\ st r20, [r8, 80]
307 \\ st r21, [r8, 84]
308 \\ st r22, [r8, 88]
309 \\ st r23, [r8, 92]
310 \\ st r24, [r8, 96]
311 \\ st r25, [r8, 100]
312 \\ st r26, [r8, 104]
313 \\ st r27, [r8, 108]
314 \\ st r28, [r8, 112]
315 \\ st r29, [r8, 116]
316 \\ st r30, [r8, 120]
317 \\ st r31, [r8, 124]
318 \\ st pcl, [r8, 128]
280 :319 :
281 : [gprs] "{rdi}" (&ctx.gprs.values),320 : [ctx] "{r8}" (&ctx),
282 : .{ .memory = true });321 : .{ .memory = true });
283 return ctx;322 return ctx;
284 }323 }
285324
286 pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 {325 pub fn dwarfRegisterBytes(ctx: *Arc, register_num: u16) DwarfRegisterError![]u8 {
287 // System V Application Binary Interface AMD64 Architecture Processor Supplement
288 // § 3.6.2 "DWARF Register Number Mapping"
289 switch (register_num) {326 switch (register_num) {
290 // The order of `Gpr` intentionally matches DWARF's mappings.327 0...31 => return @ptrCast(&ctx.r[register_num]),
291 0...16 => return @ptrCast(&ctx.gprs.values[register_num]),328 160 => return @ptrCast(&ctx.pcl),
292329
293 17...32 => return error.UnsupportedRegister, // xmm0 - xmm15330 32...57 => return error.UnsupportedRegister, // Extension Core Registers
294 33...40 => return error.UnsupportedRegister, // st0 - st7331 58...127 => return error.UnsupportedRegister, // Reserved
295 41...48 => return error.UnsupportedRegister, // mm0 - mm7332 128...159 => return error.UnsupportedRegister, // f0 - f31
296 49 => return error.UnsupportedRegister, // rflags
297 50...55 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs
298 58...59 => return error.UnsupportedRegister, // fs.base, gs.base
299 62 => return error.UnsupportedRegister, // tr
300 63 => return error.UnsupportedRegister, // ldtr
301 64 => return error.UnsupportedRegister, // mxcsr
302 65 => return error.UnsupportedRegister, // fcw
303 66 => return error.UnsupportedRegister, // fsw
304 67...82 => return error.UnsupportedRegister, // xmm16 - xmm31 (AVX-512)
305 118...125 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
306 130...145 => return error.UnsupportedRegister, // r16 - r31 (APX)
307333
308 else => return error.InvalidRegister,334 else => return error.InvalidRegister,
309 }335 }
...@@ -317,11 +343,11 @@ const Arm = struct {...@@ -317,11 +343,11 @@ const Arm = struct {
317 pub inline fn current() Arm {343 pub inline fn current() Arm {
318 var ctx: Arm = undefined;344 var ctx: Arm = undefined;
319 asm volatile (345 asm volatile (
320 \\// For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm.346 \\ // For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm.
321 \\stm r0, {r0-r12}347 \\ stm r0, {r0-r12}
322 \\str r13, [r0, #0x34]348 \\ str r13, [r0, #0x34]
323 \\str r14, [r0, #0x38]349 \\ str r14, [r0, #0x38]
324 \\str r15, [r0, #0x3c]350 \\ str r15, [r0, #0x3c]
325 :351 :
326 : [r] "{r0}" (&ctx.r),352 : [r] "{r0}" (&ctx.r),
327 : .{ .memory = true });353 : .{ .memory = true });
...@@ -369,62 +395,30 @@ const Arm = struct {...@@ -369,62 +395,30 @@ const Arm = struct {
369};395};
370396
371/// This is an `extern struct` so that inline assembly in `current` can use field offsets.397/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
372const Aarch64 = extern struct {398const Csky = extern struct {
373 /// The numbered general-purpose registers X0 - X30.399 /// The numbered general-purpose registers r0 - r31.
374 x: [31]u64,400 r: [32]u32,
375 sp: u64,401 pc: u32,
376 pc: u64,
377402
378 pub inline fn current() Aarch64 {403 pub inline fn current() Csky {
379 var ctx: Aarch64 = undefined;404 var ctx: Csky = undefined;
380 asm volatile (405 asm volatile (
381 \\stp x0, x1, [x0, #0x000]406 \\ stm r0-r31, (t0)
382 \\stp x2, x3, [x0, #0x010]407 \\ grs t1, 1f
383 \\stp x4, x5, [x0, #0x020]408 \\1:
384 \\stp x6, x7, [x0, #0x030]409 \\ st32.w t1, (t0, 128)
385 \\stp x8, x9, [x0, #0x040]
386 \\stp x10, x11, [x0, #0x050]
387 \\stp x12, x13, [x0, #0x060]
388 \\stp x14, x15, [x0, #0x070]
389 \\stp x16, x17, [x0, #0x080]
390 \\stp x18, x19, [x0, #0x090]
391 \\stp x20, x21, [x0, #0x0a0]
392 \\stp x22, x23, [x0, #0x0b0]
393 \\stp x24, x25, [x0, #0x0c0]
394 \\stp x26, x27, [x0, #0x0d0]
395 \\stp x28, x29, [x0, #0x0e0]
396 \\str x30, [x0, #0x0f0]
397 \\mov x1, sp
398 \\str x1, [x0, #0x0f8]
399 \\adr x1, .
400 \\str x1, [x0, #0x100]
401 \\ldr x1, [x0, #0x008]
402 :410 :
403 : [gprs] "{x0}" (&ctx),411 : [ctx] "{r12}" (&ctx),
404 : .{ .memory = true });412 : .{ .r13 = true, .memory = true });
405 return ctx;413 return ctx;
406 }414 }
407415
408 pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {416 pub fn dwarfRegisterBytes(ctx: *Csky, register_num: u16) DwarfRegisterError![]u8 {
409 // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"
410 switch (register_num) {417 switch (register_num) {
411 0...30 => return @ptrCast(&ctx.x[register_num]),418 0...31 => return @ptrCast(&ctx.r[register_num]),
412 31 => return @ptrCast(&ctx.sp),419 64 => return @ptrCast(&ctx.pc),
413 32 => return @ptrCast(&ctx.pc),
414420
415 33 => return error.UnsupportedRegister, // ELR_mode421 32...63 => return error.UnsupportedRegister, // f0 - f31
416 34 => return error.UnsupportedRegister, // RA_SIGN_STATE
417 35 => return error.UnsupportedRegister, // TPIDRRO_ELO
418 36 => return error.UnsupportedRegister, // TPIDR_ELO
419 37 => return error.UnsupportedRegister, // TPIDR_EL1
420 38 => return error.UnsupportedRegister, // TPIDR_EL2
421 39 => return error.UnsupportedRegister, // TPIDR_EL3
422 40...45 => return error.UnsupportedRegister, // Reserved
423 46 => return error.UnsupportedRegister, // VG
424 47 => return error.UnsupportedRegister, // FFR
425 48...63 => return error.UnsupportedRegister, // P0 - P15
426 64...95 => return error.UnsupportedRegister, // V0 - V31
427 96...127 => return error.UnsupportedRegister, // Z0 - Z31
428422
429 else => return error.InvalidRegister,423 else => return error.InvalidRegister,
430 }424 }
...@@ -474,10 +468,9 @@ const Hexagon = extern struct {...@@ -474,10 +468,9 @@ const Hexagon = extern struct {
474 \\ memw(r0 + #124) = r31468 \\ memw(r0 + #124) = r31
475 \\ r1 = pc469 \\ r1 = pc
476 \\ memw(r0 + #128) = r1470 \\ memw(r0 + #128) = r1
477 \\ r1 = memw(r0 + #4)
478 :471 :
479 : [gprs] "{r0}" (&ctx),472 : [ctx] "{r0}" (&ctx),
480 : .{ .memory = true });473 : .{ .r1 = true, .memory = true });
481 return ctx;474 return ctx;
482 }475 }
483476
...@@ -499,6 +492,60 @@ const Hexagon = extern struct {...@@ -499,6 +492,60 @@ const Hexagon = extern struct {
499 }492 }
500};493};
501494
495/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
496const Lanai = extern struct {
497 r: [32]u32,
498
499 pub inline fn current() Lanai {
500 var ctx: Lanai = undefined;
501 asm volatile (
502 \\ st %%r0, 0[r9]
503 \\ st %%r1, 4[r9]
504 \\ st %%r2, 8[r9]
505 \\ st %%r3, 12[r9]
506 \\ st %%r4, 16[r9]
507 \\ st %%r5, 20[r9]
508 \\ st %%r6, 24[r9]
509 \\ st %%r7, 28[r9]
510 \\ st %%r8, 32[r9]
511 \\ st %%r9, 36[r9]
512 \\ st %%r10, 40[r9]
513 \\ st %%r11, 44[r9]
514 \\ st %%r12, 48[r9]
515 \\ st %%r13, 52[r9]
516 \\ st %%r14, 56[r9]
517 \\ st %%r15, 60[r9]
518 \\ st %%r16, 64[r9]
519 \\ st %%r17, 68[r9]
520 \\ st %%r18, 72[r9]
521 \\ st %%r19, 76[r9]
522 \\ st %%r20, 80[r9]
523 \\ st %%r21, 84[r9]
524 \\ st %%r22, 88[r9]
525 \\ st %%r23, 92[r9]
526 \\ st %%r24, 96[r9]
527 \\ st %%r25, 100[r9]
528 \\ st %%r26, 104[r9]
529 \\ st %%r27, 108[r9]
530 \\ st %%r28, 112[r9]
531 \\ st %%r29, 116[r9]
532 \\ st %%r30, 120[r9]
533 \\ st %%r31, 124[r9]
534 :
535 : [ctx] "{r9}" (&ctx),
536 : .{ .memory = true });
537 return ctx;
538 }
539
540 pub fn dwarfRegisterBytes(ctx: *Lanai, register_num: u16) DwarfRegisterError![]u8 {
541 switch (register_num) {
542 0...31 => return @ptrCast(&ctx.s[register_num]),
543
544 else => return error.InvalidRegister,
545 }
546 }
547};
548
502/// This is an `extern struct` so that inline assembly in `current` can use field offsets.549/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
503const LoongArch = extern struct {550const LoongArch = extern struct {
504 /// The numbered general-purpose registers r0 - r31. r0 must be zero.551 /// The numbered general-purpose registers r0 - r31. r0 must be zero.
...@@ -545,7 +592,6 @@ const LoongArch = extern struct {...@@ -545,7 +592,6 @@ const LoongArch = extern struct {
545 \\ bl 1f592 \\ bl 1f
546 \\1:593 \\1:
547 \\ st.d $ra, $t0, 256594 \\ st.d $ra, $t0, 256
548 \\ ld.d $ra, $t0, 8
549 else595 else
550 \\ st.w $zero, $t0, 0596 \\ st.w $zero, $t0, 0
551 \\ st.w $ra, $t0, 4597 \\ st.w $ra, $t0, 4
...@@ -582,10 +628,9 @@ const LoongArch = extern struct {...@@ -582,10 +628,9 @@ const LoongArch = extern struct {
582 \\ bl 1f628 \\ bl 1f
583 \\1:629 \\1:
584 \\ st.w $ra, $t0, 128630 \\ st.w $ra, $t0, 128
585 \\ ld.w $ra, $t0, 4
586 :631 :
587 : [gprs] "{$r12}" (&ctx),632 : [ctx] "{$r12}" (&ctx),
588 : .{ .memory = true });633 : .{ .r1 = true, .memory = true });
589 return ctx;634 return ctx;
590 }635 }
591636
...@@ -601,6 +646,40 @@ const LoongArch = extern struct {...@@ -601,6 +646,40 @@ const LoongArch = extern struct {
601 }646 }
602};647};
603648
649/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
650const M68k = extern struct {
651 /// The numbered data registers d0 - d7.
652 d: [8]u32,
653 /// The numbered address registers a0 - a7.
654 a: [8]u32,
655 pc: u32,
656
657 pub inline fn current() M68k {
658 var ctx: M68k = undefined;
659 asm volatile (
660 \\ movem.l %%d0 - %%a7, (%%a0)
661 \\ lea.l (%%pc), %%a1
662 \\ move.l %%a1, (%%a0, 64)
663 :
664 : [ctx] "{a0}" (&ctx),
665 : .{ .a1 = true, .memory = true });
666 return ctx;
667 }
668
669 pub fn dwarfRegisterBytes(ctx: *M68k, register_num: u16) DwarfRegisterError![]u8 {
670 switch (register_num) {
671 0...7 => return @ptrCast(&ctx.d[register_num]),
672 8...15 => return @ptrCast(&ctx.a[register_num - 8]),
673 26 => return @ptrCast(&ctx.pc),
674
675 16...23 => return error.UnsupportedRegister, // fp0 - fp7
676 24...25 => return error.UnsupportedRegister, // Return columns in GCC...?
677
678 else => return error.InvalidRegister,
679 }
680 }
681};
682
604/// This is an `extern struct` so that inline assembly in `current` can use field offsets.683/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
605const Mips = extern struct {684const Mips = extern struct {
606 /// The numbered general-purpose registers r0 - r31. r0 must be zero.685 /// The numbered general-purpose registers r0 - r31. r0 must be zero.
...@@ -651,7 +730,6 @@ const Mips = extern struct {...@@ -651,7 +730,6 @@ const Mips = extern struct {
651 \\ bal 1f730 \\ bal 1f
652 \\1:731 \\1:
653 \\ sd $ra, 256($t0)732 \\ sd $ra, 256($t0)
654 \\ ld $ra, 248($t0)
655 \\ .set pop733 \\ .set pop
656 else734 else
657 \\ .set push735 \\ .set push
...@@ -693,11 +771,10 @@ const Mips = extern struct {...@@ -693,11 +771,10 @@ const Mips = extern struct {
693 \\ bal 1f771 \\ bal 1f
694 \\1:772 \\1:
695 \\ sw $ra, 128($t4)773 \\ sw $ra, 128($t4)
696 \\ lw $ra, 124($t4)
697 \\ .set pop774 \\ .set pop
698 :775 :
699 : [gprs] "{$12}" (&ctx),776 : [ctx] "{$12}" (&ctx),
700 : .{ .memory = true });777 : .{ .r31 = true, .memory = true });
701 return ctx;778 return ctx;
702 }779 }
703780
...@@ -723,6 +800,66 @@ const Mips = extern struct {...@@ -723,6 +800,66 @@ const Mips = extern struct {
723 }800 }
724};801};
725802
803/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
804const Or1k = extern struct {
805 /// The numbered general-purpose registers r0 - r31.
806 r: [32]u32,
807 pc: u32,
808
809 pub inline fn current() Or1k {
810 var ctx: Or1k = undefined;
811 asm volatile (
812 \\ l.sw 0(r15), r0
813 \\ l.sw 4(r15), r1
814 \\ l.sw 8(r15), r2
815 \\ l.sw 12(r15), r3
816 \\ l.sw 16(r15), r4
817 \\ l.sw 20(r15), r5
818 \\ l.sw 24(r15), r6
819 \\ l.sw 28(r15), r7
820 \\ l.sw 32(r15), r8
821 \\ l.sw 36(r15), r9
822 \\ l.sw 40(r15), r10
823 \\ l.sw 44(r15), r11
824 \\ l.sw 48(r15), r12
825 \\ l.sw 52(r15), r13
826 \\ l.sw 56(r15), r14
827 \\ l.sw 60(r15), r15
828 \\ l.sw 64(r15), r16
829 \\ l.sw 68(r15), r17
830 \\ l.sw 72(r15), r18
831 \\ l.sw 76(r15), r19
832 \\ l.sw 80(r15), r20
833 \\ l.sw 84(r15), r21
834 \\ l.sw 88(r15), r22
835 \\ l.sw 92(r15), r23
836 \\ l.sw 96(r15), r24
837 \\ l.sw 100(r15), r25
838 \\ l.sw 104(r15), r26
839 \\ l.sw 108(r15), r27
840 \\ l.sw 112(r15), r28
841 \\ l.sw 116(r15), r29
842 \\ l.sw 120(r15), r30
843 \\ l.sw 124(r15), r31
844 \\ l.jal 1f
845 \\1:
846 \\ l.sw 128(r15), r9
847 :
848 : [ctx] "{r15}" (&ctx),
849 : .{ .r9 = true, .memory = true });
850 return ctx;
851 }
852
853 pub fn dwarfRegisterBytes(ctx: *Or1k, register_num: u16) DwarfRegisterError![]u8 {
854 switch (register_num) {
855 0...31 => return @ptrCast(&ctx.r[register_num]),
856 35 => return @ptrCast(&ctx.pc),
857
858 else => return error.InvalidRegister,
859 }
860 }
861};
862
726/// This is an `extern struct` so that inline assembly in `current` can use field offsets.863/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
727const Powerpc = extern struct {864const Powerpc = extern struct {
728 /// The numbered general-purpose registers r0 - r31.865 /// The numbered general-purpose registers r0 - r31.
...@@ -773,7 +910,6 @@ const Powerpc = extern struct {...@@ -773,7 +910,6 @@ const Powerpc = extern struct {
773 \\1:910 \\1:
774 \\ mflr 8911 \\ mflr 8
775 \\ std 8, 256(10)912 \\ std 8, 256(10)
776 \\ ld 8, 64(10)
777 else913 else
778 \\ stw 0, 0(10)914 \\ stw 0, 0(10)
779 \\ stw 1, 4(10)915 \\ stw 1, 4(10)
...@@ -813,10 +949,9 @@ const Powerpc = extern struct {...@@ -813,10 +949,9 @@ const Powerpc = extern struct {
813 \\1:949 \\1:
814 \\ mflr 8950 \\ mflr 8
815 \\ stw 8, 128(10)951 \\ stw 8, 128(10)
816 \\ lwz 8, 32(10)
817 :952 :
818 : [gprs] "{r10}" (&ctx),953 : [ctx] "{r10}" (&ctx),
819 : .{ .lr = true, .memory = true });954 : .{ .r8 = true, .lr = true, .memory = true });
820 return ctx;955 return ctx;
821 }956 }
822957
...@@ -879,104 +1014,6 @@ const Powerpc = extern struct {...@@ -879,104 +1014,6 @@ const Powerpc = extern struct {
879 }1014 }
880};1015};
8811016
882/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
883const Sparc = extern struct {
884 g: [8]Gpr,
885 o: [8]Gpr,
886 l: [8]Gpr,
887 i: [8]Gpr,
888 pc: Gpr,
889
890 pub const Gpr = if (native_arch == .sparc64) u64 else u32;
891
892 pub inline fn current() Sparc {
893 flushWindows();
894
895 var ctx: Sparc = undefined;
896 asm volatile (if (Gpr == u64)
897 \\ stx %g0, [%l0 + 0]
898 \\ stx %g1, [%l0 + 8]
899 \\ stx %g2, [%l0 + 16]
900 \\ stx %g3, [%l0 + 24]
901 \\ stx %g4, [%l0 + 32]
902 \\ stx %g5, [%l0 + 40]
903 \\ stx %g6, [%l0 + 48]
904 \\ stx %g7, [%l0 + 56]
905 \\ stx %o0, [%l0 + 64]
906 \\ stx %o1, [%l0 + 72]
907 \\ stx %o2, [%l0 + 80]
908 \\ stx %o3, [%l0 + 88]
909 \\ stx %o4, [%l0 + 96]
910 \\ stx %o5, [%l0 + 104]
911 \\ stx %o6, [%l0 + 112]
912 \\ stx %o7, [%l0 + 120]
913 \\ stx %l0, [%l0 + 128]
914 \\ stx %l1, [%l0 + 136]
915 \\ stx %l2, [%l0 + 144]
916 \\ stx %l3, [%l0 + 152]
917 \\ stx %l4, [%l0 + 160]
918 \\ stx %l5, [%l0 + 168]
919 \\ stx %l6, [%l0 + 176]
920 \\ stx %l7, [%l0 + 184]
921 \\ stx %i0, [%l0 + 192]
922 \\ stx %i1, [%l0 + 200]
923 \\ stx %i2, [%l0 + 208]
924 \\ stx %i3, [%l0 + 216]
925 \\ stx %i4, [%l0 + 224]
926 \\ stx %i5, [%l0 + 232]
927 \\ stx %i6, [%l0 + 240]
928 \\ stx %i7, [%l0 + 248]
929 \\ call 1f
930 \\ stx %o7, [%l0 + 256]
931 \\1:
932 else
933 \\ std %g0, [%l0 + 0]
934 \\ std %g2, [%l0 + 8]
935 \\ std %g4, [%l0 + 16]
936 \\ std %g6, [%l0 + 24]
937 \\ std %o0, [%l0 + 32]
938 \\ std %o2, [%l0 + 40]
939 \\ std %o4, [%l0 + 48]
940 \\ std %o6, [%l0 + 56]
941 \\ std %l0, [%l0 + 64]
942 \\ std %l2, [%l0 + 72]
943 \\ std %l4, [%l0 + 80]
944 \\ std %l6, [%l0 + 88]
945 \\ std %i0, [%l0 + 96]
946 \\ std %i2, [%l0 + 104]
947 \\ std %i4, [%l0 + 112]
948 \\ std %i6, [%l0 + 120]
949 \\ call 1f
950 \\ st %o7, [%l0 + 128]
951 \\1:
952 :
953 : [gprs] "{l0}" (&ctx),
954 : .{ .o7 = true, .memory = true });
955 return ctx;
956 }
957
958 noinline fn flushWindows() void {
959 // Flush all register windows except the current one (hence `noinline`). This ensures that
960 // we actually see meaningful data on the stack when we walk the frame chain.
961 if (comptime builtin.target.cpu.has(.sparc, .v9))
962 asm volatile ("flushw" ::: .{ .memory = true })
963 else
964 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
965 }
966
967 pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 {
968 switch (register_num) {
969 0...7 => return @ptrCast(&ctx.g[register_num]),
970 8...15 => return @ptrCast(&ctx.o[register_num - 8]),
971 16...23 => return @ptrCast(&ctx.l[register_num - 16]),
972 24...31 => return @ptrCast(&ctx.i[register_num - 24]),
973 32 => return @ptrCast(&ctx.pc),
974
975 else => return error.InvalidRegister,
976 }
977 }
978};
979
980/// This is an `extern struct` so that inline assembly in `current` can use field offsets.1017/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
981const Riscv = extern struct {1018const Riscv = extern struct {
982 /// The numbered general-purpose registers r0 - r31. r0 must be zero.1019 /// The numbered general-purpose registers r0 - r31. r0 must be zero.
...@@ -1023,7 +1060,6 @@ const Riscv = extern struct {...@@ -1023,7 +1060,6 @@ const Riscv = extern struct {
1023 \\ jal ra, 1f1060 \\ jal ra, 1f
1024 \\1:1061 \\1:
1025 \\ sd ra, 256(t0)1062 \\ sd ra, 256(t0)
1026 \\ ld ra, 8(t0)
1027 else1063 else
1028 \\ sw zero, 0(t0)1064 \\ sw zero, 0(t0)
1029 \\ sw ra, 4(t0)1065 \\ sw ra, 4(t0)
...@@ -1060,10 +1096,9 @@ const Riscv = extern struct {...@@ -1060,10 +1096,9 @@ const Riscv = extern struct {
1060 \\ jal ra, 1f1096 \\ jal ra, 1f
1061 \\1:1097 \\1:
1062 \\ sw ra, 128(t0)1098 \\ sw ra, 128(t0)
1063 \\ lw ra, 4(t0)
1064 :1099 :
1065 : [gprs] "{t0}" (&ctx),1100 : [ctx] "{t0}" (&ctx),
1066 : .{ .memory = true });1101 : .{ .x1 = true, .memory = true });
1067 return ctx;1102 return ctx;
1068 }1103 }
10691104
...@@ -1101,11 +1136,9 @@ const S390x = extern struct {...@@ -1101,11 +1136,9 @@ const S390x = extern struct {
1101 \\ stm %%r0, %%r1, 128(%%r2)1136 \\ stm %%r0, %%r1, 128(%%r2)
1102 \\ larl %%r0, .1137 \\ larl %%r0, .
1103 \\ stg %%r0, 136(%%r2)1138 \\ stg %%r0, 136(%%r2)
1104 \\ lg %%r0, 0(%%r2)
1105 \\ lg %%r1, 8(%%r2)
1106 :1139 :
1107 : [gprs] "{r2}" (&ctx),1140 : [ctx] "{r2}" (&ctx),
1108 : .{ .memory = true });1141 : .{ .r0 = true, .r1 = true, .memory = true });
1109 return ctx;1142 return ctx;
1110 }1143 }
11111144
...@@ -1126,6 +1159,326 @@ const S390x = extern struct {...@@ -1126,6 +1159,326 @@ const S390x = extern struct {
1126 }1159 }
1127};1160};
11281161
1162/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
1163const Sparc = extern struct {
1164 g: [8]Gpr,
1165 o: [8]Gpr,
1166 l: [8]Gpr,
1167 i: [8]Gpr,
1168 pc: Gpr,
1169
1170 pub const Gpr = if (native_arch == .sparc64) u64 else u32;
1171
1172 pub inline fn current() Sparc {
1173 flushWindows();
1174
1175 var ctx: Sparc = undefined;
1176 asm volatile (if (Gpr == u64)
1177 \\ stx %g0, [%l0 + 0]
1178 \\ stx %g1, [%l0 + 8]
1179 \\ stx %g2, [%l0 + 16]
1180 \\ stx %g3, [%l0 + 24]
1181 \\ stx %g4, [%l0 + 32]
1182 \\ stx %g5, [%l0 + 40]
1183 \\ stx %g6, [%l0 + 48]
1184 \\ stx %g7, [%l0 + 56]
1185 \\ stx %o0, [%l0 + 64]
1186 \\ stx %o1, [%l0 + 72]
1187 \\ stx %o2, [%l0 + 80]
1188 \\ stx %o3, [%l0 + 88]
1189 \\ stx %o4, [%l0 + 96]
1190 \\ stx %o5, [%l0 + 104]
1191 \\ stx %o6, [%l0 + 112]
1192 \\ stx %o7, [%l0 + 120]
1193 \\ stx %l0, [%l0 + 128]
1194 \\ stx %l1, [%l0 + 136]
1195 \\ stx %l2, [%l0 + 144]
1196 \\ stx %l3, [%l0 + 152]
1197 \\ stx %l4, [%l0 + 160]
1198 \\ stx %l5, [%l0 + 168]
1199 \\ stx %l6, [%l0 + 176]
1200 \\ stx %l7, [%l0 + 184]
1201 \\ stx %i0, [%l0 + 192]
1202 \\ stx %i1, [%l0 + 200]
1203 \\ stx %i2, [%l0 + 208]
1204 \\ stx %i3, [%l0 + 216]
1205 \\ stx %i4, [%l0 + 224]
1206 \\ stx %i5, [%l0 + 232]
1207 \\ stx %i6, [%l0 + 240]
1208 \\ stx %i7, [%l0 + 248]
1209 \\ call 1f
1210 \\1:
1211 \\ stx %o7, [%l0 + 256]
1212 else
1213 \\ std %g0, [%l0 + 0]
1214 \\ std %g2, [%l0 + 8]
1215 \\ std %g4, [%l0 + 16]
1216 \\ std %g6, [%l0 + 24]
1217 \\ std %o0, [%l0 + 32]
1218 \\ std %o2, [%l0 + 40]
1219 \\ std %o4, [%l0 + 48]
1220 \\ std %o6, [%l0 + 56]
1221 \\ std %l0, [%l0 + 64]
1222 \\ std %l2, [%l0 + 72]
1223 \\ std %l4, [%l0 + 80]
1224 \\ std %l6, [%l0 + 88]
1225 \\ std %i0, [%l0 + 96]
1226 \\ std %i2, [%l0 + 104]
1227 \\ std %i4, [%l0 + 112]
1228 \\ std %i6, [%l0 + 120]
1229 \\ call 1f
1230 \\1:
1231 \\ st %o7, [%l0 + 128]
1232 :
1233 : [ctx] "{l0}" (&ctx),
1234 : .{ .o7 = true, .memory = true });
1235 return ctx;
1236 }
1237
1238 noinline fn flushWindows() void {
1239 // Flush all register windows except the current one (hence `noinline`). This ensures that
1240 // we actually see meaningful data on the stack when we walk the frame chain.
1241 if (comptime builtin.target.cpu.has(.sparc, .v9))
1242 asm volatile ("flushw" ::: .{ .memory = true })
1243 else
1244 asm volatile ("ta 3" ::: .{ .memory = true }); // ST_FLUSH_WINDOWS
1245 }
1246
1247 pub fn dwarfRegisterBytes(ctx: *Sparc, register_num: u16) DwarfRegisterError![]u8 {
1248 switch (register_num) {
1249 0...7 => return @ptrCast(&ctx.g[register_num]),
1250 8...15 => return @ptrCast(&ctx.o[register_num - 8]),
1251 16...23 => return @ptrCast(&ctx.l[register_num - 16]),
1252 24...31 => return @ptrCast(&ctx.i[register_num - 24]),
1253 32 => return @ptrCast(&ctx.pc),
1254
1255 else => return error.InvalidRegister,
1256 }
1257 }
1258};
1259
1260/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
1261const Ve = extern struct {
1262 s: [64]u64,
1263 ic: u64,
1264
1265 pub inline fn current() Ve {
1266 var ctx: Ve = undefined;
1267 asm volatile (
1268 \\ st %%s0, 0(, %%s8)
1269 \\ st %%s1, 8(, %%s8)
1270 \\ st %%s2, 16(, %%s8)
1271 \\ st %%s3, 24(, %%s8)
1272 \\ st %%s4, 32(, %%s8)
1273 \\ st %%s5, 40(, %%s8)
1274 \\ st %%s6, 48(, %%s8)
1275 \\ st %%s7, 56(, %%s8)
1276 \\ st %%s8, 64(, %%s8)
1277 \\ st %%s9, 72(, %%s8)
1278 \\ st %%s10, 80(, %%s8)
1279 \\ st %%s11, 88(, %%s8)
1280 \\ st %%s12, 96(, %%s8)
1281 \\ st %%s13, 104(, %%s8)
1282 \\ st %%s14, 112(, %%s8)
1283 \\ st %%s15, 120(, %%s8)
1284 \\ st %%s16, 128(, %%s8)
1285 \\ st %%s17, 136(, %%s8)
1286 \\ st %%s18, 144(, %%s8)
1287 \\ st %%s19, 152(, %%s8)
1288 \\ st %%s20, 160(, %%s8)
1289 \\ st %%s21, 168(, %%s8)
1290 \\ st %%s22, 176(, %%s8)
1291 \\ st %%s23, 184(, %%s8)
1292 \\ st %%s24, 192(, %%s8)
1293 \\ st %%s25, 200(, %%s8)
1294 \\ st %%s26, 208(, %%s8)
1295 \\ st %%s27, 216(, %%s8)
1296 \\ st %%s28, 224(, %%s8)
1297 \\ st %%s29, 232(, %%s8)
1298 \\ st %%s30, 240(, %%s8)
1299 \\ st %%s31, 248(, %%s8)
1300 \\ st %%s32, 256(, %%s8)
1301 \\ st %%s33, 264(, %%s8)
1302 \\ st %%s34, 272(, %%s8)
1303 \\ st %%s35, 280(, %%s8)
1304 \\ st %%s36, 288(, %%s8)
1305 \\ st %%s37, 296(, %%s8)
1306 \\ st %%s38, 304(, %%s8)
1307 \\ st %%s39, 312(, %%s8)
1308 \\ st %%s40, 320(, %%s8)
1309 \\ st %%s41, 328(, %%s8)
1310 \\ st %%s42, 336(, %%s8)
1311 \\ st %%s43, 344(, %%s8)
1312 \\ st %%s44, 352(, %%s8)
1313 \\ st %%s45, 360(, %%s8)
1314 \\ st %%s46, 368(, %%s8)
1315 \\ st %%s47, 376(, %%s8)
1316 \\ st %%s48, 384(, %%s8)
1317 \\ st %%s49, 392(, %%s8)
1318 \\ st %%s50, 400(, %%s8)
1319 \\ st %%s51, 408(, %%s8)
1320 \\ st %%s52, 416(, %%s8)
1321 \\ st %%s53, 424(, %%s8)
1322 \\ st %%s54, 432(, %%s8)
1323 \\ st %%s55, 440(, %%s8)
1324 \\ st %%s56, 448(, %%s8)
1325 \\ st %%s57, 456(, %%s8)
1326 \\ st %%s58, 464(, %%s8)
1327 \\ st %%s59, 472(, %%s8)
1328 \\ st %%s60, 480(, %%s8)
1329 \\ st %%s61, 488(, %%s8)
1330 \\ st %%s62, 496(, %%s8)
1331 \\ st %%s63, 504(, %%s8)
1332 \\ br.l 1f
1333 \\1:
1334 \\ st %%lr, 512(, %%s8)
1335 :
1336 : [ctx] "{s8}" (&ctx),
1337 : .{ .s10 = true, .memory = true });
1338 return ctx;
1339 }
1340
1341 pub fn dwarfRegisterBytes(ctx: *Ve, register_num: u16) DwarfRegisterError![]u8 {
1342 switch (register_num) {
1343 0...63 => return @ptrCast(&ctx.s[register_num]),
1344 144 => return @ptrCast(&ctx.ic),
1345
1346 64...127 => return error.UnsupportedRegister, // v0 - v63
1347 128...143 => return error.UnsupportedRegister, // vm0 - vm15
1348
1349 else => return error.InvalidRegister,
1350 }
1351 }
1352};
1353
1354const X86 = struct {
1355 /// The first 8 registers here intentionally match the order of registers in the x86 instruction
1356 /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,
1357 /// among other things.
1358 pub const Gpr = enum {
1359 // zig fmt: off
1360 eax, ecx, edx, ebx,
1361 esp, ebp, esi, edi,
1362 eip,
1363 // zig fmt: on
1364 };
1365 gprs: std.enums.EnumArray(Gpr, u32),
1366
1367 pub inline fn current() X86 {
1368 var ctx: X86 = undefined;
1369 asm volatile (
1370 \\ movl %%eax, 0x00(%%edi)
1371 \\ movl %%ecx, 0x04(%%edi)
1372 \\ movl %%edx, 0x08(%%edi)
1373 \\ movl %%ebx, 0x0c(%%edi)
1374 \\ movl %%esp, 0x10(%%edi)
1375 \\ movl %%ebp, 0x14(%%edi)
1376 \\ movl %%esi, 0x18(%%edi)
1377 \\ movl %%edi, 0x1c(%%edi)
1378 \\ call 1f
1379 \\1:
1380 \\ popl 0x20(%%edi)
1381 :
1382 : [gprs] "{edi}" (&ctx.gprs.values),
1383 : .{ .memory = true });
1384 return ctx;
1385 }
1386
1387 pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 {
1388 // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1
1389 // § 2.4.2 "DWARF Register Number Mapping"
1390 switch (register_num) {
1391 // The order of `Gpr` intentionally matches DWARF's mappings.
1392 //
1393 // x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind
1394 // information is from `__eh_frame`). This deviation is not considered here, because
1395 // x86-macos is a deprecated target which is not supported by the Zig Standard Library.
1396 0...8 => return @ptrCast(&ctx.gprs.values[register_num]),
1397
1398 9 => return error.UnsupportedRegister, // eflags
1399 11...18 => return error.UnsupportedRegister, // st0 - st7
1400 21...28 => return error.UnsupportedRegister, // xmm0 - xmm7
1401 29...36 => return error.UnsupportedRegister, // mm0 - mm7
1402 39 => return error.UnsupportedRegister, // mxcsr
1403 40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs
1404 48 => return error.UnsupportedRegister, // tr
1405 49 => return error.UnsupportedRegister, // ldtr
1406 93...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
1407
1408 else => return error.InvalidRegister,
1409 }
1410 }
1411};
1412
1413const X86_64 = struct {
1414 /// The order here intentionally matches the order of the DWARF register mappings. It's unclear
1415 /// where those mappings actually originated from---the ordering of the first 4 registers seems
1416 /// quite unusual---but it is currently convenient for us to match DWARF.
1417 pub const Gpr = enum {
1418 // zig fmt: off
1419 rax, rdx, rcx, rbx,
1420 rsi, rdi, rbp, rsp,
1421 r8, r9, r10, r11,
1422 r12, r13, r14, r15,
1423 rip,
1424 // zig fmt: on
1425 };
1426 gprs: std.enums.EnumArray(Gpr, u64),
1427
1428 pub inline fn current() X86_64 {
1429 var ctx: X86_64 = undefined;
1430 asm volatile (
1431 \\ movq %%rax, 0x00(%%rdi)
1432 \\ movq %%rdx, 0x08(%%rdi)
1433 \\ movq %%rcx, 0x10(%%rdi)
1434 \\ movq %%rbx, 0x18(%%rdi)
1435 \\ movq %%rsi, 0x20(%%rdi)
1436 \\ movq %%rdi, 0x28(%%rdi)
1437 \\ movq %%rbp, 0x30(%%rdi)
1438 \\ movq %%rsp, 0x38(%%rdi)
1439 \\ movq %%r8, 0x40(%%rdi)
1440 \\ movq %%r9, 0x48(%%rdi)
1441 \\ movq %%r10, 0x50(%%rdi)
1442 \\ movq %%r11, 0x58(%%rdi)
1443 \\ movq %%r12, 0x60(%%rdi)
1444 \\ movq %%r13, 0x68(%%rdi)
1445 \\ movq %%r14, 0x70(%%rdi)
1446 \\ movq %%r15, 0x78(%%rdi)
1447 \\ leaq (%%rip), %%rax
1448 \\ movq %%rax, 0x80(%%rdi)
1449 :
1450 : [gprs] "{rdi}" (&ctx.gprs.values),
1451 : .{ .rax = true, .memory = true });
1452 return ctx;
1453 }
1454
1455 pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 {
1456 // System V Application Binary Interface AMD64 Architecture Processor Supplement
1457 // § 3.6.2 "DWARF Register Number Mapping"
1458 switch (register_num) {
1459 // The order of `Gpr` intentionally matches DWARF's mappings.
1460 0...16 => return @ptrCast(&ctx.gprs.values[register_num]),
1461
1462 17...32 => return error.UnsupportedRegister, // xmm0 - xmm15
1463 33...40 => return error.UnsupportedRegister, // st0 - st7
1464 41...48 => return error.UnsupportedRegister, // mm0 - mm7
1465 49 => return error.UnsupportedRegister, // rflags
1466 50...55 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs
1467 58...59 => return error.UnsupportedRegister, // fs.base, gs.base
1468 62 => return error.UnsupportedRegister, // tr
1469 63 => return error.UnsupportedRegister, // ldtr
1470 64 => return error.UnsupportedRegister, // mxcsr
1471 65 => return error.UnsupportedRegister, // fcw
1472 66 => return error.UnsupportedRegister, // fsw
1473 67...82 => return error.UnsupportedRegister, // xmm16 - xmm31 (AVX-512)
1474 118...125 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
1475 130...145 => return error.UnsupportedRegister, // r16 - r31 (APX)
1476
1477 else => return error.InvalidRegister,
1478 }
1479 }
1480};
1481
1129/// The native operating system's `ucontext_t` as seen in the third argument to signal handlers.1482/// The native operating system's `ucontext_t` as seen in the third argument to signal handlers.
1130///1483///
1131/// These are dramatically simplified since we only need general-purpose registers and don't care1484/// These are dramatically simplified since we only need general-purpose registers and don't care
...@@ -1227,7 +1580,7 @@ const signal_ucontext_t = switch (native_os) {...@@ -1227,7 +1580,7 @@ const signal_ucontext_t = switch (native_os) {
1227 _count: u32,1580 _count: u32,
1228 },1581 },
1229 _status32: u32,1582 _status32: u32,
1230 pc: u32,1583 pcl: u32,
1231 r31: u32,1584 r31: u32,
1232 r27_26: [2]u32,1585 r27_26: [2]u32,
1233 r12_0: [13]u32,1586 r12_0: [13]u32,