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