YJIT: Add Opnd#with_num_bits to use only 8 bits (#6359)

* YJIT: Add Opnd#sub_opnd to use only 8 bits

* Add with_num_bits and let arm64_split use it

* Add another assertion to with_num_bits

* Use only with_num_bits
This commit is contained in:
Takashi Kokubun 2022-09-14 23:27:52 +09:00 committed by GitHub
parent 2e25b85a7e
commit 8f37e9c918
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
Notes: git 2022-09-14 23:28:16 +09:00
Merged-By: maximecb <maximecb@ruby-lang.org>
7 changed files with 61 additions and 16 deletions

View File

@ -2,6 +2,7 @@ use super::super::arg::truncate_imm;
/// The size of the operands being operated on.
enum Size {
Size8 = 0b00,
Size32 = 0b10,
Size64 = 0b11,
}
@ -81,6 +82,12 @@ impl LoadStore {
Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDR, size: num_bits.into() }
}
/// LDURB (load register, byte, unscaled)
/// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURB--Load-Register-Byte--unscaled--?lang=en
pub fn ldurb(rt: u8, rn: u8, imm9: i16) -> Self {
Self { rt, rn, idx: Index::None, imm9, opc: Opc::LDR, size: Size::Size8 }
}
/// LDURSW (load register, unscaled, signed)
/// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDURSW--Load-Register-Signed-Word--unscaled--?lang=en
pub fn ldursw(rt: u8, rn: u8, imm9: i16) -> Self {
@ -157,6 +164,13 @@ mod tests {
assert_eq!(0xf8400020, result);
}
#[test]
fn test_ldurb() {
let inst = LoadStore::ldurb(0, 1, 0);
let result: u32 = inst.into();
assert_eq!(0x38400020, result);
}
#[test]
fn test_ldur_with_imm() {
let inst = LoadStore::ldur(0, 1, 123, 64);

View File

@ -507,6 +507,22 @@ pub fn ldur(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) {
cb.write_bytes(&bytes);
}
/// LDURB - load a byte from memory, zero-extend it, and write it to a register
pub fn ldurb(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) {
let bytes: [u8; 4] = match (rt, rn) {
(A64Opnd::Reg(rt), A64Opnd::Mem(rn)) => {
assert!(rt.num_bits == rn.num_bits, "Expected registers to be the same size");
assert!(rt.num_bits == 8, "Expected registers to have size 8");
assert!(mem_disp_fits_bits(rn.disp), "Expected displacement to be 9 bits or less");
LoadStore::ldurb(rt.reg_no, rn.base_reg_no, rn.disp as i16).into()
},
_ => panic!("Invalid operands for LDURB")
};
cb.write_bytes(&bytes);
}
/// LDURSW - load a 32-bit memory address into a register and sign-extend it
pub fn ldursw(cb: &mut CodeBlock, rt: A64Opnd, rn: A64Opnd) {
let bytes: [u8; 4] = match (rt, rn) {

View File

@ -12,10 +12,8 @@ pub struct A64Reg
}
impl A64Reg {
pub fn sub_reg(&self, num_bits: u8) -> Self {
assert!(num_bits == 32 || num_bits == 64);
assert!(num_bits <= self.num_bits);
pub fn with_num_bits(&self, num_bits: u8) -> Self {
assert!(num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64);
Self { num_bits, reg_no: self.reg_no }
}
}

View File

@ -89,16 +89,13 @@ pub enum X86Opnd
}
impl X86Reg {
pub fn sub_reg(&self, num_bits: u8) -> Self {
pub fn with_num_bits(&self, num_bits: u8) -> Self {
assert!(
num_bits == 8 ||
num_bits == 16 ||
num_bits == 32 ||
num_bits == 64
);
assert!(num_bits <= self.num_bits);
Self {
num_bits,
reg_type: self.reg_type,

View File

@ -140,7 +140,14 @@ impl Assembler
Opnd::Reg(_) | Opnd::InsnOut { .. } => opnd,
Opnd::Mem(_) => {
let split_opnd = split_memory_address(asm, opnd);
asm.load(split_opnd)
let out_opnd = asm.load(split_opnd);
// Many Arm insns support only 32-bit or 64-bit operands. asm.load with fewer
// bits zero-extends the value, so it's safe to recognize it as a 32-bit value.
if out_opnd.rm_num_bits() < 32 {
out_opnd.with_num_bits(32).unwrap()
} else {
out_opnd
}
},
_ => asm.load(opnd)
}
@ -747,7 +754,11 @@ impl Assembler
emit_load_value(cb, out.into(), imm as u64);
},
Opnd::Mem(_) => {
ldur(cb, out.into(), opnd.into());
match opnd.rm_num_bits() {
64 | 32 => ldur(cb, out.into(), opnd.into()),
8 => ldurb(cb, out.into(), opnd.into()),
num_bits => panic!("unexpected num_bits: {}", num_bits)
};
},
Opnd::Value(value) => {
// We dont need to check if it's a special const

View File

@ -151,6 +151,16 @@ impl Opnd
}
}
pub fn with_num_bits(&self, num_bits: u8) -> Option<Opnd> {
assert!(num_bits == 8 || num_bits == 16 || num_bits == 32 || num_bits == 64);
match *self {
Opnd::Reg(reg) => Some(Opnd::Reg(reg.with_num_bits(num_bits))),
Opnd::Mem(Mem { base, disp, .. }) => Some(Opnd::Mem(Mem { base, disp, num_bits })),
Opnd::InsnOut { idx, .. } => Some(Opnd::InsnOut { idx, num_bits }),
_ => None,
}
}
/// Get the size in bits for register/memory operands.
pub fn rm_num_bits(&self) -> u8 {
self.num_bits().unwrap()
@ -1052,15 +1062,15 @@ impl Assembler
// output operand on this instruction because the live range
// extends beyond the index of the instruction.
let out = insn.out_opnd_mut().unwrap();
*out = Opnd::Reg(out_reg.unwrap().sub_reg(out_num_bits));
*out = Opnd::Reg(out_reg.unwrap().with_num_bits(out_num_bits));
}
// Replace InsnOut operands by their corresponding register
let mut opnd_iter = insn.opnd_iter_mut();
while let Some(opnd) = opnd_iter.next() {
match *opnd {
Opnd::InsnOut { idx, .. } => {
*opnd = *asm.insns[idx].out_opnd().unwrap();
Opnd::InsnOut { idx, num_bits } => {
*opnd = (*asm.insns[idx].out_opnd().unwrap()).with_num_bits(num_bits).unwrap();
},
Opnd::Mem(Mem { base: MemBase::InsnOut(idx), disp, num_bits }) => {
let base = MemBase::Reg(asm.insns[idx].out_opnd().unwrap().unwrap_reg().reg_no);

View File

@ -3477,8 +3477,7 @@ fn jit_guard_known_klass(
asm.comment("guard object is static symbol");
assert!(RUBY_SPECIAL_SHIFT == 8);
let flag_bits = asm.and(obj_opnd, Opnd::UImm(0xf));
asm.cmp(flag_bits, Opnd::UImm(RUBY_SYMBOL_FLAG as u64));
asm.cmp(obj_opnd.with_num_bits(8).unwrap(), Opnd::UImm(RUBY_SYMBOL_FLAG as u64));
jit_chain_guard(JCC_JNE, jit, ctx, asm, ocb, max_chain_depth, side_exit);
ctx.upgrade_opnd_type(insn_opnd, Type::ImmSymbol);
}