1 use crate::error::{Error, Result};
2 use arbitrary::{size_hint, Arbitrary};
4 use serde::{Deserialize, Serialize};
5 use std::{iter::FusedIterator, num::NonZeroU32};
30 /// since `==` doesn't work with enums in const context
31 pub const fn const_eq(self, other: Self) -> bool {
32 self as u8 == other as u8
34 pub const fn base_ty(self) -> BaseTy {
36 Self::Gpr | Self::StackBits64 => BaseTy::Bits64,
37 Self::Ca => BaseTy::Ca,
38 Self::VlMaxvl => BaseTy::VlMaxvl,
42 pub const fn loc_count(self) -> NonZeroU32 {
44 Self::StackBits64 => nzu32_lit!(512),
45 Self::Gpr | Self::Ca | Self::VlMaxvl => self.base_ty().max_reg_len(),
72 /// since `==` doesn't work with enums in const context
73 pub const fn const_eq(self, other: Self) -> bool {
74 self as u8 == other as u8
77 pub const fn only_scalar(self) -> bool {
78 self.max_reg_len().get() == 1
81 pub const fn max_reg_len(self) -> NonZeroU32 {
83 Self::Bits64 => nzu32_lit!(128),
84 Self::Ca | Self::VlMaxvl => nzu32_lit!(1),
88 pub const fn loc_kinds(self) -> &'static [LocKind] {
90 BaseTy::Bits64 => &[LocKind::Gpr, LocKind::StackBits64],
91 BaseTy::Ca => &[LocKind::Ca],
92 BaseTy::VlMaxvl => &[LocKind::VlMaxvl],
96 pub fn arbitrary_reg_len(
98 u: &mut arbitrary::Unstructured<'_>,
99 ) -> arbitrary::Result<NonZeroU32> {
100 Ok(NonZeroU32::new(u.int_in_range(1..=self.max_reg_len().get())?).unwrap())
103 pub fn arbitrary_reg_len_size_hint(depth: usize) -> (usize, Option<usize>) {
104 (0, NonZeroU32::size_hint(depth).1)
109 #[fields_ty = TyFields]
110 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
113 pub reg_len: NonZeroU32,
117 impl<'a> Arbitrary<'a> for TyFields {
118 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
119 let base_ty: BaseTy = u.arbitrary()?;
120 let reg_len = base_ty.arbitrary_reg_len(u)?;
121 Ok(Self { base_ty, reg_len })
123 fn size_hint(depth: usize) -> (usize, Option<usize>) {
124 let base_ty = BaseTy::size_hint(depth);
125 let reg_len = BaseTy::arbitrary_reg_len_size_hint(depth);
126 size_hint::and(base_ty, reg_len)
130 impl<'a> Arbitrary<'a> for Ty {
131 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
132 Ok(Ty::new(u.arbitrary()?)?)
134 fn size_hint(depth: usize) -> (usize, Option<usize>) {
135 TyFields::size_hint(depth)
140 pub const fn new(fields: TyFields) -> Result<Ty> {
141 let TyFields { base_ty, reg_len } = fields;
142 if base_ty.only_scalar() && reg_len.get() != 1 {
143 Err(Error::TriedToCreateVectorOfOnlyScalarType { base_ty })
144 } else if reg_len.get() > base_ty.max_reg_len().get() {
145 Err(Error::RegLenOutOfRange)
150 /// returns the `Ty` for `fields` or if there was an error, returns the corresponding scalar type (where `reg_len` is `1`)
151 pub const fn new_or_scalar(fields: TyFields) -> Self {
152 match Self::new(fields) {
154 Err(_) => Self::scalar(fields.base_ty),
157 pub const fn scalar(base_ty: BaseTy) -> Self {
160 reg_len: nzu32_lit!(1),
163 pub const fn bits64(reg_len: NonZeroU32) -> Self {
165 base_ty: BaseTy::Bits64,
169 pub const fn try_concat(self, rhs: Self) -> Result<Ty> {
170 if !self.get().base_ty.const_eq(rhs.get().base_ty) {
171 Err(Error::BaseTyMismatch)
173 let Some(reg_len) = self.get().reg_len.checked_add(rhs.get().reg_len.get()) else {
174 return Err(Error::RegLenOutOfRange);
177 base_ty: self.get().base_ty,
185 #[fields_ty = SubLocFields]
186 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
194 pub const fn new(fields: SubLocFields) -> Result<SubLoc> {
195 const_try!(Loc::new(LocFields {
196 reg_len: nzu32_lit!(1),
205 #[fields_ty = LocFields]
206 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
208 pub reg_len: NonZeroU32,
214 impl<'a> Arbitrary<'a> for LocFields {
215 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
216 let kind: LocKind = u.arbitrary()?;
217 let reg_len = kind.base_ty().arbitrary_reg_len(u)?;
218 let start = Loc::arbitrary_start(kind, reg_len, u)?;
226 fn size_hint(depth: usize) -> (usize, Option<usize>) {
227 let kind = LocKind::size_hint(depth);
228 let reg_len = BaseTy::arbitrary_reg_len_size_hint(depth);
229 let start = Loc::arbitrary_start_size_hint(depth);
230 size_hint::and(size_hint::and(kind, reg_len), start)
234 impl<'a> Arbitrary<'a> for Loc {
235 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
236 Ok(Loc::new(u.arbitrary()?)?)
238 fn size_hint(depth: usize) -> (usize, Option<usize>) {
239 LocFields::size_hint(depth)
244 pub const fn ty(self) -> Result<Ty> {
246 base_ty: self.kind.base_ty(),
247 reg_len: self.reg_len,
250 pub const fn stop(self) -> NonZeroU32 {
251 const_unwrap_opt!(self.reg_len.checked_add(self.start), "overflow")
253 pub const fn first_subloc(self) -> SubLocFields {
262 pub const fn first_subloc(self) -> SubLoc {
263 SubLoc(self.get().first_subloc())
265 pub fn arbitrary_with_ty(
267 u: &mut arbitrary::Unstructured<'_>,
268 ) -> arbitrary::Result<Self> {
269 let kind = *u.choose(ty.base_ty.loc_kinds())?;
270 let start = Self::arbitrary_start(kind, ty.reg_len, u)?;
271 Ok(Self::new(LocFields {
277 pub fn arbitrary_start(
280 u: &mut arbitrary::Unstructured<'_>,
281 ) -> arbitrary::Result<u32> {
282 u.int_in_range(0..=Loc::max_start(kind, reg_len)?)
284 pub fn arbitrary_start_size_hint(depth: usize) -> (usize, Option<usize>) {
285 (0, u32::size_hint(depth).1)
287 pub const fn ty(self) -> Ty {
288 const_unwrap_res!(self.0.ty(), "Loc can only be constructed with valid fields")
290 /// does all `Loc` validation except checking `start`, returns the maximum
291 /// value `start` can have, so a `Loc` is valid if
292 /// `start < Loc::max_start(kind, reg_len)?`
293 pub const fn max_start(kind: LocKind, reg_len: NonZeroU32) -> Result<u32, Error> {
295 const_try!(Ty::new(TyFields {
296 base_ty: kind.base_ty(),
299 let loc_count: u32 = kind.loc_count().get();
300 let Some(max_start) = loc_count.checked_sub(reg_len.get()) else {
301 return Err(Error::InvalidRegLen)
305 pub const fn new(fields: LocFields) -> Result<Loc> {
312 if start > const_try!(Self::max_start(kind, reg_len)) {
313 Err(Error::StartNotInValidRange)
318 pub const fn conflicts(self, other: Loc) -> bool {
319 self.0.kind.const_eq(other.0.kind)
320 && self.0.start < other.0.stop().get()
321 && other.0.start < self.0.stop().get()
323 pub const fn get_sub_loc_at_offset(self, sub_loc_ty: Ty, offset: u32) -> Result<Self> {
324 if !sub_loc_ty.get().base_ty.const_eq(self.get().kind.base_ty()) {
325 return Err(Error::BaseTyMismatch);
327 let Some(stop) = sub_loc_ty.get().reg_len.checked_add(offset) else {
328 return Err(Error::InvalidSubLocOutOfRange)
330 if stop.get() > self.get().reg_len.get() {
331 Err(Error::InvalidSubLocOutOfRange)
333 Self::new(LocFields {
334 kind: self.get().kind,
335 start: self.get().start + offset,
336 reg_len: sub_loc_ty.get().reg_len,
340 /// get the Loc containing `self` such that:
341 /// `retval.get_sub_loc_at_offset(self.ty(), offset) == self`
342 /// and `retval.ty() == super_loc_ty`
343 pub const fn get_super_loc_with_self_at_offset(
351 .const_eq(self.get().kind.base_ty())
353 return Err(Error::BaseTyMismatch);
355 let Some(stop) = self.get().reg_len.checked_add(offset) else {
356 return Err(Error::InvalidSubLocOutOfRange)
358 if stop.get() > super_loc_ty.get().reg_len.get() {
359 Err(Error::InvalidSubLocOutOfRange)
361 Self::new(LocFields {
362 kind: self.get().kind,
363 start: self.get().start - offset,
364 reg_len: super_loc_ty.get().reg_len,
368 pub const SPECIAL_GPRS: &[Loc] = &[
372 reg_len: nzu32_lit!(1),
377 reg_len: nzu32_lit!(1),
382 reg_len: nzu32_lit!(1),
387 reg_len: nzu32_lit!(1),
392 ) -> impl Iterator<Item = SubLoc> + FusedIterator + ExactSizeIterator + DoubleEndedIterator
399 (start..self.stop().get()).map(move |start| SubLoc(SubLocFields { kind, start }))
408 fn test_base_ty_loc_kinds() {
409 for loc_kind in 0..LocKind::LENGTH {
410 let loc_kind = LocKind::from_usize(loc_kind);
411 let base_ty = loc_kind.base_ty();
412 let loc_kinds = base_ty.loc_kinds();
414 loc_kinds.contains(&loc_kind),
415 "loc_kind:{loc_kind:?} base_ty:{base_ty:?} loc_kinds:{loc_kinds:?}"
418 for base_ty in 0..BaseTy::LENGTH {
419 let base_ty = BaseTy::from_usize(base_ty);
420 let loc_kinds = base_ty.loc_kinds();
421 for &loc_kind in loc_kinds {
425 "loc_kind:{loc_kind:?} base_ty:{base_ty:?} loc_kinds:{loc_kinds:?}"