Skip to content

Commit 548c25c

Browse files
committed
ZJIT: A64: Have add/sub to SP be single-instruction
Previously a missed optimization for add followed by mov. While we're at it, have Add and Sub share the same match arm in arm64_split().
1 parent 482f4ca commit 548c25c

1 file changed

Lines changed: 51 additions & 18 deletions

File tree

zjit/src/backend/arm64/mod.rs

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -416,9 +416,11 @@ impl Assembler
416416
// being used. It is okay not to use their output here.
417417
#[allow(unused_must_use)]
418418
match &mut insn {
419-
Insn::Add { left, right, .. } => {
419+
Insn::Sub { left, right, out } |
420+
Insn::Add { left, right, out } => {
420421
match (*left, *right) {
421422
(Opnd::Reg(_) | Opnd::VReg { .. }, Opnd::Reg(_) | Opnd::VReg { .. }) => {
423+
merge_three_reg_mov(&live_ranges, &mut iterator, left, right, out);
422424
asm.push_insn(insn);
423425
},
424426
(reg_opnd @ (Opnd::Reg(_) | Opnd::VReg { .. }), other_opnd) |
@@ -441,18 +443,7 @@ impl Assembler
441443
*left = opnd0;
442444
*right = opnd1;
443445

444-
// Since these instructions are lowered to an instruction that have 2 input
445-
// registers and an output register, look to merge with an `Insn::Mov` that
446-
// follows which puts the output in another register. For example:
447-
// `Add a, b => out` followed by `Mov c, out` becomes `Add a, b => c`.
448-
if let (Opnd::Reg(_), Opnd::Reg(_), Some(Insn::Mov { dest, src })) = (left, right, iterator.peek().map(|(_, insn)| insn)) {
449-
if live_ranges[out.vreg_idx()].end() == index + 1 {
450-
if out == src && matches!(*dest, Opnd::Reg(_)) {
451-
*out = *dest;
452-
iterator.next(); // Pop merged Insn::Mov
453-
}
454-
}
455-
}
446+
merge_three_reg_mov(&live_ranges, &mut iterator, left, right, out);
456447

457448
asm.push_insn(insn);
458449
}
@@ -700,11 +691,6 @@ impl Assembler
700691
}
701692
}
702693
},
703-
Insn::Sub { left, right, .. } => {
704-
*left = split_load_operand(asm, *left);
705-
*right = split_shifted_immediate(asm, *right);
706-
asm.push_insn(insn);
707-
},
708694
Insn::Mul { left, right, .. } => {
709695
*left = split_load_operand(asm, *left);
710696
*right = split_load_operand(asm, *right);
@@ -1337,6 +1323,36 @@ impl Assembler
13371323
}
13381324
}
13391325

1326+
/// LIR Instructions that are lowered to an instruction that have 2 input registers and an output
1327+
/// register can look to merge with a succeeding `Insn::Mov`.
1328+
/// For example:
1329+
///
1330+
/// Add out, a, b
1331+
/// Mov c, out
1332+
///
1333+
/// Can become:
1334+
///
1335+
/// Add c, a, b
1336+
///
1337+
/// If a, b, and c are all registers.
1338+
fn merge_three_reg_mov(
1339+
live_ranges: &Vec<LiveRange>,
1340+
iterator: &mut std::iter::Peekable<impl Iterator<Item = (usize, Insn)>>,
1341+
left: &Opnd,
1342+
right: &Opnd,
1343+
out: &mut Opnd,
1344+
) {
1345+
if let (Opnd::Reg(_) | Opnd::VReg{..},
1346+
Opnd::Reg(_) | Opnd::VReg{..},
1347+
Some((mov_idx, Insn::Mov { dest, src })))
1348+
= (left, right, iterator.peek()) {
1349+
if out == src && live_ranges[out.vreg_idx()].end() == *mov_idx && matches!(*dest, Opnd::Reg(_) | Opnd::VReg{..}) {
1350+
*out = *dest;
1351+
iterator.next(); // Pop merged Insn::Mov
1352+
}
1353+
}
1354+
}
1355+
13401356
#[cfg(test)]
13411357
mod tests {
13421358
use super::*;
@@ -1363,6 +1379,23 @@ mod tests {
13631379
"});
13641380
}
13651381

1382+
#[test]
1383+
fn sp_movements_are_single_instruction() {
1384+
let (mut asm, mut cb) = setup_asm();
1385+
1386+
let sp = Opnd::Reg(XZR_REG);
1387+
let new_sp = asm.add(sp, 0x20.into());
1388+
asm.mov(sp, new_sp);
1389+
let new_sp = asm.sub(sp, 0x20.into());
1390+
asm.mov(sp, new_sp);
1391+
asm.compile_with_num_regs(&mut cb, 2);
1392+
1393+
assert_disasm!(cb, "e08300b11f000091e08300f11f000091", {"
1394+
0x0: add sp, sp, #0x20
1395+
0x4: sub sp, sp, #0x20
1396+
"});
1397+
}
1398+
13661399
#[test]
13671400
fn test_emit_add() {
13681401
let (mut asm, mut cb) = setup_asm();

0 commit comments

Comments
 (0)