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::marker::PhantomData;
14
15pub const unsafe trait ZcmpRegister
22where
23 Self: [const] Register,
24{
25 const RVE: bool;
27}
28
29const unsafe impl ZcmpRegister for Reg<u32> {
31 const RVE: bool = false;
32}
33
34const unsafe impl ZcmpRegister for Reg<u64> {
36 const RVE: bool = false;
37}
38
39const unsafe impl ZcmpRegister for EReg<u32> {
41 const RVE: bool = true;
42}
43
44const unsafe impl ZcmpRegister for EReg<u64> {
46 const RVE: bool = true;
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[repr(u8)]
53enum ZcmpUrlistInner {
54 Ra = 4,
56 RaS0 = 5,
58 RaS0S1 = 6,
60 RaS0S2 = 7,
62 RaS0S3 = 8,
64 RaS0S4 = 9,
66 RaS0S5 = 10,
68 RaS0S6 = 11,
70 RaS0S7 = 12,
72 RaS0S8 = 13,
74 RaS0S9 = 14,
76 RaS0S11 = 15,
80}
81
82#[derive(Debug, Clone, Copy, PartialEq, Eq)]
87pub struct ZcmpUrlist<Reg> {
88 inner: ZcmpUrlistInner,
89 reg: PhantomData<Reg>,
90}
91
92impl<Reg> ZcmpUrlist<Reg>
93where
94 Reg: ZcmpRegister,
95{
96 const XLEN_32: u8 = 32;
97 const XLEN_64: u8 = 64;
98
99 #[inline(always)]
105 pub const fn try_from_raw(raw: u8) -> Option<Self>
106 where
107 Reg: [const] ZcmpRegister,
108 {
109 if !(Reg::XLEN == Self::XLEN_32 || Reg::XLEN == Self::XLEN_64) {
110 return None;
111 }
112
113 let inner = if Reg::RVE {
114 match raw {
116 4 => ZcmpUrlistInner::Ra,
117 5 => ZcmpUrlistInner::RaS0,
118 6 => ZcmpUrlistInner::RaS0S1,
119 _ => {
120 return None;
121 }
122 }
123 } else {
124 match raw {
125 4 => ZcmpUrlistInner::Ra,
126 5 => ZcmpUrlistInner::RaS0,
127 6 => ZcmpUrlistInner::RaS0S1,
128 7 => ZcmpUrlistInner::RaS0S2,
129 8 => ZcmpUrlistInner::RaS0S3,
130 9 => ZcmpUrlistInner::RaS0S4,
131 10 => ZcmpUrlistInner::RaS0S5,
132 11 => ZcmpUrlistInner::RaS0S6,
133 12 => ZcmpUrlistInner::RaS0S7,
134 13 => ZcmpUrlistInner::RaS0S8,
135 14 => ZcmpUrlistInner::RaS0S9,
136 15 => ZcmpUrlistInner::RaS0S11,
137 _ => {
138 return None;
139 }
140 }
141 };
142
143 Some(Self {
144 inner,
145 reg: PhantomData,
146 })
147 }
148
149 #[inline(always)]
151 pub const fn as_u8(self) -> u8 {
152 self.inner as u8
153 }
154
155 #[inline]
163 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic)]
164 pub fn reg_list(self) -> impl Iterator<Item = Reg> {
165 let regs: &[u8] = match self.inner {
166 ZcmpUrlistInner::Ra => &[1],
167 ZcmpUrlistInner::RaS0 => &[1, 8],
168 ZcmpUrlistInner::RaS0S1 => &[1, 8, 9],
169 ZcmpUrlistInner::RaS0S2 => &[1, 8, 9, 18],
170 ZcmpUrlistInner::RaS0S3 => &[1, 8, 9, 18, 19],
171 ZcmpUrlistInner::RaS0S4 => &[1, 8, 9, 18, 19, 20],
172 ZcmpUrlistInner::RaS0S5 => &[1, 8, 9, 18, 19, 20, 21],
173 ZcmpUrlistInner::RaS0S6 => &[1, 8, 9, 18, 19, 20, 21, 22],
174 ZcmpUrlistInner::RaS0S7 => &[1, 8, 9, 18, 19, 20, 21, 22, 23],
175 ZcmpUrlistInner::RaS0S8 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24],
176 ZcmpUrlistInner::RaS0S9 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25],
177 ZcmpUrlistInner::RaS0S11 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
178 };
179
180 regs.iter().map(|&bits| {
181 unsafe { Reg::from_bits(bits).unwrap_unchecked() }
184 })
185 }
186
187 #[inline(always)]
194 pub const fn stack_adj_base(self) -> u8 {
195 match Reg::XLEN {
196 Self::XLEN_32 => match self.inner {
198 ZcmpUrlistInner::Ra
199 | ZcmpUrlistInner::RaS0
200 | ZcmpUrlistInner::RaS0S1
201 | ZcmpUrlistInner::RaS0S2 => 16,
202 ZcmpUrlistInner::RaS0S3
203 | ZcmpUrlistInner::RaS0S4
204 | ZcmpUrlistInner::RaS0S5
205 | ZcmpUrlistInner::RaS0S6 => 32,
206 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 | ZcmpUrlistInner::RaS0S9 => 48,
207 ZcmpUrlistInner::RaS0S11 => 64,
208 },
209 Self::XLEN_64 => match self.inner {
211 ZcmpUrlistInner::Ra | ZcmpUrlistInner::RaS0 => 16,
212 ZcmpUrlistInner::RaS0S1 | ZcmpUrlistInner::RaS0S2 => 32,
213 ZcmpUrlistInner::RaS0S3 | ZcmpUrlistInner::RaS0S4 => 48,
214 ZcmpUrlistInner::RaS0S5 | ZcmpUrlistInner::RaS0S6 => 64,
215 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 => 80,
216 ZcmpUrlistInner::RaS0S9 => 96,
217 ZcmpUrlistInner::RaS0S11 => 112,
218 },
219 _ => {
220 unsafe { unreachable_unchecked() }
223 }
224 }
225 }
226}
227
228impl<Reg> fmt::Display for ZcmpUrlist<Reg> {
229 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
230 match self.inner {
231 ZcmpUrlistInner::Ra => write!(f, "{{ra}}"),
232 ZcmpUrlistInner::RaS0 => write!(f, "{{ra, s0}}"),
233 ZcmpUrlistInner::RaS0S1 => write!(f, "{{ra, s0-s1}}"),
234 ZcmpUrlistInner::RaS0S2 => write!(f, "{{ra, s0-s2}}"),
235 ZcmpUrlistInner::RaS0S3 => write!(f, "{{ra, s0-s3}}"),
236 ZcmpUrlistInner::RaS0S4 => write!(f, "{{ra, s0-s4}}"),
237 ZcmpUrlistInner::RaS0S5 => write!(f, "{{ra, s0-s5}}"),
238 ZcmpUrlistInner::RaS0S6 => write!(f, "{{ra, s0-s6}}"),
239 ZcmpUrlistInner::RaS0S7 => write!(f, "{{ra, s0-s7}}"),
240 ZcmpUrlistInner::RaS0S8 => write!(f, "{{ra, s0-s8}}"),
241 ZcmpUrlistInner::RaS0S9 => write!(f, "{{ra, s0-s9}}"),
242 ZcmpUrlistInner::RaS0S11 => write!(f, "{{ra, s0-s11}}"),
243 }
244 }
245}
246
247#[instruction(
249 inherit = [Rv32ZcaInstruction, Rv32ZcmpOnlyInstruction],
250)]
251#[derive(Debug, Clone, Copy, PartialEq, Eq)]
252pub enum Rv32ZcmpInstruction<Reg> {}
253
254#[instruction]
255const impl<Reg> Instruction for Rv32ZcmpInstruction<Reg>
256where
257 Reg: [const] Register<Type = u32>,
258{
259 type Reg = Reg;
260
261 #[inline(always)]
262 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
263 fn try_decode(instruction: u32) -> Option<Self> {
264 None
265 }
266
267 #[inline(always)]
268 fn alignment() -> u8 {
269 align_of::<u16>() as u8
270 }
271
272 #[inline(always)]
273 fn size(&self) -> u8 {
274 size_of::<u16>() as u8
275 }
276}
277
278#[instruction]
279impl<Reg> fmt::Display for Rv32ZcmpInstruction<Reg>
280where
281 Reg: Register,
282{
283 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
284 match self {}
285 }
286}
287
288#[instruction]
290#[derive(Debug, Clone, Copy, PartialEq, Eq)]
291#[doc(hidden)]
292pub enum Rv32ZcmpOnlyInstruction<Reg> {
293 CmPush {
297 urlist: ZcmpUrlist<Reg>,
298 stack_adj: u8,
299 },
300 CmPop {
302 urlist: ZcmpUrlist<Reg>,
303 stack_adj: u8,
304 },
305 CmPopretz {
307 urlist: ZcmpUrlist<Reg>,
308 stack_adj: u8,
309 },
310 CmPopret {
312 urlist: ZcmpUrlist<Reg>,
313 stack_adj: u8,
314 },
315 CmMva01s { rs1: Reg, rs2: Reg },
320 CmMvsa01 { rs1: Reg, rs2: Reg },
325}
326
327#[instruction]
328const impl<Reg> Instruction for Rv32ZcmpOnlyInstruction<Reg>
329where
330 Reg: [const] ZcmpRegister<Type = u32>,
331{
332 type Reg = Reg;
333
334 #[inline(always)]
335 #[cfg_attr(feature = "no-panic", no_panic_const::no_panic(const))]
336 fn try_decode(instruction: u32) -> Option<Self> {
337 #[inline(always)]
340 const fn sreg_bits(field: u8) -> u8 {
341 match field {
342 0 => 8,
343 1 => 9,
344 f => f + 16,
345 }
346 }
347
348 let inst = instruction as u16;
349 let quadrant = inst & 0b11;
350 let funct3 = ((inst >> 13) & 0b111) as u8;
351
352 if quadrant != 0b10 || funct3 != 0b101 {
354 None?;
355 }
356
357 let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
358
359 match funct2_12_11 {
360 0b11 => {
362 let op_sel = ((inst >> 9) & 0b11) as u8;
363 let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
364 let spimm = ((inst >> 2) & 0b11) as u8;
365 let stack_adj = urlist.stack_adj_base() + spimm * 16;
366 match op_sel {
367 0b00 => Some(Self::CmPush { urlist, stack_adj }),
368 0b01 => Some(Self::CmPop { urlist, stack_adj }),
369 0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
370 0b11 => Some(Self::CmPopret { urlist, stack_adj }),
371 _ => None,
372 }
373 }
374 0b01 => {
376 if (inst >> 10) & 1 != 1 {
377 None?;
378 }
379
380 let r1s_bits = ((inst >> 7) & 0b111) as u8;
381 let funct2 = ((inst >> 5) & 0b11) as u8;
382 let r2s_bits = ((inst >> 2) & 0b111) as u8;
383
384 let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
389 let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
390
391 match funct2 {
393 0b11 => Some(Self::CmMva01s { rs1: r1s, rs2: r2s }),
394 0b01 => {
395 if r1s_bits == r2s_bits {
397 None?;
398 }
399 Some(Self::CmMvsa01 { rs1: r1s, rs2: r2s })
400 }
401 _ => None,
402 }
403 }
404 _ => None,
406 }
407 }
408
409 #[inline(always)]
410 fn alignment() -> u8 {
411 align_of::<u16>() as u8
412 }
413
414 #[inline(always)]
415 fn size(&self) -> u8 {
416 size_of::<u16>() as u8
417 }
418}
419
420#[instruction]
421impl<Reg> fmt::Display for Rv32ZcmpOnlyInstruction<Reg>
422where
423 Reg: Register,
424{
425 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
426 match self {
427 Self::CmPush { urlist, stack_adj } => {
428 write!(f, "cm.push {urlist}, -{stack_adj}")
429 }
430 Self::CmPop { urlist, stack_adj } => {
431 write!(f, "cm.pop {urlist}, {stack_adj}")
432 }
433 Self::CmPopretz { urlist, stack_adj } => {
434 write!(f, "cm.popretz {urlist}, {stack_adj}")
435 }
436 Self::CmPopret { urlist, stack_adj } => {
437 write!(f, "cm.popret {urlist}, {stack_adj}")
438 }
439 Self::CmMva01s { rs1, rs2 } => write!(f, "cm.mva01s {rs1}, {rs2}"),
440 Self::CmMvsa01 { rs1, rs2 } => write!(f, "cm.mvsa01 {rs1}, {rs2}"),
441 }
442 }
443}