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