Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/std/debug.zig
Original file line number Diff line number Diff line change
Expand Up @@ -952,6 +952,9 @@ const StackIterator = union(enum) {
/// Offset of the saved return address wrt the frame pointer.
const ra_offset = off: {
if (native_arch == .powerpc64le) break :off 2 * @sizeOf(usize);
// On s390x, r14 is the link register and we need to grab it from its customary slot in the
// register save area (ELF ABI s390x Supplement §1.2.2.2).
if (native_arch == .s390x) break :off 14 * @sizeOf(usize);
break :off @sizeOf(usize);
};

Expand Down
3 changes: 3 additions & 0 deletions lib/std/debug/Dwarf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1433,6 +1433,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 {
.arm, .armeb, .thumb, .thumbeb => 15,
.loongarch32, .loongarch64 => 32,
.riscv32, .riscv32be, .riscv64, .riscv64be => 32,
.s390x => 65,
.x86 => 8,
.x86_64 => 16,
else => null,
Expand All @@ -1445,6 +1446,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 {
.arm, .armeb, .thumb, .thumbeb => 11,
.loongarch32, .loongarch64 => 22,
.riscv32, .riscv32be, .riscv64, .riscv64be => 8,
.s390x => 11,
.x86 => 5,
.x86_64 => 6,
else => unreachable,
Expand All @@ -1457,6 +1459,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 {
.arm, .armeb, .thumb, .thumbeb => 13,
.loongarch32, .loongarch64 => 3,
.riscv32, .riscv32be, .riscv64, .riscv64be => 2,
.s390x => 15,
.x86 => 4,
.x86_64 => 7,
else => unreachable,
Expand Down
16 changes: 12 additions & 4 deletions lib/std/debug/Dwarf/SelfUnwinder.zig
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,11 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
break :cfa try applyOffset(ptr.*, ro.offset);
},
.expression => |expr| cfa: {
// On all implemented architectures, the CFA is defined to be the previous frame's SP
// On most implemented architectures, the CFA is defined to be the previous frame's SP.
//
// On s390x, it's defined to be SP + 160 (ELF ABI s390x Supplement §1.6.3); however,
// what this actually means is that there will be a `def_cfa r15 + 160`, so nothing
// special for us to do.
const prev_cfa_val = (try regNative(&unwinder.cpu_state, sp_reg_num)).*;
unwinder.expr_vm.reset();
const value = try unwinder.expr_vm.run(expr, gpa, .{
Expand All @@ -193,9 +197,13 @@ fn nextInner(unwinder: *SelfUnwinder, gpa: Allocator, cache_entry: *const CacheE
// If unspecified, we'll use the default rule for the return address register, which is
// typically equivalent to `.undefined` (meaning there is no return address), but may be
// overriden by ABIs.
var has_return_address: bool = builtin.cpu.arch.isAARCH64() and
return_address_register >= 19 and
return_address_register <= 28;
var has_return_address: bool = switch (builtin.cpu.arch) {
// DWARF for the Arm 64-bit Architecture (AArch64) §4.3, p1
.aarch64, .aarch64_be => return_address_register >= 19 and return_address_register <= 28,
// ELF ABI s390x Supplement §1.6.4
.s390x => return_address_register >= 6 and return_address_register <= 15,
else => false,
};

// Create a copy of the CPU state, to which we will apply the new rules.
var new_cpu_state = unwinder.cpu_state;
Expand Down
1 change: 1 addition & 0 deletions lib/std/debug/SelfInfo/Elf.zig
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ pub const can_unwind: bool = s: {
.loongarch64,
.riscv32,
.riscv64,
.s390x,
.x86,
.x86_64,
},
Expand Down
87 changes: 87 additions & 0 deletions lib/std/debug/cpu_context.zig
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ else switch (native_arch) {
.arm, .armeb, .thumb, .thumbeb => Arm,
.loongarch32, .loongarch64 => LoongArch,
.riscv32, .riscv32be, .riscv64, .riscv64be => Riscv,
.s390x => S390x,
.x86 => X86,
.x86_64 => X86_64,
else => noreturn,
Expand Down Expand Up @@ -189,6 +190,17 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native {
},
else => null,
},
.s390x => switch (builtin.os.tag) {
.linux => .{
.r = uc.mcontext.gregs,
.f = uc.mcontext.fregs,
.psw = .{
.mask = uc.mcontext.psw.mask,
.addr = uc.mcontext.psw.addr,
},
},
else => null,
},
else => null,
};
}
Expand Down Expand Up @@ -677,6 +689,81 @@ pub const Riscv = extern struct {
}
};

/// This is an `extern struct` so that inline assembly in `current` can use field offsets.
pub const S390x = extern struct {
/// The numbered general-purpose registers r0 - r15.
r: [16]u64,
/// The numbered floating-point registers f0 - f15. Yes, really - they can be used in DWARF CFI.
f: [16]f64,
/// The program counter.
psw: extern struct {
mask: u64,
addr: u64,
},

pub inline fn current() S390x {
var ctx: S390x = undefined;
asm volatile (
\\ stmg %%r0, %%r15, 0(%%r2)
\\ std %%f0, 128(%%r2)
\\ std %%f1, 136(%%r2)
\\ std %%f2, 144(%%r2)
\\ std %%f3, 152(%%r2)
\\ std %%f4, 160(%%r2)
\\ std %%f5, 168(%%r2)
\\ std %%f6, 176(%%r2)
\\ std %%f7, 184(%%r2)
\\ std %%f8, 192(%%r2)
\\ std %%f9, 200(%%r2)
\\ std %%f10, 208(%%r2)
\\ std %%f11, 216(%%r2)
\\ std %%f12, 224(%%r2)
\\ std %%f13, 232(%%r2)
\\ std %%f14, 240(%%r2)
\\ std %%f15, 248(%%r2)
\\ epsw %%r0, %%r1
\\ stm %%r0, %%r1, 256(%%r2)
\\ larl %%r0, .
\\ stg %%r0, 264(%%r2)
\\ lg %%r0, 0(%%r2)
\\ lg %%r1, 8(%%r2)
:
: [gprs] "{r2}" (&ctx),
: .{ .memory = true });
return ctx;
}

pub fn dwarfRegisterBytes(ctx: *S390x, register_num: u16) DwarfRegisterError![]u8 {
switch (register_num) {
0...15 => return @ptrCast(&ctx.r[register_num]),
// Why???
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is really underselling the mess below! Its floating point registers, but also they're not stored in DWARF order? Its some haphazard index remapping? 28 => 9?!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well it's the DWARF order that's out of whack. It's easier to handle that here than in both current and fromPosixSignalContext.

16 => return @ptrCast(&ctx.f[0]),
17 => return @ptrCast(&ctx.f[2]),
18 => return @ptrCast(&ctx.f[4]),
19 => return @ptrCast(&ctx.f[6]),
20 => return @ptrCast(&ctx.f[1]),
21 => return @ptrCast(&ctx.f[3]),
22 => return @ptrCast(&ctx.f[5]),
23 => return @ptrCast(&ctx.f[7]),
24 => return @ptrCast(&ctx.f[8]),
25 => return @ptrCast(&ctx.f[10]),
26 => return @ptrCast(&ctx.f[12]),
27 => return @ptrCast(&ctx.f[14]),
28 => return @ptrCast(&ctx.f[9]),
29 => return @ptrCast(&ctx.f[11]),
30 => return @ptrCast(&ctx.f[13]),
31 => return @ptrCast(&ctx.f[15]),
64 => return @ptrCast(&ctx.psw.mask),
65 => return @ptrCast(&ctx.psw.addr),

48...63 => return error.UnsupportedRegister, // a0 - a15
68...83 => return error.UnsupportedRegister, // v16 - v31

else => return error.InvalidRegister,
}
}
};

const signal_ucontext_t = switch (native_os) {
.linux => std.os.linux.ucontext_t,
.emscripten => std.os.emscripten.ucontext_t,
Expand Down
11 changes: 8 additions & 3 deletions lib/std/os/linux/s390x.zig
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,12 @@ pub const ucontext_t = extern struct {
};

pub const mcontext_t = extern struct {
__regs1: [18]u64,
__regs2: [18]u32,
__regs3: [16]f64,
psw: extern struct {
mask: u64,
addr: u64,
},
gregs: [16]u64,
aregs: [16]u32,
fpc: u32,
fregs: [16]f64,
};
8 changes: 6 additions & 2 deletions src/Compilation.zig
Original file line number Diff line number Diff line change
Expand Up @@ -7193,6 +7193,9 @@ pub fn addCCArgs(
}

try argv.append(if (mod.omit_frame_pointer) "-fomit-frame-pointer" else "-fno-omit-frame-pointer");
if (target.cpu.arch == .s390x) {
try argv.append(if (mod.omit_frame_pointer) "-mbackchain" else "-mno-backchain");
}

const ssp_buf_size = mod.stack_protector;
if (ssp_buf_size != 0) {
Expand Down Expand Up @@ -7258,9 +7261,10 @@ pub fn addCCArgs(
const is_enabled = target.cpu.features.isEnabled(index);

if (feature.llvm_name) |llvm_name| {
// We communicate float ABI to Clang through the dedicated options.
// We communicate these to Clang through the dedicated options.
if (std.mem.startsWith(u8, llvm_name, "soft-float") or
std.mem.startsWith(u8, llvm_name, "hard-float"))
std.mem.startsWith(u8, llvm_name, "hard-float") or
(target.cpu.arch == .s390x and std.mem.eql(u8, llvm_name, "backchain")))
continue;

// Ignore these until we figure out how to handle the concept of omitting features.
Expand Down
5 changes: 4 additions & 1 deletion src/Package/Module.zig
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,10 @@ pub fn create(arena: Allocator, options: CreateOptions) !*Package.Module {
// See https://github.com/ziglang/zig/issues/23539
if (target_util.isDynamicAMDGCNFeature(target, feature)) continue;

const is_enabled = target.cpu.features.isEnabled(feature.index);
var is_enabled = target.cpu.features.isEnabled(feature.index);
if (target.cpu.arch == .s390x and @as(std.Target.s390x.Feature, @enumFromInt(feature.index)) == .backchain) {
is_enabled = !omit_frame_pointer;
}

if (is_enabled) {
try buf.ensureUnusedCapacity(2 + llvm_name.len);
Expand Down