Commit 5e83441096

Jacob Young <jacobly0@users.noreply.github.com>
2023-10-25 23:15:48
x86_64: implement `@divFloor` and `@mod` for `i128`
1 parent da06269
Changed files (8)
lib/std/fs/test.zig
@@ -1460,8 +1460,6 @@ test "walker without fully iterating" {
 test ". and .. in fs.Dir functions" {
     if (builtin.os.tag == .wasi and builtin.link_libc) return error.SkipZigTest;
 
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     if (builtin.os.tag == .windows and builtin.cpu.arch == .aarch64) {
         // https://github.com/ziglang/zig/issues/17134
         return error.SkipZigTest;
@@ -1502,8 +1500,6 @@ test ". and .. in fs.Dir functions" {
 test ". and .. in absolute functions" {
     if (builtin.os.tag == .wasi) return error.SkipZigTest;
 
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
 
lib/std/io/test.zig
@@ -161,8 +161,6 @@ test "setEndPos" {
 }
 
 test "updateTimes" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var tmp = tmpDir(.{});
     defer tmp.cleanup();
 
lib/std/math/big/int_test.zig
@@ -1182,8 +1182,6 @@ test "big.int div single-single with rem" {
 }
 
 test "big.int div multi-single no rem" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var op1: u128 = 0xffffeeeeddddcccc;
     var op2: u128 = 34;
 
@@ -1203,8 +1201,6 @@ test "big.int div multi-single no rem" {
 }
 
 test "big.int div multi-single with rem" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var op1: u128 = 0xffffeeeeddddcccf;
     var op2: u128 = 34;
 
@@ -1224,8 +1220,6 @@ test "big.int div multi-single with rem" {
 }
 
 test "big.int div multi>2-single" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var op1: u128 = 0xfefefefefefefefefefefefefefefefe;
     var op2: u128 = 0xefab8;
 
@@ -1405,8 +1399,6 @@ test "big.int div trunc single-single -/-" {
 }
 
 test "big.int divTrunc #15535" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var one = try Managed.initSet(testing.allocator, 1);
     defer one.deinit();
     var x = try Managed.initSet(testing.allocator, std.math.pow(u128, 2, 64));
@@ -1949,8 +1941,6 @@ test "big.int truncate multi to multi signed" {
 }
 
 test "big.int truncate negative multi to single" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Managed.initSet(testing.allocator, -@as(SignedDoubleLimb, maxInt(Limb) + 1));
     defer a.deinit();
 
@@ -2362,8 +2352,6 @@ test "big.int bitwise xor simple" {
 }
 
 test "big.int bitwise xor multi-limb" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var x: DoubleLimb = maxInt(Limb) + 1;
     var y: DoubleLimb = maxInt(Limb);
     var a = try Managed.initSet(testing.allocator, x);
@@ -2538,8 +2526,6 @@ test "big.int var args" {
 }
 
 test "big.int gcd non-one small" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Managed.initSet(testing.allocator, 17);
     defer a.deinit();
     var b = try Managed.initSet(testing.allocator, 97);
@@ -2553,8 +2539,6 @@ test "big.int gcd non-one small" {
 }
 
 test "big.int gcd non-one medium" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Managed.initSet(testing.allocator, 4864);
     defer a.deinit();
     var b = try Managed.initSet(testing.allocator, 3458);
@@ -2568,8 +2552,6 @@ test "big.int gcd non-one medium" {
 }
 
 test "big.int gcd non-one large" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Managed.initSet(testing.allocator, 0xffffffffffffffff);
     defer a.deinit();
     var b = try Managed.initSet(testing.allocator, 0xffffffffffffffff7777);
@@ -2602,8 +2584,6 @@ test "big.int gcd large multi-limb result" {
 }
 
 test "big.int gcd one large" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Managed.initSet(testing.allocator, 1897056385327307);
     defer a.deinit();
     var b = try Managed.initSet(testing.allocator, 2251799813685248);
lib/std/math/big/rational.zig
@@ -514,8 +514,6 @@ test "big.rational extractLowBits" {
 }
 
 test "big.rational set" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
 
@@ -615,8 +613,6 @@ test "big.rational set/to Float round-trip" {
 }
 
 test "big.rational copy" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
 
@@ -681,8 +677,6 @@ test "big.rational abs" {
 }
 
 test "big.rational swap" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
     var b = try Rational.init(testing.allocator);
@@ -707,8 +701,6 @@ test "big.rational swap" {
 }
 
 test "big.rational order" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
     var b = try Rational.init(testing.allocator);
@@ -724,8 +716,6 @@ test "big.rational order" {
 }
 
 test "big.rational order/orderAbs with negative" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
     var b = try Rational.init(testing.allocator);
@@ -738,8 +728,6 @@ test "big.rational order/orderAbs with negative" {
 }
 
 test "big.rational add single-limb" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
     var b = try Rational.init(testing.allocator);
@@ -755,8 +743,6 @@ test "big.rational add single-limb" {
 }
 
 test "big.rational add" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
     var b = try Rational.init(testing.allocator);
@@ -773,8 +759,6 @@ test "big.rational add" {
 }
 
 test "big.rational sub" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
     var b = try Rational.init(testing.allocator);
@@ -791,8 +775,6 @@ test "big.rational sub" {
 }
 
 test "big.rational mul" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var a = try Rational.init(testing.allocator);
     defer a.deinit();
     var b = try Rational.init(testing.allocator);
@@ -809,8 +791,6 @@ test "big.rational mul" {
 }
 
 test "big.rational div" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     {
         var a = try Rational.init(testing.allocator);
         defer a.deinit();
lib/std/os/linux/io_uring.zig
@@ -2204,8 +2204,6 @@ test "sendmsg/recvmsg" {
 test "timeout (after a relative time)" {
     if (builtin.os.tag != .linux) return error.SkipZigTest;
 
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     var ring = IO_Uring.init(1, 0) catch |err| switch (err) {
         error.SystemOutdated => return error.SkipZigTest,
         error.PermissionDenied => return error.SkipZigTest,
lib/std/fs.zig
@@ -3248,9 +3248,7 @@ test {
         _ = &makeDirAbsolute;
         _ = &makeDirAbsoluteZ;
         _ = &copyFileAbsolute;
-        if (builtin.zig_backend != .stage2_x86_64) {
-            _ = &updateFileAbsolute;
-        }
+        _ = &updateFileAbsolute;
     }
     _ = &Dir.copyFile;
     _ = @import("fs/test.zig");
lib/std/time.zig
@@ -122,8 +122,6 @@ pub fn nanoTimestamp() i128 {
 }
 
 test "timestamp" {
-    if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
-
     const margin = ns_per_ms * 50;
 
     const time_0 = milliTimestamp();
src/arch/x86_64/CodeGen.zig
@@ -3221,6 +3221,7 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
             .Float, .Vector => break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs),
             else => {},
         }
+        const dst_abi_size: u32 = @intCast(dst_ty.abiSize(mod));
 
         const dst_info = dst_ty.intInfo(mod);
         const src_ty = try mod.intType(dst_info.signedness, switch (tag) {
@@ -3232,12 +3233,153 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void {
             ),
             .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_info.bits,
         });
+        const src_abi_size: u32 = @intCast(src_ty.abiSize(mod));
+
+        if (dst_abi_size == 16 and src_abi_size == 16) switch (tag) {
+            else => unreachable,
+            .mul, .mul_wrap => {},
+            .div_trunc, .div_floor, .div_exact, .rem, .mod => {
+                const signed = dst_ty.isSignedInt(mod);
+                var callee_buf: ["__udiv?i3".len]u8 = undefined;
+                const signed_div_floor_state: struct {
+                    frame_index: FrameIndex,
+                    reloc: Mir.Inst.Index,
+                } = if (signed and tag == .div_floor) state: {
+                    const frame_index = try self.allocFrameIndex(FrameAlloc.initType(Type.usize, mod));
+                    try self.asmMemoryImmediate(
+                        .{ ._, .mov },
+                        Memory.sib(.qword, .{ .base = .{ .frame = frame_index } }),
+                        Immediate.u(0),
+                    );
+
+                    const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp);
+                    const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg);
+                    defer self.register_manager.unlockReg(tmp_lock);
+
+                    const lhs_mcv = try self.resolveInst(bin_op.lhs);
+                    if (lhs_mcv.isMemory()) try self.asmRegisterMemory(
+                        .{ ._, .mov },
+                        tmp_reg,
+                        lhs_mcv.address().offset(8).deref().mem(.qword),
+                    ) else try self.asmRegisterRegister(
+                        .{ ._, .mov },
+                        tmp_reg,
+                        lhs_mcv.register_pair[1],
+                    );
+
+                    const rhs_mcv = try self.resolveInst(bin_op.rhs);
+                    if (rhs_mcv.isMemory()) try self.asmRegisterMemory(
+                        .{ ._, .xor },
+                        tmp_reg,
+                        rhs_mcv.address().offset(8).deref().mem(.qword),
+                    ) else try self.asmRegisterRegister(
+                        .{ ._, .xor },
+                        tmp_reg,
+                        rhs_mcv.register_pair[1],
+                    );
+                    const reloc = try self.asmJccReloc(.ns, undefined);
+
+                    break :state .{ .frame_index = frame_index, .reloc = reloc };
+                } else undefined;
+                const call_mcv = try self.genCall(
+                    .{ .lib = .{
+                        .return_type = dst_ty.toIntern(),
+                        .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() },
+                        .callee = std.fmt.bufPrint(&callee_buf, "__{s}{s}{c}i3", .{
+                            if (signed) "" else "u",
+                            switch (tag) {
+                                .div_trunc, .div_exact => "div",
+                                .div_floor => if (signed) "mod" else "div",
+                                .rem, .mod => "mod",
+                                else => unreachable,
+                            },
+                            intCompilerRtAbiName(@intCast(dst_ty.bitSize(mod))),
+                        }) catch unreachable,
+                    } },
+                    &.{ src_ty, src_ty },
+                    &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } },
+                );
+                break :result if (signed) switch (tag) {
+                    .div_floor => {
+                        try self.asmRegisterRegister(
+                            .{ ._, .@"or" },
+                            call_mcv.register_pair[0],
+                            call_mcv.register_pair[1],
+                        );
+                        try self.asmSetccMemory(.nz, Memory.sib(.byte, .{
+                            .base = .{ .frame = signed_div_floor_state.frame_index },
+                        }));
+                        try self.performReloc(signed_div_floor_state.reloc);
+                        const dst_mcv = try self.genCall(
+                            .{ .lib = .{
+                                .return_type = dst_ty.toIntern(),
+                                .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() },
+                                .callee = std.fmt.bufPrint(&callee_buf, "__div{c}i3", .{
+                                    intCompilerRtAbiName(@intCast(dst_ty.bitSize(mod))),
+                                }) catch unreachable,
+                            } },
+                            &.{ src_ty, src_ty },
+                            &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } },
+                        );
+                        try self.asmRegisterMemory(
+                            .{ ._, .sub },
+                            dst_mcv.register_pair[0],
+                            Memory.sib(.qword, .{
+                                .base = .{ .frame = signed_div_floor_state.frame_index },
+                            }),
+                        );
+                        try self.asmRegisterImmediate(
+                            .{ ._, .sbb },
+                            dst_mcv.register_pair[1],
+                            Immediate.u(0),
+                        );
+                        try self.freeValue(
+                            .{ .load_frame = .{ .index = signed_div_floor_state.frame_index } },
+                        );
+                        break :result dst_mcv;
+                    },
+                    .mod => {
+                        const dst_regs = call_mcv.register_pair;
+                        const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs);
+                        defer for (dst_locks) |lock| self.register_manager.unlockReg(lock);
+
+                        const tmp_regs =
+                            try self.register_manager.allocRegs(2, .{null} ** 2, abi.RegisterClass.gp);
+                        const tmp_locks = self.register_manager.lockRegsAssumeUnused(2, tmp_regs);
+                        defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock);
+
+                        const rhs_mcv = try self.resolveInst(bin_op.rhs);
+
+                        for (tmp_regs, dst_regs) |tmp_reg, dst_reg|
+                            try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, dst_reg);
+                        if (rhs_mcv.isMemory()) {
+                            try self.asmRegisterMemory(.{ ._, .add }, tmp_regs[0], rhs_mcv.mem(.qword));
+                            try self.asmRegisterMemory(
+                                .{ ._, .adc },
+                                tmp_regs[1],
+                                rhs_mcv.address().offset(8).deref().mem(.qword),
+                            );
+                        } else for (
+                            [_]Mir.Inst.Tag{ .add, .adc },
+                            tmp_regs,
+                            rhs_mcv.register_pair,
+                        ) |op, tmp_reg, rhs_reg|
+                            try self.asmRegisterRegister(.{ ._, op }, tmp_reg, rhs_reg);
+                        try self.asmRegisterRegister(.{ ._, .@"test" }, dst_regs[1], dst_regs[1]);
+                        for (dst_regs, tmp_regs) |dst_reg, tmp_reg|
+                            try self.asmCmovccRegisterRegister(.s, dst_reg, tmp_reg);
+                        break :result call_mcv;
+                    },
+                    else => call_mcv,
+                } else call_mcv;
+            },
+        };
 
         try self.spillEflagsIfOccupied();
         try self.spillRegisters(&.{ .rax, .rdx });
