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
29unsafe impl const ZcmpRegister for Reg<u32> {
31 const RVE: bool = false;
32}
33
34unsafe impl const ZcmpRegister for Reg<u64> {
36 const RVE: bool = false;
37}
38
39unsafe impl const ZcmpRegister for EReg<u32> {
41 const RVE: bool = true;
42}
43
44unsafe impl const 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 pub fn reg_list(self) -> impl Iterator<Item = Reg> {
164 let regs: &[u8] = match self.inner {
165 ZcmpUrlistInner::Ra => &[1],
166 ZcmpUrlistInner::RaS0 => &[1, 8],
167 ZcmpUrlistInner::RaS0S1 => &[1, 8, 9],
168 ZcmpUrlistInner::RaS0S2 => &[1, 8, 9, 18],
169 ZcmpUrlistInner::RaS0S3 => &[1, 8, 9, 18, 19],
170 ZcmpUrlistInner::RaS0S4 => &[1, 8, 9, 18, 19, 20],
171 ZcmpUrlistInner::RaS0S5 => &[1, 8, 9, 18, 19, 20, 21],
172 ZcmpUrlistInner::RaS0S6 => &[1, 8, 9, 18, 19, 20, 21, 22],
173 ZcmpUrlistInner::RaS0S7 => &[1, 8, 9, 18, 19, 20, 21, 22, 23],
174 ZcmpUrlistInner::RaS0S8 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24],
175 ZcmpUrlistInner::RaS0S9 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25],
176 ZcmpUrlistInner::RaS0S11 => &[1, 8, 9, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27],
177 };
178
179 regs.iter().map(|&bits| {
180 unsafe { Reg::from_bits(bits).unwrap_unchecked() }
183 })
184 }
185
186 #[inline(always)]
193 pub const fn stack_adj_base(self) -> u8 {
194 match Reg::XLEN {
195 Self::XLEN_32 => match self.inner {
197 ZcmpUrlistInner::Ra
198 | ZcmpUrlistInner::RaS0
199 | ZcmpUrlistInner::RaS0S1
200 | ZcmpUrlistInner::RaS0S2 => 16,
201 ZcmpUrlistInner::RaS0S3
202 | ZcmpUrlistInner::RaS0S4
203 | ZcmpUrlistInner::RaS0S5
204 | ZcmpUrlistInner::RaS0S6 => 32,
205 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 | ZcmpUrlistInner::RaS0S9 => 48,
206 ZcmpUrlistInner::RaS0S11 => 64,
207 },
208 Self::XLEN_64 => match self.inner {
210 ZcmpUrlistInner::Ra | ZcmpUrlistInner::RaS0 => 16,
211 ZcmpUrlistInner::RaS0S1 | ZcmpUrlistInner::RaS0S2 => 32,
212 ZcmpUrlistInner::RaS0S3 | ZcmpUrlistInner::RaS0S4 => 48,
213 ZcmpUrlistInner::RaS0S5 | ZcmpUrlistInner::RaS0S6 => 64,
214 ZcmpUrlistInner::RaS0S7 | ZcmpUrlistInner::RaS0S8 => 80,
215 ZcmpUrlistInner::RaS0S9 => 96,
216 ZcmpUrlistInner::RaS0S11 => 112,
217 },
218 _ => {
219 unsafe { unreachable_unchecked() }
222 }
223 }
224 }
225}
226
227impl<Reg> fmt::Display for ZcmpUrlist<Reg> {
228 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
229 match self.inner {
230 ZcmpUrlistInner::Ra => write!(f, "{{ra}}"),
231 ZcmpUrlistInner::RaS0 => write!(f, "{{ra, s0}}"),
232 ZcmpUrlistInner::RaS0S1 => write!(f, "{{ra, s0-s1}}"),
233 ZcmpUrlistInner::RaS0S2 => write!(f, "{{ra, s0-s2}}"),
234 ZcmpUrlistInner::RaS0S3 => write!(f, "{{ra, s0-s3}}"),
235 ZcmpUrlistInner::RaS0S4 => write!(f, "{{ra, s0-s4}}"),
236 ZcmpUrlistInner::RaS0S5 => write!(f, "{{ra, s0-s5}}"),
237 ZcmpUrlistInner::RaS0S6 => write!(f, "{{ra, s0-s6}}"),
238 ZcmpUrlistInner::RaS0S7 => write!(f, "{{ra, s0-s7}}"),
239 ZcmpUrlistInner::RaS0S8 => write!(f, "{{ra, s0-s8}}"),
240 ZcmpUrlistInner::RaS0S9 => write!(f, "{{ra, s0-s9}}"),
241 ZcmpUrlistInner::RaS0S11 => write!(f, "{{ra, s0-s11}}"),
242 }
243 }
244}
245
246#[instruction(
248 inherit = [Rv32ZcaInstruction, Rv32ZcmpOnlyInstruction],
249)]
250#[derive(Debug, Clone, Copy, PartialEq, Eq)]
251pub enum Rv32ZcmpInstruction<Reg> {}
252
253#[instruction]
254impl<Reg> const Instruction for Rv32ZcmpInstruction<Reg>
255where
256 Reg: [const] Register<Type = u32>,
257{
258 type Reg = Reg;
259
260 #[inline(always)]
261 fn try_decode(instruction: u32) -> Option<Self> {
262 None
263 }
264
265 #[inline(always)]
266 fn alignment() -> u8 {
267 align_of::<u16>() as u8
268 }
269
270 #[inline(always)]
271 fn size(&self) -> u8 {
272 size_of::<u16>() as u8
273 }
274}
275
276#[instruction]
277impl<Reg> fmt::Display for Rv32ZcmpInstruction<Reg>
278where
279 Reg: Register,
280{
281 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
282 match self {}
283 }
284}
285
286#[instruction]
288#[derive(Debug, Clone, Copy, PartialEq, Eq)]
289#[doc(hidden)]
290pub enum Rv32ZcmpOnlyInstruction<Reg> {
291 CmPush {
295 urlist: ZcmpUrlist<Reg>,
296 stack_adj: u8,
297 },
298 CmPop {
300 urlist: ZcmpUrlist<Reg>,
301 stack_adj: u8,
302 },
303 CmPopretz {
305 urlist: ZcmpUrlist<Reg>,
306 stack_adj: u8,
307 },
308 CmPopret {
310 urlist: ZcmpUrlist<Reg>,
311 stack_adj: u8,
312 },
313 CmMva01s { rs1: Reg, rs2: Reg },
318 CmMvsa01 { rs1: Reg, rs2: Reg },
323}
324
325#[instruction]
326impl<Reg> const Instruction for Rv32ZcmpOnlyInstruction<Reg>
327where
328 Reg: [const] ZcmpRegister<Type = u32>,
329{
330 type Reg = Reg;
331
332 #[inline(always)]
333 fn try_decode(instruction: u32) -> Option<Self> {
334 #[inline(always)]
337 const fn sreg_bits(field: u8) -> u8 {
338 match field {
339 0 => 8,
340 1 => 9,
341 f => f + 16,
342 }
343 }
344
345 let inst = instruction as u16;
346 let quadrant = inst & 0b11;
347 let funct3 = ((inst >> 13) & 0b111) as u8;
348
349 if quadrant != 0b10 || funct3 != 0b101 {
351 None?;
352 }
353
354 let funct2_12_11 = ((inst >> 11) & 0b11) as u8;
355
356 match funct2_12_11 {
357 0b11 => {
359 let op_sel = ((inst >> 9) & 0b11) as u8;
360 let urlist = ZcmpUrlist::try_from_raw(((inst >> 4) & 0xf) as u8)?;
361 let spimm = ((inst >> 2) & 0b11) as u8;
362 let stack_adj = urlist.stack_adj_base() + spimm * 16;
363 match op_sel {
364 0b00 => Some(Self::CmPush { urlist, stack_adj }),
365 0b01 => Some(Self::CmPop { urlist, stack_adj }),
366 0b10 => Some(Self::CmPopretz { urlist, stack_adj }),
367 0b11 => Some(Self::CmPopret { urlist, stack_adj }),
368 _ => None,
369 }
370 }
371 0b01 => {
373 if (inst >> 10) & 1 != 1 {
374 None?;
375 }
376
377 let r1s_bits = ((inst >> 7) & 0b111) as u8;
378 let funct2 = ((inst >> 5) & 0b11) as u8;
379 let r2s_bits = ((inst >> 2) & 0b111) as u8;
380
381 let r1s = Reg::from_bits(sreg_bits(r1s_bits))?;
386 let r2s = Reg::from_bits(sreg_bits(r2s_bits))?;
387
388 match funct2 {
390 0b11 => Some(Self::CmMva01s { rs1: r1s, rs2: r2s }),
391 0b01 => {
392 if r1s_bits == r2s_bits {
394 None?;
395 }
396 Some(Self::CmMvsa01 { rs1: r1s, rs2: r2s })
397 }
398 _ => None,
399 }
400 }
401 _ => None,
403 }
404 }
405
406 #[inline(always)]
407 fn alignment() -> u8 {
408 align_of::<u16>() as u8
409 }
410
411 #[inline(always)]
412 fn size(&self) -> u8 {
413 size_of::<u16>() as u8
414 }
415}
416
417#[instruction]
418impl<Reg> fmt::Display for Rv32ZcmpOnlyInstruction<Reg>
419where
420 Reg: Register,
421{
422 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
423 match self {
424 Self::CmPush { urlist, stack_adj } => {
425 write!(f, "cm.push {urlist}, -{stack_adj}")
426 }
427 Self::CmPop { urlist, stack_adj } => {
428 write!(f, "cm.pop {urlist}, {stack_adj}")
429 }
430 Self::CmPopretz { urlist, stack_adj } => {
431 write!(f, "cm.popretz {urlist}, {stack_adj}")
432 }
433 Self::CmPopret { urlist, stack_adj } => {
434 write!(f, "cm.popret {urlist}, {stack_adj}")
435 }
436 Self::CmMva01s { rs1, rs2 } => write!(f, "cm.mva01s {rs1}, {rs2}"),
437 Self::CmMvsa01 { rs1, rs2 } => write!(f, "cm.mvsa01 {rs1}, {rs2}"),
438 }
439 }
440}