use anyhow::{Context, Result, anyhow};
use cranelift_codegen::VCodeConstant;
use cranelift_codegen::{
ir::types,
isa::aarch64::inst::{AMode, PairAMode, SImm7Scaled, SImm9},
};
use super::regs;
use crate::reg::Reg;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum Indexing {
Pre,
Post,
}
#[derive(Debug, Copy, Clone)]
pub(crate) enum Address {
Offset {
base: Reg,
offset: i64,
},
IndexedSPOffset {
offset: i64,
indexing: Indexing,
},
Const(VCodeConstant),
}
impl Address {
pub fn pre_indexed_from_sp(offset: i64) -> Self {
Self::IndexedSPOffset {
offset,
indexing: Indexing::Pre,
}
}
pub fn post_indexed_from_sp(offset: i64) -> Self {
Self::IndexedSPOffset {
offset,
indexing: Indexing::Post,
}
}
pub fn from_shadow_sp(offset: i64) -> Self {
Self::Offset {
base: regs::shadow_sp(),
offset,
}
}
pub fn offset(base: Reg, offset: i64) -> Self {
assert!(
base != regs::sp(),
"stack pointer not allowed in arbitrary offset addressing mode"
);
Self::Offset { base, offset }
}
pub fn constant(data: VCodeConstant) -> Self {
Self::Const(data)
}
pub fn unwrap_offset(&self) -> (Reg, i64) {
match self {
Self::Offset { base, offset } => (*base, *offset),
_ => panic!("Expected register and offset addressing mode"),
}
}
}
impl TryFrom<Address> for PairAMode {
type Error = anyhow::Error;
fn try_from(addr: Address) -> Result<Self> {
use Address::*;
use Indexing::*;
match addr {
IndexedSPOffset { offset, indexing } => {
let simm7 = SImm7Scaled::maybe_from_i64(offset, types::I64).with_context(|| {
format!("Failed to convert {offset} to signed scaled 7 bit offset")
})?;
if indexing == Pre {
Ok(PairAMode::SPPreIndexed { simm7 })
} else {
Ok(PairAMode::SPPostIndexed { simm7 })
}
}
other => Err(anyhow!(
"Could not convert {:?} to addressing mode for register pairs",
other
)),
}
}
}
impl TryFrom<Address> for AMode {
type Error = anyhow::Error;
fn try_from(addr: Address) -> Result<Self> {
use Address::*;
use Indexing::*;
match addr {
IndexedSPOffset { offset, indexing } => {
let simm9 = SImm9::maybe_from_i64(offset).ok_or_else(|| {
anyhow!("Failed to convert {} to signed 9-bit offset", offset)
})?;
if indexing == Pre {
Ok(AMode::SPPreIndexed { simm9 })
} else {
Ok(AMode::SPPostIndexed { simm9 })
}
}
Offset { base, offset } => Ok(AMode::RegOffset {
rn: base.into(),
off: offset,
}),
Const(data) => Ok(AMode::Const { addr: data }),
}
}
}