zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

commit d591145f5dcab5aba1ab6aa65e7a57663f3a0f19 (tree)
parent a06f23c8ea132a4e45dcc396a35826aa1b0e0aea
Author: Alex Rønne Petersen <alex@alexrp.com>
Date:   Sat, 23 May 2026 13:54:35 +0200

Merge pull request 'add `m88k-openbsd` target info and stack tracing + some bonus commits' (#35429) from alexrp/zig:m88k into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/35429

Diffstat:
Mlib/std/Target.zig | 29+++++++++++++++++++++--------
Mlib/std/atomic.zig | 2++
Mlib/std/debug/Dwarf.zig | 3+++
Mlib/std/debug/SelfInfo/Elf.zig | 1+
Mlib/std/debug/cpu_context.zig | 97++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mlib/std/heap.zig | 2++
Mlib/std/lang.zig | 2++
Mlib/std/lang/assembly.zig | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mlib/std/start.zig | 11+++++++++++
Mlib/zig.h | 26++++++++++++++++++++++++--
Msrc/Sema.zig | 1+
Msrc/codegen/llvm.zig | 4++++
Msrc/target.zig | 4+++-
13 files changed, 239 insertions(+), 14 deletions(-)

diff --git a/lib/std/Target.zig b/lib/std/Target.zig @@ -751,6 +751,7 @@ pub const kvx = @import("Target/kvx.zig"); pub const lanai = @import("Target/lanai.zig"); pub const loongarch = @import("Target/loongarch.zig"); pub const m68k = @import("Target/m68k.zig"); +pub const m88k = @import("Target/generic.zig"); pub const microblaze = @import("Target/generic.zig"); pub const mips = @import("Target/mips.zig"); pub const msp430 = @import("Target/msp430.zig"); @@ -1091,6 +1092,7 @@ pub fn toElfMachine(target: *const Target) std.elf.EM { .kvx => .KVX, .lanai => .LANAI, .loongarch32, .loongarch64 => .LOONGARCH, + .m88k => .@"88K", .m68k => .@"68K", .microblaze, .microblazeel => .MICROBLAZE, .mips, .mips64, .mipsel, .mips64el => .MIPS, @@ -1154,6 +1156,7 @@ pub fn toCoffMachine(target: *const Target) std.coff.IMAGE.FILE.MACHINE { .kalimba, .kvx, .lanai, + .m88k, .m68k, .microblaze, .microblazeel, @@ -1364,6 +1367,7 @@ pub const Cpu = struct { loongarch32, loongarch64, m68k, + m88k, microblaze, microblazeel, mips, @@ -1439,6 +1443,7 @@ pub const Cpu = struct { lanai, loongarch, m68k, + m88k, microblaze, mips, msp430, @@ -1477,6 +1482,7 @@ pub const Cpu = struct { .lanai => .lanai, .loongarch32, .loongarch64 => .loongarch, .m68k => .m68k, + .m88k => .m88k, .microblaze, .microblazeel => .microblaze, .mips, .mipsel, .mips64, .mips64el => .mips, .msp430 => .msp430, @@ -1710,6 +1716,7 @@ pub const Cpu = struct { .hppa64, .lanai, .m68k, + .m88k, .microblaze, .mips, .mips64, @@ -1922,6 +1929,9 @@ pub const Cpu = struct { .m68k_interrupt, => &.{.m68k}, + .m88k_sysv, + => &.{.m88k}, + .microblaze_std, .microblaze_interrupt, => &.{ .microblaze, .microblazeel }, @@ -2554,6 +2564,7 @@ pub const DynamicLinker = struct { .m68k, .microblaze, .microblazeel, + .or1k, .powerpc64, .powerpc64le, .s390x, @@ -2631,8 +2642,6 @@ pub const DynamicLinker = struct { .arm, .armeb, - .thumb, - .thumbeb, => initFmt("/lib/ld-linux{s}.so.3", .{switch (abi) { .gnueabi => "", .gnueabihf => "-armhf", @@ -2641,6 +2650,7 @@ pub const DynamicLinker = struct { .aarch64, .aarch64_be, + .or1k, => |arch| if (abi == .gnu) initFmt("/lib/ld-linux-{s}.so.1", .{@tagName(arch)}) else none, // TODO: `-be` architecture support. @@ -2803,6 +2813,7 @@ pub const DynamicLinker = struct { .arm, .aarch64, .hppa, + .m88k, .mips64, .mips64el, .powerpc, @@ -2910,6 +2921,7 @@ pub fn ptrBitWidth_arch_abi(cpu_arch: Cpu.Arch, abi: Abi) u16 { .lanai, .loongarch32, .m68k, + .m88k, .microblaze, .microblazeel, .mips, @@ -3495,6 +3507,7 @@ pub fn cTypeAlignment(target: *const Target, c_type: CType) u16 { .hppa, .lanai, .m68k, + .m88k, .mips, .mipsel, .nvptx, @@ -3604,6 +3617,7 @@ pub fn cTypePreferredAlignment(target: *const Target, c_type: CType) u16 { .hppa, .lanai, .m68k, + .m88k, .mips, .mipsel, .nvptx, @@ -3675,6 +3689,7 @@ pub fn cMaxIntAlignment(target: *const Target) u16 { .lanai, .loongarch32, .m68k, + .m88k, .mips, .mipsel, .powerpc, @@ -3723,7 +3738,7 @@ pub fn cCallingConvention(target: *const Target) ?std.builtin.CallingConvention .x86_64 => switch (target.os.tag) { .windows, .uefi => .{ .x86_64_win = .{} }, else => switch (target.abi) { - .gnuabin32, .muslabin32 => .{ .x86_64_x32 = .{} }, + .gnux32, .muslx32 => .{ .x86_64_x32 = .{} }, else => .{ .x86_64_sysv = .{} }, }, }, @@ -3752,7 +3767,7 @@ pub fn cCallingConvention(target: *const Target) ?std.builtin.CallingConvention .riscv32, .riscv32be => .{ .riscv32_ilp32 = .{} }, .sparc64 => .{ .sparc64_sysv = .{} }, .sparc => .{ .sparc_sysv = .{} }, - .powerpc64 => if (target.abi.isGnu()) + .powerpc64 => if (target.os.tag == .ps3 or target.abi.isGnu()) .{ .powerpc64_elf = .{} } else .{ .powerpc64_elf_v2 = .{} }, @@ -3774,10 +3789,8 @@ pub fn cCallingConvention(target: *const Target) ?std.builtin.CallingConvention .lanai => .{ .lanai_sysv = .{} }, .loongarch64 => .{ .loongarch64_lp64 = .{} }, .loongarch32 => .{ .loongarch32_ilp32 = .{} }, - .m68k => if (target.abi.isGnu() or target.abi.isMusl()) - .{ .m68k_gnu = .{} } - else - .{ .m68k_sysv = .{} }, + .m68k => .{ .m68k_gnu = .{} }, + .m88k => .{ .m88k_sysv = .{} }, .microblaze, .microblazeel => .{ .microblaze_std = .{} }, .msp430 => .{ .msp430_eabi = .{} }, .or1k => .{ .or1k_sysv = .{} }, diff --git a/lib/std/atomic.zig b/lib/std/atomic.zig @@ -442,10 +442,12 @@ pub fn cacheLineForCpu(cpu: std.Target.Cpu) u16 { // - https://github.com/torvalds/linux/blob/3a7e02c040b130b5545e4b115aada7bacd80a2b6/arch/sparc/include/asm/cache.h#L14 // - https://github.com/torvalds/linux/blob/3a7e02c040b130b5545e4b115aada7bacd80a2b6/arch/microblaze/include/asm/cache.h#L15 // - https://github.com/torvalds/linux/blob/3a7e02c040b130b5545e4b115aada7bacd80a2b6/arch/sh/include/cpu-sh4/cpu/cache.h#L10 + // - https://github.com/openbsd/src/blob/1957873d2063db11dab780eca75b5e629d1e838d/sys/arch/m88k/m88k/atomic.S#L22 .arm, .armeb, .thumb, .thumbeb, + .m88k, .microblaze, .microblazeel, .mips, diff --git a/lib/std/debug/Dwarf.zig b/lib/std/debug/Dwarf.zig @@ -1445,6 +1445,7 @@ pub fn ipRegNum(arch: std.Target.Cpu.Arch) ?u16 { .lanai => 2, .loongarch32, .loongarch64 => 64, .m68k => 26, + .m88k => 64, .mips, .mipsel, .mips64, .mips64el => 66, .or1k => 35, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 67, @@ -1470,6 +1471,7 @@ pub fn fpRegNum(arch: std.Target.Cpu.Arch) u16 { .lanai => 5, .loongarch32, .loongarch64 => 22, .m68k => 14, + .m88k => 30, .mips, .mipsel, .mips64, .mips64el => 30, .or1k => 2, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, @@ -1495,6 +1497,7 @@ pub fn spRegNum(arch: std.Target.Cpu.Arch) u16 { .lanai => 4, .loongarch32, .loongarch64 => 3, .m68k => 15, + .m88k => 31, .mips, .mipsel, .mips64, .mips64el => 29, .or1k => 1, .powerpc, .powerpcle, .powerpc64, .powerpc64le => 1, diff --git a/lib/std/debug/SelfInfo/Elf.zig b/lib/std/debug/SelfInfo/Elf.zig @@ -161,6 +161,7 @@ pub const can_unwind: bool = s: { // Not supported yet: arm .openbsd => &.{ .aarch64, + .m88k, .mips64, .mips64el, .riscv64, diff --git a/lib/std/debug/cpu_context.zig b/lib/std/debug/cpu_context.zig @@ -14,6 +14,7 @@ else switch (native_arch) { .lanai => Lanai, .loongarch32, .loongarch64 => LoongArch, .m68k => M68k, + .m88k => M88k, .mips, .mipsel, .mips64, .mips64el => Mips, .or1k => Or1k, .powerpc, .powerpcle, .powerpc64, .powerpc64le => Powerpc, @@ -62,6 +63,13 @@ pub fn fromPosixSignalContext(ctx_ptr: ?*const anyopaque) ?Native { }, .pc = @truncate(uc.mcontext.pc), }; + } else if (native_arch == .m88k and native_os == .openbsd) { + // OpenBSD makes no effort to clear the V and E bits of the SXIP register when presenting it + // to user space, so we need to do that here. + return .{ + .r = uc.mcontext.r, + .xip = uc.mcontext.xip & ~@as(u32, 0b11), + }; } else if (native_arch.isMIPS32() and native_os == .linux) { // The O32 kABI uses 64-bit fields for some reason. return .{ @@ -895,6 +903,75 @@ const M68k = extern struct { }; /// This is an `extern struct` so that inline assembly in `current` can use field offsets. +const M88k = extern struct { + /// The numbered general-purpose registers r0 - r31. + r: [32]u32, + xip: u32, + + pub inline fn current() M88k { + var ctx: M88k = undefined; + asm volatile ( + \\ st %%r0, %%r2, 0 + \\ st %%r1, %%r2, 4 + \\ st %%r2, %%r2, 8 + \\ st %%r3, %%r2, 12 + \\ st %%r4, %%r2, 16 + \\ st %%r5, %%r2, 20 + \\ st %%r6, %%r2, 24 + \\ st %%r7, %%r2, 28 + \\ st %%r8, %%r2, 32 + \\ st %%r9, %%r2, 36 + \\ st %%r10, %%r2, 40 + \\ st %%r11, %%r2, 44 + \\ st %%r12, %%r2, 48 + \\ st %%r13, %%r2, 52 + \\ st %%r14, %%r2, 56 + \\ st %%r15, %%r2, 60 + \\ st %%r16, %%r2, 64 + \\ st %%r17, %%r2, 68 + \\ st %%r18, %%r2, 72 + \\ st %%r19, %%r2, 76 + \\ st %%r20, %%r2, 80 + \\ st %%r21, %%r2, 84 + \\ st %%r22, %%r2, 88 + \\ st %%r23, %%r2, 92 + \\ st %%r24, %%r2, 96 + \\ st %%r25, %%r2, 100 + \\ st %%r26, %%r2, 104 + \\ st %%r27, %%r2, 108 + \\ st %%r28, %%r2, 112 + \\ st %%r29, %%r2, 116 + \\ st %%r30, %%r2, 120 + \\ st %%r31, %%r2, 124 + \\ bsr.n 1f + \\1: + \\ st %%r1, %%r2, 128 + : + : [ctx] "{r2}" (&ctx), + : .{ .r1 = true, .memory = true }); + return ctx; + } + + pub fn getFp(ctx: *const M88k) u32 { + return ctx.r[30]; + } + pub fn getPc(ctx: *const M88k) u32 { + return ctx.xip; + } + + pub fn dwarfRegisterBytes(ctx: *M88k, register_num: u16) DwarfRegisterError![]u8 { + switch (register_num) { + 0...31 => return @ptrCast(&ctx.r[register_num]), + 64 => return @ptrCast(&ctx.xip), + + 32...63 => return error.UnsupportedRegister, // x0 - x31 + + else => return error.InvalidRegister, + } + } +}; + +/// This is an `extern struct` so that inline assembly in `current` can use field offsets. const Mips = extern struct { /// The numbered general-purpose registers r0 - r31. r0 must be zero. r: [32]Gpr, @@ -2330,9 +2407,11 @@ const signal_ucontext_t = switch (native_os) { .alpha => extern struct { _cookie: i64, _mask: i64, - pc: u64, - _ps: i64, - r: [32]u64, + mcontext: extern struct { + pc: u64, + _ps: i64, + r: [32]u64, + }, }, // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/arm/include/signal.h .arm => extern struct { @@ -2361,6 +2440,18 @@ const signal_ucontext_t = switch (native_os) { r23_29: [7]u32, r31: u32, }, + // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/m88k/include/signal.h + .m88k => extern struct { + _cookie: i32, + _mask: i32, + mcontext: extern struct { + r: [32]u32, + _epsr: u32, + _fpsr: u32, + _fpcr: u32, + xip: u32, + }, + }, // https://github.com/openbsd/src/blob/42468faed8369d07ae49ae02dd71ec34f59b66cd/sys/arch/mips64/include/signal.h .mips64, .mips64el => extern struct { _cookie: i64, diff --git a/lib/std/heap.zig b/lib/std/heap.zig @@ -608,6 +608,7 @@ const page_size_min_default: ?usize = switch (builtin.os.tag) { .hppa => 4 << 10, .x86, .x86_64 => 4 << 10, .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10, + .m88k => 4 << 10, .mips64, .mips64el => 4 << 10, .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10, .riscv64 => 4 << 10, @@ -771,6 +772,7 @@ const page_size_max_default: ?usize = switch (builtin.os.tag) { .hppa => 4 << 10, .x86, .x86_64 => 4 << 10, .thumb, .thumbeb, .arm, .armeb, .aarch64, .aarch64_be => 4 << 10, + .m88k => 4 << 10, .mips64, .mips64el => 16 << 10, .powerpc, .powerpc64, .powerpc64le, .powerpcle => 4 << 10, .riscv64 => 4 << 10, diff --git a/lib/std/lang.zig b/lib/std/lang.zig @@ -292,6 +292,8 @@ pub const CallingConvention = union(enum(u8)) { m68k_rtd: CommonOptions, m68k_interrupt: CommonOptions, + m88k_sysv: CommonOptions, + /// The standard `microblaze`/`microblazeel` calling convention. microblaze_std: CommonOptions, microblaze_interrupt: MicroblazeInterruptOptions, diff --git a/lib/std/lang/assembly.zig b/lib/std/lang/assembly.zig @@ -1553,6 +1553,77 @@ pub const Clobbers = switch (@import("builtin").cpu.arch) { r14: bool = false, r15: bool = false, }, + .m88k => packed struct { + /// Whether the inline assembly code may perform stores to memory + /// addresses other than those derived from input pointer provenance. + memory: bool = false, + + r0: bool = false, + r1: bool = false, + r2: bool = false, + r3: bool = false, + r4: bool = false, + r5: bool = false, + r6: bool = false, + r7: bool = false, + r8: bool = false, + r9: bool = false, + r10: bool = false, + r11: bool = false, + r12: bool = false, + r13: bool = false, + r14: bool = false, + r15: bool = false, + r16: bool = false, + r17: bool = false, + r18: bool = false, + r19: bool = false, + r20: bool = false, + r21: bool = false, + r22: bool = false, + r23: bool = false, + r24: bool = false, + r25: bool = false, + r26: bool = false, + r27: bool = false, + r28: bool = false, + r29: bool = false, + r30: bool = false, + r31: bool = false, + + x0: bool = false, + x1: bool = false, + x2: bool = false, + x3: bool = false, + x4: bool = false, + x5: bool = false, + x6: bool = false, + x7: bool = false, + x8: bool = false, + x9: bool = false, + x10: bool = false, + x11: bool = false, + x12: bool = false, + x13: bool = false, + x14: bool = false, + x15: bool = false, + x16: bool = false, + x17: bool = false, + x18: bool = false, + x19: bool = false, + x20: bool = false, + x21: bool = false, + x22: bool = false, + x23: bool = false, + x24: bool = false, + x25: bool = false, + x26: bool = false, + x27: bool = false, + x28: bool = false, + x29: bool = false, + x30: bool = false, + x31: bool = false, + }, .m68k => packed struct { /// Whether the inline assembly code may perform stores to memory /// addresses other than those derived from input pointer provenance. diff --git a/lib/std/start.zig b/lib/std/start.zig @@ -169,6 +169,7 @@ fn _start() callconv(.naked) noreturn { .kvx => ".cfi_undefined r14", .loongarch32, .loongarch64 => ".cfi_undefined 1", .m68k => ".cfi_undefined %%pc", + .m88k => ".cfi_undefined %%r1", .microblaze, .microblazeel => ".cfi_undefined r15", .mips, .mipsel, .mips64, .mips64el => ".cfi_undefined $ra", .or1k => ".cfi_undefined r9", @@ -331,6 +332,16 @@ fn _start() callconv(.naked) noreturn { \\ lea %[posixCallMainAndExit] - . - 8, %%a0 \\ jsr (%%pc, %%a0) , + .m88k => + // r1 = LR, r30 = FP, r31 = SP + \\ or %%r0, %%r0, %%r0 + \\ or %%r0, %%r0, %%r0 + \\ or %%30, %%r0, %%r0 + \\ or %%r1, %%r0, %%r0 + \\ or %%r2, %%r31, %%r0 + \\ clr %%r31, %%r31, 4<0> + \\ br.n %[posixCallMainAndExit] + , .microblaze, .microblazeel => // r1 = SP, r15 = LR, r19 = FP, r20 = GP \\ ori r15, r0, r0 diff --git a/lib/zig.h b/lib/zig.h @@ -21,6 +21,8 @@ #if defined(__aarch64__) || (defined(zig_msvc) && defined(_M_ARM64)) #define zig_aarch64 +#elif defined(__alpha__) +#define zig_alpha #elif defined(__thumb__) || (defined(zig_msvc) && defined(_M_ARM)) #define zig_thumb #define zig_arm @@ -36,6 +38,10 @@ #elif defined(__loongarch64) #define zig_loongarch64 #define zig_loongarch +#elif defined(__m68k__) +#define zig_m68k +#elif defined(__m88k__) +#define zig_m88k #elif defined(__mips64) #define zig_mips64 #define zig_mips @@ -79,6 +85,8 @@ #elif defined(__I86__) #define zig_x86_16 #define zig_x86 +#elif defined(__xtensa__) +#define zig_xtensa #elif defined (__ez80) #define zig_ez80 #define zig_z80 @@ -390,7 +398,9 @@ #elif defined(zig_gnuc_asm) -#if defined(zig_thumb) +#if defined(zig_alpha) +#define zig_trap() __asm__ volatile("call_pal 0x000000") +#elif defined(zig_thumb) #define zig_trap() __asm__ volatile("udf #0xfe") #elif defined(zig_arm) || defined(zig_aarch64) #define zig_trap() __asm__ volatile("udf #0xfdee") @@ -398,6 +408,10 @@ #define zig_trap() __asm__ volatile("r27:26 = memd(#0xbadc0fee)") #elif defined(zig_kvx) || defined(zig_loongarch) || defined(zig_powerpc) #define zig_trap() __asm__ volatile(".word 0x0") +#elif defined(zig_m68k) +#define zig_trap() __asm__ volatile("illegal") +#elif defined(zig_m88k) +#define zig_trap() __asm__ volatile("tb0 0, %%r0, 511") #elif defined(zig_mips) #define zig_trap() __asm__ volatile(".word 0x3d") #elif defined(zig_or1k) @@ -412,6 +426,8 @@ #define zig_trap() __asm__ volatile("int $0x3") #elif defined(zig_x86) #define zig_trap() __asm__ volatile("ud2") +#elif defined(zig_xtensa) +#define zig_trap() __asm__ volatile("ill") #elif defined(zig_z80) #define zig_trap() __asm__ volatile("rst 00h") #else @@ -428,7 +444,9 @@ #define zig_breakpoint() __debugbreak() #elif defined(zig_gnuc_asm) -#if defined(zig_arm) +#if defined(zig_alpha) +#define zig_breakpoint() __asm__ volatile("call_pal 0x000080") +#elif defined(zig_arm) #define zig_breakpoint() __asm__ volatile("bkpt #0x0") #elif defined(zig_aarch64) #define zig_breakpoint() __asm__ volatile("brk #0xf000") @@ -436,6 +454,8 @@ #define zig_breakpoint() __asm__ volatile("brkpt") #elif defined(zig_kvx) || defined(zig_loongarch) #define zig_breakpoint() __asm__ volatile("break 0x0") +#elif defined(zig_m88k) +#define zig_breakpoint() __asm__ volatile("illop1") #elif defined(zig_mips) #define zig_breakpoint() __asm__ volatile("break") #elif defined(zig_or1k) @@ -450,6 +470,8 @@ #define zig_breakpoint() __asm__ volatile("ta 0x1") #elif defined(zig_x86) #define zig_breakpoint() __asm__ volatile("int $0x3") +#elif defined(zig_xtensa) +#define zig_breakpoint() __asm__ volatile("break 1, 1") #else #define zig_breakpoint() zig_breakpoint_unavailable #endif diff --git a/src/Sema.zig b/src/Sema.zig @@ -8558,6 +8558,7 @@ const calling_conventions_supporting_var_args = [_]std.lang.CallingConvention.Ta .m68k_sysv, .m68k_gnu, .m68k_rtd, + .m88k_sysv, .msp430_eabi, .or1k_sysv, .s390x_sysv, diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig @@ -112,6 +112,7 @@ pub fn targetTriple(allocator: Allocator, target: *const std.Target) ![]const u8 .hppa64, .kalimba, .kvx, + .m88k, .microblaze, .microblazeel, .or1k, @@ -478,6 +479,7 @@ pub fn dataLayout(target: *const std.Target) []const u8 { .hppa64, .kalimba, .kvx, + .m88k, .microblaze, .microblazeel, .or1k, @@ -4531,6 +4533,7 @@ pub fn toLlvmCallConvTag(cc_tag: std.lang.CallingConvention.Tag, target: *const .loongarch32_ilp32, .m68k_sysv, .m68k_gnu, + .m88k_sysv, .msp430_eabi, .or1k_sysv, .propeller_sysv, @@ -4903,6 +4906,7 @@ pub fn initializeLLVMTarget(arch: std.Target.Cpu.Arch) void { .hppa64, .kalimba, .kvx, + .m88k, .microblaze, .microblazeel, .or1k, diff --git a/src/target.zig b/src/target.zig @@ -245,6 +245,7 @@ pub fn hasLlvmSupport(target: *const std.Target, ofmt: std.Target.ObjectFormat) .hppa64, .kalimba, .kvx, + .m88k, .microblaze, .microblazeel, .or1k, @@ -695,7 +696,8 @@ pub fn llvmMachineAbi(target: *const std.Target) ?[:0]const u8 { .gnuabin32, .muslabin32 => "n32", else => "n64", }, - .powerpc64, .powerpc64le => if (target.os.tag == .ps3) "elfv1" else "elfv2", + .powerpc64 => if (target.os.tag == .ps3) "elfv1" else "elfv2", + .powerpc64le => "elfv2", .riscv64, .riscv64be => if (target.cpu.has(.riscv, .e)) "lp64e" else if (target.cpu.has(.riscv, .d))