stage2 AArch64: implement genSetReg for condition flags

This commit is contained in:
joachimschmidt557
2021-11-07 17:57:21 +01:00
parent 8cb00519cd
commit a5a012e859
4 changed files with 159 additions and 0 deletions

View File

@@ -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,

View File

@@ -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;

View File

@@ -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

View File

@@ -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| {