use crate::CompilerError;
use crate::compiler::{Architecture, Executable, util};
use crate::program::{Instruction, InstructionArray, NUM_INSTRUCTIONS};
use crate::register::{RegisterFile, RegisterId};
use dynasmrt::{DynasmApi, DynasmLabelApi, aarch64};
use std::mem;
impl Architecture for Executable {
fn compile(program: &InstructionArray) -> Result<Self, CompilerError> {
let mut asm = Assembler::new();
{
emit_load_input(&mut asm);
emit_init_locals(&mut asm);
debug_assert_eq!(asm.len(), PROLOGUE_SIZE);
}
for inst in program {
let prev_len = asm.len();
emit_instruction(&mut asm, inst);
debug_assert!(asm.len() - prev_len <= INSTRUCTION_SIZE_LIMIT);
}
{
let prev_len = asm.len();
emit_store_output(&mut asm);
emit_return(&mut asm);
debug_assert_eq!(asm.len() - prev_len, EPILOGUE_SIZE);
}
asm.finalize()
}
fn invoke(&self, regs: &mut RegisterFile) {
let entry = self.buffer.ptr(Assembler::entry());
let entry: extern "system" fn(*mut RegisterFile) -> () = unsafe { mem::transmute(entry) };
entry(regs);
}
}
const PROLOGUE_SIZE: usize = 0x28;
const EPILOGUE_SIZE: usize = 0x24;
const INSTRUCTION_SIZE_LIMIT: usize = 0x18;
const BUFFER_CAPACITY: usize =
PROLOGUE_SIZE + EPILOGUE_SIZE + NUM_INSTRUCTIONS * INSTRUCTION_SIZE_LIMIT;
type Assembler = util::Assembler<aarch64::Aarch64Relocation, BUFFER_CAPACITY>;
trait RegisterMapper {
fn x(&self) -> u8;
fn offset(&self) -> u32;
}
impl RegisterMapper for RegisterId {
#[inline(always)]
fn x(&self) -> u8 {
1 + self.as_u8()
}
#[inline(always)]
fn offset(&self) -> u32 {
(self.as_usize() * mem::size_of::<u64>()) as u32
}
}
macro_rules! dynasm {
($asm:ident $($t:tt)*) => {
#[allow(
clippy::as_conversions,
clippy::cast_lossless,
clippy::useless_conversion
)]
{
dynasmrt::dynasm!($asm
; .arch aarch64
; .alias register_file_ptr, x0
; .alias mulh_result32, w9
; .alias branch_prohibit_flag, w10
; .alias const_temp_64, x11
; .alias const_temp_32, w11
$($t)*
)
}
}
}
#[inline(always)]
fn emit_init_locals<A: DynasmApi>(asm: &mut A) {
dynasm!(asm
; mov mulh_result32, wzr
; mov branch_prohibit_flag, wzr
);
}
#[inline(always)]
fn emit_load_input<A: DynasmApi>(asm: &mut A) {
for reg in RegisterId::all() {
dynasm!(asm; ldr X(reg.x()), [register_file_ptr, #reg.offset()]);
}
}
#[inline(always)]
fn emit_store_output<A: DynasmApi>(asm: &mut A) {
for reg in RegisterId::all() {
dynasm!(asm; str X(reg.x()), [register_file_ptr, #reg.offset()]);
}
}
#[inline(always)]
fn emit_return<A: DynasmApi>(asm: &mut A) {
dynasm!(asm; ret);
}
#[inline(always)]
fn emit_i32_const_temp_64<A: DynasmApi>(asm: &mut A, value: i32) {
let high = (value >> 16) as u32;
let low = (value & 0xFFFF) as u32;
if value < 0 {
dynasm!(asm; movn const_temp_64, #!high, lsl #16);
} else {
dynasm!(asm; movz const_temp_64, #high, lsl #16);
}
dynasm!(asm; movk const_temp_64, #low, lsl #0);
}
#[inline(always)]
fn emit_u32_const_temp_32<A: DynasmApi>(asm: &mut A, value: u32) {
let high = value >> 16;
let low = value & 0xFFFF;
dynasm!(asm
; movz const_temp_32, #high, lsl #16
; movk const_temp_32, #low, lsl #0
);
}
#[inline(always)]
fn emit_instruction(asm: &mut Assembler, inst: &Instruction) {
macro_rules! reg_op {
($op:tt, $dst:ident, $src:ident) => {
dynasm!(asm
; $op X($dst.x()), X($dst.x()), X($src.x())
)
}
}
macro_rules! const_i32_op {
($op:tt, $dst:ident, $src:expr) => {
emit_i32_const_temp_64(asm, *$src);
dynasm!(asm
; $op X($dst.x()), X($dst.x()), const_temp_64
)
}
}
macro_rules! mulh_op {
($op:tt, $dst:ident, $src:ident) => {
reg_op!($op, $dst, $src);
dynasm!(asm
; mov mulh_result32, W($dst.x())
)
}
}
match inst {
Instruction::Target => {
dynasm!(asm; target: );
}
Instruction::Branch { mask } => {
emit_u32_const_temp_32(asm, *mask);
dynasm!(asm
; orr mulh_result32, mulh_result32, branch_prohibit_flag
; tst mulh_result32, const_temp_32
; csinv branch_prohibit_flag, branch_prohibit_flag, wzr, ne
; b.eq <target
);
}
Instruction::Rotate { dst, right_rotate } => {
let right_rotate: u32 = (*right_rotate).into();
dynasm!(asm
; ror X(dst.x()), X(dst.x()), #right_rotate
);
}
Instruction::AddShift {
dst,
src,
left_shift,
} => {
let left_shift: u32 = (*left_shift).into();
dynasm!(asm
; add X(dst.x()), X(dst.x()), X(src.x()), lsl #left_shift
);
}
Instruction::UMulH { dst, src } => {
mulh_op!(umulh, dst, src);
}
Instruction::SMulH { dst, src } => {
mulh_op!(smulh, dst, src);
}
Instruction::Mul { dst, src } => {
reg_op!(mul, dst, src);
}
Instruction::Xor { dst, src } => {
reg_op!(eor, dst, src);
}
Instruction::Sub { dst, src } => {
reg_op!(sub, dst, src);
}
Instruction::AddConst { dst, src } => {
const_i32_op!(add, dst, src);
}
Instruction::XorConst { dst, src } => {
const_i32_op!(eor, dst, src);
}
}
}