1#[cfg(test)]
4mod tests;
5
6use crate::instructions::Instruction;
7use crate::instructions::rv32::c::zca::Rv32ZcaInstruction;
8use crate::instructions::utils::I24;
9use crate::registers::general_purpose::{EReg, Reg, Register};
10use ab_riscv_macros::instruction;
11use core::fmt;
12use core::hint::unreachable_unchecked;
13use core::iter::TrustedLen;
14use core::marker::{Destruct, PhantomData};
15
16pub const unsafe trait ZcmpRegister
23where
24 Self: [const] Register,
25{
26 const RVE: bool;
28}
29
30const unsafe impl ZcmpRegister for Reg<u32> {
32 const RVE: bool = false;
33}
34
35const unsafe impl ZcmpRegister for Reg<u64> {
37 const RVE: bool = false;
38}
39
40const unsafe impl ZcmpRegister for EReg<u32> {
42 const RVE: bool = true;
43}
44
45const unsafe impl ZcmpRegister for EReg<u64> {
47 const RVE: bool = true;
48}
49
50#[derive(Debug, Clone, Copy)]
53#[derive_const(PartialEq, Eq)]
54#[repr(u8)]
55enum ZcmpUrlistInner {
56 Ra = 4,
58 RaS0 = 5,
60 RaS0S1 = 6,
62 RaS0S2 = 7,
64 RaS0S3 = 8,
66 RaS0S4 = 9,
68 RaS0S5 = 10,
70 RaS0S6 = 11,
72 RaS0S7 = 12,
74 RaS0S8 = 13,
76 RaS0S9 = 14,
78 RaS0S11 = 15,
82}
83
84#[derive(Debug, Clone, Copy)]
89pub struct ZcmpUrlist<Reg> {
90 inner: ZcmpUrlistInner,
91 reg: PhantomData<Reg>,
92}
93
94const impl<Reg> PartialEq<ZcmpUrlist<Reg>> for ZcmpUrlist<Reg> {
95 #[inline(always)]
96 fn eq(&self, other: &ZcmpUrlist<Reg>) -> bool {
97 self.inner == other.inner
98 }
99}
100
101const impl<Reg> Eq for ZcmpUrlist<Reg> {}
102
103#[derive(Debug, Clone)]
105struct ZcmpRegList<Reg> {
106 bits: &'static [u8],
108 reg: PhantomData<Reg>,
109}
110
111const impl<Reg> Iterator for ZcmpRegList<Reg>
112where
113 Reg: [const] ZcmpRegister,
114{
115 type Item = Reg;
116
117 #[inline(always)]
118 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
119 fn next(&mut self) -> Option<Self::Item> {
120 let (&bits, rest) = self.bits.split_first()?;
121 self.bits = rest;
122
123 Some(unsafe { Reg::from_bits(bits).unwrap_unchecked() })
126 }
127
128 #[inline(always)]
129 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
130 fn size_hint(&self) -> (usize, Option<usize>) {
131 (self.bits.len(), Some(self.bits.len()))
132 }
133}
134
135impl<Reg> ExactSizeIterator for ZcmpRegList<Reg>
136where
137 Reg: ZcmpRegister,
138{
139 #[inline(always)]
140 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
141 fn len(&self) -> usize {
142 self.bits.len()
143 }
144}
145
146const unsafe impl<Reg> TrustedLen for ZcmpRegList<Reg> where Reg: [const] ZcmpRegister {}
148
149impl<Reg> ZcmpUrlist<Reg>
150where
151 Reg: ZcmpRegister,
152{
153 const XLEN_32: u8 = 32;
154 const XLEN_64: u8 = 64;
155
156 #[inline(always)]
162 pub const fn try_from_raw(raw: u8) -> Option<Self>
163 where
164 Reg: [const] ZcmpRegister,
165 {
166 if !(Reg::XLEN == Self::XLEN_32 || Reg::XLEN == Self::XLEN_64) {
167 return None;
168 }
169
170 let inner = if Reg::RVE {
171 match raw {
173 4 => ZcmpUrlistInner::Ra,
174 5 => ZcmpUrlistInner::RaS0,
175 6 => ZcmpUrlistInner::RaS0S1,
176 _ => {
177 return None;
178 }
179 }
180 } else {
181 match raw {
182 4 => ZcmpUrlistInner::Ra,
183 5 => ZcmpUrlistInner::RaS0,
184 6 => ZcmpUrlistInner::RaS0S1,
185 7 => ZcmpUrlistInner::RaS0S2,
186 8 => ZcmpUrlistInner::RaS0S3,
187 9 => ZcmpUrlistInner::RaS0S4,
188 10 => ZcmpUrlistInner::RaS0S5,
189 11 => ZcmpUrlistInner::RaS0S6,
190 12 => ZcmpUrlistInner::RaS0S7,
191 13 => ZcmpUrlistInner::RaS0S8,
192 14 => ZcmpUrlistInner::RaS0S9,
193 15 => ZcmpUrlistInner::RaS0S11,
194 _ => {
195 return None;
196 }
197 }
198 };
199
200 Some(Self {
201 inner,
202 reg: PhantomData,
203 })
204 }
205
206 #[inline(always)]
208 pub const fn as_u8(self) -> u8 {
209 self.inner as u8
210 }
211
212 #[inline]
220 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
221 pub const fn reg_list(
222 self,
223 ) -> impl [const] Iterator<Item = Reg> + ExactSizeIterator + [const] TrustedLen + const Destruct
224 where
225 Reg: [const] ZcmpRegister,
226 {
227 let bits: &[u8] = match self.inner {
228 ZcmpUrlistInner::Ra => &[1],
229 ZcmpUrlistInner::RaS0 => &[1, 8],
230 ZcmpUrlistInner::RaS0S1 => &[1, 8, 9],
231 ZcmpUrlistInner::RaS0S2 => &[1, 8, 9, 18],
232 ZcmpUrlistInner::RaS0S3 => &[1, 8, 9, 18, 19],
233 ZcmpUrlistInner::RaS0S4 => &[1, 8, 9, 18, 19, 20],
234 ZcmpUrlistInner::RaS0S5 => &[1, 8, 9, 18, 19, 20, 21],
235 ZcmpUrlistInner::RaS0S6 => &[1, 8, 9, 18, 19, 20, 21, 22],
236 ZcmpUrlistInner::RaS0S7 => &[1, 8, 9, 18, 19, 20, 21, 22, 23],
237 ZcmpUrlistInner::RaS0S8 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24],
238 ZcmpUrlistInner::RaS0S9 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25],
239 ZcmpUrlistInner::RaS0S11 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
240 };
241
242 ZcmpRegList {
243 bits,
244 reg: PhantomData,
245 }
246 }
247
248 #[inline(always)]
255 pub const fn stack_adj_base(self) -> u8 {
256 match Reg::XLEN {
257 Self::XLEN_32 => match self.inner {
259 ZcmpUrlistInner::Ra
260 | ZcmpUrlistInner::RaS0
261 | ZcmpUrlistInner::RaS0S1
262 | ZcmpUrlistInner::RaS0S2 => 16,
263 ZcmpUrlistInner::RaS0S3
264 | ZcmpUrlistInner::RaS0S4
265 | ZcmpUrlistInner::RaS0S5
266 | ZcmpUrlistInner::RaS0S6 => 32,
267 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 | ZcmpUrlistInner::RaS0S9 => 48,
268 ZcmpUrlistInner::RaS0S11 => 64,
269 },
270 Self::XLEN_64 => match self.inner {
272 ZcmpUrlistInner::Ra | ZcmpUrlistInner::RaS0 => 16,
273 ZcmpUrlistInner::RaS0S1 | ZcmpUrlistInner::RaS0S2 => 32,
274 ZcmpUrlistInner::RaS0S3 | ZcmpUrlistInner::RaS0S4 => 48,
275 ZcmpUrlistInner::RaS0S5 | ZcmpUrlistInner::RaS0S6 => 64,
276 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 => 80,
277 ZcmpUrlistInner::RaS0S9 => 96,
278 ZcmpUrlistInner::RaS0S11 => 112,
279 },
280 _ => {
281 unsafe { unreachable_unchecked() }
284 }
285 }
286 }
287}
288
289impl<Reg> fmt::Display for ZcmpUrlist<Reg> {
290 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291 match self.inner {
292 ZcmpUrlistInner::Ra => write!(f, "{{ra}}"),
293 ZcmpUrlistInner::RaS0 => write!(f, "{{ra, s0}}"),
294 ZcmpUrlistInner::RaS0S1 => write!(f, "{{ra, s0-s1}}"),
295 ZcmpUrlistInner::RaS0S2 => write!(f, "{{ra, s0-s2}}"),
296 ZcmpUrlistInner::RaS0S3 => write!(f, "{{ra, s0-s3}}"),
297 ZcmpUrlistInner::RaS0S4 => write!(f, "{{ra, s0-s4}}"),
298 ZcmpUrlistInner::RaS0S5 => write!(f, "{{ra, s0-s5}}"),
299 ZcmpUrlistInner::RaS0S6 => write!(f, "{{ra, s0-s6}}"),
300 ZcmpUrlistInner::RaS0S7 => write!(f, "{{ra, s0-s7}}"),
301 ZcmpUrlistInner::RaS0S8 => write!(f, "{{ra, s0-s8}}"),
302 ZcmpUrlistInner::RaS0S9 => write!(f, "{{ra, s0-s9}}"),
303 ZcmpUrlistInner::RaS0S11 => write!(f, "{{ra, s0-s11}}"),
304 }
305 }
306}
307
308#[instruction(
310 inherit = [Rv32ZcaInstruction, Rv32ZcmpOnlyInstruction],
311)]
312#[derive(Debug, Clone, Copy)]
313#[derive_const(PartialEq, Eq)]
314pub enum Rv32ZcmpInstruction<Reg> {}
315
316#[instruction]
317const impl<Reg> Instruction for Rv32ZcmpInstruction<Reg>
318where
319 Reg: [const] Register<Type = u32>,
320{
321 type Reg = Reg;
322
323 #[inline(always)]
324 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
325 fn try_decode(instruction: u32) -> Option<Self> {
326 None
327 }
328
329 #[inline(always)]
330 fn alignment() -> u8 {
331 align_of::<u16>() as u8
332 }
333
334 #[inline(always)]
335 fn size(&self) -> u8 {
336 size_of::<u16>() as u8
337 }
338}
339
340#[instruction]
341impl<Reg> fmt::Display for Rv32ZcmpInstruction<Reg>
342where
343 Reg: Register,
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<Reg> {
356 CmPush {
360 urlist: ZcmpUrlist<Reg>,
361 stack_adj: u8,
362 },
363 CmPop {
365 urlist: ZcmpUrlist<Reg>,
366 stack_adj: u8,
367 },
368 CmPopretz {
370 urlist: ZcmpUrlist<Reg>,
371 stack_adj: u8,
372 },
373 CmPopret {
375 urlist: ZcmpUrlist<Reg>,
376 stack_adj: u8,
377 },
378 CmMva01s { rs1: Reg, rs2: Reg },
383 CmMvsa01 { rs1: Reg, rs2: Reg },
388}
389
390#[instruction]
391const impl<Reg> Instruction for Rv32ZcmpOnlyInstruction<Reg>
392where
393 Reg: [const] ZcmpRegister<Type = u32>,
394{
395 type Reg = Reg;
396
397 #[inline(always)]
398 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
399 fn try_decode(instruction: u32) -> Option<Self> {
400 #[inline(always)]
403 const fn sreg_bits(field: u8) -> u8 {
404 match field {
405 0 => 8,
406 1 => 9,
407 f => f + 16,
408 }
409 }
410
411 let inst = instruction as u16;
412 let quadrant = inst & 0b11;
413 let funct3 = ((inst >> 13) & 0b111) as u8;
414
415 if quadrant != 0b10 || funct3 != 0b101 {
417 None?;
418 }
419
420 let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
421
422 match funct2_12_11 {
423 0b11 => {
425 let op_sel = ((inst >> 9) & 0b11) as u8;
426 let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
427 let spimm = ((inst >> 2) & 0b11) as u8;
428 let stack_adj = urlist.stack_adj_base() + spimm * 16;
429 match op_sel {
430 0b00 => Some(Self::CmPush { urlist, stack_adj }),
431 0b01 => Some(Self::CmPop { urlist, stack_adj }),
432 0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
433 0b11 => Some(Self::CmPopret { urlist, stack_adj }),
434 _ => None,
435 }
436 }
437 0b01 => {
439 if (inst >> 10) & 1 != 1 {
440 None?;
441 }
442
443 let r1s_bits = ((inst >> 7) & 0b111) as u8;
444 let funct2 = ((inst >> 5) & 0b11) as u8;
445 let r2s_bits = ((inst >> 2) & 0b111) as u8;
446
447 let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
452 let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
453
454 match funct2 {
456 0b11 => Some(Self::CmMva01s { rs1: r1s, rs2: r2s }),
457 0b01 => {
458 if r1s_bits == r2s_bits {
460 None?;
461 }
462 Some(Self::CmMvsa01 { rs1: r1s, rs2: r2s })
463 }
464 _ => None,
465 }
466 }
467 _ => None,
469 }
470 }
471
472 #[inline(always)]
473 fn alignment() -> u8 {
474 align_of::<u16>() as u8
475 }
476
477 #[inline(always)]
478 fn size(&self) -> u8 {
479 size_of::<u16>() as u8
480 }
481}
482
483#[instruction]
484impl<Reg> fmt::Display for Rv32ZcmpOnlyInstruction<Reg>
485where
486 Reg: Register,
487{
488 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
489 match self {
490 Self::CmPush { urlist, stack_adj } => {
491 write!(f, "cm.push {urlist}, -{stack_adj}")
492 }
493 Self::CmPop { urlist, stack_adj } => {
494 write!(f, "cm.pop {urlist}, {stack_adj}")
495 }
496 Self::CmPopretz { urlist, stack_adj } => {
497 write!(f, "cm.popretz {urlist}, {stack_adj}")
498 }
499 Self::CmPopret { urlist, stack_adj } => {
500 write!(f, "cm.popret {urlist}, {stack_adj}")
501 }
502 Self::CmMva01s { rs1, rs2 } => write!(f, "cm.mva01s {rs1}, {rs2}"),
503 Self::CmMvsa01 { rs1, rs2 } => write!(f, "cm.mvsa01 {rs1}, {rs2}"),
504 }
505 }
506}