Commit bd396d7e07

Jakub Konka <kubkon@jakubkonka.com>
2022-02-18 17:36:03
x64: add unsigned div and move logic into a helper fn
1 parent 2c13a4b
Changed files (3)
src/arch/x86_64/CodeGen.zig
@@ -1252,37 +1252,54 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
     return self.fail("TODO implement airShlWithOverflow for {}", .{self.target.cpu.arch});
 }
 
-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 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,
-        }
+/// Perform signed and unsigned integer division.
+/// TODO it might be wise to split some functionality into integer and floating-point
+/// specialised functions.
+/// Supports AIR tag:
+/// .div_exact, .div_trunc, .div_floor, .mod, .rem
+fn genDivOp(self: *Self, inst: Air.Inst.Index, op_lhs: Air.Inst.Ref, op_rhs: Air.Inst.Ref) !MCValue {
+    const dst_ty = self.air.typeOfIndex(inst);
+    const tag = self.air.instructions.items(.tag)[inst];
 
-        if (dst_ty.zigTypeTag() != .Int) {
-            return self.fail("TODO implement {} for operands of type {}", .{ tag, dst_ty.zigTypeTag() });
-        }
+    switch (tag) {
+        .div_exact, .div_trunc, .div_floor, .mod, .rem => {},
+        .div_float => return self.fail("TODO implement genDivOp for {}", .{tag}),
+        else => unreachable,
+    }
+
+    if (dst_ty.zigTypeTag() != .Int) {
+        return self.fail("TODO implement {} for operands of type {}", .{ tag, dst_ty.zigTypeTag() });
+    }
+    if (dst_ty.abiSize(self.target.*) > 8) {
+        return self.fail("TODO implement {} for ABI size larger than 8", .{tag});
+    }
 
-        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 signedness = dst_ty.intInfo(self.target.*).signedness;
+    const tmp_ty = switch (signedness) {
+        .signed => Type.isize,
+        .unsigned => dst_ty,
+    };
+    const abi_size = @intCast(u32, tmp_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);
+    const lhs = try self.resolveInst(op_lhs);
+    blk: {
+        switch (lhs) {
+            .register => |reg| {
+                if (reg.to64() == .rax) break :blk;
+            },
+            else => {},
         }
-        if (signedness == .signed) {
+        try self.register_manager.getReg(.rax, inst); // track inst -> rax in register manager
+        try self.genSetReg(tmp_ty, .rax, lhs);
+    }
+
+    try self.register_manager.getReg(.rdx, null);
+    self.register_manager.freezeRegs(&.{ .rax, .rdx });
+    defer self.register_manager.unfreezeRegs(&.{ .rax, .rdx });
+
+    // Prep rdx for the op
+    switch (signedness) {
+        .signed => {
             _ = try self.addInst(.{
                 .tag = .cwd,
                 .ops = (Mir.Ops{
@@ -1290,56 +1307,76 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
                 }).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 };
-                },
-            }
-        };
+        },
+        .unsigned => {
+            _ = try self.addInst(.{
+                .tag = .xor,
+                .ops = (Mir.Ops{
+                    .reg1 = .rdx,
+                    .reg2 = .rdx,
+                }).encode(),
+                .data = undefined,
+            });
+        },
+    }
 
-        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) },
-                });
+    const rhs = try self.resolveInst(op_rhs);
+    const divisor = blk: {
+        switch (rhs) {
+            .register, .stack_offset => break :blk rhs,
+            else => {
+                const reg = try self.copyToTmpRegister(tmp_ty, rhs);
+                break :blk MCValue{ .register = reg };
             },
-            else => unreachable,
         }
+    };
+    const op_tag: Mir.Inst.Tag = switch (signedness) {
+        .signed => .idiv,
+        .unsigned => .div,
+    };
 
-        break :result dst_mcv;
+    switch (divisor) {
+        .register => |reg| {
+            _ = try self.addInst(.{
+                .tag = op_tag,
+                .ops = (Mir.Ops{
+                    .reg1 = registerAlias(reg, abi_size),
+                }).encode(),
+                .data = undefined,
+            });
+        },
+        .stack_offset => |off| {
+            _ = try self.addInst(.{
+                .tag = op_tag,
+                .ops = (Mir.Ops{
+                    .reg2 = .rbp,
+                    .flags = switch (abi_size) {
+                        1 => 0b00,
+                        2 => 0b01,
+                        4 => 0b10,
+                        8 => 0b11,
+                        else => unreachable,
+                    },
+                }).encode(),
+                .data = .{ .imm = @bitCast(u32, -off) },
+            });
+        },
+        else => unreachable,
+    }
+
+    return switch (tag) {
+        .mod, .div_exact, .div_trunc, .div_floor => MCValue{ .register = .rax },
+        .rem => MCValue{ .register = .rdx },
+        else => unreachable,
     };
+}
+
+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
+        try self.genDivOp(inst, bin_op.lhs, bin_op.rhs);
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
@@ -1348,7 +1385,7 @@ fn airRem(self: *Self, inst: Air.Inst.Index) !void {
     const result: MCValue = if (self.liveness.isUnused(inst))
         .dead
     else
-        return self.fail("TODO implement rem for {}", .{self.target.cpu.arch});
+        try self.genDivOp(inst, bin_op.lhs, bin_op.rhs);
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
@@ -1357,7 +1394,7 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
     const result: MCValue = if (self.liveness.isUnused(inst))
         .dead
     else
-        return self.fail("TODO implement mod for {}", .{self.target.cpu.arch});
+        try self.genDivOp(inst, bin_op.lhs, bin_op.rhs);
     return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
 }
 
src/arch/x86_64/Emit.zig
@@ -138,8 +138,9 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
             .shr => try emit.mirShift(.shr, inst),
             .sar => try emit.mirShift(.sar, inst),
 
-            .imul => try emit.mirIMulIDiv(.imul, inst),
-            .idiv => try emit.mirIMulIDiv(.idiv, inst),
+            .imul => try emit.mirMulDiv(.imul, inst),
+            .idiv => try emit.mirMulDiv(.idiv, inst),
+            .div => try emit.mirMulDiv(.div, inst),
             .imul_complex => try emit.mirIMulComplex(inst),
 
             .cwd => try emit.mirCwd(inst),
@@ -687,7 +688,7 @@ fn mirShift(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
     }
 }
 
-fn mirIMulIDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
+fn mirMulDiv(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
     const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
     if (ops.reg1 != .none) {
         assert(ops.reg2 == .none);
@@ -1085,6 +1086,7 @@ const Tag = enum {
     nop,
     imul,
     idiv,
+    div,
     syscall,
     ret_near,
     ret_far,
@@ -1319,7 +1321,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
             .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),
             .setle, .setng => OpCode.twoByte(0x0f, 0x9e),
             .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),
-            .idiv, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
+            .idiv, .div, .imul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
             else => null,
         },
         .o => return switch (tag) {
@@ -1455,6 +1457,7 @@ inline fn getModRmExt(tag: Tag) ?u3 {
         .sar => 0x7,
         .imul => 0x5,
         .idiv => 0x7,
+        .div => 0x6,
         else => null,
     };
 }
src/arch/x86_64/Mir.zig
@@ -227,6 +227,7 @@ pub const Inst = struct {
         ///      0b11  qword ptr [reg2 + imm32]
         imul,
         idiv,
+        div,
 
         /// ops flags: form:
         ///      0b00  AX      <- AL