Skip to content

Commit

Permalink
YJIT: Optimize cmp REG, 0 into test REG, REG
Browse files Browse the repository at this point in the history
  • Loading branch information
k0kubun committed Mar 8, 2023
1 parent 3e731cd commit 3d9ff80
Showing 1 changed file with 48 additions and 3 deletions.
51 changes: 48 additions & 3 deletions yjit/src/backend/x86_64/mod.rs
Expand Up @@ -105,9 +105,40 @@ impl Assembler
// These are the callee-saved registers in the x86-64 SysV ABI
// RBX, RSP, RBP, and R12–R15

/// Merge IR instructions for the x86 platform. As of x86_split, all `out` operands
/// are Opnd::Out, but you sometimes want to use Opnd::Reg for example to shorten the
/// generated code, which is what this pass does.
/// Replace IR instructions for the x86 platform. This function optimizes
/// IR, looking at IR *before* x86_split. Optimization in this function
/// depends on IR to NOT interleave inserted by x86_split, e.g. Load.
fn x86_replace(mut self) -> Assembler {
let live_ranges: Vec<usize> = take(&mut self.live_ranges);
let mut asm = Assembler::new_with_label_names(take(&mut self.label_names));
let mut iterator = self.into_draining_iter();

while let Some((index, mut insn)) = iterator.next_unmapped() {
match (&insn, iterator.peek()) {
// Replace `cmp REG, 0` (4 bytes) with `test REG, REG` (3 bytes)
// when next IR is `je`, `jne`, `csel_e`, or `csel_ne`
(Insn::Cmp { left, right: Opnd::UImm(0) | Opnd::Imm(0) },
Some(Insn::Je(_) | Insn::Jne(_) | Insn::CSelE { .. } | Insn::CSelNE { .. }))
if matches!(left, Opnd::InsnOut { .. }) => {
let left = iterator.map_opnd(*left);
asm.push_insn(Insn::Test { left, right: left });
}
_ => {
let mut opnd_iter = insn.opnd_iter_mut();
while let Some(opnd) = opnd_iter.next() {
*opnd = iterator.map_opnd(*opnd);
}
asm.push_insn(insn);
}
}
iterator.map_insn_index(&mut asm);
}
asm
}

/// Merge IR instructions for the x86 platform. This function optimizes IR,
/// looking at IR *after* x86_split. x86_split overwrites all out operands
/// to Opnd::Out, but this function may emit Opnd::Reg operands on out operands.
fn x86_merge(mut self) -> Assembler {
let live_ranges: Vec<usize> = take(&mut self.live_ranges);
let mut asm = Assembler::new_with_label_names(take(&mut self.label_names));
Expand Down Expand Up @@ -732,6 +763,7 @@ impl Assembler
pub fn compile_with_regs(self, cb: &mut CodeBlock, regs: Vec<Reg>) -> Vec<u32>
{
let asm = self.lower_stack();
let asm = asm.x86_replace();
let asm = asm.x86_split();
let asm = asm.x86_merge();
let mut asm = asm.alloc_regs(regs);
Expand Down Expand Up @@ -943,4 +975,17 @@ mod tests {
asm.compile_with_num_regs(&mut cb, 1);
assert_eq!(format!("{:x}", cb), "488d4308488903");
}

#[test]
fn test_replace_cmp_0() {
let (mut asm, mut cb) = setup_asm();

let val = asm.load(Opnd::mem(64, SP, 8));
asm.cmp(val, 0.into());
let result = asm.csel_e(Qtrue.into(), Qfalse.into());
asm.mov(Opnd::Reg(RAX_REG), result);
asm.compile_with_num_regs(&mut cb, 2);

assert_eq!(format!("{:x}", cb), "488b43084885c0b814000000b900000000480f45c14889c0");
}
}

0 comments on commit 3d9ff80

Please sign in to comment.