WARNING! Access to this system is limited to authorised users only.
Unauthorised users may be subject to prosecution.
Unauthorised access to this system is a criminal offence under Australian law (Federal Crimes Act 1914 Part VIA)
It is a criminal offence to:
(1) Obtain access to data without authority. -Penalty 2 years imprisonment.
(2) Damage, delete, alter or insert data without authority. -Penalty 10 years imprisonment.
User activity is monitored and recorded. Anyone using this system expressly consents to such monitoring and recording.

To protect your data, the CISO officer has suggested users to enable 2FA as soon as possible.
Currently 2.6% of users enabled 2FA.

Commit 197382c4 authored by qinsoon's avatar qinsoon
Browse files

Merge branch 'master' into develop

parents 6d2e26dd bc43c9ea
......@@ -13,3 +13,7 @@ Cargo.lock
*.dylib
**mu-client-pypy**
**RPySOM**
.gitignore
cmake-build-debug/*
Notes/*
CMakeLists.txt
\ No newline at end of file
......@@ -8,4 +8,25 @@ fn main() {
gcc::Config::new().flag("-O3").flag("-c")
.file("src/runtime/swap_stack_x64_sysv.S")
.compile("libswap_stack.a");
}
#[cfg(target_os = "linux")]
#[cfg(target_arch = "aarch64")]
fn main() {
gcc::compile_library("libruntime.a", &["src/runtime/runtime_aarch64_sysv.c"]);
gcc::Config::new().flag("-O3").flag("-c")
.file("src/runtime/swap_stack_aarch64_sysv.S")
.compile("libswap_stack.a");
}
// This is here to enable cross compiling from windows/x86_64 to linux/aarch64
#[cfg(target_os = "windows")]
#[cfg(target_arch = "x86_64")]
fn main() {
gcc::compile_library("libruntime.a", &["src/runtime/runtime_aarch64_sysv.c"]);
gcc::Config::new().flag("-O3").flag("-c")
.file("src/runtime/swap_stack_aarch64_sysv.S")
.compile("libswap_stack.a");
}
\ No newline at end of file
......@@ -576,8 +576,8 @@ pub struct Destination {
impl Destination {
fn debug_str(&self, ops: &Vec<P<TreeNode>>) -> String {
let mut ret = format!("{} with ", self.target);
ret.push('[');
let mut ret = format!("{}", self.target);
ret.push('(');
for i in 0..self.args.len() {
let ref arg = self.args[i];
ret.push_str(arg.debug_str(ops).as_str());
......@@ -585,7 +585,7 @@ impl Destination {
ret.push_str(", ");
}
}
ret.push(']');
ret.push(')');
ret
}
......
......@@ -667,7 +667,7 @@ impl fmt::Display for TreeNode {
match self.v {
TreeNode_::Value(ref pv) => pv.fmt(f),
TreeNode_::Instruction(ref inst) => {
write!(f, "+({})", inst)
write!(f, "({})", inst)
}
}
}
......@@ -756,7 +756,8 @@ impl Value {
}
}
const DISPLAY_TYPE : bool = true;
const DISPLAY_ID : bool = true;
const DISPLAY_TYPE : bool = false;
const PRINT_ABBREVIATE_NAME: bool = true;
impl fmt::Debug for Value {
......@@ -770,16 +771,16 @@ impl fmt::Display for Value {
if DISPLAY_TYPE {
match self.v {
Value_::SSAVar(_) => {
write!(f, "+({} %{})", self.ty, self.hdr)
write!(f, "{}(%{})", self.ty, self.hdr)
},
Value_::Constant(ref c) => {
write!(f, "+({} {} @{})", self.ty, c, self.hdr)
write!(f, "{}({})", self.ty, c)
},
Value_::Global(ref ty) => {
write!(f, "+(GLOBAL {} @{})", ty, self.hdr)
write!(f, "{}(@{})", ty, self.hdr)
},
Value_::Memory(ref mem) => {
write!(f, "+(MEM {} %{})", mem, self.hdr)
write!(f, "%{}{})", self.hdr, mem)
}
}
} else {
......@@ -791,10 +792,10 @@ impl fmt::Display for Value {
write!(f, "{}", c)
},
Value_::Global(_) => {
write!(f, "GLOBAL @{}", self.hdr)
write!(f, "@{}", self.hdr)
},
Value_::Memory(ref mem) => {
write!(f, "MEM {} %{}", mem, self.hdr)
write!(f, "%{}{}", self.hdr, mem)
}
}
}
......@@ -968,6 +969,7 @@ impl fmt::Display for Constant {
}
}
#[cfg(target_arch = "x86_64")]
#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum MemoryLocation {
Address{
......@@ -983,6 +985,7 @@ pub enum MemoryLocation {
}
}
#[cfg(target_arch = "x86_64")]
impl fmt::Display for MemoryLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
......@@ -1010,6 +1013,67 @@ impl fmt::Display for MemoryLocation {
}
}
#[cfg(target_arch = "aarch64")]
#[derive(Debug, Clone, PartialEq, RustcEncodable, RustcDecodable)]
pub enum MemoryLocation {
// Represents how an adress should be computed,
// will need to be converted to a real Address before being used
VirtualAddress{
// Represents base + offset*scale
// With offset being inerpreted as signed if 'signed' is true
base: P<Value>,
offset: Option<P<Value>>,
scale: u64,
signed: bool
},
Address{
base: P<Value>, // Must be a normal 64-bit register or SP
offset: Option<P<Value>>, // Can be any GPR or a 12-bit unsigned immediate << n
shift: u8, // valid values are 0, log2(n)
signed: bool, // Whether offset is signed or not (only set this if offset is a register)
// Note: n is the number of bytes the adress refers two
},
Symbolic{
label: MuName,
is_global: bool
}
}
#[cfg(target_arch = "aarch64")]
impl fmt::Display for MemoryLocation {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
&MemoryLocation::VirtualAddress{ref base, ref offset, scale, signed} => {
write!(f, "[{}", base).unwrap();
if offset.is_some() {
let sign_type = if signed { "SInt"} else { "UInt" };
write!(f, " + {}({})", sign_type, offset.as_ref().unwrap()).unwrap();
}
write!(f, " * {}", scale).unwrap();
write!(f, "]")
}
&MemoryLocation::Address{ref base, ref offset, shift, signed} => {
write!(f, "[{}", base).unwrap();
if offset.is_some() {
let sign_type = if signed { "SInt"} else { "UInt" };
write!(f, " + {}({})", sign_type, offset.as_ref().unwrap()).unwrap();
}
if shift != 0 {
write!(f, " LSL {}", shift).unwrap();
}
write!(f, "]")
}
&MemoryLocation::Symbolic{ref label, ..} => {
write!(f, "{}", label)
}
}
}
}
#[repr(C)]
#[derive(Debug)] // Display, PartialEq, Clone
pub struct MuEntityHeader {
......@@ -1121,13 +1185,25 @@ impl PartialEq for MuEntityHeader {
impl fmt::Display for MuEntityHeader {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
if self.name().is_none() {
write!(f, "UNNAMED #{}", self.id)
if DISPLAY_ID {
if self.name().is_none() {
write!(f, "{}", self.id)
} else {
if PRINT_ABBREVIATE_NAME {
write!(f, "{} #{}", self.abbreviate_name().unwrap(), self.id)
} else {
write!(f, "{} #{}", self.name().unwrap(), self.id)
}
}
} else {
if PRINT_ABBREVIATE_NAME {
write!(f, "{} #{}", self.abbreviate_name().unwrap(), self.id)
if self.name().is_none() {
write!(f, "{}", self.id)
} else {
write!(f, "{} #{}", self.name().unwrap(), self.id)
if PRINT_ABBREVIATE_NAME {
write!(f, "{}", self.abbreviate_name().unwrap())
} else {
write!(f, "{}", self.name().unwrap())
}
}
}
}
......
......@@ -186,21 +186,81 @@ pub enum CmpOp {
}
impl CmpOp {
// Returns the CmpOp c, such that (a self b) is equivelent to (b c a)
pub fn swap_operands(self) -> CmpOp {
use op::CmpOp::*;
match self {
EQ => EQ,
NE => NE,
SGE => SLE,
SLE => SGE,
SGT => SLT,
SLT => SGT,
UGE => ULE,
ULE => UGE,
UGT => ULT,
ULT => UGT,
FOGE => FOLE,
FOLE => FOGE,
FOGT => FOLT,
FOLT => FOGT,
FUGE => FULE,
FULE => FUGE,
FUGT => FULT,
FULT => FUGT,
_ => self, // all other comparisons are reflexive
}
}
pub fn invert(self) -> CmpOp {
use op::CmpOp::*;
match self {
EQ => NE,
NE => EQ,
FOEQ => FUNE,
FUNE => FOEQ,
FUGE => FOLT,
FOLT => FUGE,
FUNO => FORD,
FORD => FUNO,
UGT => ULE,
ULE => UGT,
FUGT => FOLE,
FOLE => FUGT,
SGE => SLT,
SLT => SGE,
FOGE => FULT,
FULT => FOGE,
SGT => SLE,
SLE => SGT,
SLT => SGE,
FOGT => FULE,
FULE => FOGT,
UGE => ULT,
UGT => ULE,
ULE => UGT,
ULT => UGE,
_ => unimplemented!()
FUEQ => FONE,
FONE => FUEQ,
FFALSE => FTRUE,
FTRUE => FFALSE,
}
}
pub fn is_signed(self) -> bool {
use op::CmpOp::*;
match self {
SGE | SLT | SGT | SLE => true,
_ => false
}
}
}
......
......@@ -32,15 +32,19 @@ lazy_static! {
pub static ref UINT64_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::int(64))
);
pub static ref DOUBLE_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::double())
pub static ref UINT128_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::int(128))
);
pub static ref FLOAT_TYPE : P<MuType> = P(
pub static ref FLOAT_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::float())
);
pub static ref DOUBLE_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::double())
);
pub static ref VOID_TYPE : P<MuType> = P(
MuType::new(new_internal_id(), MuType_::void())
);
......@@ -52,6 +56,8 @@ lazy_static! {
UINT16_TYPE.clone(),
UINT32_TYPE.clone(),
UINT64_TYPE.clone(),
UINT128_TYPE.clone(),
FLOAT_TYPE.clone(),
DOUBLE_TYPE.clone(),
FLOAT_TYPE.clone(),
VOID_TYPE.clone()
......
This diff is collapsed.
use ast::ptr::P;
use ast::ir::*;
use runtime::ValueLocation;
use compiler::machine_code::MachineCode;
use compiler::backend::{Reg, Mem};
pub trait CodeGenerator {
fn start_code(&mut self, func_name: MuName, entry: MuName) -> ValueLocation;
fn finish_code(&mut self, func_name: MuName) -> (Box<MachineCode + Sync + Send>, ValueLocation);
// generate unnamed sequence of linear code (no branch)
fn start_code_sequence(&mut self);
fn finish_code_sequence(&mut self) -> Box<MachineCode + Sync + Send>;
fn print_cur_code(&self);
fn start_block(&mut self, block_name: MuName);
fn block_exists(&self, block_name: MuName) -> bool;
fn start_exception_block(&mut self, block_name: MuName) -> ValueLocation;
fn set_block_livein(&mut self, block_name: MuName, live_in: &Vec<P<Value>>);
fn set_block_liveout(&mut self, block_name: MuName, live_out: &Vec<P<Value>>);
fn end_block(&mut self, block_name: MuName);
// add CFI info
fn add_cfi_startproc(&mut self);
fn add_cfi_endproc(&mut self);
fn add_cfi_def_cfa_register(&mut self, reg: Reg);
fn add_cfi_def_cfa_offset(&mut self, offset: i32);
fn add_cfi_offset(&mut self, reg: Reg, offset: i32);
//==================================================================================================
// emit code to adjust frame
fn emit_frame_grow(&mut self); // Emits a SUB
fn emit_frame_shrink(&mut self); // Emits an ADD
// stack minimpulation
fn emit_push_pair(&mut self, src1: Reg, src2: Reg, stack: Reg); // Emits a STP
fn emit_pop_pair(&mut self, dest1: Reg, dest2: Reg, stack: Reg); // Emits a LDP
// For callee saved loads and stores (flags them so that only they are removed)
fn emit_ldr_callee_saved(&mut self, dest: Reg, src: Mem);
fn emit_str_callee_saved(&mut self, dest: Mem, src: Reg);
/* Bellow ar all ARMv8-A Aarch64 instruction menmonics (with all operand modes) except:
PRFM, PRFUM, CRC32*
All advanced SIMD instructions (except MOVI)
NOTE:
with loads and stores the menmonic indicated may be given a suffix indicating the size and signenedness of the access
also b_cond's menmononic is 'B.cond' (where cond is the value of the 'cond' parameter)
all other instructions have the menmonic being the first word of the function name after emit_
(subsequent words are used to disambiguate different overloads)
NOTE unless otherwise indicated:
An instruction that dosn't start with an F operates on GPRS, those that start with an F operate on FPRs.
All instructions operate on 32-bit and 64-bit registers (but all register arguments must be the same size)
Also all arguments that may take the SP can't take the ZR (and vice versa)
*/
// loads
fn emit_ldr(&mut self, dest: Reg/*GPR or FPR*/, src: Mem, signed: bool); // supports the full full range of addressing modes
fn emit_ldtr(&mut self, dest: Reg, src: Mem, signed: bool); // [base, #simm9]
fn emit_ldur(&mut self, dest: Reg/*GPR or FPR*/, src: Mem, signed: bool); // [base, #simm9]
fn emit_ldxr(&mut self, dest: Reg, src: Mem);// [base]
fn emit_ldaxr(&mut self, dest: Reg, src: Mem);// [base]
fn emit_ldar(&mut self, dest: Reg, src: Mem);// [base]
fn emit_ldp(&mut self, dest1: Reg, dest2: Reg/*GPR or FPR*/, src: Mem); // [base, #simm7], [base], #simm7, [base, #simm7]!
fn emit_ldxp(&mut self, dest1: Reg, dest2: Reg, src: Mem); // [base]
fn emit_ldaxp(&mut self, dest1: Reg, dest2: Reg, src: Mem); // [base]
fn emit_ldnp(&mut self, dest1: Reg/*GPR or FPR*/, dest2: Reg/*GPR or FPR*/, src: Mem); // [base, #simm7]
// Stores
fn emit_str(&mut self, dest: Mem, src: Reg/*GPR or FPR*/); // supports the full full range of addressing modes
fn emit_sttr(&mut self, dest: Mem, src: Reg); // [base, #simm9]
fn emit_stur(&mut self, dest: Mem, src: Reg/*GPR or FPR*/); // [base, #simm9]
fn emit_stlr(&mut self, dest: Mem, src: Reg); // [base]
fn emit_stxr(&mut self, dest: Mem, status: Reg, src: Reg); // [base]
fn emit_stlxr(&mut self, dest: Mem, status: Reg, src: Reg); // [base]
fn emit_stp(&mut self, dest: Mem, src1: Reg, src2: Reg); // [base, #simm7], [base], #simm7, [base, #simm7]!
fn emit_stxp(&mut self, dest: Mem, status: Reg, src1: Reg, src2: Reg); // [base]
fn emit_stlxp(&mut self, dest: Mem, status: Reg, src1: Reg, src2: Reg); // [base]
fn emit_stnp(&mut self, dest: Mem, src1: Reg/*GPR or FPR*/, src2: Reg/*GPR or FPR*/); // [base, #simm7]
// branching
// calls
fn emit_bl(&mut self, callsite: String, func: MuName, pe: Option<MuName>) -> ValueLocation;
fn emit_blr(&mut self, callsite: String, func: Reg, pe: Option<MuName>) -> ValueLocation;
// Branches
fn emit_b(&mut self, dest_name: MuName);
fn emit_b_cond(&mut self, cond: &str, dest_name: MuName);
fn emit_br(&mut self, dest_address: Reg);
fn emit_ret(&mut self, src: Reg);
fn emit_cbnz(&mut self, src: Reg, dest_name: MuName);
fn emit_cbz(&mut self, src: Reg, dest_name: MuName);
fn emit_tbnz(&mut self, src1: Reg, src2: u8, dest_name: MuName);
fn emit_tbz(&mut self, src1: Reg, src2: u8, dest_name: MuName);
// Read and write flags
fn emit_msr(&mut self, dest: &str, src: Reg);
fn emit_mrs(&mut self, dest: Reg, src: &str);
// Address calculation
fn emit_adr(&mut self, dest: Reg, src: Reg);
fn emit_adrp(&mut self, dest: Reg, src: Reg);
// Unary ops
fn emit_mov(&mut self, dest: Reg, src: Reg);
fn emit_mvn(&mut self, dest: Reg, src: Reg);
fn emit_neg(&mut self, dest: Reg, src: Reg);
fn emit_negs(&mut self, dest: Reg, src: Reg);
fn emit_ngc(&mut self, dest: Reg, src: Reg);
fn emit_ngcs(&mut self, dest: Reg, src: Reg);
fn emit_sxtb(&mut self, dest: Reg/*32*/, src: Reg/*32*/);
fn emit_sxth(&mut self, dest: Reg/*32*/, src: Reg/*32*/);
fn emit_sxtw(&mut self, dest: Reg/*64*/, src: Reg/*32*/);
fn emit_uxtb(&mut self, dest: Reg/*32*/, src: Reg/*32*/);
fn emit_uxth(&mut self, dest: Reg/*32*/, src: Reg/*32*/);
fn emit_cls(&mut self, dest: Reg, src: Reg);
fn emit_clz(&mut self, dest: Reg, src: Reg);
fn emit_rbit(&mut self, dest: Reg, src: Reg);
fn emit_rev(&mut self, dest: Reg, src: Reg);
fn emit_rev16(&mut self, dest: Reg, src: Reg);
fn emit_rev32(&mut self, dest: Reg/*64*/, src: Reg);
fn emit_rev64(&mut self, dest: Reg/*64*/, src: Reg); // alias of REV
fn emit_fabs(&mut self, dest: Reg, src: Reg/*Must have different size*/);
fn emit_fcvt(&mut self, dest: Reg, src: Reg/*Must have different size*/);
fn emit_fcvtas(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtau(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtms(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtmu(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtns(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtnu(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtps(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtpu(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtzs(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fcvtzu(&mut self, dest: Reg/*GPR, may have different size*/, src: Reg);
fn emit_fmov(&mut self, dest: Reg, src: Reg); // One register must be an FPR, the other may be a GPR or an FPR
fn emit_fneg(&mut self, dest: Reg, src: Reg);
fn emit_frinta(&mut self, dest: Reg, src: Reg);
fn emit_frinti(&mut self, dest: Reg, src: Reg);
fn emit_frintm(&mut self, dest: Reg, src: Reg);
fn emit_frintn(&mut self, dest: Reg, src: Reg);
fn emit_frintp(&mut self, dest: Reg, src: Reg);
fn emit_frintx(&mut self, dest: Reg, src: Reg);
fn emit_frintz(&mut self, dest: Reg, src: Reg);
fn emit_fsqrt(&mut self, dest: Reg, src: Reg);
fn emit_scvtf(&mut self, dest: Reg/*FPR, may have different size*/, src: Reg);
fn emit_ucvtf(&mut self, dest: Reg/*FPR, may have different size*/, src: Reg);
// Unary operations with shift
fn emit_mov_shift(&mut self, dest: Reg, src: Reg, shift: &str, ammount: u8);
fn emit_mvn_shift(&mut self, dest: Reg, src: Reg, shift: &str, ammount: u8);
fn emit_neg_shift(&mut self, dest: Reg, src: Reg, shift: &str, ammount: u8);
fn emit_negs_shift(&mut self, dest: Reg, src: Reg, shift: &str, ammount: u8);
// Unary operations with immediates
fn emit_mov_imm(&mut self, dest: Reg, src: u64);
fn emit_movz(&mut self, dest: Reg, src: u16, shift: u8);
fn emit_movk(&mut self, dest: Reg, src: u16, shift: u8);
fn emit_movn(&mut self, dest: Reg, src: u16, shift: u8);
fn emit_movi(&mut self, dest: Reg /*FPR*/, src: u64);
fn emit_fmov_imm(&mut self, dest: Reg, src: f32);
// Extended binary ops
fn emit_add_ext(&mut self, dest: Reg/*GPR or SP*/, src1: Reg/*GPR or SP*/, src2: Reg, signed: bool, shift: u8);
fn emit_adds_ext(&mut self, dest: Reg, src1: Reg/*GPR or SP*/, src2: Reg, signed: bool, shift: u8);
fn emit_sub_ext(&mut self, dest: Reg/*GPR or SP*/, src1: Reg/*GPR or SP*/, src2: Reg, signed: bool, shift: u8);
fn emit_subs_ext(&mut self, dest: Reg, src1: Reg/*GPR or SP*/, src2: Reg, signed: bool, shift: u8);
// Multiplication
fn emit_mul(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_mneg(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_smulh(&mut self, dest: Reg/*64*/, src1: Reg/*64*/, src2: Reg/*64*/);
fn emit_umulh(&mut self, dest: Reg/*64*/, src1: Reg/*64*/, src2: Reg/*64*/);
fn emit_smnegl(&mut self, dest: Reg/*64*/, src1: Reg/*32*/, src2: Reg/*32*/);
fn emit_smull(&mut self, dest: Reg/*64*/, src1: Reg/*32*/, src2: Reg/*32*/);
fn emit_umnegl(&mut self, dest: Reg/*64*/, src1: Reg/*32*/, src2: Reg/*32*/);
fn emit_umull(&mut self, dest: Reg/*64*/, src1: Reg/*32*/, src2: Reg/*32*/);
// Other binaries
fn emit_adc(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_adcs(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_add(&mut self, dest: Reg, src1: Reg/*GPR or SP*/, src2: Reg);
fn emit_adds(&mut self, dest: Reg, src1: Reg/*GPR or SP*/, src2: Reg);
fn emit_sbc(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_sbcs(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_sub(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_subs(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_sdiv(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_udiv(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_asr(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_asrv(&mut self, dest: Reg, src1: Reg, src2: Reg); // Alias of ASR
fn emit_lsl(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_lslv(&mut self, dest: Reg, src1: Reg, src2: Reg); // Alias of LSL
fn emit_lsr(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_lsrv(&mut self, dest: Reg, src1: Reg, src2: Reg); // Alias of LSR
fn emit_ror(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_bic(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_bics(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_and(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_ands(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_eon(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_eor(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_orn(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_orr(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fadd(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fdiv(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fmax(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fmaxnm(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fmin(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fminm(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fmul(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fnmul(&mut self, dest: Reg, src1: Reg, src2: Reg);
fn emit_fsub(&mut self, dest: Reg, src1: Reg, src2: Reg);
// Binary operations with shift
fn emit_add_shift(&mut self, dest: Reg, src1: Reg, src2: Reg, shift: &str, amount: u8);
fn emit_adds_shift(&mut self, dest: Reg, src1: Reg, src2: Reg, shift: &str, amount: u8);
fn emit_sub_shift(&mut self, dest: Reg, src1: Reg, src2: Reg, shift: &str, amount: u8);
fn emit_subs_shift(&mut self, dest: Reg, src1: Reg, src2: Reg, shift: &str, amount: u8);
fn emit_bic_shift(&mut self, dest: Reg, src1: Reg, src2: Reg, shift: &str, amount: u8);