Skip to main content

ab_riscv_primitives/instructions/rv64/zce/
zcmp.rs

1//! RV64 Zcmp extension
2
3#[cfg(test)]
4mod tests;
5
6use crate::instructions::Instruction;
7use crate::instructions::rv32::zce::zcmp::{ZcmpRegister, ZcmpUrlist};
8use crate::registers::general_purpose::Register;
9use ab_riscv_macros::instruction;
10use core::fmt;
11
12/// Zcmp compressed instruction set
13#[instruction]
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum Rv64ZcmpInstruction<Reg> {
16    /// CM.PUSH - push reg_list, decrement sp by `stack_adj`
17    ///
18    /// `stack_adj = urlist.stack_adj_base() + spimm * 16` from the encoding.
19    CmPush {
20        urlist: ZcmpUrlist<Reg>,
21        stack_adj: u32,
22    },
23    /// CM.POP - pop reg_list, increment sp by `stack_adj` (no return)
24    CmPop {
25        urlist: ZcmpUrlist<Reg>,
26        stack_adj: u32,
27    },
28    /// CM.POPRETZ - pop reg_list, set a0=0, increment sp, return
29    CmPopretz {
30        urlist: ZcmpUrlist<Reg>,
31        stack_adj: u32,
32    },
33    /// CM.POPRET - pop reg_list, increment sp, return
34    CmPopret {
35        urlist: ZcmpUrlist<Reg>,
36        stack_adj: u32,
37    },
38    /// CM.MVA01S - a0 = r1s', a1 = r2s'
39    CmMva01s { r1s: Reg, r2s: Reg },
40    /// CM.MVSA01 - r1s' = a0, r2s' = a1  (r1s' != r2s')
41    CmMvsa01 { r1s: Reg, r2s: Reg },
42}
43
44#[instruction]
45impl<Reg> const Instruction for Rv64ZcmpInstruction<Reg>
46where
47    Reg: [const] ZcmpRegister<Type = u64>,
48{
49    type Reg = Reg;
50
51    #[inline(always)]
52    fn try_decode(instruction: u32) -> Option<Self> {
53        /// Map the Zcmp 3-bit "s-register" field to an absolute register number.
54        /// 000->x8(s0), 001->x9(s1), 010->x18(s2)..111->x23(s7)
55        #[inline(always)]
56        const fn sreg_bits(field: u8) -> u8 {
57            match field {
58                0 => 8,
59                1 => 9,
60                f => f + 16,
61            }
62        }
63
64        let inst = instruction as u16;
65        let quadrant = inst & 0b11;
66        let funct3 = ((inst >> 13) & 0b111) as u8;
67
68        // All Zcmp instructions: Q10, funct3=101
69        if quadrant != 0b10 || funct3 != 0b101 {
70            None?;
71        }
72
73        let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
74
75        match funct2_12_11 {
76            // CM.PUSH / CM.POP / CM.POPRETZ / CM.POPRET
77            0b11 => {
78                let op_sel = ((inst >> 9) & 0b11) as u8;
79                let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
80                let spimm = ((inst >> 2) & 0b11) as u32;
81                let stack_adj = urlist.stack_adj_base() + spimm * 16;
82                match op_sel {
83                    0b00 => Some(Self::CmPush { urlist, stack_adj }),
84                    0b01 => Some(Self::CmPop { urlist, stack_adj }),
85                    0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
86                    0b11 => Some(Self::CmPopret { urlist, stack_adj }),
87                    _ => None,
88                }
89            }
90            // CM.MVA01S / CM.MVSA01: require bit 10 = 1 (full funct6 = 101_011)
91            0b01 => {
92                if (inst >> 10) & 1 != 1 {
93                    None?;
94                }
95
96                let r1s_bits = ((inst >> 7) & 0b111) as u8;
97                let funct2 = ((inst >> 5) & 0b11) as u8;
98                let r2s_bits = ((inst >> 2) & 0b111) as u8;
99
100                // Reg::from_bits returns None for registers inaccessible in the current ISA
101                // variant. Under RVE this covers field > 1 (i.e. r1sc/r2sc > 1 in the spec
102                // pseudocode), which maps to x18-x23 - registers that do not exist in the E
103                // extension.
104                let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
105                let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
106
107                // funct2[6:5]: 0b11 -> CM.MVA01S, 0b01 -> CM.MVSA01, others reserved
108                match funct2 {
109                    0b11 => Some(Self::CmMva01s { r1s, r2s }),
110                    0b01 => {
111                        // CM.MVSA01 requires r1s' != r2s'
112                        if r1s_bits == r2s_bits {
113                            None?;
114                        }
115                        Some(Self::CmMvsa01 { r1s, r2s })
116                    }
117                    _ => None,
118                }
119            }
120            // funct2_12_11 values 0b00 and 0b10 are not defined by Zcmp
121            _ => None,
122        }
123    }
124
125    #[inline(always)]
126    fn alignment() -> u8 {
127        align_of::<u16>() as u8
128    }
129
130    #[inline(always)]
131    fn size(&self) -> u8 {
132        size_of::<u16>() as u8
133    }
134}
135
136impl<Reg> fmt::Display for Rv64ZcmpInstruction<Reg>
137where
138    Reg: Register,
139{
140    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
141        match self {
142            Self::CmPush { urlist, stack_adj } => {
143                write!(f, "cm.push {urlist}, -{stack_adj}")
144            }
145            Self::CmPop { urlist, stack_adj } => {
146                write!(f, "cm.pop {urlist}, {stack_adj}")
147            }
148            Self::CmPopretz { urlist, stack_adj } => {
149                write!(f, "cm.popretz {urlist}, {stack_adj}")
150            }
151            Self::CmPopret { urlist, stack_adj } => {
152                write!(f, "cm.popret {urlist}, {stack_adj}")
153            }
154            Self::CmMva01s { r1s, r2s } => write!(f, "cm.mva01s {r1s}, {r2s}"),
155            Self::CmMvsa01 { r1s, r2s } => write!(f, "cm.mvsa01 {r1s}, {r2s}"),
156        }
157    }
158}