Skip to content

Commit f58fca7

Browse files
committed
ZJIT: A64: Use MOVN for small negative immediates
Save a couple instructions to load a small negative constant into a register. In fact MOVN is speced to alias as `mov` in the official disassembly.
1 parent faa6750 commit f58fca7

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

zjit/src/asm/arm64/inst/mov.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ use super::super::arg::Sf;
22

33
/// Which operation is being performed.
44
enum Op {
5+
/// A movn operation which inverts the immediate and zeroes out the other bits.
6+
MOVN = 0b00,
7+
58
/// A movz operation which zeroes out the other bits.
69
MOVZ = 0b10,
710

@@ -61,6 +64,12 @@ impl Mov {
6164
Self { rd, imm16, hw: hw.into(), op: Op::MOVK, sf: num_bits.into() }
6265
}
6366

67+
/// MOVN
68+
/// <https://developer.arm.com/documentation/ddi0602/2025-06/Base-Instructions/MOVN--Move-wide-with-NOT->
69+
pub fn movn(rd: u8, imm16: u16, hw: u8, num_bits: u8) -> Self {
70+
Self { rd, imm16, hw: hw.into(), op: Op::MOVN, sf: num_bits.into() }
71+
}
72+
6473
/// MOVZ
6574
/// <https://developer.arm.com/documentation/ddi0602/2022-03/Base-Instructions/MOVZ--Move-wide-with-zero-?lang=en>
6675
pub fn movz(rd: u8, imm16: u16, hw: u8, num_bits: u8) -> Self {
@@ -104,6 +113,34 @@ mod tests {
104113
assert_eq!(0xf2800f60, result);
105114
}
106115

116+
#[test]
117+
fn test_movn_unshifted() {
118+
let inst = Mov::movn(0, 123, 0, 64);
119+
let result: u32 = inst.into();
120+
assert_eq!(0x92800f60, result);
121+
}
122+
123+
#[test]
124+
fn test_movn_shifted_16() {
125+
let inst = Mov::movn(0, 123, 16, 64);
126+
let result: u32 = inst.into();
127+
assert_eq!(0x92a00f60, result);
128+
}
129+
130+
#[test]
131+
fn test_movn_shifted_32() {
132+
let inst = Mov::movn(0, 123, 32, 64);
133+
let result: u32 = inst.into();
134+
assert_eq!(0x92c00f60, result);
135+
}
136+
137+
#[test]
138+
fn test_movn_shifted_48() {
139+
let inst = Mov::movn(0, 123, 48, 64);
140+
let result: u32 = inst.into();
141+
assert_eq!(0x92e00f60, result);
142+
}
143+
107144
#[test]
108145
fn test_movk_shifted_16() {
109146
let inst = Mov::movk(0, 123, 16, 64);

zjit/src/asm/arm64/mod.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -716,6 +716,21 @@ pub fn movk(cb: &mut CodeBlock, rd: A64Opnd, imm16: A64Opnd, shift: u8) {
716716
cb.write_bytes(&bytes);
717717
}
718718

719+
/// MOVN - load a register with the complement of a shifted then zero extended 16-bit immediate
720+
/// <https://developer.arm.com/documentation/ddi0602/2025-06/Base-Instructions/MOVN--Move-wide-with-NOT->
721+
pub fn movn(cb: &mut CodeBlock, rd: A64Opnd, imm16: A64Opnd, shift: u8) {
722+
let bytes: [u8; 4] = match (rd, imm16) {
723+
(A64Opnd::Reg(rd), A64Opnd::UImm(imm16)) => {
724+
assert!(uimm_fits_bits(imm16, 16), "The immediate operand must be 16 bits or less.");
725+
726+
Mov::movn(rd.reg_no, imm16 as u16, shift, rd.num_bits).into()
727+
},
728+
_ => panic!("Invalid operand combination to movn instruction.")
729+
};
730+
731+
cb.write_bytes(&bytes);
732+
}
733+
719734
/// MOVZ - move a 16 bit immediate into a register, zero the other bits
720735
pub fn movz(cb: &mut CodeBlock, rd: A64Opnd, imm16: A64Opnd, shift: u8) {
721736
let bytes: [u8; 4] = match (rd, imm16) {
@@ -1543,6 +1558,11 @@ mod tests {
15431558
check_bytes("600fa0f2", |cb| movk(cb, X0, A64Opnd::new_uimm(123), 16));
15441559
}
15451560

1561+
#[test]
1562+
fn test_movn() {
1563+
check_bytes("600fa092", |cb| movn(cb, X0, A64Opnd::new_uimm(123), 16));
1564+
}
1565+
15461566
#[test]
15471567
fn test_movz() {
15481568
check_bytes("600fa0d2", |cb| movz(cb, X0, A64Opnd::new_uimm(123), 16));

zjit/src/backend/arm64/mod.rs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,10 @@ fn emit_load_value(cb: &mut CodeBlock, rd: A64Opnd, value: u64) -> usize {
140140
// instruction, then we'll use that.
141141
movz(cb, rd, A64Opnd::new_uimm(current), 0);
142142
return 1;
143+
} else if u16::try_from(!value).is_ok() {
144+
// For small negative values, use a single movn
145+
movn(cb, rd, A64Opnd::new_uimm(!value), 0);
146+
return 1;
143147
} else if BitmaskImmediate::try_from(current).is_ok() {
144148
// Otherwise, if the immediate can be encoded
145149
// with the special bitmask immediate encoding,
@@ -1592,15 +1596,16 @@ mod tests {
15921596

15931597
// Test values that exercise various types of immediates.
15941598
// - 9 bit displacement for Load/Store
1595-
// - 12 bit shifted immediate
1599+
// - 12 bit ADD/SUB shifted immediate
1600+
// - 16 bit MOV family shifted immediates
15961601
// - bit mask immediates
1597-
for displacement in [i32::MAX, 0x10008, 0x1800, 0x208, -0x208, -0x1800, -0x1008, i32::MIN] {
1602+
for displacement in [i32::MAX, 0x10008, 0x1800, 0x208, -0x208, -0x1800, -0x10008, i32::MIN] {
15981603
let mem = Opnd::mem(64, NATIVE_STACK_PTR, displacement);
15991604
asm.lea_into(Opnd::Reg(X0_REG), mem);
16001605
}
16011606

16021607
asm.compile_with_num_regs(&mut cb, 0);
1603-
assert_disasm!(cb, "e07b40b2e063208b000180d22000a0f2e063208b000083d2e063208be0230891e02308d100009dd2e0ffbff2e0ffdff2e0fffff2e063208b00ff9dd2e0ffbff2e0ffdff2e0fffff2e063208be08361b2e063208b", "
1608+
assert_disasm!(cb, "e07b40b2e063208b000180d22000a0f2e063208b000083d2e063208be0230891e02308d1e0ff8292e063208b00ff9fd2c0ffbff2e0ffdff2e0fffff2e063208be08361b2e063208b", "
16041609
0x0: orr x0, xzr, #0x7fffffff
16051610
0x4: add x0, sp, x0
16061611
0x8: mov x0, #8
@@ -1610,18 +1615,15 @@ mod tests {
16101615
0x18: add x0, sp, x0
16111616
0x1c: add x0, sp, #0x208
16121617
0x20: sub x0, sp, #0x208
1613-
0x24: mov x0, #0xe800
1614-
0x28: movk x0, #0xffff, lsl #16
1615-
0x2c: movk x0, #0xffff, lsl #32
1616-
0x30: movk x0, #0xffff, lsl #48
1617-
0x34: add x0, sp, x0
1618-
0x38: mov x0, #0xeff8
1619-
0x3c: movk x0, #0xffff, lsl #16
1620-
0x40: movk x0, #0xffff, lsl #32
1621-
0x44: movk x0, #0xffff, lsl #48
1622-
0x48: add x0, sp, x0
1623-
0x4c: orr x0, xzr, #0xffffffff80000000
1624-
0x50: add x0, sp, x0
1618+
0x24: mov x0, #-0x1800
1619+
0x28: add x0, sp, x0
1620+
0x2c: mov x0, #0xfff8
1621+
0x30: movk x0, #0xfffe, lsl #16
1622+
0x34: movk x0, #0xffff, lsl #32
1623+
0x38: movk x0, #0xffff, lsl #48
1624+
0x3c: add x0, sp, x0
1625+
0x40: orr x0, xzr, #0xffffffff80000000
1626+
0x44: add x0, sp, x0
16251627
");
16261628
}
16271629

0 commit comments

Comments
 (0)