Add immediate addition and subtraction for AArch46

This commit is contained in:
Brendan Hansknecht 2020-12-01 18:29:10 -08:00
parent f9d571ebc2
commit be979b4218
3 changed files with 108 additions and 21 deletions

View file

@ -147,12 +147,11 @@ impl CallConv<AArch64GPReg> for AArch64Call {
requested_stack_size.checked_add(8 * saved_regs.len() as i32 + offset as i32) requested_stack_size.checked_add(8 * saved_regs.len() as i32 + offset as i32)
{ {
if aligned_stack_size > 0 { if aligned_stack_size > 0 {
// TODO deal with sizes over imm12. AArch64Assembler::sub_reg64_reg64_imm32(
sub_reg64_reg64_imm12(
buf, buf,
AArch64GPReg::ZRSP, AArch64GPReg::ZRSP,
AArch64GPReg::ZRSP, AArch64GPReg::ZRSP,
aligned_stack_size as u16, aligned_stack_size,
); );
// All the following stores could be optimized by using `STP` to store pairs. // All the following stores could be optimized by using `STP` to store pairs.
@ -196,12 +195,11 @@ impl CallConv<AArch64GPReg> for AArch64Call {
offset -= 8; offset -= 8;
AArch64Assembler::mov_reg64_stack32(buf, *reg, offset); AArch64Assembler::mov_reg64_stack32(buf, *reg, offset);
} }
// TODO deal with sizes over imm12. AArch64Assembler::add_reg64_reg64_imm32(
add_reg64_reg64_imm12(
buf, buf,
AArch64GPReg::ZRSP, AArch64GPReg::ZRSP,
AArch64GPReg::ZRSP, AArch64GPReg::ZRSP,
aligned_stack_size as u16, aligned_stack_size,
); );
} }
Ok(()) Ok(())
@ -209,6 +207,29 @@ impl CallConv<AArch64GPReg> for AArch64Call {
} }
impl Assembler<AArch64GPReg> for AArch64Assembler { impl Assembler<AArch64GPReg> for AArch64Assembler {
#[inline(always)]
fn abs_reg64_reg64<'a>(_buf: &mut Vec<'a, u8>, _dst: AArch64GPReg, _src: AArch64GPReg) {
unimplemented!("abs_reg64_reg64 is not yet implement for AArch64");
}
#[inline(always)]
fn add_reg64_reg64_imm32<'a>(
buf: &mut Vec<'a, u8>,
dst: AArch64GPReg,
src: AArch64GPReg,
imm32: i32,
) {
if imm32 < 0 {
unimplemented!("immediate addition with values less than 0 are not yet implemented");
} else if imm32 < 0xFFF {
add_reg64_reg64_imm12(buf, dst, src, imm32 as u16);
} else {
unimplemented!(
"immediate additions with values greater than 12bits are not yet implemented"
);
}
}
#[inline(always)] #[inline(always)]
fn add_reg64_reg64_reg64<'a>( fn add_reg64_reg64_reg64<'a>(
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
@ -267,8 +288,23 @@ impl Assembler<AArch64GPReg> for AArch64Assembler {
} }
#[inline(always)] #[inline(always)]
fn abs_reg64_reg64<'a>(_buf: &mut Vec<'a, u8>, _dst: AArch64GPReg, _src: AArch64GPReg) { fn sub_reg64_reg64_imm32<'a>(
unimplemented!("abs_reg64_reg64 is not yet implement for AArch64"); buf: &mut Vec<'a, u8>,
dst: AArch64GPReg,
src: AArch64GPReg,
imm32: i32,
) {
if imm32 < 0 {
unimplemented!(
"immediate subtractions with values less than 0 are not yet implemented"
);
} else if imm32 < 0xFFF {
sub_reg64_reg64_imm12(buf, dst, src, imm32 as u16);
} else {
unimplemented!(
"immediate subtractions with values greater than 12bits are not yet implemented"
);
}
} }
#[inline(always)] #[inline(always)]
@ -277,6 +313,8 @@ impl Assembler<AArch64GPReg> for AArch64Assembler {
} }
} }
impl AArch64Assembler {}
/// AArch64Instruction, maps all instructions to an enum. /// AArch64Instruction, maps all instructions to an enum.
/// Decoding the function should be cheap because we will always inline. /// Decoding the function should be cheap because we will always inline.
/// All of the operations should resolved by constants, leave just some bit manipulation. /// All of the operations should resolved by constants, leave just some bit manipulation.

