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 32 => match self.inner {
253 ZcmpUrlistInner::Ra
254 | ZcmpUrlistInner::RaS0
255 | ZcmpUrlistInner::RaS0S1
256 | ZcmpUrlistInner::RaS0S2 => 16,
257 ZcmpUrlistInner::RaS0S3
258 | ZcmpUrlistInner::RaS0S4
259 | ZcmpUrlistInner::RaS0S5
260 | ZcmpUrlistInner::RaS0S6 => 32,
261 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 | ZcmpUrlistInner::RaS0S9 => 48,
262 ZcmpUrlistInner::RaS0S11 => 64,
263 },
264 64 => match self.inner {
269 ZcmpUrlistInner::Ra | ZcmpUrlistInner::RaS0 => 16,
270 ZcmpUrlistInner::RaS0S1 | ZcmpUrlistInner::RaS0S2 => 32,
271 ZcmpUrlistInner::RaS0S3 | ZcmpUrlistInner::RaS0S4 => 48,
272 ZcmpUrlistInner::RaS0S5 | ZcmpUrlistInner::RaS0S6 => 64,
273 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 => 80,
274 ZcmpUrlistInner::RaS0S9 => 96,
275 ZcmpUrlistInner::RaS0S11 => 112,
276 },
277 _ => {
278 unsafe { unreachable_unchecked() }
281 }
282 }
283 }
284}
285
286impl<Reg> fmt::Display for ZcmpUrlist<Reg> {
287 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
288 match self.inner {
289 ZcmpUrlistInner::Ra => write!(f, "{{ra}}"),
290 ZcmpUrlistInner::RaS0 => write!(f, "{{ra, s0}}"),
291 ZcmpUrlistInner::RaS0S1 => write!(f, "{{ra, s0-s1}}"),
292 ZcmpUrlistInner::RaS0S2 => write!(f, "{{ra, s0-s2}}"),
293 ZcmpUrlistInner::RaS0S3 => write!(f, "{{ra, s0-s3}}"),
294 ZcmpUrlistInner::RaS0S4 => write!(f, "{{ra, s0-s4}}"),
295 ZcmpUrlistInner::RaS0S5 => write!(f, "{{ra, s0-s5}}"),
296 ZcmpUrlistInner::RaS0S6 => write!(f, "{{ra, s0-s6}}"),
297 ZcmpUrlistInner::RaS0S7 => write!(f, "{{ra, s0-s7}}"),
298 ZcmpUrlistInner::RaS0S8 => write!(f, "{{ra, s0-s8}}"),
299 ZcmpUrlistInner::RaS0S9 => write!(f, "{{ra, s0-s9}}"),
300 ZcmpUrlistInner::RaS0S11 => write!(f, "{{ra, s0-s11}}"),
301 }
302 }
303}
304
305#[instruction(
307 inherit = [Rv32ZcaInstruction, Rv32ZcmpOnlyInstruction],
308)]
309#[derive(Debug, Clone, Copy)]
310#[derive_const(PartialEq, Eq)]
311pub enum Rv32ZcmpInstruction<Hart>
312where
313 Hart: HartConfig, {}
314
315#[instruction]
316const impl<Reg, Hart> Instruction for Rv32ZcmpInstruction<Hart>
317where
318 Reg: [const] Register<Type = u32>,
319 Hart: [const] HartConfig<Reg = Reg>,
320{
321 const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[IsaExtension::new("zcmp", 1, 0)];
322
323 const ALIGNMENT: u8 = align_of::<u16>() as u8;
324
325 type Hart = Hart;
326
327 #[inline(always)]
328 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
329 fn try_decode(instruction: u32) -> Option<Self> {
330 None
331 }
332
333 #[inline(always)]
334 fn size(&self) -> u8 {
335 size_of::<u16>() as u8
336 }
337}
338
339#[instruction]
340impl<Reg, Hart> fmt::Display for Rv32ZcmpInstruction<Hart>
341where
342 Reg: Register,
343 Hart: HartConfig<Reg = Reg>,
344{
345 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
346 match self {}
347 }
348}
349
350#[instruction]
352#[derive(Debug, Clone, Copy)]
353#[derive_const(PartialEq, Eq)]
354#[doc(hidden)]
355pub enum Rv32ZcmpOnlyInstruction<Hart>
356where
357 Hart: HartConfig,
358{
359 CmPush {
363 urlist: ZcmpUrlist<Hart::Reg>,
364 stack_adj: u8,
365 },
366 CmPop {
368 urlist: ZcmpUrlist<Hart::Reg>,
369 stack_adj: u8,
370 },
371 CmPopretz {
373 urlist: ZcmpUrlist<Hart::Reg>,
374 stack_adj: u8,
375 },
376 CmPopret {
378 urlist: ZcmpUrlist<Hart::Reg>,
379 stack_adj: u8,
380 },
381 CmMva01s { rs1: Hart::Reg, rs2: Hart::Reg },
386 CmMvsa01 { rs1: Hart::Reg, rs2: Hart::Reg },
391}
392
393#[instruction]
394const impl<Reg, Hart> Instruction for Rv32ZcmpOnlyInstruction<Hart>
395where
396 Reg: [const] ZcmpRegister<Type = u32>,
397 Hart: [const] HartConfig<Reg = Reg>,
398{
399 const OWN_ISA_EXTENSIONS: &'static [IsaExtension] = &[];
400
401 const ALIGNMENT: u8 = align_of::<u16>() as u8;
402
403 type Hart = Hart;
404
405 #[inline(always)]
406 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
407 fn try_decode(instruction: u32) -> Option<Self> {
408 #[inline(always)]
411 const fn sreg_bits(field: u8) -> u8 {
412 match field {
413 0 => 8,
414 1 => 9,
415 f => f + 16,
416 }
417 }
418
419 let inst = instruction as u16;
420 let quadrant = inst & 0b11;
421 let funct3 = ((inst >> 13) & 0b111) as u8;
422
423 if quadrant != 0b10 || funct3 != 0b101 {
425 None?;
426 }
427
428 let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
429
430 match funct2_12_11 {
431 0b11 => {
433 let op_sel = ((inst >> 9) & 0b11) as u8;
434 let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
435 let spimm = ((inst >> 2) & 0b11) as u8;
436 let stack_adj = urlist.stack_adj_base() + spimm * 16;
437 match op_sel {
438 0b00 => Some(Self::CmPush { urlist, stack_adj }),
439 0b01 => Some(Self::CmPop { urlist, stack_adj }),
440 0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
441 0b11 => Some(Self::CmPopret { urlist, stack_adj }),
442 _ => None,
443 }
444 }
445 0b01 => {
447 if (inst >> 10) & 1 != 1 {
448 None?;
449 }
450
451 let r1s_bits = ((inst >> 7) & 0b111) as u8;
452 let funct2 = ((inst >> 5) & 0b11) as u8;
453 let r2s_bits = ((inst >> 2) & 0b111) as u8;
454
455 let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
460 let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
461
462 match funct2 {
464 0b11 => Some(Self::CmMva01s { rs1: r1s, rs2: r2s }),
465 0b01 => {
466 if r1s_bits == r2s_bits {
468 None?;
469 }
470 Some(Self::CmMvsa01 { rs1: r1s, rs2: r2s })
471 }
472 _ => None,
473 }
474 }
475 _ => None,
477 }
478 }
479
480 #[inline(always)]
481 fn size(&self) -> u8 {
482 size_of::<u16>() as u8
483 }
484}
485
486#[instruction]
487impl<Reg, Hart> fmt::Display for Rv32ZcmpOnlyInstruction<Hart>
488where
489 Reg: Register,
490 Hart: HartConfig<Reg = Reg>,
491{
492 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
493 match self {
494 Self::CmPush { urlist, stack_adj } => {
495 write!(f, "cm.push {urlist}, -{stack_adj}")
496 }
497 Self::CmPop { urlist, stack_adj } => {
498 write!(f, "cm.pop {urlist}, {stack_adj}")
499 }
500 Self::CmPopretz { urlist, stack_adj } => {
501 write!(f, "cm.popretz {urlist}, {stack_adj}")
502 }
503 Self::CmPopret { urlist, stack_adj } => {
504 write!(f, "cm.popret {urlist}, {stack_adj}")
505 }
506 Self::CmMva01s { rs1, rs2 } => write!(f, "cm.mva01s {rs1}, {rs2}"),
507 Self::CmMvsa01 { rs1, rs2 } => write!(f, "cm.mvsa01 {rs1}, {rs2}"),
508 }
509 }
510}