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) {
872872 };
873873
874874 const fp_usability: FpUsability = switch (builtin.target.cpu.arch) {
875 .avr,
876 .csky,
875877 .mips,
876878 .mipsel,
877879 .mips64,
878880 .mips64el,
881 .msp430,
882 .xcore,
879883 => .useless,
880884 .hexagon,
881885 // 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 {
14301430pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
14311431 return switch (arch) {
14321432 .aarch64, .aarch64_be => 32,
1433 .arc => 160,
14331434 .arm, .armeb, .thumb, .thumbeb => 15,
1435 .csky => 64,
14341436 .hexagon => 76,
1437 .lanai => 2,
14351438 .loongarch32, .loongarch64 => 64,
1439 .m68k => 26,
14361440 .mips, .mipsel, .mips64, .mips64el => 66,
1441 .or1k => 35,
14371442 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67,
14381443 .riscv32, .riscv32be, .riscv64, .riscv64be => 65,
14391444 .s390x => 65,
14401445 .sparc, .sparc64 => 32,
1446 .ve => 144,
14411447 .x86 => 8,
14421448 .x86_64 => 16,
14431449 else => null,
......@@ -1447,14 +1453,20 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
14471453pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
14481454 return switch (arch) {
14491455 .aarch64, .aarch64_be => 29,
1456 .arc => 27,
14501457 .arm, .armeb, .thumb, .thumbeb => 11,
1458 .csky => 14,
14511459 .hexagon => 30,
1460 .lanai => 5,
14521461 .loongarch32, .loongarch64 => 22,
1462 .m68k => 14,
14531463 .mips, .mipsel, .mips64, .mips64el => 30,
1464 .or1k => 2,
14541465 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,
14551466 .riscv32, .riscv32be, .riscv64, .riscv64be => 8,
14561467 .s390x => 11,
14571468 .sparc, .sparc64 => 30,
1469 .ve => 9,
14581470 .x86 => 5,
14591471 .x86_64 => 6,
14601472 else => unreachable,
......@@ -1464,14 +1476,20 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
14641476pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {
14651477 return switch (arch) {
14661478 .aarch64, .aarch64_be => 31,
1479 .arc => 28,
14671480 .arm, .armeb, .thumb, .thumbeb => 13,
1481 .csky => 14,
14681482 .hexagon => 29,
1483 .lanai => 4,
14691484 .loongarch32, .loongarch64 => 3,
1485 .m68k => 15,
14701486 .mips, .mipsel, .mips64, .mips64el => 29,
1487 .or1k => 1,
14711488 .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1,
14721489 .riscv32, .riscv32be, .riscv64, .riscv64be => 2,
14731490 .s390x => 15,
14741491 .sparc, .sparc64 => 14,
1492 .ve => 11,
14751493 .x86 => 4,
14761494 .x86_64 => 7,
14771495 else => unreachable,
lib/std/debug/SelfInfo/Elf.zig+9-3
......@@ -94,22 +94,27 @@ pub const can_unwind: bool = s: {
9494 // Notably, we are yet to support unwinding on ARM. There, unwinding is not done through
9595 // `.eh_frame`, but instead with the `.ARM.exidx` section, which has a different format.
9696 const archs: []const std.Target.Cpu.Arch = switch (builtin.target.os.tag) {
97 // Not supported yet: arm, m68k
97 // Not supported yet: arm
9898 .haiku => &.{
9999 .aarch64,
100 .m68k,
100101 .riscv64,
101102 .x86,
102103 .x86_64,
103104 },
104 // Not supported yet: arc, arm/armeb/thumb/thumbeb, csky, m68k, or1k, xtensa
105 // Not supported yet: arm/armeb/thumb/thumbeb, xtensa
105106 .linux => &.{
106107 .aarch64,
107108 .aarch64_be,
109 .arc,
110 .csky,
108111 .loongarch64,
112 .m68k,
109113 .mips,
110114 .mipsel,
111115 .mips64,
112116 .mips64el,
117 .or1k,
113118 .riscv32,
114119 .riscv64,
115120 .s390x,
......@@ -131,10 +136,11 @@ pub const can_unwind: bool = s: {
131136 .riscv64,
132137 .x86_64,
133138 },
134 // Not supported yet: arm/armeb, m68k, mips64/mips64el
139 // Not supported yet: arm/armeb, mips64/mips64el
135140 .netbsd => &.{
136141 .aarch64,
137142 .aarch64_be,
143 .m68k,
138144 .mips,
139145 .mipsel,
140146 .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
55 root.debug.CpuContext
66else switch (native_arch) {
77 .aarch64, .aarch64_be => Aarch64,
8 .arc => Arc,
89 .arm, .armeb, .thumb, .thumbeb => Arm,
10 .csky => Csky,
911 .hexagon => Hexagon,
12 .lanai => Lanai,
1013 .loongarch32, .loongarch64 => LoongArch,
14 .m68k => M68k,
1115 .mips, .mipsel, .mips64, .mips64el => Mips,
16 .or1k => Or1k,
1217 .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc,
1318 .sparc, .sparc64 => Sparc,
1419 .riscv32, .riscv32be, .riscv64, .riscv64be => Riscv,
20 .ve => Ve,
1521 .s390x => S390x,
1622 .x86 => X86,
1723 .x86_64 => X86_64,
......@@ -30,7 +36,20 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
3036 const uc: *const signal_ucontext_t = @ptrCast(@alignCast(ctx_ptr));
3137
3238 // 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) {
3453 // The O32 kABI uses 64-bit fields for some reason.
3554 return .{
3655 .r = s: {
......@@ -72,10 +91,22 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
7291 .sp = uc.mcontext.sp,
7392 .pc = uc.mcontext.pc,
7493 },
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 },
75101 .hexagon, .loongarch32, .loongarch64, .mips, .mipsel, .mips64, .mips64el, .or1k => .{
76102 .r = uc.mcontext.r,
77103 .pc = uc.mcontext.pc,
78104 },
105 .m68k => .{
106 .d = uc.mcontext.d,
107 .a = uc.mcontext.a,
108 .pc = uc.mcontext.pc,
109 },
79110 .powerpc, .powerpcle, .powerpc64, .powerpc64le => .{
80111 .r = uc.mcontext.r,
81112 .pc = uc.mcontext.pc,
......@@ -181,129 +212,124 @@ pub fn fromWindowsContext(ctx: *const std.os.windows.CONTEXT) Native {
181212 };
182213}
183214
184const X86 = struct {
185 /// The first 8 registers here intentionally match the order of registers in the x86 instruction
186 /// encoding. This order is inherited by the PUSHA instruction and the DWARF register mappings,
187 /// among other things.
188 pub const Gpr = enum {
189 // zig fmt: off
190 eax, ecx, edx, ebx,
191 esp, ebp, esi, edi,
192 eip,
193 // zig fmt: on
194 };
195 gprs: std.enums.EnumArray(Gpr, u32),
215/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
216const Aarch64 = extern struct {
217 /// The numbered general-purpose registers X0 - X30.
218 x: [31]u64,
219 sp: u64,
220 pc: u64,
196221
197 pub inline fn current() X86 {
198 var ctx: X86 = undefined;
222 pub inline fn current() Aarch64 {
223 var ctx: Aarch64 = undefined;
199224 asm volatile (
200 \\movl %%eax, 0x00(%%edi)
201 \\movl %%ecx, 0x04(%%edi)
202 \\movl %%edx, 0x08(%%edi)
203 \\movl %%ebx, 0x0c(%%edi)
204 \\movl %%esp, 0x10(%%edi)
205 \\movl %%ebp, 0x14(%%edi)
206 \\movl %%esi, 0x18(%%edi)
207 \\movl %%edi, 0x1c(%%edi)
208 \\call 1f
209 \\1:
210 \\popl 0x20(%%edi)
225 \\ stp x0, x1, [x0, #0x000]
226 \\ stp x2, x3, [x0, #0x010]
227 \\ stp x4, x5, [x0, #0x020]
228 \\ stp x6, x7, [x0, #0x030]
229 \\ stp x8, x9, [x0, #0x040]
230 \\ stp x10, x11, [x0, #0x050]
231 \\ stp x12, x13, [x0, #0x060]
232 \\ stp x14, x15, [x0, #0x070]
233 \\ stp x16, x17, [x0, #0x080]
234 \\ stp x18, x19, [x0, #0x090]
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]
211245 :
212 : [gprs] "{edi}" (&ctx.gprs.values),
213 : .{ .memory = true });
246 : [ctx] "{x0}" (&ctx),
247 : .{ .x1 = true, .memory = true });
214248 return ctx;
215249 }
216250
217 pub fn dwarfRegisterBytes(ctx: *X86, register_num: u16) DwarfRegisterError![]u8 {
218 // System V Application Binary Interface Intel386 Architecture Processor Supplement Version 1.1
219 // § 2.4.2 "DWARF Register Number Mapping"
251 pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {
252 // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"
220253 switch (register_num) {
221 // The order of `Gpr` intentionally matches DWARF's mappings.
222 //
223 // x86-macos sometimes uses different mappings (ebp and esp are reversed when the unwind
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]),
254 0...30 => return @ptrCast(&ctx.x[register_num]),
255 31 => return @ptrCast(&ctx.sp),
256 32 => return @ptrCast(&ctx.pc),
227257
228 9 => return error.UnsupportedRegister, // eflags
229 11...18 => return error.UnsupportedRegister, // st0 - st7
230 21...28 => return error.UnsupportedRegister, // xmm0 - xmm7
231 29...36 => return error.UnsupportedRegister, // mm0 - mm7
232 39 => return error.UnsupportedRegister, // mxcsr
233 40...45 => return error.UnsupportedRegister, // es, cs, ss, ds, fs, gs
234 48 => return error.UnsupportedRegister, // tr
235 49 => return error.UnsupportedRegister, // ldtr
236 93...100 => return error.UnsupportedRegister, // k0 - k7 (AVX-512)
258 33 => return error.UnsupportedRegister, // ELR_mode
259 34 => return error.UnsupportedRegister, // RA_SIGN_STATE
260 35 => return error.UnsupportedRegister, // TPIDRRO_ELO
261 36 => return error.UnsupportedRegister, // TPIDR_ELO
262 37 => return error.UnsupportedRegister, // TPIDR_EL1
263 38 => return error.UnsupportedRegister, // TPIDR_EL2
264 39 => return error.UnsupportedRegister, // TPIDR_EL3
265 40...45 => return error.UnsupportedRegister, // Reserved
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
238272 else => return error.InvalidRegister,
239273 }
240274 }
241275};
242276
243const X86_64 = struct {
244 /// The order here intentionally matches the order of the DWARF register mappings. It's unclear
245 /// where those mappings actually originated from---the ordering of the first 4 registers seems
246 /// quite unusual---but it is currently convenient for us to match DWARF.
247 pub const Gpr = enum {
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),
277/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
278const Arc = extern struct {
279 /// The numbered general-purpose registers r0 - r31.
280 r: [32]u32,
281 pcl: u32,
257282
258 pub inline fn current() X86_64 {
259 var ctx: X86_64 = undefined;
283 pub inline fn current() Arc {
284 var ctx: Arc = undefined;
260285 asm volatile (
261 \\movq %%rax, 0x00(%%rdi)
262 \\movq %%rdx, 0x08(%%rdi)
263 \\movq %%rcx, 0x10(%%rdi)
264 \\movq %%rbx, 0x18(%%rdi)
265 \\movq %%rsi, 0x20(%%rdi)
266 \\movq %%rdi, 0x28(%%rdi)
267 \\movq %%rbp, 0x30(%%rdi)
268 \\movq %%rsp, 0x38(%%rdi)
269 \\movq %%r8, 0x40(%%rdi)
270 \\movq %%r9, 0x48(%%rdi)
271 \\movq %%r10, 0x50(%%rdi)
272 \\movq %%r11, 0x58(%%rdi)
273 \\movq %%r12, 0x60(%%rdi)
274 \\movq %%r13, 0x68(%%rdi)
275 \\movq %%r14, 0x70(%%rdi)
276 \\movq %%r15, 0x78(%%rdi)
277 \\leaq (%%rip), %%rax
278 \\movq %%rax, 0x80(%%rdi)
279 \\movq 0x00(%%rdi), %%rax
286 \\ st r0, [r8, 0]
287 \\ st r1, [r8, 4]
288 \\ st r2, [r8, 8]
289 \\ st r3, [r8, 12]
290 \\ st r4, [r8, 16]
291 \\ st r5, [r8, 20]
292 \\ st r6, [r8, 24]
293 \\ st r7, [r8, 28]
294 \\ st r8, [r8, 32]
295 \\ st r9, [r8, 36]
296 \\ st r10, [r8, 40]
297 \\ st r11, [r8, 44]
298 \\ st r12, [r8, 48]
299 \\ st r13, [r8, 52]
300 \\ st r14, [r8, 56]
301 \\ st r15, [r8, 60]
302 \\ st r16, [r8, 64]
303 \\ st r17, [r8, 68]
304 \\ 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]
280319 :
281 : [gprs] "{rdi}" (&ctx.gprs.values),
320 : [ctx] "{r8}" (&ctx),
282321 : .{ .memory = true });
283322 return ctx;
284323 }
285324
286 pub fn dwarfRegisterBytes(ctx: *X86_64, register_num: u16) DwarfRegisterError![]u8 {
287 // System V Application Binary Interface AMD64 Architecture Processor Supplement
288 // § 3.6.2 "DWARF Register Number Mapping"
325 pub fn dwarfRegisterBytes(ctx: *Arc, register_num: u16) DwarfRegisterError![]u8 {
289326 switch (register_num) {
290 // The order of `Gpr` intentionally matches DWARF's mappings.
291 0...16 => return @ptrCast(&ctx.gprs.values[register_num]),
327 0...31 => return @ptrCast(&ctx.r[register_num]),
328 160 => return @ptrCast(&ctx.pcl),
292329
293 17...32 => return error.UnsupportedRegister, // xmm0 - xmm15
294 33...40 => return error.UnsupportedRegister, // st0 - st7
295 41...48 => return error.UnsupportedRegister, // mm0 - mm7
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)
330 32...57 => return error.UnsupportedRegister, // Extension Core Registers
331 58...127 => return error.UnsupportedRegister, // Reserved
332 128...159 => return error.UnsupportedRegister, // f0 - f31
307333
308334 else => return error.InvalidRegister,
309335 }
......@@ -317,11 +343,11 @@ const Arm = struct {
317343 pub inline fn current() Arm {
318344 var ctx: Arm = undefined;
319345 asm volatile (
320 \\// For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm.
321 \\stm r0, {r0-r12}
322 \\str r13, [r0, #0x34]
323 \\str r14, [r0, #0x38]
324 \\str r15, [r0, #0x3c]
346 \\ // For compatibility with Thumb, we can't write r13 (sp) or r15 (pc) with stm.
347 \\ stm r0, {r0-r12}
348 \\ str r13, [r0, #0x34]
349 \\ str r14, [r0, #0x38]
350 \\ str r15, [r0, #0x3c]
325351 :
326352 : [r] "{r0}" (&ctx.r),
327353 : .{ .memory = true });
......@@ -369,62 +395,30 @@ const Arm = struct {
369395};
370396
371397/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
372const Aarch64 = extern struct {
373 /// The numbered general-purpose registers X0 - X30.
374 x: [31]u64,
375 sp: u64,
376 pc: u64,
398const Csky = extern struct {
399 /// The numbered general-purpose registers r0 - r31.
400 r: [32]u32,
401 pc: u32,
377402
378 pub inline fn current() Aarch64 {
379 var ctx: Aarch64 = undefined;
403 pub inline fn current() Csky {
404 var ctx: Csky = undefined;
380405 asm volatile (
381 \\stp x0, x1, [x0, #0x000]
382 \\stp x2, x3, [x0, #0x010]
383 \\stp x4, x5, [x0, #0x020]
384 \\stp x6, x7, [x0, #0x030]
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]
406 \\ stm r0-r31, (t0)
407 \\ grs t1, 1f
408 \\1:
409 \\ st32.w t1, (t0, 128)
402410 :
403 : [gprs] "{x0}" (&ctx),
404 : .{ .memory = true });
411 : [ctx] "{r12}" (&ctx),
412 : .{ .r13 = true, .memory = true });
405413 return ctx;
406414 }
407415
408 pub fn dwarfRegisterBytes(ctx: *Aarch64, register_num: u16) DwarfRegisterError![]u8 {
409 // DWARF for the Arm(r) 64-bit Architecture (AArch64) § 4.1 "DWARF register names"
416 pub fn dwarfRegisterBytes(ctx: *Csky, register_num: u16) DwarfRegisterError![]u8 {
410417 switch (register_num) {
411 0...30 => return @ptrCast(&ctx.x[register_num]),
412 31 => return @ptrCast(&ctx.sp),
413 32 => return @ptrCast(&ctx.pc),
418 0...31 => return @ptrCast(&ctx.r[register_num]),
419 64 => return @ptrCast(&ctx.pc),
414420
415 33 => return error.UnsupportedRegister, // ELR_mode
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
421 32...63 => return error.UnsupportedRegister, // f0 - f31
428422
429423 else => return error.InvalidRegister,
430424 }
......@@ -474,10 +468,9 @@ const Hexagon = extern struct {
474468 \\ memw(r0 + #124) = r31
475469 \\ r1 = pc
476470 \\ memw(r0 + #128) = r1
477 \\ r1 = memw(r0 + #4)
478471 :
479 : [gprs] "{r0}" (&ctx),
480 : .{ .memory = true });
472 : [ctx] "{r0}" (&ctx),
473 : .{ .r1 = true, .memory = true });
481474 return ctx;
482475 }
483476
......@@ -499,6 +492,60 @@ const Hexagon = extern struct {
499492 }
500493};
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
502549/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
503550const LoongArch = extern struct {
504551 /// The numbered general-purpose registers r0 - r31. r0 must be zero.
......@@ -545,7 +592,6 @@ const LoongArch = extern struct {
545592 \\ bl 1f
546593 \\1:
547594 \\ st.d $ra, $t0, 256
548 \\ ld.d $ra, $t0, 8
549595 else
550596 \\ st.w $zero, $t0, 0
551597 \\ st.w $ra, $t0, 4
......@@ -582,10 +628,9 @@ const LoongArch = extern struct {
582628 \\ bl 1f
583629 \\1:
584630 \\ st.w $ra, $t0, 128
585 \\ ld.w $ra, $t0, 4
586631 :
587 : [gprs] "{$r12}" (&ctx),
588 : .{ .memory = true });
632 : [ctx] "{$r12}" (&ctx),
633 : .{ .r1 = true, .memory = true });
589634 return ctx;
590635 }
591636
......@@ -601,6 +646,40 @@ const LoongArch = extern struct {
601646 }
602647};
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
604683/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
605684const Mips = extern struct {
606685 /// The numbered general-purpose registers r0 - r31. r0 must be zero.
......@@ -651,7 +730,6 @@ const Mips = extern struct {
651730 \\ bal 1f
652731 \\1:
653732 \\ sd $ra, 256($t0)
654 \\ ld $ra, 248($t0)
655733 \\ .set pop
656734 else
657735 \\ .set push
......@@ -693,11 +771,10 @@ const Mips = extern struct {
693771 \\ bal 1f
694772 \\1:
695773 \\ sw $ra, 128($t4)
696 \\ lw $ra, 124($t4)
697774 \\ .set pop
698775 :
699 : [gprs] "{$12}" (&ctx),
700 : .{ .memory = true });
776 : [ctx] "{$12}" (&ctx),
777 : .{ .r31 = true, .memory = true });
701778 return ctx;
702779 }
703780
......@@ -723,6 +800,66 @@ const Mips = extern struct {
723800 }
724801};
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
726863/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
727864const Powerpc = extern struct {
728865 /// The numbered general-purpose registers r0 - r31.
......@@ -773,7 +910,6 @@ const Powerpc = extern struct {
773910 \\1:
774911 \\ mflr 8
775912 \\ std 8, 256(10)
776 \\ ld 8, 64(10)
777913 else
778914 \\ stw 0, 0(10)
779915 \\ stw 1, 4(10)
......@@ -813,10 +949,9 @@ const Powerpc = extern struct {
813949 \\1:
814950 \\ mflr 8
815951 \\ stw 8, 128(10)
816 \\ lwz 8, 32(10)
817952 :
818 : [gprs] "{r10}" (&ctx),
819 : .{ .lr = true, .memory = true });
953 : [ctx] "{r10}" (&ctx),
954 : .{ .r8 = true, .lr = true, .memory = true });
820955 return ctx;
821956 }
822957
......@@ -879,104 +1014,6 @@ const Powerpc = extern struct {
8791014 }
8801015};
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
9801017/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
9811018const Riscv = extern struct {
9821019 /// The numbered general-purpose registers r0 - r31. r0 must be zero.
......@@ -1023,7 +1060,6 @@ const Riscv = extern struct {
10231060 \\ jal ra, 1f
10241061 \\1:
10251062 \\ sd ra, 256(t0)
1026 \\ ld ra, 8(t0)
10271063 else
10281064 \\ sw zero, 0(t0)
10291065 \\ sw ra, 4(t0)
......@@ -1060,10 +1096,9 @@ const Riscv = extern struct {
10601096 \\ jal ra, 1f
10611097 \\1:
10621098 \\ sw ra, 128(t0)
1063 \\ lw ra, 4(t0)
10641099 :
1065 : [gprs] "{t0}" (&ctx),
1066 : .{ .memory = true });
1100 : [ctx] "{t0}" (&ctx),
1101 : .{ .x1 = true, .memory = true });
10671102 return ctx;
10681103 }
10691104
......@@ -1101,11 +1136,9 @@ const S390x = extern struct {
11011136 \\ stm %%r0, %%r1, 128(%%r2)
11021137 \\ larl %%r0, .
11031138 \\ stg %%r0, 136(%%r2)
1104 \\ lg %%r0, 0(%%r2)
1105 \\ lg %%r1, 8(%%r2)
11061139 :
1107 : [gprs] "{r2}" (&ctx),
1108 : .{ .memory = true });
1140 : [ctx] "{r2}" (&ctx),
1141 : .{ .r0 = true, .r1 = true, .memory = true });
11091142 return ctx;
11101143 }
11111144
......@@ -1126,6 +1159,326 @@ const S390x = extern struct {
11261159 }
11271160};
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
11291482/// The native operating system's `ucontext_t` as seen in the third argument to signal handlers.
11301483///
11311484/// 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) {
12271580 _count: u32,
12281581 },
12291582 _status32: u32,
1230 pc: u32,
1583 pcl: u32,
12311584 r31: u32,
12321585 r27_26: [2]u32,
12331586 r12_0: [13]u32,