stage2 AArch64: implement genSetReg for condition flags
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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| {
|
||||
|
||||
Reference in New Issue
Block a user