use super::{address::Address, regs};
use crate::{masm::OperandSize, reg::Reg};
use cranelift_codegen::{
ir::MemFlags,
isa::aarch64::inst::{
self,
emit::{EmitInfo, EmitState},
ALUOp, ALUOp3, AMode, ExtendOp, Imm12, Inst, PairAMode,
},
settings, Final, MachBuffer, MachBufferFinalized, MachInstEmit, MachInstEmitState, MachLabel,
Writable,
};
#[derive(Debug)]
pub(crate) enum Operand {
Reg(Reg),
Mem(Address),
Imm(i64),
}
impl From<OperandSize> for inst::OperandSize {
fn from(size: OperandSize) -> Self {
match size {
OperandSize::S32 => Self::Size32,
OperandSize::S64 => Self::Size64,
}
}
}
pub(crate) struct Assembler {
buffer: MachBuffer<Inst>,
emit_info: EmitInfo,
emit_state: EmitState,
}
impl Assembler {
pub fn new(shared_flags: settings::Flags) -> Self {
Self {
buffer: MachBuffer::<Inst>::new(),
emit_state: Default::default(),
emit_info: EmitInfo::new(shared_flags),
}
}
}
impl Assembler {
pub fn finalize(mut self) -> MachBufferFinalized<Final> {
let constants = Default::default();
let stencil = self
.buffer
.finish(&constants, self.emit_state.ctrl_plane_mut());
stencil.apply_base_srcloc(Default::default())
}
fn emit(&mut self, inst: Inst) {
inst.emit(&[], &mut self.buffer, &self.emit_info, &mut self.emit_state);
}
pub fn load_constant(&mut self, imm: u64, rd: Reg) {
let writable = Writable::from_reg(rd.into());
Inst::load_constant(writable, imm, &mut |_| writable)
.into_iter()
.for_each(|i| self.emit(i));
}
pub fn stp(&mut self, xt1: Reg, xt2: Reg, addr: Address) {
let mem: PairAMode = addr.try_into().unwrap();
self.emit(Inst::StoreP64 {
rt: xt1.into(),
rt2: xt2.into(),
mem,
flags: MemFlags::trusted(),
});
}
pub fn str(&mut self, reg: Reg, addr: Address, size: OperandSize) {
let mem: AMode = addr.try_into().unwrap();
let flags = MemFlags::trusted();
use OperandSize::*;
let inst = match size {
S64 => Inst::Store64 {
rd: reg.into(),
mem,
flags,
},
S32 => Inst::Store32 {
rd: reg.into(),
mem,
flags,
},
};
self.emit(inst);
}
pub fn ldr(&mut self, addr: Address, rd: Reg, size: OperandSize) {
use OperandSize::*;
let writable_reg = Writable::from_reg(rd.into());
let mem: AMode = addr.try_into().unwrap();
let flags = MemFlags::trusted();
let inst = match size {
S64 => Inst::ULoad64 {
rd: writable_reg,
mem,
flags,
},
S32 => Inst::ULoad32 {
rd: writable_reg,
mem,
flags,
},
};
self.emit(inst);
}
pub fn ldp(&mut self, xt1: Reg, xt2: Reg, addr: Address) {
let writable_xt1 = Writable::from_reg(xt1.into());
let writable_xt2 = Writable::from_reg(xt2.into());
let mem = addr.try_into().unwrap();
self.emit(Inst::LoadP64 {
rt: writable_xt1,
rt2: writable_xt2,
mem,
flags: MemFlags::trusted(),
});
}
pub fn mov(&mut self, src: Operand, dst: Operand, size: OperandSize) {
match &(src, dst) {
(Operand::Imm(imm), Operand::Reg(rd)) => {
let scratch = regs::scratch();
self.load_constant(*imm as u64, scratch);
self.mov_rr(scratch, *rd, size);
}
(Operand::Reg(src), Operand::Reg(rd)) => {
self.mov_rr(*src, *rd, size);
}
(src, dst) => panic!(
"Invalid combination for mov: src = {:?}, dst = {:?}",
src, dst
),
}
}
pub fn mov_rr(&mut self, rm: Reg, rd: Reg, size: OperandSize) {
let writable_rd = Writable::from_reg(rd.into());
self.emit(Inst::Mov {
size: size.into(),
rd: writable_rd,
rm: rm.into(),
});
}
pub fn add(&mut self, opm: Operand, opn: Operand, opd: Operand, size: OperandSize) {
match &(opm, opn, opd) {
(Operand::Imm(imm), Operand::Reg(rn), Operand::Reg(rd)) => {
self.add_ir(*imm as u64, *rn, *rd, size);
}
(Operand::Reg(rm), Operand::Reg(rn), Operand::Reg(rd)) => {
self.emit_alu_rrr_extend(ALUOp::Add, *rm, *rn, *rd, size);
}
(rm, rn, rd) => panic!(
"Invalid combination for add: rm = {:?}, rn = {:?}, rd = {:?}",
rm, rn, rd
),
}
}
pub fn add_ir(&mut self, imm: u64, rn: Reg, rd: Reg, size: OperandSize) {
let alu_op = ALUOp::Add;
if let Some(imm) = Imm12::maybe_from_u64(imm) {
self.emit_alu_rri(alu_op, imm, rn, rd, size);
} else {
let scratch = regs::scratch();
self.load_constant(imm, scratch);
self.emit_alu_rrr_extend(alu_op, scratch, rn, rd, size);
}
}
pub fn sub(&mut self, opm: Operand, opn: Operand, opd: Operand, size: OperandSize) {
match &(opm, opn, opd) {
(Operand::Imm(imm), Operand::Reg(rn), Operand::Reg(rd)) => {
self.sub_ir(*imm as u64, *rn, *rd, size);
}
(Operand::Reg(rm), Operand::Reg(rn), Operand::Reg(rd)) => {
self.emit_alu_rrr_extend(ALUOp::Sub, *rm, *rn, *rd, size);
}
(rm, rn, rd) => panic!(
"Invalid combination for sub: rm = {:?}, rn = {:?}, rd = {:?}",
rm, rn, rd
),
}
}
pub fn sub_ir(&mut self, imm: u64, rn: Reg, rd: Reg, size: OperandSize) {
let alu_op = ALUOp::Sub;
if let Some(imm) = Imm12::maybe_from_u64(imm) {
self.emit_alu_rri(alu_op, imm, rn, rd, size);
} else {
let scratch = regs::scratch();
self.load_constant(imm, scratch);
self.emit_alu_rrr_extend(alu_op, scratch, rn, rd, size);
}
}
pub fn mul(&mut self, opm: Operand, opn: Operand, opd: Operand, size: OperandSize) {
match &(opm, opn, opd) {
(Operand::Imm(imm), Operand::Reg(rn), Operand::Reg(rd)) => {
self.mul_ir(*imm as u64, *rn, *rd, size);
}
(Operand::Reg(rm), Operand::Reg(rn), Operand::Reg(rd)) => {
self.emit_alu_rrrr(ALUOp3::MAdd, *rm, *rn, *rd, regs::zero(), size);
}
(rm, rn, rd) => panic!(
"Invalid combination for sub: rm = {:?}, rn = {:?}, rd = {:?}",
rm, rn, rd
),
}
}
pub fn mul_ir(&mut self, imm: u64, rn: Reg, rd: Reg, size: OperandSize) {
let scratch = regs::scratch();
self.load_constant(imm, scratch);
self.emit_alu_rrrr(ALUOp3::MAdd, scratch, rn, rd, regs::zero(), size);
}
pub fn ret(&mut self) {
self.emit(Inst::Ret {
rets: vec![],
stack_bytes_to_pop: 0,
});
}
fn emit_alu_rri(&mut self, op: ALUOp, imm: Imm12, rn: Reg, rd: Reg, size: OperandSize) {
self.emit(Inst::AluRRImm12 {
alu_op: op,
size: size.into(),
rd: Writable::from_reg(rd.into()),
rn: rn.into(),
imm12: imm,
});
}
fn emit_alu_rrr_extend(&mut self, op: ALUOp, rm: Reg, rn: Reg, rd: Reg, size: OperandSize) {
self.emit(Inst::AluRRRExtend {
alu_op: op,
size: size.into(),
rd: Writable::from_reg(rd.into()),
rn: rn.into(),
rm: rm.into(),
extendop: ExtendOp::UXTX,
});
}
fn emit_alu_rrrr(&mut self, op: ALUOp3, rm: Reg, rn: Reg, rd: Reg, ra: Reg, size: OperandSize) {
self.emit(Inst::AluRRRR {
alu_op: op,
size: size.into(),
rd: Writable::from_reg(rd.into()),
rn: rn.into(),
rm: rm.into(),
ra: ra.into(),
});
}
pub fn get_label(&mut self) -> MachLabel {
self.buffer.get_label()
}
pub fn buffer_mut(&mut self) -> &mut MachBuffer<Inst> {
&mut self.buffer
}
}