-        const lhs = try self.resolveInst(bin_op.lhs);
-        const rhs = try self.resolveInst(bin_op.rhs);
-        break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs, rhs);
+        const lhs_mcv = try self.resolveInst(bin_op.lhs);
+        const rhs_mcv = try self.resolveInst(bin_op.rhs);
+        break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs_mcv, rhs_mcv);
     };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
@@ -7201,37 +7343,7 @@ fn genMulDivBinOp(
     assert(self.eflags_inst == null);
 
     if (dst_abi_size == 16 and src_abi_size == 16) {
-        switch (tag) {
-            else => unreachable,
-            .mul, .mul_wrap => {},
-            .div_trunc, .div_floor, .div_exact, .rem, .mod => {
-                const signed = dst_ty.isSignedInt(mod);
-                if (signed) switch (tag) {
-                    .div_floor, .mod => return self.fail(
-                        "TODO implement genMulDivBinOp for {s} from {} to {}",
-                        .{ @tagName(tag), src_ty.fmt(mod), dst_ty.fmt(mod) },
-                    ),
-                    else => {},
-                };
-                var callee_buf: ["__udiv?i3".len]u8 = undefined;
-                return try self.genCall(.{ .lib = .{
-                    .return_type = dst_ty.toIntern(),
-                    .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() },
-                    .callee = std.fmt.bufPrint(&callee_buf, "__{s}{s}{c}i3", .{
-                        if (signed) "" else "u",
-                        switch (tag) {
-                            .div_trunc, .div_exact => "div",
-                            .div_floor => if (signed) unreachable else "div",
-                            .rem => "mod",
-                            .mod => if (signed) unreachable else "mod",
-                            else => unreachable,
-                        },
-                        intCompilerRtAbiName(@intCast(dst_ty.bitSize(mod))),
-                    }) catch unreachable,
-                } }, &.{ src_ty, src_ty }, &.{ lhs_mcv, rhs_mcv });
-            },
-        }
-
+        assert(tag == .mul or tag == .mul_wrap);
         const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx });
         defer for (reg_locks) |lock| self.register_manager.unlockReg(lock);