Commit 508b90fcfa

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-08-05 15:22:53
stage2 AArch64: implement basic integer rem/mod
1 parent 8b24c78
Changed files (3)
src/arch/aarch64/CodeGen.zig
@@ -1038,6 +1038,7 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
     const operand_info = operand_ty.intInfo(self.target.*);
 
     const dest_ty = self.air.typeOfIndex(inst);
+    const dest_abi_size = dest_ty.abiSize(self.target.*);
     const dest_info = dest_ty.intInfo(self.target.*);
 
     const result: MCValue = result: {
@@ -1047,16 +1048,21 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
         };
         defer if (operand_lock) |lock| self.register_manager.unlockReg(lock);
 
+        const truncated: MCValue = switch (operand_mcv) {
+            .register => |r| MCValue{ .register = registerAlias(r, dest_abi_size) },
+            else => operand_mcv,
+        };
+
         if (dest_info.bits > operand_info.bits) {
             const dest_mcv = try self.allocRegOrMem(inst, true);
-            try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, operand_mcv);
+            try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);
             break :result dest_mcv;
         } else {
-            if (self.reuseOperand(inst, operand, 0, operand_mcv)) {
-                break :result operand_mcv;
+            if (self.reuseOperand(inst, operand, 0, truncated)) {
+                break :result truncated;
             } else {
                 const dest_mcv = try self.allocRegOrMem(inst, true);
-                try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, operand_mcv);
+                try self.setRegOrMem(self.air.typeOfIndex(inst), dest_mcv, truncated);
                 break :result dest_mcv;
             }
         }
@@ -1145,7 +1151,7 @@ fn trunc(
 
         return MCValue{ .register = dest_reg };
     } else {
-        return self.fail("TODO: truncate to ints > 32 bits", .{});
+        return self.fail("TODO: truncate to ints > 64 bits", .{});
     }
 }
 
@@ -1679,14 +1685,67 @@ fn binOp(
                 .Int => {
                     assert(lhs_ty.eql(rhs_ty, mod));
                     const int_info = lhs_ty.intInfo(self.target.*);
-                    if (int_info.bits <= 32) {
-                        switch (int_info.signedness) {
-                            .signed => {
-                                return self.fail("TODO rem/mod on signed integers", .{});
-                            },
-                            .unsigned => {
-                                return self.fail("TODO rem/mod on unsigned integers", .{});
-                            },
+                    if (int_info.bits <= 64) {
+                        if (int_info.signedness == .signed and tag == .mod) {
+                            return self.fail("TODO mod on signed integers", .{});
+                        } else {
+                            const lhs_is_register = lhs == .register;
+                            const rhs_is_register = rhs == .register;
+
+                            const lhs_lock: ?RegisterLock = if (lhs_is_register)
+                                self.register_manager.lockReg(lhs.register)
+                            else
+                                null;
+                            defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+                            const lhs_reg = if (lhs_is_register)
+                                lhs.register
+                            else
+                                try self.register_manager.allocReg(null, gp);
+                            const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
+                            defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+                            const rhs_reg = if (rhs_is_register)
+                                rhs.register
+                            else
+                                try self.register_manager.allocReg(null, gp);
+                            const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
+                            defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+                            const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
+                            const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs);
+                            defer for (dest_regs_locks) |reg| {
+                                self.register_manager.unlockReg(reg);
+                            };
+                            const quotient_reg = dest_regs[0];
+                            const remainder_reg = dest_regs[1];
+
+                            if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
+                            if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
+
+                            _ = try self.addInst(.{
+                                .tag = switch (int_info.signedness) {
+                                    .signed => .sdiv,
+                                    .unsigned => .udiv,
+                                },
+                                .data = .{ .rrr = .{
+                                    .rd = quotient_reg,
+                                    .rn = lhs_reg,
+                                    .rm = rhs_reg,
+                                } },
+                            });
+
+                            _ = try self.addInst(.{
+                                .tag = .msub,
+                                .data = .{ .rrrr = .{
+                                    .rd = remainder_reg,
+                                    .rn = quotient_reg,
+                                    .rm = rhs_reg,
+                                    .ra = lhs_reg,
+                                } },
+                            });
+
+                            return MCValue{ .register = remainder_reg };
                         }
                     } else {
                         return self.fail("TODO rem/mod for integers with bits > 64", .{});
src/arch/aarch64/Emit.zig
@@ -190,6 +190,7 @@ pub fn emitMir(
             .movk => try emit.mirMoveWideImmediate(inst),
             .movz => try emit.mirMoveWideImmediate(inst),
 
+            .msub => try emit.mirDataProcessing3Source(inst),
             .mul => try emit.mirDataProcessing3Source(inst),
             .smulh => try emit.mirDataProcessing3Source(inst),
             .smull => try emit.mirDataProcessing3Source(inst),
@@ -1140,14 +1141,31 @@ fn mirMoveWideImmediate(emit: *Emit, inst: Mir.Inst.Index) !void {
 
 fn mirDataProcessing3Source(emit: *Emit, inst: Mir.Inst.Index) !void {
     const tag = emit.mir.instructions.items(.tag)[inst];
-    const rrr = emit.mir.instructions.items(.data)[inst].rrr;
 
     switch (tag) {
-        .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)),
-        .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd, rrr.rn, rrr.rm)),
-        .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)),
-        .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd, rrr.rn, rrr.rm)),
-        .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)),
+        .mul,
+        .smulh,
+        .smull,
+        .umulh,
+        .umull,
+        => {
+            const rrr = emit.mir.instructions.items(.data)[inst].rrr;
+            switch (tag) {
+                .mul => try emit.writeInstruction(Instruction.mul(rrr.rd, rrr.rn, rrr.rm)),
+                .smulh => try emit.writeInstruction(Instruction.smulh(rrr.rd, rrr.rn, rrr.rm)),
+                .smull => try emit.writeInstruction(Instruction.smull(rrr.rd, rrr.rn, rrr.rm)),
+                .umulh => try emit.writeInstruction(Instruction.umulh(rrr.rd, rrr.rn, rrr.rm)),
+                .umull => try emit.writeInstruction(Instruction.umull(rrr.rd, rrr.rn, rrr.rm)),
+                else => unreachable,
+            }
+        },
+        .msub => {
+            const rrrr = emit.mir.instructions.items(.data)[inst].rrrr;
+            switch (tag) {
+                .msub => try emit.writeInstruction(Instruction.msub(rrrr.rd, rrrr.rn, rrrr.rm, rrrr.ra)),
+                else => unreachable,
+            }
+        },
         else => unreachable,
     }
 }
src/arch/aarch64/Mir.zig
@@ -148,6 +148,8 @@ pub const Inst = struct {
         movk,
         /// Move wide with zero
         movz,
+        /// Multiply-subtract
+        msub,
         /// Multiply
         mul,
         /// Bitwise NOT
@@ -446,6 +448,15 @@ pub const Inst = struct {
             rn: Register,
             offset: bits.Instruction.LoadStorePairOffset,
         },
+        /// Four registers
+        ///
+        /// Used by e.g. msub
+        rrrr: struct {
+            rd: Register,
+            rn: Register,
+            rm: Register,
+            ra: Register,
+        },
         /// Debug info: line and column
         ///
         /// Used by e.g. dbg_line