1use crate::U6;
4use core::hash::Hash;
5use core::marker::PhantomData;
6use core::{fmt, ops::Range};
7
8use cranelift_bitset::ScalarBitSet;
9
10pub trait Reg: Sized + Copy + Eq + Ord + Hash + Into<AnyReg> + fmt::Debug + fmt::Display {
12 const RANGE: Range<u8>;
14
15 unsafe fn new_unchecked(index: u8) -> Self;
17
18 fn new(index: u8) -> Option<Self> {
20 if Self::RANGE.contains(&index) {
21 Some(unsafe { Self::new_unchecked(index) })
22 } else {
23 None
24 }
25 }
26
27 fn to_u8(self) -> u8;
29
30 fn index(self) -> usize {
32 self.to_u8().into()
33 }
34}
35
36macro_rules! impl_reg {
37 ($reg_ty:ty, $any:ident, $range:expr) => {
38 impl From<$reg_ty> for AnyReg {
39 fn from(r: $reg_ty) -> Self {
40 AnyReg::$any(r)
41 }
42 }
43
44 impl fmt::Display for $reg_ty {
45 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46 fmt::Debug::fmt(&self, f)
47 }
48 }
49
50 impl Reg for $reg_ty {
51 const RANGE: Range<u8> = $range;
52
53 unsafe fn new_unchecked(index: u8) -> Self {
54 core::mem::transmute(index)
55 }
56
57 fn to_u8(self) -> u8 {
58 self as u8
59 }
60 }
61 };
62}
63
64#[repr(u8)]
66#[derive(Debug,Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
67#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
68#[allow(missing_docs, reason = "self-describing variants")]
69#[expect(non_camel_case_types, reason = "matching in-asm register names")]
70#[rustfmt::skip]
71pub enum XReg {
72 x0, x1, x2, x3, x4, x5, x6, x7, x8, x9,
73 x10, x11, x12, x13, x14, x15, x16, x17, x18, x19,
74 x20, x21, x22, x23, x24, x25, x26, x27, x28, x29,
75
76 sp,
78
79 spilltmp0,
81
82}
83
84impl XReg {
85 pub const SPECIAL_START: u8 = XReg::sp as u8;
87
88 pub fn is_special(self) -> bool {
90 matches!(self, Self::sp | Self::spilltmp0)
91 }
92}
93
94#[test]
95fn assert_special_start_is_right() {
96 for i in 0..XReg::SPECIAL_START {
97 assert!(!XReg::new(i).unwrap().is_special());
98 }
99 for i in XReg::SPECIAL_START.. {
100 match XReg::new(i) {
101 Some(r) => assert!(r.is_special()),
102 None => break,
103 }
104 }
105}
106
107#[repr(u8)]
109#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
110#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
111#[allow(missing_docs, reason = "self-describing variants")]
112#[expect(non_camel_case_types, reason = "matching in-asm register names")]
113#[rustfmt::skip]
114pub enum FReg {
115 f0, f1, f2, f3, f4, f5, f6, f7, f8, f9,
116 f10, f11, f12, f13, f14, f15, f16, f17, f18, f19,
117 f20, f21, f22, f23, f24, f25, f26, f27, f28, f29,
118 f30, f31,
119}
120
121#[repr(u8)]
123#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
124#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
125#[allow(missing_docs, reason = "self-describing variants")]
126#[expect(non_camel_case_types, reason = "matching in-asm register names")]
127#[rustfmt::skip]
128pub enum VReg {
129 v0, v1, v2, v3, v4, v5, v6, v7, v8, v9,
130 v10, v11, v12, v13, v14, v15, v16, v17, v18, v19,
131 v20, v21, v22, v23, v24, v25, v26, v27, v28, v29,
132 v30, v31,
133}
134
135impl_reg!(XReg, X, 0..32);
136impl_reg!(FReg, F, 0..32);
137impl_reg!(VReg, V, 0..32);
138
139#[allow(missing_docs, reason = "self-describing variants")]
144#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
145#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
146pub enum AnyReg {
147 X(XReg),
148 F(FReg),
149 V(VReg),
150}
151
152impl fmt::Display for AnyReg {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 fmt::Debug::fmt(self, f)
155 }
156}
157
158impl fmt::Debug for AnyReg {
159 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
160 match self {
161 AnyReg::X(r) => fmt::Debug::fmt(r, f),
162 AnyReg::F(r) => fmt::Debug::fmt(r, f),
163 AnyReg::V(r) => fmt::Debug::fmt(r, f),
164 }
165 }
166}
167
168#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
170#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
171pub struct BinaryOperands<D, S1 = D, S2 = D> {
172 pub dst: D,
174 pub src1: S1,
176 pub src2: S2,
178}
179
180impl<D, S1, S2> BinaryOperands<D, S1, S2> {
181 pub fn new(dst: impl Into<D>, src1: impl Into<S1>, src2: impl Into<S2>) -> Self {
183 Self {
184 dst: dst.into(),
185 src1: src1.into(),
186 src2: src2.into(),
187 }
188 }
189}
190
191impl<D: Reg, S1: Reg, S2: Reg> BinaryOperands<D, S1, S2> {
192 pub fn to_bits(self) -> u16 {
194 let dst = self.dst.to_u8();
195 let src1 = self.src1.to_u8();
196 let src2 = self.src2.to_u8();
197 (dst as u16) | ((src1 as u16) << 5) | ((src2 as u16) << 10)
198 }
199
200 pub fn from_bits(bits: u16) -> Self {
202 Self {
203 dst: D::new((bits & 0b11111) as u8).unwrap(),
204 src1: S1::new(((bits >> 5) & 0b11111) as u8).unwrap(),
205 src2: S2::new(((bits >> 10) & 0b11111) as u8).unwrap(),
206 }
207 }
208}
209
210impl<D: Reg, S1: Reg> BinaryOperands<D, S1, U6> {
211 pub fn to_bits(self) -> u16 {
213 let dst = self.dst.to_u8();
214 let src1 = self.src1.to_u8();
215 let src2 = u8::from(self.src2);
216 (dst as u16) | ((src1 as u16) << 5) | ((src2 as u16) << 10)
217 }
218
219 pub fn from_bits(bits: u16) -> Self {
221 Self {
222 dst: D::new((bits & 0b11111) as u8).unwrap(),
223 src1: S1::new(((bits >> 5) & 0b11111) as u8).unwrap(),
224 src2: U6::new(((bits >> 10) & 0b111111) as u8).unwrap(),
225 }
226 }
227}
228
229pub struct UpperRegSet<R> {
234 bitset: ScalarBitSet<u16>,
235 phantom: PhantomData<R>,
236}
237
238impl<R: Reg> UpperRegSet<R> {
239 pub fn from_bitset(bitset: ScalarBitSet<u16>) -> Self {
241 Self {
242 bitset,
243 phantom: PhantomData,
244 }
245 }
246
247 pub fn to_bitset(self) -> ScalarBitSet<u16> {
249 self.bitset
250 }
251}
252
253impl<R: Reg> From<ScalarBitSet<u16>> for UpperRegSet<R> {
254 fn from(bitset: ScalarBitSet<u16>) -> Self {
255 Self {
256 bitset,
257 phantom: PhantomData,
258 }
259 }
260}
261
262impl<R: Reg> Into<ScalarBitSet<u16>> for UpperRegSet<R> {
263 fn into(self) -> ScalarBitSet<u16> {
264 self.bitset
265 }
266}
267
268impl<R: Reg> IntoIterator for UpperRegSet<R> {
269 type Item = R;
270 type IntoIter = UpperRegSetIntoIter<R>;
271
272 fn into_iter(self) -> Self::IntoIter {
273 UpperRegSetIntoIter {
274 iter: self.bitset.into_iter(),
275 _marker: PhantomData,
276 }
277 }
278}
279
280pub struct UpperRegSetIntoIter<R> {
282 iter: cranelift_bitset::scalar::Iter<u16>,
283 _marker: PhantomData<R>,
284}
285
286impl<R: Reg> Iterator for UpperRegSetIntoIter<R> {
287 type Item = R;
288 fn next(&mut self) -> Option<R> {
289 Some(R::new(self.iter.next()? + 16).unwrap())
290 }
291}
292
293impl<R: Reg> DoubleEndedIterator for UpperRegSetIntoIter<R> {
294 fn next_back(&mut self) -> Option<R> {
295 Some(R::new(self.iter.next_back()? + 16).unwrap())
296 }
297}
298
299impl<R: Reg> Default for UpperRegSet<R> {
300 fn default() -> Self {
301 Self {
302 bitset: Default::default(),
303 phantom: Default::default(),
304 }
305 }
306}
307
308impl<R: Reg> Copy for UpperRegSet<R> {}
309impl<R: Reg> Clone for UpperRegSet<R> {
310 fn clone(&self) -> Self {
311 *self
312 }
313}
314
315impl<R: Reg> PartialEq for UpperRegSet<R> {
316 fn eq(&self, other: &Self) -> bool {
317 self.bitset == other.bitset
318 }
319}
320impl<R: Reg> Eq for UpperRegSet<R> {}
321
322impl<R: Reg> fmt::Debug for UpperRegSet<R> {
323 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
324 f.debug_set().entries(self.into_iter()).finish()
325 }
326}
327
328#[cfg(feature = "arbitrary")]
329impl<'a, R: Reg> arbitrary::Arbitrary<'a> for UpperRegSet<R> {
330 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
331 ScalarBitSet::arbitrary(u).map(Self::from)
332 }
333}
334
335#[cfg(test)]
336mod tests {
337 use super::*;
338
339 #[test]
340 fn special_x_regs() {
341 assert!(XReg::sp.is_special());
342 assert!(XReg::spilltmp0.is_special());
343 }
344
345 #[test]
346 fn not_special_x_regs() {
347 for i in 0..27 {
348 assert!(!XReg::new(i).unwrap().is_special());
349 }
350 }
351
352 #[test]
353 fn binary_operands() {
354 let mut i = 0;
355 for src2 in XReg::RANGE {
356 for src1 in XReg::RANGE {
357 for dst in XReg::RANGE {
358 let operands = BinaryOperands {
359 dst: XReg::new(dst).unwrap(),
360 src1: XReg::new(src1).unwrap(),
361 src2: XReg::new(src2).unwrap(),
362 };
363 assert_eq!(operands.to_bits(), i);
364 assert_eq!(BinaryOperands::<XReg>::from_bits(i), operands);
365 assert_eq!(BinaryOperands::<XReg>::from_bits(0x8000 | i), operands);
366 i += 1;
367 }
368 }
369 }
370 }
371}