diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 3bb5bbe0d3..f9d8858ef8 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -539,8 +539,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), - .min => try self.airMin(inst), - .max => try self.airMax(inst), + .min => try self.airMinMax(inst), + .max => try self.airMinMax(inst), .add_sat => try self.airAddSat(inst), .sub_sat => try self.airSubSat(inst), @@ -1234,15 +1234,102 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airMin(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 min for {}", .{self.target.cpu.arch}); - return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); +fn minMax( + self: *Self, + tag: Air.Inst.Tag, + lhs_bind: ReadArg.Bind, + rhs_bind: ReadArg.Bind, + lhs_ty: Type, + rhs_ty: Type, + maybe_inst: ?Air.Inst.Index, +) !MCValue { + switch (lhs_ty.zigTypeTag()) { + .Float => return self.fail("TODO ARM min/max on floats", .{}), + .Vector => return self.fail("TODO ARM min/max on vectors", .{}), + .Int => { + const mod = self.bin_file.options.module.?; + assert(lhs_ty.eql(rhs_ty, mod)); + const int_info = lhs_ty.intInfo(self.target.*); + if (int_info.bits <= 64) { + var lhs_reg: Register = undefined; + var rhs_reg: Register = undefined; + var dest_reg: Register = undefined; + + const read_args = [_]ReadArg{ + .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, + .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, + }; + const write_args = [_]WriteArg{ + .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, + }; + try self.allocRegs( + &read_args, + &write_args, + if (maybe_inst) |inst| .{ + .corresponding_inst = inst, + .operand_mapping = &.{ 0, 1 }, + } else null, + ); + + // lhs == reg should have been checked by airMinMax + assert(lhs_reg != rhs_reg); // see note above + + _ = try self.addInst(.{ + .tag = .cmp_shifted_register, + .data = .{ .rr_imm6_shift = .{ + .rn = lhs_reg, + .rm = rhs_reg, + .imm6 = 0, + .shift = .lsl, + } }, + }); + + const cond_choose_lhs: Condition = switch (tag) { + .max => switch (int_info.signedness) { + .signed => Condition.gt, + .unsigned => Condition.hi, + }, + .min => switch (int_info.signedness) { + .signed => Condition.lt, + .unsigned => Condition.cc, + }, + else => unreachable, + }; + + _ = try self.addInst(.{ + .tag = .csel, + .data = .{ .rrr_cond = .{ + .rd = dest_reg, + .rn = lhs_reg, + .rm = rhs_reg, + .cond = cond_choose_lhs, + } }, + }); + + return MCValue{ .register = dest_reg }; + } else { + return self.fail("TODO ARM min/max on integers > u32/i32", .{}); + } + }, + else => unreachable, + } } -fn airMax(self: *Self, inst: Air.Inst.Index) !void { +fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { + const tag = self.air.instructions.items(.tag)[inst]; 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 max for {}", .{self.target.cpu.arch}); + const lhs_ty = self.air.typeOf(bin_op.lhs); + const rhs_ty = self.air.typeOf(bin_op.rhs); + + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; + const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; + + const lhs = try self.resolveInst(bin_op.lhs); + if (bin_op.lhs == bin_op.rhs) break :result lhs; + + break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } diff --git a/src/arch/aarch64/Emit.zig b/src/arch/aarch64/Emit.zig index febe29d9a9..d9f10d5103 100644 --- a/src/arch/aarch64/Emit.zig +++ b/src/arch/aarch64/Emit.zig @@ -131,6 +131,7 @@ pub fn emitMir( .subs_extended_register => try emit.mirAddSubtractExtendedRegister(inst), .cmp_extended_register => try emit.mirAddSubtractExtendedRegister(inst), + .csel => try emit.mirConditionalSelect(inst), .cset => try emit.mirConditionalSelect(inst), .dbg_line => try emit.mirDbgLine(inst), @@ -804,6 +805,14 @@ fn mirAddSubtractExtendedRegister(emit: *Emit, inst: Mir.Inst.Index) !void { fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void { const tag = emit.mir.instructions.items(.tag)[inst]; switch (tag) { + .csel => { + const rrr_cond = emit.mir.instructions.items(.data)[inst].rrr_cond; + const rd = rrr_cond.rd; + const rn = rrr_cond.rn; + const rm = rrr_cond.rm; + const cond = rrr_cond.cond; + try emit.writeInstruction(Instruction.csel(rd, rn, rm, cond)); + }, .cset => { const r_cond = emit.mir.instructions.items(.data)[inst].r_cond; const zr: Register = switch (r_cond.rd.size()) { diff --git a/src/arch/aarch64/Mir.zig b/src/arch/aarch64/Mir.zig index 927e4c9893..1855117a7f 100644 --- a/src/arch/aarch64/Mir.zig +++ b/src/arch/aarch64/Mir.zig @@ -62,6 +62,8 @@ pub const Inst = struct { cmp_shifted_register, /// Compare (extended register) cmp_extended_register, + /// Conditional Select + csel, /// Conditional set cset, /// Pseudo-instruction: End of prologue @@ -387,6 +389,15 @@ pub const Inst = struct { rn: Register, rm: Register, }, + /// Three registers and a condition + /// + /// Used by e.g. csel + rrr_cond: struct { + rd: Register, + rn: Register, + rm: Register, + cond: bits.Instruction.Condition, + }, /// Three registers and a shift (shift type and 6-bit amount) /// /// Used by e.g. add_shifted_register