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