1#[cfg(test)]
4mod tests;
5
6use crate::hart::HartConfig;
7use crate::instructions::rv32::c::zca::Rv32ZcaInstruction;
8use crate::instructions::utils::{I24, Shamt};
9use crate::instructions::{ImplementedExtension, Instruction, IsaExtension};
10use crate::registers::general_purpose::{EReg, Reg, Register};
11use ab_riscv_macros::instruction;
12use core::fmt;
13use core::hint::unreachable_unchecked;
14use core::iter::TrustedLen;
15use core::marker::{Destruct, PhantomData};
16
17pub const unsafe trait ZcmpRegister
24where
25 Self: [const] Register,
26{
27}
28
29const unsafe impl ZcmpRegister for Reg<u32> {}
31
32const unsafe impl ZcmpRegister for Reg<u64> {}
34
35const unsafe impl ZcmpRegister for EReg<u32> {}
37
38const unsafe impl ZcmpRegister for EReg<u64> {}
40
41#[derive(Debug, Clone, Copy)]
44#[derive_const(PartialEq, Eq)]
45#[repr(u8)]
46enum ZcmpUrlistInner {
47 Ra = 4,
49 RaS0 = 5,
51 RaS0S1 = 6,
53 RaS0S2 = 7,
55 RaS0S3 = 8,
57 RaS0S4 = 9,
59 RaS0S5 = 10,
61 RaS0S6 = 11,
63 RaS0S7 = 12,
65 RaS0S8 = 13,
67 RaS0S9 = 14,
69 RaS0S11 = 15,
73}
74
75#[derive(Debug, Clone, Copy)]
80pub struct ZcmpUrlist<Reg> {
81 inner: ZcmpUrlistInner,
82 reg: PhantomData<Reg>,
83}
84
85const impl<Reg> PartialEq<ZcmpUrlist<Reg>> for ZcmpUrlist<Reg> {
86 #[inline(always)]
87 fn eq(&self, other: &ZcmpUrlist<Reg>) -> bool {
88 self.inner == other.inner
89 }
90}
91
92const impl<Reg> Eq for ZcmpUrlist<Reg> {}
93
94#[derive(Debug, Clone)]
96struct ZcmpRegList<Reg> {
97 bits: &'static [u8],
99 reg: PhantomData<Reg>,
100}
101
102const impl<Reg> Iterator for ZcmpRegList<Reg>
103where
104 Reg: [const] ZcmpRegister,
105{
106 type Item = Reg;
107
108 #[inline(always)]
109 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
110 fn next(&mut self) -> Option<Self::Item> {
111 let (&bits, rest) = self.bits.split_first()?;
112 self.bits = rest;
113
114 Some(unsafe { Reg::from_bits(bits).unwrap_unchecked() })
117 }
118
119 #[inline(always)]
120 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
121 fn size_hint(&self) -> (usize, Option<usize>) {
122 (self.bits.len(), Some(self.bits.len()))
123 }
124}
125
126impl<Reg> ExactSizeIterator for ZcmpRegList<Reg>
127where
128 Reg: ZcmpRegister,
129{
130 #[inline(always)]
131 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
132 fn len(&self) -> usize {
133 self.bits.len()
134 }
135}
136
137const unsafe impl<Reg> TrustedLen for ZcmpRegList<Reg> where Reg: [const] ZcmpRegister {}
139
140impl<Reg> ZcmpUrlist<Reg>
141where
142 Reg: ZcmpRegister,
143{
144 const XLEN_32: u8 = 32;
145 const XLEN_64: u8 = 64;
146
147 #[inline(always)]
153 pub const fn try_from_raw(raw: u8) -> Option<Self>
154 where
155 Reg: [const] ZcmpRegister,
156 {
157 if !(Reg::XLEN == Self::XLEN_32 || Reg::XLEN == Self::XLEN_64) {
158 return None;
159 }
160
161 let inner = if Reg::RVE {
162 match raw {
164 4 => ZcmpUrlistInner::Ra,
165 5 => ZcmpUrlistInner::RaS0,
166 6 => ZcmpUrlistInner::RaS0S1,
167 _ => {
168 return None;
169 }
170 }
171 } else {
172 match raw {
173 4 => ZcmpUrlistInner::Ra,
174 5 => ZcmpUrlistInner::RaS0,
175 6 => ZcmpUrlistInner::RaS0S1,
176 7 => ZcmpUrlistInner::RaS0S2,
177 8 => ZcmpUrlistInner::RaS0S3,
178 9 => ZcmpUrlistInner::RaS0S4,
179 10 => ZcmpUrlistInner::RaS0S5,
180 11 => ZcmpUrlistInner::RaS0S6,
181 12 => ZcmpUrlistInner::RaS0S7,
182 13 => ZcmpUrlistInner::RaS0S8,
183 14 => ZcmpUrlistInner::RaS0S9,
184 15 => ZcmpUrlistInner::RaS0S11,
185 _ => {
186 return None;
187 }
188 }
189 };
190
191 Some(Self {
192 inner,
193 reg: PhantomData,
194 })
195 }
196
197 #[inline(always)]
199 pub const fn as_u8(self) -> u8 {
200 self.inner as u8
201 }
202
203 #[inline]
211 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
212 pub const fn reg_list(
213 self,
214 ) -> impl [const] Iterator<Item = Reg> + ExactSizeIterator + [const] TrustedLen + const Destruct
215 where
216 Reg: [const] ZcmpRegister,
217 {
218 let bits: &[u8] = match self.inner {
219 ZcmpUrlistInner::Ra => &[1],
220 ZcmpUrlistInner::RaS0 => &[1, 8],
221 ZcmpUrlistInner::RaS0S1 => &[1, 8, 9],
222 ZcmpUrlistInner::RaS0S2 => &[1, 8, 9, 18],
223 ZcmpUrlistInner::RaS0S3 => &[1, 8, 9, 18, 19],
224 ZcmpUrlistInner::RaS0S4 => &[1, 8, 9, 18, 19, 20],
225 ZcmpUrlistInner::RaS0S5 => &[1, 8, 9, 18, 19, 20, 21],
226 ZcmpUrlistInner::RaS0S6 => &[1, 8, 9, 18, 19, 20, 21, 22],
227 ZcmpUrlistInner::RaS0S7 => &[1, 8, 9, 18, 19, 20, 21, 22, 23],
228 ZcmpUrlistInner::RaS0S8 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24],
229 ZcmpUrlistInner::RaS0S9 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25],
230 ZcmpUrlistInner::RaS0S11 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
231 };
232
233 ZcmpRegList {
234 bits,
235 reg: PhantomData,
236 }
237 }
238
239 #[inline(always)]
246 pub const fn stack_adj_base(self) -> u8 {
247 match Reg::XLEN {
248 Self::XLEN_32 => match self.inner {
250 ZcmpUrlistInner::Ra
251 | ZcmpUrlistInner::RaS0
252 | ZcmpUrlistInner::RaS0S1
253 | ZcmpUrlistInner::RaS0S2 => 16,
254 ZcmpUrlistInner::RaS0S3
255 | ZcmpUrlistInner::RaS0S4
256 | ZcmpUrlistInner::RaS0S5
257 | ZcmpUrlistInner::RaS0S6 => 32,
258 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 | ZcmpUrlistInner::RaS0S9 => 48,
259 ZcmpUrlistInner::RaS0S11 => 64,
260 },
261 Self::XLEN_64 => match self.inner {
263 ZcmpUrlistInner::Ra | ZcmpUrlistInner::RaS0 => 16,
264 ZcmpUrlistInner::RaS0S1 | ZcmpUrlistInner::RaS0S2 => 32,
265 ZcmpUrlistInner::RaS0S3 | ZcmpUrlistInner::RaS0S4 => 48,
266 ZcmpUrlistInner::RaS0S5 | ZcmpUrlistInner::RaS0S6 => 64,
267 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 => 80,
268 ZcmpUrlistInner::RaS0S9 => 96,
269 ZcmpUrlistInner::RaS0S11 => 112,
270 },
271 _ => {
272 unsafe { unreachable_unchecked() }
275 }
276 }
277 }
278}
279
280impl<Reg> fmt::Display for ZcmpUrlist<Reg> {
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 match self.inner {
283 ZcmpUrlistInner::Ra => write!(f, "{{ra}}"),
284 ZcmpUrlistInner::RaS0 => write!(f, "{{ra, s0}}"),
285 ZcmpUrlistInner::RaS0S1 => write!(f, "{{ra, s0-s1}}"),
286 ZcmpUrlistInner::RaS0S2 => write!(f, "{{ra, s0-s2}}"),
287 ZcmpUrlistInner::RaS0S3 => write!(f, "{{ra, s0-s3}}"),
288 ZcmpUrlistInner::RaS0S4 => write!(f, "{{ra, s0-s4}}"),
289 ZcmpUrlistInner::RaS0S5 => write!(f, "{{ra, s0-s5}}"),
290 ZcmpUrlistInner::RaS0S6 => write!(f, "{{ra, s0-s6}}"),
291 ZcmpUrlistInner::RaS0S7 => write!(f, "{{ra, s0-s7}}"),
292 ZcmpUrlistInner::RaS0S8 => write!(f, "{{ra, s0-s8}}"),
293 ZcmpUrlistInner::RaS0S9 => write!(f, "{{ra, s0-s9}}"),
294 ZcmpUrlistInner::RaS0S11 => write!(f, "{{ra, s0-s11}}"),
295 }
296 }
297}
298
299#[instruction(
301 inherit = [Rv32ZcaInstruction, Rv32ZcmpOnlyInstruction],
302)]
303#[derive(Debug, Clone, Copy)]
304#[derive_const(PartialEq, Eq)]
305pub enum Rv32ZcmpInstruction<Hart>
306where
307 Hart: HartConfig, {}
308
309#[instruction]
310const impl<Reg, Hart> Instruction for Rv32ZcmpInstruction<Hart>
311where
312 Reg: [const] Register<Type = u32>,
313 Hart: [const] HartConfig<Reg = Reg>,
314{
315 const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zcmp", 1, 0)];
316
317 const ALIGNMENT: u8 = align_of::<u16>() as u8;
318
319 type Hart = Hart;
320
321 #[inline(always)]
322 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
323 fn try_decode(instruction: u32) -> Option<Self> {
324 None
325 }
326
327 #[inline(always)]
328 fn size(&self) -> u8 {
329 size_of::<u16>() as u8
330 }
331}
332
333#[instruction]
334impl<Reg, Hart> fmt::Display for Rv32ZcmpInstruction<Hart>
335where
336 Reg: Register,
337 Hart: HartConfig<Reg = Reg>,
338{
339 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340 match self {}
341 }
342}
343
344#[instruction]
346#[derive(Debug, Clone, Copy)]
347#[derive_const(PartialEq, Eq)]
348#[doc(hidden)]
349pub enum Rv32ZcmpOnlyInstruction<Hart>
350where
351 Hart: HartConfig,
352{
353 CmPush {
357 urlist: ZcmpUrlist<Hart::Reg>,
358 stack_adj: u8,
359 },
360 CmPop {
362 urlist: ZcmpUrlist<Hart::Reg>,
363 stack_adj: u8,
364 },
365 CmPopretz {
367 urlist: ZcmpUrlist<Hart::Reg>,
368 stack_adj: u8,
369 },
370 CmPopret {
372 urlist: ZcmpUrlist<Hart::Reg>,
373 stack_adj: u8,
374 },
375 CmMva01s { rs1: Hart::Reg, rs2: Hart::Reg },
380 CmMvsa01 { rs1: Hart::Reg, rs2: Hart::Reg },
385}
386
387#[instruction]
388const impl<Reg, Hart> Instruction for Rv32ZcmpOnlyInstruction<Hart>
389where
390 Reg: [const] ZcmpRegister<Type = u32>,
391 Hart: [const] HartConfig<Reg = Reg>,
392{
393 const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[];
394
395 const ALIGNMENT: u8 = align_of::<u16>() as u8;
396
397 type Hart = Hart;
398
399 #[inline(always)]
400 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
401 fn try_decode(instruction: u32) -> Option<Self> {
402 #[inline(always)]
405 const fn sreg_bits(field: u8) -> u8 {
406 match field {
407 0 => 8,
408 1 => 9,
409 f => f + 16,
410 }
411 }
412
413 let inst = instruction as u16;
414 let quadrant = inst & 0b11;
415 let funct3 = ((inst >> 13) & 0b111) as u8;
416
417 if quadrant != 0b10 || funct3 != 0b101 {
419 None?;
420 }
421
422 let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
423
424 match funct2_12_11 {
425 0b11 => {
427 let op_sel = ((inst >> 9) & 0b11) as u8;
428 let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
429 let spimm = ((inst >> 2) & 0b11) as u8;
430 let stack_adj = urlist.stack_adj_base() + spimm * 16;
431 match op_sel {
432 0b00 => Some(Self::CmPush { urlist, stack_adj }),
433 0b01 => Some(Self::CmPop { urlist, stack_adj }),
434 0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
435 0b11 => Some(Self::CmPopret { urlist, stack_adj }),
436 _ => None,
437 }
438 }
439 0b01 => {
441 if (inst >> 10) & 1 != 1 {
442 None?;
443 }
444
445 let r1s_bits = ((inst >> 7) & 0b111) as u8;
446 let funct2 = ((inst >> 5) & 0b11) as u8;
447 let r2s_bits = ((inst >> 2) & 0b111) as u8;
448
449 let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
454 let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
455
456 match funct2 {
458 0b11 => Some(Self::CmMva01s { rs1: r1s, rs2: r2s }),
459 0b01 => {
460 if r1s_bits == r2s_bits {
462 None?;
463 }
464 Some(Self::CmMvsa01 { rs1: r1s, rs2: r2s })
465 }
466 _ => None,
467 }
468 }
469 _ => None,
471 }
472 }
473
474 #[inline(always)]
475 fn size(&self) -> u8 {
476 size_of::<u16>() as u8
477 }
478}
479
480#[instruction]
481impl<Reg, Hart> fmt::Display for Rv32ZcmpOnlyInstruction<Hart>
482where
483 Reg: Register,
484 Hart: HartConfig<Reg = Reg>,
485{
486 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487 match self {
488 Self::CmPush { urlist, stack_adj } => {
489 write!(f, "cm.push {urlist}, -{stack_adj}")
490 }
491 Self::CmPop { urlist, stack_adj } => {
492 write!(f, "cm.pop {urlist}, {stack_adj}")
493 }
494 Self::CmPopretz { urlist, stack_adj } => {
495 write!(f, "cm.popretz {urlist}, {stack_adj}")
496 }
497 Self::CmPopret { urlist, stack_adj } => {
498 write!(f, "cm.popret {urlist}, {stack_adj}")
499 }
500 Self::CmMva01s { rs1, rs2 } => write!(f, "cm.mva01s {rs1}, {rs2}"),
501 Self::CmMvsa01 { rs1, rs2 } => write!(f, "cm.mvsa01 {rs1}, {rs2}"),
502 }
503 }
504}