diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 07403e9f93..9a660ceff6 100644 --- a/src/arch/arm/CodeGen.zig +++ b/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, diff --git a/src/arch/arm/Emit.zig b/src/arch/arm/Emit.zig index 10da79e1cb..77fa82d1d2 100644 --- a/src/arch/arm/Emit.zig +++ b/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()); } diff --git a/src/arch/arm/Mir.zig b/src/arch/arm/Mir.zig index fe96d4209f..209b3e508e 100644 --- a/src/arch/arm/Mir.zig +++ b/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