start adding fuzzing
[bigint-presentation-code.git] / register_allocator / src / loc.rs
index eec4290222a5b8c42cabbd98b056e3d11a4f561c..e6473ef52213e9fdd21cbd266736642f8dc3c5a0 100644 (file)
@@ -1,10 +1,22 @@
 use crate::error::{Error, Result};
+use arbitrary::{size_hint, Arbitrary};
 use enum_map::Enum;
 use serde::{Deserialize, Serialize};
 use std::num::NonZeroU32;
 
 #[derive(
-    Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Enum,
+    Serialize,
+    Deserialize,
+    Copy,
+    Clone,
+    PartialEq,
+    Eq,
+    PartialOrd,
+    Ord,
+    Debug,
+    Hash,
+    Enum,
+    Arbitrary,
 )]
 #[repr(u8)]
 pub enum LocKind {
@@ -36,7 +48,18 @@ impl LocKind {
 }
 
 #[derive(
-    Serialize, Deserialize, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash, Enum,
+    Serialize,
+    Deserialize,
+    Copy,
+    Clone,
+    PartialEq,
+    Eq,
+    PartialOrd,
+    Ord,
+    Debug,
+    Hash,
+    Enum,
+    Arbitrary,
 )]
 #[repr(u8)]
 pub enum BaseTy {
@@ -61,6 +84,25 @@ impl BaseTy {
             Self::Ca | Self::VlMaxvl => nzu32_lit!(1),
         }
     }
+
+    pub const fn loc_kinds(self) -> &'static [LocKind] {
+        match self {
+            BaseTy::Bits64 => &[LocKind::Gpr, LocKind::StackBits64],
+            BaseTy::Ca => &[LocKind::Ca],
+            BaseTy::VlMaxvl => &[LocKind::VlMaxvl],
+        }
+    }
+
+    pub fn arbitrary_reg_len(
+        self,
+        u: &mut arbitrary::Unstructured<'_>,
+    ) -> arbitrary::Result<NonZeroU32> {
+        Ok(NonZeroU32::new(u.int_in_range(1..=self.max_reg_len().get())?).unwrap())
+    }
+
+    pub fn arbitrary_reg_len_size_hint(depth: usize) -> (usize, Option<usize>) {
+        (0, NonZeroU32::size_hint(depth).1)
+    }
 }
 
 validated_fields! {
@@ -72,6 +114,28 @@ validated_fields! {
     }
 }
 
+impl<'a> Arbitrary<'a> for TyFields {
+    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+        let base_ty: BaseTy = u.arbitrary()?;
+        let reg_len = base_ty.arbitrary_reg_len(u)?;
+        Ok(Self { base_ty, reg_len })
+    }
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        let base_ty = BaseTy::size_hint(depth);
+        let reg_len = BaseTy::arbitrary_reg_len_size_hint(depth);
+        size_hint::and(base_ty, reg_len)
+    }
+}
+
+impl<'a> Arbitrary<'a> for Ty {
+    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+        Ok(Ty::new(u.arbitrary()?)?)
+    }
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        TyFields::size_hint(depth)
+    }
+}
+
 impl Ty {
     pub const fn new(fields: TyFields) -> Result<Ty> {
         let TyFields { base_ty, reg_len } = fields;
@@ -127,6 +191,35 @@ validated_fields! {
     }
 }
 
+impl<'a> Arbitrary<'a> for LocFields {
+    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+        let kind: LocKind = u.arbitrary()?;
+        let reg_len = kind.base_ty().arbitrary_reg_len(u)?;
+        let start = Loc::arbitrary_start(kind, reg_len, u)?;
+        Ok(Self {
+            kind,
+            start,
+            reg_len,
+        })
+    }
+
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        let kind = LocKind::size_hint(depth);
+        let reg_len = BaseTy::arbitrary_reg_len_size_hint(depth);
+        let start = Loc::arbitrary_start_size_hint(depth);
+        size_hint::and(size_hint::and(kind, reg_len), start)
+    }
+}
+
+impl<'a> Arbitrary<'a> for Loc {
+    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
+        Ok(Loc::new(u.arbitrary()?)?)
+    }
+    fn size_hint(depth: usize) -> (usize, Option<usize>) {
+        LocFields::size_hint(depth)
+    }
+}
+
 impl LocFields {
     pub const fn ty(self) -> Result<Ty> {
         Ty::new(TyFields {
@@ -140,6 +233,28 @@ impl LocFields {
 }
 
 impl Loc {
+    pub fn arbitrary_with_ty(
+        ty: Ty,
+        u: &mut arbitrary::Unstructured<'_>,
+    ) -> arbitrary::Result<Self> {
+        let kind = *u.choose(ty.base_ty.loc_kinds())?;
+        let start = Self::arbitrary_start(kind, ty.reg_len, u)?;
+        Ok(Self::new(LocFields {
+            kind,
+            start,
+            reg_len: ty.reg_len,
+        })?)
+    }
+    pub fn arbitrary_start(
+        kind: LocKind,
+        reg_len: NonZeroU32,
+        u: &mut arbitrary::Unstructured<'_>,
+    ) -> arbitrary::Result<u32> {
+        u.int_in_range(0..=Loc::max_start(kind, reg_len)?)
+    }
+    pub fn arbitrary_start_size_hint(depth: usize) -> (usize, Option<usize>) {
+        (0, u32::size_hint(depth).1)
+    }
     pub const fn ty(self) -> Ty {
         const_unwrap_res!(self.0.ty(), "Loc can only be constructed with valid fields")
     }
@@ -241,3 +356,32 @@ impl Loc {
         }),
     ];
 }
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_base_ty_loc_kinds() {
+        for loc_kind in 0..LocKind::LENGTH {
+            let loc_kind = LocKind::from_usize(loc_kind);
+            let base_ty = loc_kind.base_ty();
+            let loc_kinds = base_ty.loc_kinds();
+            assert!(
+                loc_kinds.contains(&loc_kind),
+                "loc_kind:{loc_kind:?} base_ty:{base_ty:?} loc_kinds:{loc_kinds:?}"
+            );
+        }
+        for base_ty in 0..BaseTy::LENGTH {
+            let base_ty = BaseTy::from_usize(base_ty);
+            let loc_kinds = base_ty.loc_kinds();
+            for &loc_kind in loc_kinds {
+                assert_eq!(
+                    loc_kind.base_ty(),
+                    base_ty,
+                    "loc_kind:{loc_kind:?} base_ty:{base_ty:?} loc_kinds:{loc_kinds:?}"
+                );
+            }
+        }
+    }
+}