authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-06 20:31:48-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2023-05-08 07:36:20-04:00
log3a5e3c52e0f09112989a2a40345305bfe9508431
treeee8a957be9d65fe72fbd1e8edf405f320ea4d680
parent0bd92da0e20058942497d3f2d572799f9710959c

x86_64: implement `@mulAdd`


7 files changed, 277 insertions(+), 14 deletions(-)

src/arch/x86_64/CodeGen.zig+166-3
......@@ -1200,6 +1200,32 @@ fn asmRegisterRegisterImmediate(
12001200 });
12011201}
12021202
1203fn asmRegisterRegisterMemory(
1204 self: *Self,
1205 tag: Mir.Inst.Tag,
1206 reg1: Register,
1207 reg2: Register,
1208 m: Memory,
1209) !void {
1210 _ = try self.addInst(.{
1211 .tag = tag,
1212 .ops = switch (m) {
1213 .sib => .rrm_sib,
1214 .rip => .rrm_rip,
1215 else => unreachable,
1216 },
1217 .data = .{ .rrx = .{
1218 .r1 = reg1,
1219 .r2 = reg2,
1220 .payload = switch (m) {
1221 .sib => try self.addExtra(Mir.MemorySib.encode(m)),
1222 .rip => try self.addExtra(Mir.MemoryRip.encode(m)),
1223 else => unreachable,
1224 },
1225 } },
1226 });
1227}
1228
12031229fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void {
12041230 _ = try self.addInst(.{
12051231 .tag = tag,
......@@ -9369,9 +9395,146 @@ fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
93699395fn airMulAdd(self: *Self, inst: Air.Inst.Index) !void {
93709396 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
93719397 const extra = self.air.extraData(Air.Bin, pl_op.payload).data;
9372 _ = extra;
9373 return self.fail("TODO implement airMulAdd for x86_64", .{});
9374 //return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, pl_op.operand });
9398 const ty = self.air.typeOfIndex(inst);
9399
9400 if (!self.hasFeature(.fma)) return self.fail("TODO implement airMulAdd for {}", .{
9401 ty.fmt(self.bin_file.options.module.?),
9402 });
9403
9404 const ops = [3]Air.Inst.Ref{ extra.lhs, extra.rhs, pl_op.operand };
9405 var mcvs: [3]MCValue = undefined;
9406 var locks = [1]?RegisterManager.RegisterLock{null} ** 3;
9407 defer for (locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock);
9408 var order = [1]u2{0} ** 3;
9409 var unused = std.StaticBitSet(3).initFull();
9410 for (ops, &mcvs, &locks, 0..) |op, *mcv, *lock, op_i| {
9411 const op_index = @intCast(u2, op_i);
9412 mcv.* = try self.resolveInst(op);
9413 if (unused.isSet(0) and mcv.isRegister() and self.reuseOperand(inst, op, op_index, mcv.*)) {
9414 order[op_index] = 1;
9415 unused.unset(0);
9416 } else if (unused.isSet(2) and mcv.isMemory()) {
9417 order[op_index] = 3;
9418 unused.unset(2);
9419 }
9420 switch (mcv.*) {
9421 .register => |reg| lock.* = self.register_manager.lockReg(reg),
9422 else => {},
9423 }
9424 }
9425 for (&order, &mcvs, &locks) |*mop_index, *mcv, *lock| {
9426 if (mop_index.* != 0) continue;
9427 mop_index.* = 1 + @intCast(u2, unused.toggleFirstSet().?);
9428 if (mop_index.* > 1 and mcv.isRegister()) continue;
9429 const reg = try self.copyToTmpRegister(ty, mcv.*);
9430 mcv.* = .{ .register = reg };
9431 if (lock.*) |old_lock| self.register_manager.unlockReg(old_lock);
9432 lock.* = self.register_manager.lockRegAssumeUnused(reg);
9433 }
9434
9435 const tag: ?Mir.Inst.Tag =
9436 if (mem.eql(u2, &order, &.{ 1, 3, 2 }) or mem.eql(u2, &order, &.{ 3, 1, 2 }))
9437 switch (ty.zigTypeTag()) {
9438 .Float => switch (ty.floatBits(self.target.*)) {
9439 32 => .vfmadd132ss,
9440 64 => .vfmadd132sd,
9441 else => null,
9442 },
9443 .Vector => switch (ty.childType().zigTypeTag()) {
9444 .Float => switch (ty.childType().floatBits(self.target.*)) {
9445 32 => switch (ty.vectorLen()) {
9446 1 => .vfmadd132ss,
9447 2...8 => .vfmadd132ps,
9448 else => null,
9449 },
9450 64 => switch (ty.vectorLen()) {
9451 1 => .vfmadd132sd,
9452 2...4 => .vfmadd132pd,
9453 else => null,
9454 },
9455 else => null,
9456 },
9457 else => null,
9458 },
9459 else => unreachable,
9460 }
9461 else if (mem.eql(u2, &order, &.{ 2, 1, 3 }) or mem.eql(u2, &order, &.{ 1, 2, 3 }))
9462 switch (ty.zigTypeTag()) {
9463 .Float => switch (ty.floatBits(self.target.*)) {
9464 32 => .vfmadd213ss,
9465 64 => .vfmadd213sd,
9466 else => null,
9467 },
9468 .Vector => switch (ty.childType().zigTypeTag()) {
9469 .Float => switch (ty.childType().floatBits(self.target.*)) {
9470 32 => switch (ty.vectorLen()) {
9471 1 => .vfmadd213ss,
9472 2...8 => .vfmadd213ps,
9473 else => null,
9474 },
9475 64 => switch (ty.vectorLen()) {
9476 1 => .vfmadd213sd,
9477 2...4 => .vfmadd213pd,
9478 else => null,
9479 },
9480 else => null,
9481 },
9482 else => null,
9483 },
9484 else => unreachable,
9485 }
9486 else if (mem.eql(u2, &order, &.{ 2, 3, 1 }) or mem.eql(u2, &order, &.{ 3, 2, 1 }))
9487 switch (ty.zigTypeTag()) {
9488 .Float => switch (ty.floatBits(self.target.*)) {
9489 32 => .vfmadd231ss,
9490 64 => .vfmadd231sd,
9491 else => null,
9492 },
9493 .Vector => switch (ty.childType().zigTypeTag()) {
9494 .Float => switch (ty.childType().floatBits(self.target.*)) {
9495 32 => switch (ty.vectorLen()) {
9496 1 => .vfmadd231ss,
9497 2...8 => .vfmadd231ps,
9498 else => null,
9499 },
9500 64 => switch (ty.vectorLen()) {
9501 1 => .vfmadd231sd,
9502 2...4 => .vfmadd231pd,
9503 else => null,
9504 },
9505 else => null,
9506 },
9507 else => null,
9508 },
9509 else => null,
9510 }
9511 else
9512 unreachable;
9513 if (tag == null) return self.fail("TODO implement airMulAdd for {}", .{
9514 ty.fmt(self.bin_file.options.module.?),
9515 });
9516
9517 var mops: [3]MCValue = undefined;
9518 for (order, mcvs) |mop_index, mcv| mops[mop_index - 1] = mcv;
9519
9520 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
9521 const mop1_reg = registerAlias(mops[0].getReg().?, abi_size);
9522 const mop2_reg = registerAlias(mops[1].getReg().?, abi_size);
9523 if (mops[2].isRegister())
9524 try self.asmRegisterRegisterRegister(
9525 tag.?,
9526 mop1_reg,
9527 mop2_reg,
9528 registerAlias(mops[2].getReg().?, abi_size),
9529 )
9530 else
9531 try self.asmRegisterRegisterMemory(
9532 tag.?,
9533 mop1_reg,
9534 mop2_reg,
9535 mops[2].mem(Memory.PtrSize.fromSize(abi_size)),
9536 );
9537 return self.finishAir(inst, mops[0], ops);
93759538}
93769539
93779540fn resolveInst(self: *Self, ref: Air.Inst.Ref) InnerError!MCValue {
src/arch/x86_64/Encoding.zig+19-5
......@@ -340,6 +340,11 @@ pub const Mnemonic = enum {
340340 vpunpcklbw, vpunpckldq, vpunpcklqdq, vpunpcklwd,
341341 // F16C
342342 vcvtph2ps, vcvtps2ph,
343 // FMA
344 vfmadd132pd, vfmadd213pd, vfmadd231pd,
345 vfmadd132ps, vfmadd213ps, vfmadd231ps,
346 vfmadd132sd, vfmadd213sd, vfmadd231sd,
347 vfmadd132ss, vfmadd213ss, vfmadd231ss,
343348 // zig fmt: on
344349};
345350
......@@ -368,12 +373,13 @@ pub const Op = enum {
368373 r8, r16, r32, r64,
369374 rm8, rm16, rm32, rm64,
370375 r32_m16, r64_m16,
371 m8, m16, m32, m64, m80, m128,
376 m8, m16, m32, m64, m80, m128, m256,
372377 rel8, rel16, rel32,
373378 m,
374379 moffs,
375380 sreg,
376381 xmm, xmm_m32, xmm_m64, xmm_m128,
382 ymm, ymm_m256,
377383 // zig fmt: on
378384
379385 pub fn fromOperand(operand: Instruction.Operand) Op {
......@@ -385,6 +391,7 @@ pub const Op = enum {
385391 .segment => return .sreg,
386392 .floating_point => return switch (reg.bitSize()) {
387393 128 => .xmm,
394 256 => .ymm,
388395 else => unreachable,
389396 },
390397 .general_purpose => {
......@@ -418,6 +425,7 @@ pub const Op = enum {
418425 64 => .m64,
419426 80 => .m80,
420427 128 => .m128,
428 256 => .m256,
421429 else => unreachable,
422430 };
423431 },
......@@ -454,7 +462,8 @@ pub const Op = enum {
454462 .eax, .r32, .rm32, .r32_m16 => unreachable,
455463 .rax, .r64, .rm64, .r64_m16 => unreachable,
456464 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => unreachable,
457 .m8, .m16, .m32, .m64, .m80, .m128 => unreachable,
465 .ymm, .ymm_m256 => unreachable,
466 .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
458467 .unity => 1,
459468 .imm8, .imm8s, .rel8 => 8,
460469 .imm16, .imm16s, .rel16 => 16,
......@@ -468,12 +477,13 @@ pub const Op = enum {
468477 .none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
469478 .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
470479 .rel8, .rel16, .rel32 => unreachable,
471 .m8, .m16, .m32, .m64, .m80, .m128 => unreachable,
480 .m8, .m16, .m32, .m64, .m80, .m128, .m256 => unreachable,
472481 .al, .cl, .r8, .rm8 => 8,
473482 .ax, .r16, .rm16 => 16,
474483 .eax, .r32, .rm32, .r32_m16 => 32,
475484 .rax, .r64, .rm64, .r64_m16 => 64,
476485 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => 128,
486 .ymm, .ymm_m256 => 256,
477487 };
478488 }
479489
......@@ -482,13 +492,14 @@ pub const Op = enum {
482492 .none, .o16, .o32, .o64, .moffs, .m, .sreg => unreachable,
483493 .unity, .imm8, .imm8s, .imm16, .imm16s, .imm32, .imm32s, .imm64 => unreachable,
484494 .rel8, .rel16, .rel32 => unreachable,
485 .al, .cl, .r8, .ax, .r16, .eax, .r32, .rax, .r64, .xmm => unreachable,
495 .al, .cl, .r8, .ax, .r16, .eax, .r32, .rax, .r64, .xmm, .ymm => unreachable,
486496 .m8, .rm8 => 8,
487497 .m16, .rm16, .r32_m16, .r64_m16 => 16,
488498 .m32, .rm32, .xmm_m32 => 32,
489499 .m64, .rm64, .xmm_m64 => 64,
490500 .m80 => 80,
491501 .m128, .xmm_m128 => 128,
502 .m256, .ymm_m256 => 256,
492503 };
493504 }
494505
......@@ -513,6 +524,7 @@ pub const Op = enum {
513524 .rm8, .rm16, .rm32, .rm64,
514525 .r32_m16, .r64_m16,
515526 .xmm, .xmm_m32, .xmm_m64, .xmm_m128,
527 .ymm, .ymm_m256,
516528 => true,
517529 else => false,
518530 };
......@@ -539,7 +551,7 @@ pub const Op = enum {
539551 .r32_m16, .r64_m16,
540552 .m8, .m16, .m32, .m64, .m80, .m128,
541553 .m,
542 .xmm_m32, .xmm_m64, .xmm_m128,
554 .xmm_m32, .xmm_m64, .xmm_m128, .ymm_m256,
543555 => true,
544556 else => false,
545557 };
......@@ -562,6 +574,7 @@ pub const Op = enum {
562574 .r32_m16, .r64_m16 => .general_purpose,
563575 .sreg => .segment,
564576 .xmm, .xmm_m32, .xmm_m64, .xmm_m128 => .floating_point,
577 .ymm, .ymm_m256 => .floating_point,
565578 };
566579 }
567580
......@@ -625,6 +638,7 @@ pub const Feature = enum {
625638 none,
626639 avx,
627640 f16c,
641 fma,
628642 sse,
629643 sse2,
630644 sse3,
src/arch/x86_64/Lower.zig+22
......@@ -205,6 +205,19 @@ pub fn lowerMir(lower: *Lower, index: Mir.Inst.Index) Error!struct {
205205
206206 .vcvtph2ps,
207207 .vcvtps2ph,
208
209 .vfmadd132pd,
210 .vfmadd213pd,
211 .vfmadd231pd,
212 .vfmadd132ps,
213 .vfmadd213ps,
214 .vfmadd231ps,
215 .vfmadd132sd,
216 .vfmadd213sd,
217 .vfmadd231sd,
218 .vfmadd132ss,
219 .vfmadd213ss,
220 .vfmadd231ss,
208221 => try lower.mirGeneric(inst),
209222
210223 .cmps,
......@@ -288,6 +301,8 @@ fn imm(lower: Lower, ops: Mir.Inst.Ops, i: u32) Immediate {
288301 .rmi_rip,
289302 .mri_sib,
290303 .mri_rip,
304 .rrm_sib,
305 .rrm_rip,
291306 .rrmi_sib,
292307 .rrmi_rip,
293308 => Immediate.u(i),
......@@ -310,6 +325,7 @@ fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
310325 .mr_sib,
311326 .mrr_sib,
312327 .mri_sib,
328 .rrm_sib,
313329 .rrmi_sib,
314330 .lock_m_sib,
315331 .lock_mi_sib_u,
......@@ -327,6 +343,7 @@ fn mem(lower: Lower, ops: Mir.Inst.Ops, payload: u32) Memory {
327343 .mr_rip,
328344 .mrr_rip,
329345 .mri_rip,
346 .rrm_rip,
330347 .rrmi_rip,
331348 .lock_m_rip,
332349 .lock_mi_rip_u,
......@@ -449,6 +466,11 @@ fn mirGeneric(lower: *Lower, inst: Mir.Inst) Error!void {
449466 .{ .reg = inst.data.rix.r },
450467 .{ .imm = lower.imm(inst.ops, inst.data.rix.i) },
451468 },
469 .rrm_sib, .rrm_rip => &.{
470 .{ .reg = inst.data.rrx.r1 },
471 .{ .reg = inst.data.rrx.r2 },
472 .{ .mem = lower.mem(inst.ops, inst.data.rrx.payload) },
473 },
452474 .rrmi_sib, .rrmi_rip => &.{
453475 .{ .reg = inst.data.rrix.r1 },
454476 .{ .reg = inst.data.rrix.r2 },
src/arch/x86_64/Mir.zig+31
......@@ -324,6 +324,31 @@ pub const Inst = struct {
324324 /// Convert single-precision floating-point values to 16-bit floating-point values
325325 vcvtps2ph,
326326
327 /// Fused multiply-add of packed double-precision floating-point values
328 vfmadd132pd,
329 /// Fused multiply-add of packed double-precision floating-point values
330 vfmadd213pd,
331 /// Fused multiply-add of packed double-precision floating-point values
332 vfmadd231pd,
333 /// Fused multiply-add of packed single-precision floating-point values
334 vfmadd132ps,
335 /// Fused multiply-add of packed single-precision floating-point values
336 vfmadd213ps,
337 /// Fused multiply-add of packed single-precision floating-point values
338 vfmadd231ps,
339 /// Fused multiply-add of scalar double-precision floating-point values
340 vfmadd132sd,
341 /// Fused multiply-add of scalar double-precision floating-point values
342 vfmadd213sd,
343 /// Fused multiply-add of scalar double-precision floating-point values
344 vfmadd231sd,
345 /// Fused multiply-add of scalar single-precision floating-point values
346 vfmadd132ss,
347 /// Fused multiply-add of scalar single-precision floating-point values
348 vfmadd213ss,
349 /// Fused multiply-add of scalar single-precision floating-point values
350 vfmadd231ss,
351
327352 /// Compare string operands
328353 cmps,
329354 /// Load string
......@@ -434,6 +459,12 @@ pub const Inst = struct {
434459 /// Register, memory (SIB), immediate (byte) operands.
435460 /// Uses `rix` payload with extra data of type `MemorySib`.
436461 rmi_sib,
462 /// Register, register, memory (RIP).
463 /// Uses `rrix` payload with extra data of type `MemoryRip`.
464 rrm_rip,
465 /// Register, register, memory (SIB).
466 /// Uses `rrix` payload with extra data of type `MemorySib`.
467 rrm_sib,
437468 /// Register, register, memory (RIP), immediate (byte) operands.
438469 /// Uses `rrix` payload with extra data of type `MemoryRip`.
439470 rrmi_rip,
src/arch/x86_64/bits.zig+12-4
......@@ -485,7 +485,9 @@ pub const Memory = union(enum) {
485485 dword,
486486 qword,
487487 tbyte,
488 dqword,
488 xword,
489 yword,
490 zword,
489491
490492 pub fn fromSize(size: u32) PtrSize {
491493 return switch (size) {
......@@ -493,7 +495,9 @@ pub const Memory = union(enum) {
493495 2...2 => .word,
494496 3...4 => .dword,
495497 5...8 => .qword,
496 9...16 => .dqword,
498 9...16 => .xword,
499 17...32 => .yword,
500 33...64 => .zword,
497501 else => unreachable,
498502 };
499503 }
......@@ -505,7 +509,9 @@ pub const Memory = union(enum) {
505509 32 => .dword,
506510 64 => .qword,
507511 80 => .tbyte,
508 128 => .dqword,
512 128 => .xword,
513 256 => .yword,
514 512 => .zword,
509515 else => unreachable,
510516 };
511517 }
......@@ -517,7 +523,9 @@ pub const Memory = union(enum) {
517523 .dword => 32,
518524 .qword => 64,
519525 .tbyte => 80,
520 .dqword => 128,
526 .xword => 128,
527 .yword => 256,
528 .zword => 512,
521529 };
522530 }
523531 };
src/arch/x86_64/encodings.zig+23
......@@ -1016,5 +1016,28 @@ pub const table = [_]Entry{
10161016 .{ .vcvtph2ps, .rm, &.{ .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x13 }, 0, .vex_128, .f16c },
10171017
10181018 .{ .vcvtps2ph, .mri, &.{ .xmm_m64, .xmm, .imm8 }, &.{ 0x66, 0x0f, 0x3a, 0x1d }, 0, .vex_128, .f16c },
1019
1020 // FMA
1021 .{ .vfmadd132pd, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x98 }, 0, .vex_128_long, .fma },
1022 .{ .vfmadd132pd, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x98 }, 0, .vex_256_long, .fma },
1023 .{ .vfmadd213pd, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0xa8 }, 0, .vex_128_long, .fma },
1024 .{ .vfmadd213pd, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0xa8 }, 0, .vex_256_long, .fma },
1025 .{ .vfmadd231pd, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0xb8 }, 0, .vex_128_long, .fma },
1026 .{ .vfmadd231pd, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0xb8 }, 0, .vex_256_long, .fma },
1027
1028 .{ .vfmadd132ps, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0x98 }, 0, .vex_128, .fma },
1029 .{ .vfmadd132ps, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0x98 }, 0, .vex_256, .fma },
1030 .{ .vfmadd213ps, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0xa8 }, 0, .vex_128, .fma },
1031 .{ .vfmadd213ps, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0xa8 }, 0, .vex_256, .fma },
1032 .{ .vfmadd231ps, .rvm, &.{ .xmm, .xmm, .xmm_m128 }, &.{ 0x66, 0x0f, 0x38, 0xb8 }, 0, .vex_128, .fma },
1033 .{ .vfmadd231ps, .rvm, &.{ .ymm, .ymm, .ymm_m256 }, &.{ 0x66, 0x0f, 0x38, 0xb8 }, 0, .vex_256, .fma },
1034
1035 .{ .vfmadd132sd, .rvm, &.{ .xmm, .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0x99 }, 0, .vex_128_long, .fma },
1036 .{ .vfmadd213sd, .rvm, &.{ .xmm, .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0xa9 }, 0, .vex_128_long, .fma },
1037 .{ .vfmadd231sd, .rvm, &.{ .xmm, .xmm, .xmm_m64 }, &.{ 0x66, 0x0f, 0x38, 0xb9 }, 0, .vex_128_long, .fma },
1038
1039 .{ .vfmadd132ss, .rvm, &.{ .xmm, .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0x99 }, 0, .vex_128, .fma },
1040 .{ .vfmadd213ss, .rvm, &.{ .xmm, .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0xa9 }, 0, .vex_128, .fma },
1041 .{ .vfmadd231ss, .rvm, &.{ .xmm, .xmm, .xmm_m32 }, &.{ 0x66, 0x0f, 0x38, 0xb9 }, 0, .vex_128, .fma },
10191042};
10201043// zig fmt: on
test/behavior/muladd.zig+4-2
......@@ -1,8 +1,10 @@
1const std = @import("std");
12const builtin = @import("builtin");
2const expect = @import("std").testing.expect;
3const expect = std.testing.expect;
34
45test "@mulAdd" {
5 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
6 if (builtin.zig_backend == .stage2_x86_64 and
7 !comptime std.Target.x86.featureSetHas(builtin.cpu.features, .fma)) return error.SkipZigTest; // TODO
68 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
79 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
810 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO