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::instructions::rv64::c::zca::Rv64ZcaInstruction;
9use crate::instructions::utils::I24;
10use crate::registers::general_purpose::Register;
11use ab_riscv_macros::instruction;
12use core::fmt;
13
14/// Zcmp compressed instruction set
15#[instruction(
16    inherit = [Rv64ZcaInstruction, Rv64ZcmpOnlyInstruction],
17)]
18#[derive(Debug, Clone, Copy)]
19#[derive_const(PartialEq, Eq)]
20pub enum Rv64ZcmpInstruction<Reg> {}
21
22#[instruction]
23const impl<Reg> Instruction for Rv64ZcmpInstruction<Reg>
24where
25    Reg: [const] Register<Type = u64>,
26{
27    type Reg = Reg;
28
29    #[inline(always)]
30    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
31    fn try_decode(instruction: u32) -> Option<Self> {
32        None
33    }
34
35    #[inline(always)]
36    fn alignment() -> u8 {
37        align_of::<u16>() as u8
38    }
39
40    #[inline(always)]
41    fn size(&self) -> u8 {
42        size_of::<u16>() as u8
43    }
44}
45
46#[instruction]
47impl<Reg> fmt::Display for Rv64ZcmpInstruction<Reg>
48where
49    Reg: Register,
50{
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        match self {}
53    }
54}
55
56/// Instruction that contains isolated Zcmp instructions without inheriting Zca for testing purposes
57#[instruction]
58#[derive(Debug, Clone, Copy)]
59#[derive_const(PartialEq, Eq)]
60#[doc(hidden)]
61pub enum Rv64ZcmpOnlyInstruction<Reg> {
62    /// CM.PUSH - push reg_list, decrement sp by `stack_adj`
63    ///
64    /// `stack_adj = urlist.stack_adj_base() + spimm * 16` from the encoding.
65    CmPush {
66        urlist: ZcmpUrlist<Reg>,
67        stack_adj: u8,
68    },
69    /// CM.POP - pop reg_list, increment sp by `stack_adj` (no return)
70    CmPop {
71        urlist: ZcmpUrlist<Reg>,
72        stack_adj: u8,
73    },
74    /// CM.POPRETZ - pop reg_list, set a0=0, increment sp, return
75    CmPopretz {
76        urlist: ZcmpUrlist<Reg>,
77        stack_adj: u8,
78    },
79    /// CM.POPRET - pop reg_list, increment sp, return
80    CmPopret {
81        urlist: ZcmpUrlist<Reg>,
82        stack_adj: u8,
83    },
84    /// CM.MVA01S - a0 = r1s', a1 = r2s'.
85    ///
86    /// The fields are called both r1s/r2s and rs1/rs2 in the spec, rs1/rs2 is used here for
87    /// consistency with other instructions.
88    CmMva01s { rs1: Reg, rs2: Reg },
89    /// CM.MVSA01 - r1s' = a0, r2s' = a1  (r1s' != r2s').
90    ///
91    /// The fields are called both r1s/r2s and rs1/rs2 in the spec, rs1/rs2 is used here for
92    /// consistency with other instructions.
93    CmMvsa01 { rs1: Reg, rs2: Reg },
94}
95
96#[instruction]
97const impl<Reg> Instruction for Rv64ZcmpOnlyInstruction<Reg>
98where
99    Reg: [const] ZcmpRegister<Type = u64>,
100{
101    type Reg = Reg;
102
103    #[inline(always)]
104    #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
105    fn try_decode(instruction: u32) -> Option<Self> {
106        /// Map the Zcmp 3-bit "s-register" field to an absolute register number.
107        /// 000->x8(s0), 001->x9(s1), 010->x18(s2)..111->x23(s7)
108        #[inline(always)]
109        const fn sreg_bits(field: u8) -> u8 {
110            match field {
111                0 => 8,
112                1 => 9,
113                f => f + 16,
114            }
115        }
116
117        let inst = instruction as u16;
118        let quadrant = inst & 0b11;
119        let funct3 = ((inst >> 13) & 0b111) as u8;
120
121        // All Zcmp instructions: Q10, funct3=101
122        if quadrant != 0b10 || funct3 != 0b101 {
123            None?;
124        }
125
126        let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
127
128        match funct2_12_11 {
129            // CM.PUSH / CM.POP / CM.POPRETZ / CM.POPRET
130            0b11 => {
131                let op_sel = ((inst >> 9) & 0b11) as u8;
132                let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
133                let spimm = ((inst >> 2) & 0b11) as u8;
134                let stack_adj = urlist.stack_adj_base() + spimm * 16;
135                match op_sel {
136                    0b00 => Some(Self::CmPush { urlist, stack_adj }),
137                    0b01 => Some(Self::CmPop { urlist, stack_adj }),
138                    0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
139                    0b11 => Some(Self::CmPopret { urlist, stack_adj }),
140                    _ => None,
141                }
142            }
143            // CM.MVA01S / CM.MVSA01: require bit 10 = 1 (full funct6 = 101_011)
144            0b01 => {
145                if (inst >> 10) & 1 != 1 {
146                    None?;
147                }
148
149                let r1s_bits = ((inst >> 7) & 0b111) as u8;
150                let funct2 = ((inst >> 5) & 0b11) as u8;
151                let r2s_bits = ((inst >> 2) & 0b111) as u8;
152
153                // Reg::from_bits returns None for registers inaccessible in the current ISA
154                // variant. Under RVE this covers field > 1 (i.e. r1sc/r2sc > 1 in the spec
155                // pseudocode), which maps to x18-x23 - registers that do not exist in the E
156                // extension.
157                let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
158                let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
159
160                // funct2[6:5]: 0b11 -> CM.MVA01S, 0b01 -> CM.MVSA01, others reserved
161                match funct2 {
162                    0b11 => Some(Self::CmMva01s { rs1: r1s, rs2: r2s }),
163                    0b01 => {
164                        // CM.MVSA01 requires r1s' != r2s'
165                        if r1s_bits == r2s_bits {
166                            None?;
167                        }
168                        Some(Self::CmMvsa01 { rs1: r1s, rs2: r2s })
169                    }
170                    _ => None,
171                }
172            }
173            // funct2_12_11 values 0b00 and 0b10 are not defined by Zcmp
174            _ => None,
175        }
176    }
177
178    #[inline(always)]
179    fn alignment() -> u8 {
180        align_of::<u16>() as u8
181    }
182
183    #[inline(always)]
184    fn size(&self) -> u8 {
185        size_of::<u16>() as u8
186    }
187}
188
189#[instruction]
190impl<Reg> fmt::Display for Rv64ZcmpOnlyInstruction<Reg>
191where
192    Reg: Register,
193{
194    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
195        match self {
196            Self::CmPush { urlist, stack_adj } => {
197                write!(f, "cm.push {urlist}, -{stack_adj}")
198            }
199            Self::CmPop { urlist, stack_adj } => {
200                write!(f, "cm.pop {urlist}, {stack_adj}")
201            }
202            Self::CmPopretz { urlist, stack_adj } => {
203                write!(f, "cm.popretz {urlist}, {stack_adj}")
204            }
205            Self::CmPopret { urlist, stack_adj } => {
206                write!(f, "cm.popret {urlist}, {stack_adj}")
207            }
208            Self::CmMva01s { rs1, rs2 } => write!(f, "cm.mva01s {rs1}, {rs2}"),
209            Self::CmMvsa01 { rs1, rs2 } => write!(f, "cm.mvsa01 {rs1}, {rs2}"),
210        }
211    }
212}