1#[cfg(test)]
4mod tests;
5
6use crate::instructions::Instruction;
7use crate::registers::general_purpose::{EReg, Reg, Register};
8use ab_riscv_macros::instruction;
9use core::fmt;
10use core::hint::unreachable_unchecked;
11use core::marker::PhantomData;
12
13pub const unsafe trait ZcmpRegister
20where
21 Self: [const] Register,
22{
23 const RVE: bool;
25}
26
27unsafe impl const ZcmpRegister for Reg<u32> {
29 const RVE: bool = false;
30}
31
32unsafe impl const ZcmpRegister for Reg<u64> {
34 const RVE: bool = false;
35}
36
37unsafe impl const ZcmpRegister for EReg<u32> {
39 const RVE: bool = true;
40}
41
42unsafe impl const ZcmpRegister for EReg<u64> {
44 const RVE: bool = true;
45}
46
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
50#[repr(u8)]
51enum ZcmpUrlistInner {
52 Ra = 4,
54 RaS0 = 5,
56 RaS0S1 = 6,
58 RaS0S2 = 7,
60 RaS0S3 = 8,
62 RaS0S4 = 9,
64 RaS0S5 = 10,
66 RaS0S6 = 11,
68 RaS0S7 = 12,
70 RaS0S8 = 13,
72 RaS0S9 = 14,
74 RaS0S11 = 15,
78}
79
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
85pub struct ZcmpUrlist<Reg> {
86 inner: ZcmpUrlistInner,
87 reg: PhantomData<Reg>,
88}
89
90impl<Reg> ZcmpUrlist<Reg>
91where
92 Reg: ZcmpRegister,
93{
94 const XLEN_32: u8 = 32;
95 const XLEN_64: u8 = 64;
96
97 #[inline(always)]
103 pub const fn try_from_raw(raw: u8) -> Option<Self>
104 where
105 Reg: [const] ZcmpRegister,
106 {
107 if !(Reg::XLEN == Self::XLEN_32 || Reg::XLEN == Self::XLEN_64) {
108 return None;
109 }
110
111 let inner = if Reg::RVE {
112 match raw {
114 4 => ZcmpUrlistInner::Ra,
115 5 => ZcmpUrlistInner::RaS0,
116 6 => ZcmpUrlistInner::RaS0S1,
117 _ => {
118 return None;
119 }
120 }
121 } else {
122 match raw {
123 4 => ZcmpUrlistInner::Ra,
124 5 => ZcmpUrlistInner::RaS0,
125 6 => ZcmpUrlistInner::RaS0S1,
126 7 => ZcmpUrlistInner::RaS0S2,
127 8 => ZcmpUrlistInner::RaS0S3,
128 9 => ZcmpUrlistInner::RaS0S4,
129 10 => ZcmpUrlistInner::RaS0S5,
130 11 => ZcmpUrlistInner::RaS0S6,
131 12 => ZcmpUrlistInner::RaS0S7,
132 13 => ZcmpUrlistInner::RaS0S8,
133 14 => ZcmpUrlistInner::RaS0S9,
134 15 => ZcmpUrlistInner::RaS0S11,
135 _ => {
136 return None;
137 }
138 }
139 };
140
141 Some(Self {
142 inner,
143 reg: PhantomData,
144 })
145 }
146
147 #[inline(always)]
149 pub const fn as_u8(self) -> u8 {
150 self.inner as u8
151 }
152
153 #[inline]
161 pub fn reg_list(self) -> impl Iterator<Item = Reg> {
162 let regs: &[u8] = match self.inner {
163 ZcmpUrlistInner::Ra => &[1],
164 ZcmpUrlistInner::RaS0 => &[1, 8],
165 ZcmpUrlistInner::RaS0S1 => &[1, 8, 9],
166 ZcmpUrlistInner::RaS0S2 => &[1, 8, 9, 18],
167 ZcmpUrlistInner::RaS0S3 => &[1, 8, 9, 18, 19],
168 ZcmpUrlistInner::RaS0S4 => &[1, 8, 9, 18, 19, 20],
169 ZcmpUrlistInner::RaS0S5 => &[1, 8, 9, 18, 19, 20, 21],
170 ZcmpUrlistInner::RaS0S6 => &[1, 8, 9, 18, 19, 20, 21, 22],
171 ZcmpUrlistInner::RaS0S7 => &[1, 8, 9, 18, 19, 20, 21, 22, 23],
172 ZcmpUrlistInner::RaS0S8 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24],
173 ZcmpUrlistInner::RaS0S9 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25],
174 ZcmpUrlistInner::RaS0S11 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
175 };
176
177 regs.iter().map(|&bits| {
178 unsafe { Reg::from_bits(bits).unwrap_unchecked() }
181 })
182 }
183
184 #[inline(always)]
191 pub const fn stack_adj_base(self) -> u32 {
192 match Reg::XLEN {
193 Self::XLEN_32 => match self.inner {
195 ZcmpUrlistInner::Ra
196 | ZcmpUrlistInner::RaS0
197 | ZcmpUrlistInner::RaS0S1
198 | ZcmpUrlistInner::RaS0S2 => 16,
199 ZcmpUrlistInner::RaS0S3
200 | ZcmpUrlistInner::RaS0S4
201 | ZcmpUrlistInner::RaS0S5
202 | ZcmpUrlistInner::RaS0S6 => 32,
203 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 | ZcmpUrlistInner::RaS0S9 => 48,
204 ZcmpUrlistInner::RaS0S11 => 64,
205 },
206 Self::XLEN_64 => match self.inner {
208 ZcmpUrlistInner::Ra | ZcmpUrlistInner::RaS0 => 16,
209 ZcmpUrlistInner::RaS0S1 | ZcmpUrlistInner::RaS0S2 => 32,
210 ZcmpUrlistInner::RaS0S3 | ZcmpUrlistInner::RaS0S4 => 48,
211 ZcmpUrlistInner::RaS0S5 | ZcmpUrlistInner::RaS0S6 => 64,
212 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 => 80,
213 ZcmpUrlistInner::RaS0S9 => 96,
214 ZcmpUrlistInner::RaS0S11 => 112,
215 },
216 _ => {
217 unsafe { unreachable_unchecked() }
220 }
221 }
222 }
223}
224
225impl<Reg> fmt::Display for ZcmpUrlist<Reg> {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 match self.inner {
228 ZcmpUrlistInner::Ra => write!(f, "{{ra}}"),
229 ZcmpUrlistInner::RaS0 => write!(f, "{{ra, s0}}"),
230 ZcmpUrlistInner::RaS0S1 => write!(f, "{{ra, s0-s1}}"),
231 ZcmpUrlistInner::RaS0S2 => write!(f, "{{ra, s0-s2}}"),
232 ZcmpUrlistInner::RaS0S3 => write!(f, "{{ra, s0-s3}}"),
233 ZcmpUrlistInner::RaS0S4 => write!(f, "{{ra, s0-s4}}"),
234 ZcmpUrlistInner::RaS0S5 => write!(f, "{{ra, s0-s5}}"),
235 ZcmpUrlistInner::RaS0S6 => write!(f, "{{ra, s0-s6}}"),
236 ZcmpUrlistInner::RaS0S7 => write!(f, "{{ra, s0-s7}}"),
237 ZcmpUrlistInner::RaS0S8 => write!(f, "{{ra, s0-s8}}"),
238 ZcmpUrlistInner::RaS0S9 => write!(f, "{{ra, s0-s9}}"),
239 ZcmpUrlistInner::RaS0S11 => write!(f, "{{ra, s0-s11}}"),
240 }
241 }
242}
243
244#[instruction]
246#[derive(Debug, Clone, Copy, PartialEq, Eq)]
247pub enum Rv32ZcmpInstruction<Reg> {
248 CmPush {
252 urlist: ZcmpUrlist<Reg>,
253 stack_adj: u32,
254 },
255 CmPop {
257 urlist: ZcmpUrlist<Reg>,
258 stack_adj: u32,
259 },
260 CmPopretz {
262 urlist: ZcmpUrlist<Reg>,
263 stack_adj: u32,
264 },
265 CmPopret {
267 urlist: ZcmpUrlist<Reg>,
268 stack_adj: u32,
269 },
270 CmMva01s { r1s: Reg, r2s: Reg },
272 CmMvsa01 { r1s: Reg, r2s: Reg },
274}
275
276#[instruction]
277impl<Reg> const Instruction for Rv32ZcmpInstruction<Reg>
278where
279 Reg: [const] ZcmpRegister<Type = u32>,
280{
281 type Reg = Reg;
282
283 #[inline(always)]
284 fn try_decode(instruction: u32) -> Option<Self> {
285 #[inline(always)]
288 const fn sreg_bits(field: u8) -> u8 {
289 match field {
290 0 => 8,
291 1 => 9,
292 f => f + 16,
293 }
294 }
295
296 let inst = instruction as u16;
297 let quadrant = inst & 0b11;
298 let funct3 = ((inst >> 13) & 0b111) as u8;
299
300 if quadrant != 0b10 || funct3 != 0b101 {
302 None?;
303 }
304
305 let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
306
307 match funct2_12_11 {
308 0b11 => {
310 let op_sel = ((inst >> 9) & 0b11) as u8;
311 let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
312 let spimm = ((inst >> 2) & 0b11) as u32;
313 let stack_adj = urlist.stack_adj_base() + spimm * 16;
314 match op_sel {
315 0b00 => Some(Self::CmPush { urlist, stack_adj }),
316 0b01 => Some(Self::CmPop { urlist, stack_adj }),
317 0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
318 0b11 => Some(Self::CmPopret { urlist, stack_adj }),
319 _ => None,
320 }
321 }
322 0b01 => {
324 if (inst >> 10) & 1 != 1 {
325 None?;
326 }
327
328 let r1s_bits = ((inst >> 7) & 0b111) as u8;
329 let funct2 = ((inst >> 5) & 0b11) as u8;
330 let r2s_bits = ((inst >> 2) & 0b111) as u8;
331
332 let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
337 let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
338
339 match funct2 {
341 0b11 => Some(Self::CmMva01s { r1s, r2s }),
342 0b01 => {
343 if r1s_bits == r2s_bits {
345 None?;
346 }
347 Some(Self::CmMvsa01 { r1s, r2s })
348 }
349 _ => None,
350 }
351 }
352 _ => None,
354 }
355 }
356
357 #[inline(always)]
358 fn alignment() -> u8 {
359 align_of::<u16>() as u8
360 }
361
362 #[inline(always)]
363 fn size(&self) -> u8 {
364 size_of::<u16>() as u8
365 }
366}
367
368impl<Reg> fmt::Display for Rv32ZcmpInstruction<Reg>
369where
370 Reg: Register,
371{
372 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
373 match self {
374 Self::CmPush { urlist, stack_adj } => {
375 write!(f, "cm.push {urlist}, -{stack_adj}")
376 }
377 Self::CmPop { urlist, stack_adj } => {
378 write!(f, "cm.pop {urlist}, {stack_adj}")
379 }
380 Self::CmPopretz { urlist, stack_adj } => {
381 write!(f, "cm.popretz {urlist}, {stack_adj}")
382 }
383 Self::CmPopret { urlist, stack_adj } => {
384 write!(f, "cm.popret {urlist}, {stack_adj}")
385 }
386 Self::CmMva01s { r1s, r2s } => write!(f, "cm.mva01s {r1s}, {r2s}"),
387 Self::CmMvsa01 { r1s, r2s } => write!(f, "cm.mvsa01 {r1s}, {r2s}"),
388 }
389 }
390}