YJIT: ARM64: Move functions out of arm64_emit()

This commit is contained in:
Alan Wu 2023-02-01 17:24:50 -05:00
Родитель a690db390d
Коммит 905e12a30d
1 изменённых файлов: 90 добавлений и 90 удалений

Просмотреть файл

@ -69,6 +69,96 @@ impl From<&Opnd> for A64Opnd {
}
}
/// Call emit_jmp_ptr and immediately invalidate the written range.
/// This is needed when next_page also moves other_cb that is not invalidated
/// by compile_with_regs. Doing it here allows you to avoid invalidating a lot
/// more than necessary when other_cb jumps from a position early in the page.
/// This invalidates a small range of cb twice, but we accept the small cost.
fn emit_jmp_ptr_with_invalidation(cb: &mut CodeBlock, dst_ptr: CodePtr) {
#[cfg(not(test))]
let start = cb.get_write_ptr();
emit_jmp_ptr(cb, dst_ptr, true);
#[cfg(not(test))]
{
let end = cb.get_write_ptr();
use crate::cruby::rb_yjit_icache_invalidate;
unsafe { rb_yjit_icache_invalidate(start.raw_ptr() as _, end.raw_ptr() as _) };
}
}
fn emit_jmp_ptr(cb: &mut CodeBlock, dst_ptr: CodePtr, padding: bool) {
let src_addr = cb.get_write_ptr().into_i64();
let dst_addr = dst_ptr.into_i64();
// If the offset is short enough, then we'll use the
// branch instruction. Otherwise, we'll move the
// destination into a register and use the branch
// register instruction.
let num_insns = if b_offset_fits_bits((dst_addr - src_addr) / 4) {
b(cb, InstructionOffset::from_bytes((dst_addr - src_addr) as i32));
1
} else {
let num_insns = emit_load_value(cb, Assembler::SCRATCH0, dst_addr as u64);
br(cb, Assembler::SCRATCH0);
num_insns + 1
};
if padding {
// Make sure it's always a consistent number of
// instructions in case it gets patched and has to
// use the other branch.
for _ in num_insns..(JMP_PTR_BYTES / 4) {
nop(cb);
}
}
}
/// Emit the required instructions to load the given value into the
/// given register. Our goal here is to use as few instructions as
/// possible to get this value into the register.
fn emit_load_value(cb: &mut CodeBlock, rd: A64Opnd, value: u64) -> usize {
let mut current = value;
if current <= 0xffff {
// If the value fits into a single movz
// instruction, then we'll use that.
movz(cb, rd, A64Opnd::new_uimm(current), 0);
return 1;
} else if BitmaskImmediate::try_from(current).is_ok() {
// Otherwise, if the immediate can be encoded
// with the special bitmask immediate encoding,
// we'll use that.
mov(cb, rd, A64Opnd::new_uimm(current));
return 1;
} else {
// Finally we'll fall back to encoding the value
// using movz for the first 16 bits and movk for
// each subsequent set of 16 bits as long we
// they are necessary.
movz(cb, rd, A64Opnd::new_uimm(current & 0xffff), 0);
let mut num_insns = 1;
// (We're sure this is necessary since we
// checked if it only fit into movz above).
current >>= 16;
movk(cb, rd, A64Opnd::new_uimm(current & 0xffff), 16);
num_insns += 1;
if current > 0xffff {
current >>= 16;
movk(cb, rd, A64Opnd::new_uimm(current & 0xffff), 32);
num_insns += 1;
}
if current > 0xffff {
current >>= 16;
movk(cb, rd, A64Opnd::new_uimm(current & 0xffff), 48);
num_insns += 1;
}
return num_insns;
}
}
impl Assembler
{
// A special scratch register for intermediate processing.
@ -574,52 +664,6 @@ impl Assembler
}
}
/// Emit the required instructions to load the given value into the
/// given register. Our goal here is to use as few instructions as
/// possible to get this value into the register.
fn emit_load_value(cb: &mut CodeBlock, rd: A64Opnd, value: u64) -> usize {
let mut current = value;
if current <= 0xffff {
// If the value fits into a single movz
// instruction, then we'll use that.
movz(cb, rd, A64Opnd::new_uimm(current), 0);
return 1;
} else if BitmaskImmediate::try_from(current).is_ok() {
// Otherwise, if the immediate can be encoded
// with the special bitmask immediate encoding,
// we'll use that.
mov(cb, rd, A64Opnd::new_uimm(current));
return 1;
} else {
// Finally we'll fall back to encoding the value
// using movz for the first 16 bits and movk for
// each subsequent set of 16 bits as long we
// they are necessary.
movz(cb, rd, A64Opnd::new_uimm(current & 0xffff), 0);
let mut num_insns = 1;
// (We're sure this is necessary since we
// checked if it only fit into movz above).
current >>= 16;
movk(cb, rd, A64Opnd::new_uimm(current & 0xffff), 16);
num_insns += 1;
if current > 0xffff {
current >>= 16;
movk(cb, rd, A64Opnd::new_uimm(current & 0xffff), 32);
num_insns += 1;
}
if current > 0xffff {
current >>= 16;
movk(cb, rd, A64Opnd::new_uimm(current & 0xffff), 48);
num_insns += 1;
}
return num_insns;
}
}
/// Emit a conditional jump instruction to a specific target. This is
/// called when lowering any of the conditional jump instructions.
fn emit_conditional_jump<const CONDITION: u8>(cb: &mut CodeBlock, target: Target) {
@ -691,50 +735,6 @@ impl Assembler
ldr_post(cb, opnd, A64Opnd::new_mem(64, C_SP_REG, C_SP_STEP));
}
fn emit_jmp_ptr(cb: &mut CodeBlock, dst_ptr: CodePtr, padding: bool) {
let src_addr = cb.get_write_ptr().into_i64();
let dst_addr = dst_ptr.into_i64();
// If the offset is short enough, then we'll use the
// branch instruction. Otherwise, we'll move the
// destination into a register and use the branch
// register instruction.
let num_insns = if b_offset_fits_bits((dst_addr - src_addr) / 4) {
b(cb, InstructionOffset::from_bytes((dst_addr - src_addr) as i32));
1
} else {
let num_insns = emit_load_value(cb, Assembler::SCRATCH0, dst_addr as u64);
br(cb, Assembler::SCRATCH0);
num_insns + 1
};
if padding {
// Make sure it's always a consistent number of
// instructions in case it gets patched and has to
// use the other branch.
for _ in num_insns..(JMP_PTR_BYTES / 4) {
nop(cb);
}
}
}
/// Call emit_jmp_ptr and immediately invalidate the written range.
/// This is needed when next_page also moves other_cb that is not invalidated
/// by compile_with_regs. Doing it here allows you to avoid invalidating a lot
/// more than necessary when other_cb jumps from a position early in the page.
/// This invalidates a small range of cb twice, but we accept the small cost.
fn emit_jmp_ptr_with_invalidation(cb: &mut CodeBlock, dst_ptr: CodePtr) {
#[cfg(not(test))]
let start = cb.get_write_ptr();
emit_jmp_ptr(cb, dst_ptr, true);
#[cfg(not(test))]
{
let end = cb.get_write_ptr();
use crate::cruby::rb_yjit_icache_invalidate;
unsafe { rb_yjit_icache_invalidate(start.raw_ptr() as _, end.raw_ptr() as _) };
}
}
// dbg!(&self.insns);
// List of GC offsets