View file

@ -43,12 +43,14 @@ pub trait CallConv<GPReg: GPRegTrait> {
/// Generally, I prefer explicit sources, as opposed to dst being one of the sources. Ex: `x = x + y` would be `add x, x, y` instead of `add x, y`. /// Generally, I prefer explicit sources, as opposed to dst being one of the sources. Ex: `x = x + y` would be `add x, x, y` instead of `add x, y`.
/// dst should always come before sources. /// dst should always come before sources.
pub trait Assembler<GPReg: GPRegTrait> { pub trait Assembler<GPReg: GPRegTrait> {
fn abs_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src: GPReg);
fn add_reg64_reg64_imm32<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src1: GPReg, imm32: i32);
fn add_reg64_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src1: GPReg, src2: GPReg); fn add_reg64_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src1: GPReg, src2: GPReg);
fn mov_reg64_imm64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, imm: i64); fn mov_reg64_imm64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, imm: i64);
fn mov_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src: GPReg); fn mov_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src: GPReg);
fn mov_reg64_stack32<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, offset: i32); fn mov_reg64_stack32<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, offset: i32);
fn mov_stack32_reg64<'a>(buf: &mut Vec<'a, u8>, offset: i32, src: GPReg); fn mov_stack32_reg64<'a>(buf: &mut Vec<'a, u8>, offset: i32, src: GPReg);
fn abs_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src: GPReg); fn sub_reg64_reg64_imm32<'a>(buf: &mut Vec<'a, u8>, dst: GPReg, src1: GPReg, imm32: i32);
fn ret<'a>(buf: &mut Vec<'a, u8>); fn ret<'a>(buf: &mut Vec<'a, u8>);
} }

View file

