Commit 8c12ad98b8

joachimschmidt557 <joachim.schmidt557@outlook.com>
2022-04-01 22:51:18
stage2 ARM: implement mul_with_overflow for ints <= 32 bits
1 parent c4778fc
Changed files (3)
src/arch/arm/CodeGen.zig
@@ -1500,9 +1500,115 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
                     try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
                     try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags_unsigned = .neq });
 
+                    break :result MCValue{ .stack_offset = stack_offset };
+                } else if (int_info.bits <= 32) {
+                    const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
+
+                    try self.spillCompareFlagsIfOccupied();
+                    self.compare_flags_inst = null;
+
+                    const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
+                        .signed => .smull,
+                        .unsigned => .umull,
+                    };
+
+                    // TODO extract umull etc. to binOpTwoRegister
+                    // once MCValue.rr is implemented
+                    const lhs_is_register = lhs == .register;
+                    const rhs_is_register = rhs == .register;
+
+                    if (lhs_is_register) self.register_manager.freezeRegs(&.{lhs.register});
+                    if (rhs_is_register) self.register_manager.freezeRegs(&.{rhs.register});
+
+                    const lhs_reg = if (lhs_is_register) lhs.register else blk: {
+                        const reg = try self.register_manager.allocReg(null);
+                        self.register_manager.freezeRegs(&.{reg});
+
+                        break :blk reg;
+                    };
+                    defer self.register_manager.unfreezeRegs(&.{lhs_reg});
+
+                    const rhs_reg = if (rhs_is_register) rhs.register else blk: {
+                        const reg = try self.register_manager.allocReg(null);
+                        self.register_manager.freezeRegs(&.{reg});
+
+                        break :blk reg;
+                    };
+                    defer self.register_manager.unfreezeRegs(&.{rhs_reg});
+
+                    const dest_regs = try self.register_manager.allocRegs(2, .{ null, null });
+                    self.register_manager.freezeRegs(&dest_regs);
+                    defer self.register_manager.unfreezeRegs(&dest_regs);
+                    const rdlo = dest_regs[0];
+                    const rdhi = 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);
+
+                    const truncated_reg = try self.register_manager.allocReg(null);
+                    self.register_manager.freezeRegs(&.{truncated_reg});
+                    defer self.register_manager.unfreezeRegs(&.{truncated_reg});
+
+                    _ = try self.addInst(.{
+                        .tag = base_tag,
+                        .data = .{ .rrrr = .{
+                            .rdlo = rdlo,
+                            .rdhi = rdhi,
+                            .rn = lhs_reg,
+                            .rm = rhs_reg,
+                        } },
+                    });
+
+                    // sbfx/ubfx truncated, rdlo, #0, #bits
+                    try self.truncRegister(rdlo, truncated_reg, int_info.signedness, int_info.bits);
+
+                    // str truncated, [...]
+                    try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
+
+                    // cmp truncated, rdlo
+                    _ = try self.binOp(.cmp_eq, null, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize);
+
+                    // mov rdlo, #0
+                    _ = try self.addInst(.{
+                        .tag = .mov,
+                        .data = .{ .rr_op = .{
+                            .rd = rdlo,
+                            .rn = .r0,
+                            .op = Instruction.Operand.fromU32(0).?,
+                        } },
+                    });
+
+                    // movne rdlo, #1
+                    _ = try self.addInst(.{
+                        .tag = .mov,
+                        .cond = .ne,
+                        .data = .{ .rr_op = .{
+                            .rd = rdlo,
+                            .rn = .r0,
+                            .op = Instruction.Operand.fromU32(1).?,
+                        } },
+                    });
+
+                    // cmp rdhi, #0
+                    _ = try self.binOp(.cmp_eq, null, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize);
+
+                    // movne rdlo, #1
+                    _ = try self.addInst(.{
+                        .tag = .mov,
+                        .cond = .ne,
+                        .data = .{ .rr_op = .{
+                            .rd = rdlo,
+                            .rn = .r0,
+                            .op = Instruction.Operand.fromU32(1).?,
+                        } },
+                    });
+
+                    // strb rdlo, [...]
+                    try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .register = rdlo });
+
                     break :result MCValue{ .stack_offset = stack_offset };
                 } else {
-                    return self.fail("TODO ARM overflow operations on integers > u16/i16", .{});
+                    return self.fail("TODO ARM overflow operations on integers > u32/i32", .{});
                 }
             },
             else => unreachable,
src/arch/arm/Emit.zig
@@ -132,6 +132,9 @@ pub fn emitMir(
             .mul => try emit.mirMultiply(inst),
             .smulbb => try emit.mirMultiply(inst),
 
+            .smull => try emit.mirMultiplyLong(inst),
+            .umull => try emit.mirMultiplyLong(inst),
+
             .nop => try emit.mirNop(),
 
             .pop => try emit.mirBlockDataTransfer(inst),
@@ -695,6 +698,18 @@ fn mirMultiply(emit: *Emit, inst: Mir.Inst.Index) !void {
     }
 }
 
+fn mirMultiplyLong(emit: *Emit, inst: Mir.Inst.Index) !void {
+    const tag = emit.mir.instructions.items(.tag)[inst];
+    const cond = emit.mir.instructions.items(.cond)[inst];
+    const rrrr = emit.mir.instructions.items(.data)[inst].rrrr;
+
+    switch (tag) {
+        .smull => try emit.writeInstruction(Instruction.smull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)),
+        .umull => try emit.writeInstruction(Instruction.umull(cond, rrrr.rdlo, rrrr.rdhi, rrrr.rn, rrrr.rm)),
+        else => unreachable,
+    }
+}
+
 fn mirNop(emit: *Emit) !void {
     try emit.writeInstruction(Instruction.nop());
 }
src/arch/arm/Mir.zig
@@ -104,6 +104,8 @@ pub const Inst = struct {
         sbfx,
         /// Signed Multiply (halfwords), bottom half, bottom half
         smulbb,
+        /// Signed Multiply Long
+        smull,
         /// Store Register
         str,
         /// Store Register Byte
@@ -118,6 +120,8 @@ pub const Inst = struct {
         svc,
         /// Unsigned Bit Field Extract
         ubfx,
+        /// Unsigned Multiply Long
+        umull,
     };
 
     /// The position of an MIR instruction within the `Mir` instructions array.
@@ -215,6 +219,15 @@ pub const Inst = struct {
             rn: Register,
             rm: Register,
         },
+        /// Four registers
+        ///
+        /// Used by e.g. smull
+        rrrr: struct {
+            rdlo: Register,
+            rdhi: Register,
+            rn: Register,
+            rm: Register,
+        },
         /// An unordered list of registers
         ///
         /// Used by e.g. push