diff --git a/cranelift/codegen/src/isa/aarch64/inst/args.rs b/cranelift/codegen/src/isa/aarch64/inst/args.rs index b83f375bcf..5b3c9724de 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/args.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/args.rs @@ -525,4 +525,11 @@ impl InstSize { InstSize::Size64 } } + + pub fn sf_bit(&self) -> u32 { + match self { + InstSize::Size32 => 0, + InstSize::Size64 => 1, + } + } } diff --git a/cranelift/codegen/src/isa/aarch64/inst/emit.rs b/cranelift/codegen/src/isa/aarch64/inst/emit.rs index 9d9d98d7c8..c02980859e 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/emit.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/emit.rs @@ -257,6 +257,15 @@ fn enc_cset(rd: Writable, cond: Cond) -> u32 { | (cond.invert().bits() << 12) } +fn enc_ccmp_imm(size: InstSize, rn: Reg, imm: UImm5, nzcv: NZCV, cond: Cond) -> u32 { + 0b0_1_1_11010010_00000_0000_10_00000_0_0000 + | size.sf_bit() << 31 + | imm.bits() << 16 + | cond.bits() << 12 + | machreg_to_gpr(rn) << 5 + | nzcv.bits() +} + fn enc_vecmov(is_16b: bool, rd: Writable, rn: Reg) -> u32 { debug_assert!(!is_16b); // to be supported later. 0b00001110_101_00000_00011_1_00000_00000 @@ -831,6 +840,15 @@ impl MachInstEmit for Inst { &Inst::CSet { rd, cond } => { sink.put4(enc_cset(rd, cond)); } + &Inst::CCmpImm { + size, + rn, + imm, + nzcv, + cond, + } => { + sink.put4(enc_ccmp_imm(size, rn, imm, nzcv, cond)); + } &Inst::FpuMove64 { rd, rn } => { sink.put4(enc_vecmov(/* 16b = */ false, rd, rn)); } @@ -1419,6 +1437,17 @@ mod test { "A400068A", "and x4, x5, x6", )); + insns.push(( + Inst::AluRRR { + alu_op: ALUOp::SubS32, + rd: writable_zero_reg(), + rn: xreg(2), + rm: xreg(3), + }, + "5F00036B", + // TODO: Display as cmp + "subs wzr, w2, w3", + )); insns.push(( Inst::AluRRR { alu_op: ALUOp::SubS32, @@ -1459,6 +1488,17 @@ mod test { "A40006AB", "adds x4, x5, x6", )); + insns.push(( + Inst::AluRRImm12 { + alu_op: ALUOp::AddS64, + rd: writable_zero_reg(), + rn: xreg(5), + imm12: Imm12::maybe_from_u64(1).unwrap(), + }, + "BF0400B1", + // TODO: Display as cmn. + "adds xzr, x5, #1", + )); insns.push(( Inst::AluRRR { alu_op: ALUOp::SDiv64, @@ -3053,6 +3093,28 @@ mod test { "EFB79F9A", "cset x15, ge", )); + insns.push(( + Inst::CCmpImm { + size: InstSize::Size64, + rn: xreg(22), + imm: UImm5::maybe_from_u8(5).unwrap(), + nzcv: NZCV::new(false, false, true, true), + cond: Cond::Eq, + }, + "C30A45FA", + "ccmp x22, #5, #nzCV, eq", + )); + insns.push(( + Inst::CCmpImm { + size: InstSize::Size32, + rn: xreg(3), + imm: UImm5::maybe_from_u8(30).unwrap(), + nzcv: NZCV::new(true, true, true, true), + cond: Cond::Gt, + }, + "6FC85E7A", + "ccmp w3, #30, #NZCV, gt", + )); insns.push(( Inst::MovToVec64 { rd: writable_vreg(20), diff --git a/cranelift/codegen/src/isa/aarch64/inst/imms.rs b/cranelift/codegen/src/isa/aarch64/inst/imms.rs index 7230b4f44e..172195b6b6 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/imms.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/imms.rs @@ -11,6 +11,55 @@ use regalloc::RealRegUniverse; use core::convert::TryFrom; use std::string::String; +/// An immediate that represents the NZCV flags. +#[derive(Clone, Copy, Debug)] +pub struct NZCV { + /// The negative condition flag. + n: bool, + /// The zero condition flag. + z: bool, + /// The carry condition flag. + c: bool, + /// The overflow condition flag. + v: bool, +} + +impl NZCV { + pub fn new(n: bool, z: bool, c: bool, v: bool) -> NZCV { + NZCV { n, z, c, v } + } + + /// Bits for encoding. + pub fn bits(&self) -> u32 { + (u32::from(self.n) << 3) + | (u32::from(self.z) << 2) + | (u32::from(self.c) << 1) + | u32::from(self.v) + } +} + +/// An unsigned 5-bit immediate. +#[derive(Clone, Copy, Debug)] +pub struct UImm5 { + /// The value. + value: u8, +} + +impl UImm5 { + pub fn maybe_from_u8(value: u8) -> Option { + if value < 32 { + Some(UImm5 { value }) + } else { + None + } + } + + /// Bits for encoding. + pub fn bits(&self) -> u32 { + u32::from(self.value) + } +} + /// A signed, scaled 7-bit offset. #[derive(Clone, Copy, Debug)] pub struct SImm7Scaled { @@ -483,6 +532,25 @@ impl MoveWideConst { } } +impl ShowWithRRU for NZCV { + fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { + let fmt = |c: char, v| if v { c.to_ascii_uppercase() } else { c }; + format!( + "#{}{}{}{}", + fmt('n', self.n), + fmt('z', self.z), + fmt('c', self.c), + fmt('v', self.v) + ) + } +} + +impl ShowWithRRU for UImm5 { + fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { + format!("#{}", self.value) + } +} + impl ShowWithRRU for Imm12 { fn show_rru(&self, _mb_rru: Option<&RealRegUniverse>) -> String { let shift = if self.shift12 { 12 } else { 0 }; diff --git a/cranelift/codegen/src/isa/aarch64/inst/mod.rs b/cranelift/codegen/src/isa/aarch64/inst/mod.rs index 90fce9c5fa..3cf02c22d0 100644 --- a/cranelift/codegen/src/isa/aarch64/inst/mod.rs +++ b/cranelift/codegen/src/isa/aarch64/inst/mod.rs @@ -436,6 +436,15 @@ pub enum Inst { cond: Cond, }, + /// A conditional comparison with an immediate. + CCmpImm { + size: InstSize, + rn: Reg, + imm: UImm5, + nzcv: NZCV, + cond: Cond, + }, + /// FPU move. Note that this is distinct from a vector-register /// move; moving just 64 bits seems to be significantly faster. FpuMove64 { @@ -958,6 +967,9 @@ fn aarch64_get_regs(inst: &Inst, collector: &mut RegUsageCollector) { &Inst::CSet { rd, .. } => { collector.add_def(rd); } + &Inst::CCmpImm { rn, .. } => { + collector.add_use(rn); + } &Inst::FpuMove64 { rd, rn } => { collector.add_def(rd); collector.add_use(rn); @@ -1388,6 +1400,9 @@ fn aarch64_map_regs( &mut Inst::CSet { ref mut rd, .. } => { map_wr(d, rd); } + &mut Inst::CCmpImm { ref mut rn, .. } => { + map(u, rn); + } &mut Inst::FpuMove64 { ref mut rd, ref mut rn, @@ -2177,6 +2192,19 @@ impl ShowWithRRU for Inst { let cond = cond.show_rru(mb_rru); format!("cset {}, {}", rd, cond) } + &Inst::CCmpImm { + size, + rn, + imm, + nzcv, + cond, + } => { + let rn = show_ireg_sized(rn, mb_rru, size); + let imm = imm.show_rru(mb_rru); + let nzcv = nzcv.show_rru(mb_rru); + let cond = cond.show_rru(mb_rru); + format!("ccmp {}, {}, {}, {}", rn, imm, nzcv, cond) + } &Inst::FpuMove64 { rd, rn } => { let rd = rd.to_reg().show_rru(mb_rru); let rn = rn.show_rru(mb_rru);