Commit 2c13a4b87e

Jakub Konka <kubkon@jakubkonka.com>
2022-02-18 01:12:19
x64: implement div_exact for ints (signed+unsigned)
1 parent 1f4aa5e
Changed files (3)
src/arch/x86_64/CodeGen.zig
@@ -1254,10 +1254,92 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
 
 fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
     const bin_op = self.air.instructions.items(.data)[inst].bin_op;
-    const result: MCValue = if (self.liveness.isUnused(inst))
-        .dead
-    else
-        return self.fail("TODO implement div for {}", .{self.target.cpu.arch});
+    const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+        const dst_ty = self.air.typeOfIndex(inst);
+        const tag = self.air.instructions.items(.tag)[inst];
+        switch (tag) {
+            .div_exact => {},
+            .div_trunc, .div_floor, .div_float => return self.fail("TODO implement {}", .{tag}),
+            else => unreachable,
+        }
+
+        if (dst_ty.zigTypeTag() != .Int) {
+            return self.fail("TODO implement {} for operands of type {}", .{ tag, dst_ty.zigTypeTag() });
+        }
+
+        const signedness = dst_ty.intInfo(self.target.*).signedness;
+        const ty = if (signedness == .signed) Type.isize else dst_ty;
+        const abi_size = @intCast(u32, ty.abiSize(self.target.*));
+
+        const lhs = try self.resolveInst(bin_op.lhs);
+        blk: {
+            switch (lhs) {
+                .register => |reg| {
+                    if (reg.to64() == .rax) break :blk;
+                },
+                else => {},
+            }
+            try self.register_manager.getReg(.rax, inst); // track inst -> rax in register manager
+            try self.genSetReg(ty, .rax, lhs);
+        }
+        if (signedness == .signed) {
+            _ = try self.addInst(.{
+                .tag = .cwd,
+                .ops = (Mir.Ops{
+                    .flags = 0b11,
+                }).encode(),
+                .data = undefined,
+            });
+        }
+        const dst_mcv = MCValue{ .register = registerAlias(.rax, abi_size) };
+
+        try self.register_manager.getReg(.rdx, null);
+        self.register_manager.freezeRegs(&.{ .rax, .rdx });
+        defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
+
+        const rhs = try self.resolveInst(bin_op.rhs);
+        const divisor = blk: {
+            switch (rhs) {
+                .register, .stack_offset => break :blk rhs,
+                else => {
+                    const reg = try self.copyToTmpRegister(ty, rhs);
+                    break :blk MCValue{ .register = reg };
+                },
+            }
+        };
+
+        switch (divisor) {
+            .register => |reg| {
+                _ = try self.addInst(.{
+                    .tag = .idiv,
+                    .ops = (Mir.Ops{
+                        .reg1 = registerAlias(reg, abi_size),
+                    }).encode(),
+                    .data = undefined,
+                });
+            },
+            .stack_offset => |off| {
+                const flags: u2 = switch (abi_size) {
+                    1 => 0b00,
+                    2 => 0b01,
+                    4 => 0b10,
+                    8 => 0b11,
+                    else => unreachable,
+                };
+                _ = try self.addInst(.{
+                    .tag = .idiv,
+                    .ops = (Mir.Ops{
+                        .reg2 = .rbp,
+                        .flags = flags,
+                    }).encode(),
+                    .data = .{ .imm = @bitCast(u32, -off) },
+                });
+            },
+            else => unreachable,
+        }
+
+        break :result dst_mcv;
+    };
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
@@ -4126,7 +4208,7 @@ fn genInlineMemset(
 }
 
 fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void {
-    const abi_size = ty.abiSize(self.target.*);
+    const abi_size = @intCast(u32, ty.abiSize(self.target.*));
     switch (mcv) {
         .dead => unreachable,
         .ptr_stack_offset => |off| {
@@ -4136,7 +4218,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             _ = try self.addInst(.{
                 .tag = .lea,
                 .ops = (Mir.Ops{
-                    .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
+                    .reg1 = registerAlias(reg, abi_size),
                     .reg2 = .rbp,
                 }).encode(),
                 .data = .{ .imm = @bitCast(u32, -off) },
@@ -4202,7 +4284,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
                 _ = try self.addInst(.{
                     .tag = .mov,
                     .ops = (Mir.Ops{
-                        .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
+                        .reg1 = registerAlias(reg, abi_size),
                     }).encode(),
                     .data = .{ .imm = @truncate(u32, x) },
                 });
@@ -4272,8 +4354,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             _ = try self.addInst(.{
                 .tag = .mov,
                 .ops = (Mir.Ops{
-                    .reg1 = registerAlias(reg, @divExact(src_reg.size(), 8)),
-                    .reg2 = src_reg,
+                    .reg1 = registerAlias(reg, abi_size),
+                    .reg2 = registerAlias(src_reg, abi_size),
                 }).encode(),
                 .data = undefined,
             });
@@ -4399,7 +4481,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
             _ = try self.addInst(.{
                 .tag = .mov,
                 .ops = (Mir.Ops{
-                    .reg1 = registerAlias(reg, @intCast(u32, abi_size)),
+                    .reg1 = registerAlias(reg, abi_size),
                     .reg2 = .rbp,
                     .flags = 0b01,
                 }).encode(),
src/arch/x86_64/Emit.zig
@@ -142,6 +142,8 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
             .idiv => try emit.mirIMulIDiv(.idiv, inst),
             .imul_complex => try emit.mirIMulComplex(inst),
 
+            .cwd => try emit.mirCwd(inst),
+
             .push => try emit.mirPushPop(.push, inst),
             .pop => try emit.mirPushPop(.pop, inst),
 
@@ -737,6 +739,17 @@ fn mirIMulComplex(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
     }
 }
 
+fn mirCwd(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
+    const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
+    const tag: Tag = switch (ops.flags) {
+        0b00 => .cbw,
+        0b01 => .cwd,
+        0b10 => .cdq,
+        0b11 => .cqo,
+    };
+    return lowerToZoEnc(tag, emit.code);
+}
+
 fn mirLea(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
     const tag = emit.mir.instructions.items(.tag)[inst];
     assert(tag == .lea);
@@ -1139,6 +1152,10 @@ const Tag = enum {
     sal,
     shr,
     sar,
+    cbw,
+    cwd,
+    cdq,
+    cqo,
 
     fn isSetCC(tag: Tag) bool {
         return switch (tag) {
@@ -1258,6 +1275,8 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
             .brk => OpCode.oneByte(0xcc),
             .nop => OpCode.oneByte(0x90),
             .syscall => OpCode.twoByte(0x0f, 0x05),
+            .cbw => OpCode.oneByte(0x98),
+            .cwd, .cdq, .cqo => OpCode.oneByte(0x99),
             else => null,
         },
         .d => return switch (tag) {
@@ -1592,7 +1611,15 @@ const RegisterOrMemory = union(enum) {
 
 fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
     const opc = getOpCode(tag, .zo, false).?;
-    const encoder = try Encoder.init(code, 1);
+    const encoder = try Encoder.init(code, 2);
+    switch (tag) {
+        .cqo => {
+            encoder.rex(.{
+                .w = true,
+            });
+        },
+        else => {},
+    }
     opc.encode(encoder);
 }
 
src/arch/x86_64/Mir.zig
@@ -228,6 +228,13 @@ pub const Inst = struct {
         imul,
         idiv,
 
+        /// ops flags: form:
+        ///      0b00  AX      <- AL
+        ///      0b01  DX:AX   <- AX
+        ///      0b10  EDX:EAX <- EAX
+        ///      0b11  RDX:RAX <- RAX
+        cwd,
+
         /// ops flags:  form:
         ///      0b00  reg1, reg2
         ///      0b01  reg1, [reg2 + imm32]