#[cfg(test)]
mod tests;
use crate::instructions::Instruction;
use crate::registers::general_purpose::{EReg, Reg, Register};
use ab_riscv_macros::instruction;
use core::fmt;
use core::hint::unreachable_unchecked;
use core::marker::PhantomData;
pub const unsafe trait ZcmpRegister
where
Self: [const] Register,
{
const RVE: bool;
}
unsafe impl const ZcmpRegister for Reg<u32> {
const RVE: bool = false;
}
unsafe impl const ZcmpRegister for Reg<u64> {
const RVE: bool = false;
}
unsafe impl const ZcmpRegister for EReg<u32> {
const RVE: bool = true;
}
unsafe impl const ZcmpRegister for EReg<u64> {
const RVE: bool = true;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
enum ZcmpUrlistInner {
Ra = 4,
RaS0 = 5,
RaS0S1 = 6,
RaS0S2 = 7,
RaS0S3 = 8,
RaS0S4 = 9,
RaS0S5 = 10,
RaS0S6 = 11,
RaS0S7 = 12,
RaS0S8 = 13,
RaS0S9 = 14,
RaS0S11 = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct ZcmpUrlist<Reg> {
inner: ZcmpUrlistInner,
reg: PhantomData<Reg>,
}
impl<Reg> ZcmpUrlist<Reg>
where
Reg: ZcmpRegister,
{
const XLEN_32: u8 = 32;
const XLEN_64: u8 = 64;
#[inline(always)]
pub const fn try_from_raw(raw: u8) -> Option<Self>
where
Reg: [const] ZcmpRegister,
{
if !(Reg::XLEN == Self::XLEN_32 || Reg::XLEN == Self::XLEN_64) {
return None;
}
let inner = if Reg::RVE {
match raw {
4 => ZcmpUrlistInner::Ra,
5 => ZcmpUrlistInner::RaS0,
6 => ZcmpUrlistInner::RaS0S1,
_ => {
return None;
}
}
} else {
match raw {
4 => ZcmpUrlistInner::Ra,
5 => ZcmpUrlistInner::RaS0,
6 => ZcmpUrlistInner::RaS0S1,
7 => ZcmpUrlistInner::RaS0S2,
8 => ZcmpUrlistInner::RaS0S3,
9 => ZcmpUrlistInner::RaS0S4,
10 => ZcmpUrlistInner::RaS0S5,
11 => ZcmpUrlistInner::RaS0S6,
12 => ZcmpUrlistInner::RaS0S7,
13 => ZcmpUrlistInner::RaS0S8,
14 => ZcmpUrlistInner::RaS0S9,
15 => ZcmpUrlistInner::RaS0S11,
_ => {
return None;
}
}
};
Some(Self {
inner,
reg: PhantomData,
})
}
#[inline(always)]
pub const fn as_u8(self) -> u8 {
self.inner as u8
}
#[inline]
pub fn reg_list(self) -> impl Iterator<Item = Reg> {
let regs: &[u8] = match self.inner {
ZcmpUrlistInner::Ra => &[1],
ZcmpUrlistInner::RaS0 => &[1, 8],
ZcmpUrlistInner::RaS0S1 => &[1, 8, 9],
ZcmpUrlistInner::RaS0S2 => &[1, 8, 9, 18],
ZcmpUrlistInner::RaS0S3 => &[1, 8, 9, 18, 19],
ZcmpUrlistInner::RaS0S4 => &[1, 8, 9, 18, 19, 20],
ZcmpUrlistInner::RaS0S5 => &[1, 8, 9, 18, 19, 20, 21],
ZcmpUrlistInner::RaS0S6 => &[1, 8, 9, 18, 19, 20, 21, 22],
ZcmpUrlistInner::RaS0S7 => &[1, 8, 9, 18, 19, 20, 21, 22, 23],
ZcmpUrlistInner::RaS0S8 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24],
ZcmpUrlistInner::RaS0S9 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25],
ZcmpUrlistInner::RaS0S11 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
};
regs.iter().map(|&bits| {
unsafe { Reg::from_bits(bits).unwrap_unchecked() }
})
}
#[inline(always)]
pub const fn stack_adj_base(self) -> u32 {
match Reg::XLEN {
Self::XLEN_32 => match self.inner {
ZcmpUrlistInner::Ra
| ZcmpUrlistInner::RaS0
| ZcmpUrlistInner::RaS0S1
| ZcmpUrlistInner::RaS0S2 => 16,
ZcmpUrlistInner::RaS0S3
| ZcmpUrlistInner::RaS0S4
| ZcmpUrlistInner::RaS0S5
| ZcmpUrlistInner::RaS0S6 => 32,
ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 | ZcmpUrlistInner::RaS0S9 => 48,
ZcmpUrlistInner::RaS0S11 => 64,
},
Self::XLEN_64 => match self.inner {
ZcmpUrlistInner::Ra | ZcmpUrlistInner::RaS0 => 16,
ZcmpUrlistInner::RaS0S1 | ZcmpUrlistInner::RaS0S2 => 32,
ZcmpUrlistInner::RaS0S3 | ZcmpUrlistInner::RaS0S4 => 48,
ZcmpUrlistInner::RaS0S5 | ZcmpUrlistInner::RaS0S6 => 64,
ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 => 80,
ZcmpUrlistInner::RaS0S9 => 96,
ZcmpUrlistInner::RaS0S11 => 112,
},
_ => {
unsafe { unreachable_unchecked() }
}
}
}
}
impl<Reg> fmt::Display for ZcmpUrlist<Reg> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.inner {
ZcmpUrlistInner::Ra => write!(f, "{{ra}}"),
ZcmpUrlistInner::RaS0 => write!(f, "{{ra, s0}}"),
ZcmpUrlistInner::RaS0S1 => write!(f, "{{ra, s0-s1}}"),
ZcmpUrlistInner::RaS0S2 => write!(f, "{{ra, s0-s2}}"),
ZcmpUrlistInner::RaS0S3 => write!(f, "{{ra, s0-s3}}"),
ZcmpUrlistInner::RaS0S4 => write!(f, "{{ra, s0-s4}}"),
ZcmpUrlistInner::RaS0S5 => write!(f, "{{ra, s0-s5}}"),
ZcmpUrlistInner::RaS0S6 => write!(f, "{{ra, s0-s6}}"),
ZcmpUrlistInner::RaS0S7 => write!(f, "{{ra, s0-s7}}"),
ZcmpUrlistInner::RaS0S8 => write!(f, "{{ra, s0-s8}}"),
ZcmpUrlistInner::RaS0S9 => write!(f, "{{ra, s0-s9}}"),
ZcmpUrlistInner::RaS0S11 => write!(f, "{{ra, s0-s11}}"),
}
}
}
#[instruction]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Rv32ZcmpInstruction<Reg> {
CmPush {
urlist: ZcmpUrlist<Reg>,
stack_adj: u32,
},
CmPop {
urlist: ZcmpUrlist<Reg>,
stack_adj: u32,
},
CmPopretz {
urlist: ZcmpUrlist<Reg>,
stack_adj: u32,
},
CmPopret {
urlist: ZcmpUrlist<Reg>,
stack_adj: u32,
},
CmMva01s { r1s: Reg, r2s: Reg },
CmMvsa01 { r1s: Reg, r2s: Reg },
}
#[instruction]
impl<Reg> const Instruction for Rv32ZcmpInstruction<Reg>
where
Reg: [const] ZcmpRegister<Type = u32>,
{
type Reg = Reg;
#[inline(always)]
fn try_decode(instruction: u32) -> Option<Self> {
#[inline(always)]
const fn sreg_bits(field: u8) -> u8 {
match field {
0 => 8,
1 => 9,
f => f + 16,
}
}
let inst = instruction as u16;
let quadrant = inst & 0b11;
let funct3 = ((inst >> 13) & 0b111) as u8;
if quadrant != 0b10 || funct3 != 0b101 {
None?;
}
let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
match funct2_12_11 {
0b11 => {
let op_sel = ((inst >> 9) & 0b11) as u8;
let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
let spimm = ((inst >> 2) & 0b11) as u32;
let stack_adj = urlist.stack_adj_base() + spimm * 16;
match op_sel {
0b00 => Some(Self::CmPush { urlist, stack_adj }),
0b01 => Some(Self::CmPop { urlist, stack_adj }),
0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
0b11 => Some(Self::CmPopret { urlist, stack_adj }),
_ => None,
}
}
0b01 => {
if (inst >> 10) & 1 != 1 {
None?;
}
let r1s_bits = ((inst >> 7) & 0b111) as u8;
let funct2 = ((inst >> 5) & 0b11) as u8;
let r2s_bits = ((inst >> 2) & 0b111) as u8;
let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
match funct2 {
0b11 => Some(Self::CmMva01s { r1s, r2s }),
0b01 => {
if r1s_bits == r2s_bits {
None?;
}
Some(Self::CmMvsa01 { r1s, r2s })
}
_ => None,
}
}
_ => None,
}
}
#[inline(always)]
fn alignment() -> u8 {
align_of::<u16>() as u8
}
#[inline(always)]
fn size(&self) -> u8 {
size_of::<u16>() as u8
}
}
impl<Reg> fmt::Display for Rv32ZcmpInstruction<Reg>
where
Reg: Register,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::CmPush { urlist, stack_adj } => {
write!(f, "cm.push {urlist}, -{stack_adj}")
}
Self::CmPop { urlist, stack_adj } => {
write!(f, "cm.pop {urlist}, {stack_adj}")
}
Self::CmPopretz { urlist, stack_adj } => {
write!(f, "cm.popretz {urlist}, {stack_adj}")
}
Self::CmPopret { urlist, stack_adj } => {
write!(f, "cm.popret {urlist}, {stack_adj}")
}
Self::CmMva01s { r1s, r2s } => write!(f, "cm.mva01s {r1s}, {r2s}"),
Self::CmMvsa01 { r1s, r2s } => write!(f, "cm.mvsa01 {r1s}, {r2s}"),
}
}
}