@ -179,11 +179,11 @@ fn x86_64_generic_setup_stack<'a>(
requested_stack_size: i32, requested_stack_size: i32,
) -> Result<i32, String> { ) -> Result<i32, String> {
if !leaf_function { if !leaf_function {
push_reg64(buf, X86_64GPReg::RBP); X86_64Assembler::push_reg64(buf, X86_64GPReg::RBP);
X86_64Assembler::mov_reg64_reg64(buf, X86_64GPReg::RBP, X86_64GPReg::RSP); X86_64Assembler::mov_reg64_reg64(buf, X86_64GPReg::RBP, X86_64GPReg::RSP);
} }
for reg in saved_regs { for reg in saved_regs {
push_reg64(buf, *reg); X86_64Assembler::push_reg64(buf, *reg);
} }
// full size is upcast to i64 to make sure we don't overflow here. // full size is upcast to i64 to make sure we don't overflow here.
@ -200,8 +200,12 @@ fn x86_64_generic_setup_stack<'a>(
}; };
if let Some(aligned_stack_size) = requested_stack_size.checked_add(offset as i32) { if let Some(aligned_stack_size) = requested_stack_size.checked_add(offset as i32) {
if aligned_stack_size > 0 { if aligned_stack_size > 0 {
// TODO: move this call to one using X86_64Assembler. X86_64Assembler::sub_reg64_reg64_imm32(
sub_reg64_imm32(buf, X86_64GPReg::RSP, aligned_stack_size); buf,
X86_64GPReg::RSP,
X86_64GPReg::RSP,
aligned_stack_size,
);
Ok(aligned_stack_size) Ok(aligned_stack_size)
} else { } else {
Ok(0) Ok(0)
@ -219,15 +223,19 @@ fn x86_64_generic_cleanup_stack<'a>(
aligned_stack_size: i32, aligned_stack_size: i32,
) -> Result<(), String> { ) -> Result<(), String> {
if aligned_stack_size > 0 { if aligned_stack_size > 0 {
// TODO: move this call to one using X86_64Assembler. X86_64Assembler::add_reg64_reg64_imm32(
add_reg64_imm32(buf, X86_64GPReg::RSP, aligned_stack_size); buf,
X86_64GPReg::RSP,
X86_64GPReg::RSP,
aligned_stack_size,
);
} }
for reg in saved_regs.iter().rev() { for reg in saved_regs.iter().rev() {
pop_reg64(buf, *reg); X86_64Assembler::pop_reg64(buf, *reg);
} }
if !leaf_function { if !leaf_function {
X86_64Assembler::mov_reg64_reg64(buf, X86_64GPReg::RSP, X86_64GPReg::RBP); X86_64Assembler::mov_reg64_reg64(buf, X86_64GPReg::RSP, X86_64GPReg::RBP);
pop_reg64(buf, X86_64GPReg::RBP); X86_64Assembler::pop_reg64(buf, X86_64GPReg::RBP);
} }
Ok(()) Ok(())
} }
@ -236,6 +244,26 @@ impl Assembler<X86_64GPReg> for X86_64Assembler {
// These functions should map to the raw assembly functions below. // These functions should map to the raw assembly functions below.
// In some cases, that means you can just directly call one of the direct assembly functions. // In some cases, that means you can just directly call one of the direct assembly functions.
#[inline(always)] #[inline(always)]
fn abs_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: X86_64GPReg, src: X86_64GPReg) {
mov_reg64_reg64(buf, dst, src);
neg_reg64(buf, dst);
cmovl_reg64_reg64(buf, dst, src);
}
#[inline(always)]
fn add_reg64_reg64_imm32<'a>(
buf: &mut Vec<'a, u8>,
dst: X86_64GPReg,
src1: X86_64GPReg,
imm32: i32,
) {
if dst == src1 {
add_reg64_imm32(buf, dst, imm32);
} else {
mov_reg64_reg64(buf, dst, src1);
add_reg64_imm32(buf, dst, imm32);
}
}
#[inline(always)]
fn add_reg64_reg64_reg64<'a>( fn add_reg64_reg64_reg64<'a>(
buf: &mut Vec<'a, u8>, buf: &mut Vec<'a, u8>,
dst: X86_64GPReg, dst: X86_64GPReg,
@ -268,10 +296,18 @@ impl Assembler<X86_64GPReg> for X86_64Assembler {
mov_stack32_reg64(buf, offset, src); mov_stack32_reg64(buf, offset, src);
} }
#[inline(always)] #[inline(always)]
fn abs_reg64_reg64<'a>(buf: &mut Vec<'a, u8>, dst: X86_64GPReg, src: X86_64GPReg) { fn sub_reg64_reg64_imm32<'a>(
mov_reg64_reg64(buf, dst, src); buf: &mut Vec<'a, u8>,
neg_reg64(buf, dst); dst: X86_64GPReg,
cmovl_reg64_reg64(buf, dst, src); src1: X86_64GPReg,
imm32: i32,
) {
if dst == src1 {
sub_reg64_imm32(buf, dst, imm32);
} else {
mov_reg64_reg64(buf, dst, src1);
sub_reg64_imm32(buf, dst, imm32);
}
} }
#[inline(always)] #[inline(always)]
fn ret<'a>(buf: &mut Vec<'a, u8>) { fn ret<'a>(buf: &mut Vec<'a, u8>) {
@ -279,6 +315,17 @@ impl Assembler<X86_64GPReg> for X86_64Assembler {
} }
} }
impl X86_64Assembler {
#[inline(always)]
fn pop_reg64<'a>(buf: &mut Vec<'a, u8>, reg: X86_64GPReg) {
pop_reg64(buf, reg);
}
#[inline(always)]
fn push_reg64<'a>(buf: &mut Vec<'a, u8>, reg: X86_64GPReg) {
push_reg64(buf, reg);
}
}
const REX: u8 = 0x40; const REX: u8 = 0x40;
const REX_W: u8 = REX + 0x8; const REX_W: u8 = REX + 0x8;