Commit a5a012e859
Changed files (4)
src
arch
src/arch/aarch64/bits.zig
@@ -321,6 +321,17 @@ pub const Instruction = union(enum) {
fixed: u6 = 0b011010,
sf: u1,
},
+ conditional_select: struct {
+ rd: u5,
+ rn: u5,
+ op2: u2,
+ cond: u4,
+ rm: u5,
+ fixed: u8 = 0b11010100,
+ s: u1,
+ op: u1,
+ sf: u1,
+ },
pub const Shift = struct {
shift: Type = .lsl,
@@ -388,6 +399,57 @@ pub const Instruction = union(enum) {
/// Integer: Always
/// Floating point: Always
nv,
+
+ /// Converts a std.math.CompareOperator into a condition flag,
+ /// i.e. returns the condition that is true iff the result of the
+ /// comparison is true. Assumes signed comparison
+ pub fn fromCompareOperatorSigned(op: std.math.CompareOperator) Condition {
+ return switch (op) {
+ .gte => .ge,
+ .gt => .gt,
+ .neq => .ne,
+ .lt => .lt,
+ .lte => .le,
+ .eq => .eq,
+ };
+ }
+
+ /// Converts a std.math.CompareOperator into a condition flag,
+ /// i.e. returns the condition that is true iff the result of the
+ /// comparison is true. Assumes unsigned comparison
+ pub fn fromCompareOperatorUnsigned(op: std.math.CompareOperator) Condition {
+ return switch (op) {
+ .gte => .cs,
+ .gt => .hi,
+ .neq => .ne,
+ .lt => .cc,
+ .lte => .ls,
+ .eq => .eq,
+ };
+ }
+
+ /// Returns the condition which is true iff the given condition is
+ /// false (if such a condition exists)
+ pub fn negate(cond: Condition) Condition {
+ return switch (cond) {
+ .eq => .ne,
+ .ne => .eq,
+ .cs => .cc,
+ .cc => .cs,
+ .mi => .pl,
+ .pl => .mi,
+ .vs => .vc,
+ .vc => .vs,
+ .hi => .ls,
+ .ls => .hi,
+ .ge => .lt,
+ .lt => .ge,
+ .gt => .le,
+ .le => .gt,
+ .al => unreachable,
+ .nv => unreachable,
+ };
+ }
};
pub fn toU32(self: Instruction) u32 {
@@ -407,6 +469,7 @@ pub const Instruction = union(enum) {
// TODO once packed structs work, this can be refactored
.conditional_branch => |v| @as(u32, v.cond) | (@as(u32, v.o0) << 4) | (@as(u32, v.imm19) << 5) | (@as(u32, v.o1) << 24) | (@as(u32, v.fixed) << 25),
.compare_and_branch => |v| @as(u32, v.rt) | (@as(u32, v.imm19) << 5) | (@as(u32, v.op) << 24) | (@as(u32, v.fixed) << 25) | (@as(u32, v.sf) << 31),
+ .conditional_select => |v| @as(u32, v.rd) | @as(u32, v.rn) << 5 | @as(u32, v.op2) << 10 | @as(u32, v.cond) << 12 | @as(u32, v.rm) << 16 | @as(u32, v.fixed) << 21 | @as(u32, v.s) << 29 | @as(u32, v.op) << 30 | @as(u32, v.sf) << 31,
};
}
@@ -883,6 +946,33 @@ pub const Instruction = union(enum) {
};
}
+ fn conditionalSelect(
+ op2: u2,
+ op: u1,
+ s: u1,
+ rd: Register,
+ rn: Register,
+ rm: Register,
+ cond: Condition,
+ ) Instruction {
+ return Instruction{
+ .conditional_select = .{
+ .rd = rd.id(),
+ .rn = rn.id(),
+ .op2 = op2,
+ .cond = @enumToInt(cond),
+ .rm = rm.id(),
+ .s = s,
+ .op = op,
+ .sf = switch (rd.size()) {
+ 32 => 0b0,
+ 64 => 0b1,
+ else => unreachable, // unexpected register size
+ },
+ },
+ };
+ }
+
// Helper functions for assembly syntax functions
// Move wide (immediate)
@@ -1154,6 +1244,24 @@ pub const Instruction = union(enum) {
pub fn cbnz(rt: Register, offset: i21) Instruction {
return compareAndBranch(0b1, rt, offset);
}
+
+ // Conditional select
+
+ pub fn csel(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+ return conditionalSelect(0b00, 0b0, 0b0, rd, rn, rm, cond);
+ }
+
+ pub fn csinc(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+ return conditionalSelect(0b01, 0b0, 0b0, rd, rn, rm, cond);
+ }
+
+ pub fn csinv(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+ return conditionalSelect(0b00, 0b1, 0b0, rd, rn, rm, cond);
+ }
+
+ pub fn csneg(rd: Register, rn: Register, rm: Register, cond: Condition) Instruction {
+ return conditionalSelect(0b01, 0b1, 0b0, rd, rn, rm, cond);
+ }
};
test {
@@ -1319,6 +1427,10 @@ test "serialize instructions" {
.inst = Instruction.addShiftedRegister(.x0, .x1, .x2, .lsl, 5),
.expected = 0b1_0_0_01011_00_0_00010_000101_00001_00000,
},
+ .{ // csinc x1, x2, x4, eq
+ .inst = Instruction.csinc(.x1, .x2, .x4, .eq),
+ .expected = 0b1_0_0_11010100_00100_0000_0_1_00010_00001,
+ },
};
for (testcases) |case| {
src/arch/aarch64/CodeGen.zig
@@ -2182,6 +2182,25 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
else => unreachable, // unexpected register size
}
},
+ .compare_flags_unsigned,
+ .compare_flags_signed,
+ => |op| {
+ const condition = switch (mcv) {
+ .compare_flags_unsigned => Instruction.Condition.fromCompareOperatorUnsigned(op),
+ .compare_flags_signed => Instruction.Condition.fromCompareOperatorSigned(op),
+ else => unreachable,
+ };
+
+ _ = try self.addInst(.{
+ .tag = .cset,
+ .data = .{ .rrr_cond = .{
+ .rd = reg,
+ .rn = .xzr,
+ .rm = .xzr,
+ .cond = condition,
+ } },
+ });
+ },
.immediate => |x| {
_ = try self.addInst(.{
.tag = .movz,
src/arch/aarch64/Emit.zig
@@ -81,6 +81,8 @@ pub fn emitMir(
.cmp_shifted_register => try emit.mirAddSubtractShiftedRegister(inst),
+ .cset => try emit.mirConditionalSelect(inst),
+
.dbg_line => try emit.mirDbgLine(inst),
.dbg_prologue_end => try emit.mirDebugPrologueEnd(),
@@ -478,6 +480,21 @@ fn mirAddSubtractShiftedRegister(emit: *Emit, inst: Mir.Inst.Index) !void {
}
}
+fn mirConditionalSelect(emit: *Emit, inst: Mir.Inst.Index) !void {
+ const tag = emit.mir.instructions.items(.tag)[inst];
+ const rrr_cond = emit.mir.instructions.items(.data)[inst].rrr_cond;
+
+ switch (tag) {
+ .cset => try emit.writeInstruction(Instruction.csinc(
+ rrr_cond.rd,
+ rrr_cond.rn,
+ rrr_cond.rm,
+ rrr_cond.cond,
+ )),
+ else => unreachable,
+ }
+}
+
fn mirLoadMemory(emit: *Emit, inst: Mir.Inst.Index) !void {
assert(emit.mir.instructions.items(.tag)[inst] == .load_memory);
const payload = emit.mir.instructions.items(.data)[inst].payload;
src/arch/aarch64/Mir.zig
@@ -40,6 +40,8 @@ pub const Inst = struct {
cmp_immediate,
/// Compare (shifted register)
cmp_shifted_register,
+ /// Conditional set
+ cset,
/// Pseudo-instruction: End of prologue
dbg_prologue_end,
/// Pseudo-instruction: Beginning of epilogue
@@ -155,6 +157,15 @@ pub const Inst = struct {
imm6: u6,
shift: bits.Instruction.AddSubtractShiftedRegisterShift,
},
+ /// Three registers and a condition
+ ///
+ /// Used by e.g. cset
+ rrr_cond: struct {
+ rd: Register,
+ rn: Register,
+ rm: Register,
+ cond: bits.Instruction.Condition,
+ },
/// Three registers and a LoadStoreOffset
///
/// Used by e.g. str_register