2 error::{Error, Result},
3 interned::{GlobalState, Intern, InternTarget, Interned},
4 loc::{Loc, LocFields, LocKind, Ty},
6 use enum_map::{enum_map, EnumMap};
7 use num_bigint::BigUint;
9 use once_cell::race::OnceBox;
10 use serde::{Deserialize, Serialize};
15 btree_map::{self, Entry},
23 ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Range, Sub, SubAssign},
27 fn zero_biguint<'a>() -> &'a BigUint {
28 static ZERO: OnceBox<BigUint> = OnceBox::new();
31 || BigUint::zero().into(),
35 #[derive(Deserialize)]
36 struct LocSetSerialized {
37 starts_map: BTreeMap<NonZeroU32, EnumMap<LocKind, BigUint>>,
40 impl TryFrom<LocSetSerialized> for LocSet {
43 fn try_from(value: LocSetSerialized) -> Result<Self, Self::Error> {
44 Self::from_starts_map(value.starts_map)
48 #[derive(Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
49 #[serde(try_from = "LocSetSerialized")]
51 starts_map: BTreeMap<NonZeroU32, EnumMap<LocKind, BigUint>>,
54 impl<'a> arbitrary::Arbitrary<'a> for LocSet {
55 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
56 u.arbitrary_iter()?.collect()
60 /// computes same value as `a & !b`, but more efficiently
61 fn and_not<A: Borrow<BigUint>, B>(a: A, b: B) -> BigUint
63 BigUint: for<'a> BitXor<A, Output = BigUint>,
64 B: for<'a> BitAnd<&'a BigUint, Output = BigUint>,
66 // use logical equivalent that avoids needing to use BigInt
70 impl From<Loc> for LocSet {
71 fn from(value: Loc) -> Self {
72 Self::from_iter([value])
77 pub fn arbitrary_with_ty(
79 u: &mut arbitrary::Unstructured<'_>,
80 ) -> arbitrary::Result<Self> {
81 let kinds = ty.base_ty.loc_kinds();
83 let kinds: Vec<_> = if kinds.len() > Mask::BITS as usize {
84 let chosen_kinds = kinds
86 .zip(u.arbitrary_iter::<bool>()?)
87 .filter(|(_, cond)| !matches!(cond, Ok(false)))
88 .map(|(&kind, cond)| {
92 .collect::<arbitrary::Result<Vec<_>>>()?;
93 if chosen_kinds.is_empty() {
94 vec![*u.choose(kinds)?]
99 let max_mask = Mask::wrapping_shl(1, kinds.len() as u32).wrapping_sub(1);
100 let mask = u.int_in_range(1..=max_mask)?; // non-zero
104 .filter_map(|(idx, &kind)| {
105 if mask & (1 << idx) != 0 {
113 let mut starts = EnumMap::<LocKind, BigUint>::default();
114 let mut all_zero = true;
116 let bit_count = Loc::max_start(kind, ty.reg_len)? + 1;
117 let byte_count = (bit_count + u8::BITS - 1) / u8::BITS;
118 let bytes = u.bytes(byte_count as usize)?;
119 starts[kind] = BigUint::from_bytes_le(bytes);
120 starts[kind] &= (BigUint::from(1u8) << bit_count) - 1u8;
121 all_zero &= starts[kind].is_zero();
124 Ok(Loc::arbitrary_with_ty(ty, u)?.into())
126 Ok(Self::from_starts_map_iter_unchecked([(ty.reg_len, starts)]))
129 pub fn starts(&self, reg_len: NonZeroU32, kind: LocKind) -> &BigUint {
133 .unwrap_or_else(zero_biguint)
135 pub fn stops(&self, reg_len: NonZeroU32, kind: LocKind) -> BigUint {
136 self.starts(reg_len, kind) << reg_len.get()
138 pub fn starts_map(&self) -> &BTreeMap<NonZeroU32, EnumMap<LocKind, BigUint>> {
141 pub const fn new() -> Self {
143 starts_map: BTreeMap::new(),
146 /// filters out empty entries, but doesn't do any other checks
147 fn from_starts_map_iter_unchecked(
148 starts_map: impl IntoIterator<Item = (NonZeroU32, EnumMap<LocKind, BigUint>)>,
151 starts_map: starts_map
153 .filter(|(_, starts)| !starts.iter().all(|(_, starts)| starts.is_zero()))
157 fn for_each_reg_len_filtering_out_empty_entries(
159 mut f: impl FnMut(NonZeroU32, &mut EnumMap<LocKind, BigUint>),
161 self.starts_map.retain(|®_len, starts| {
163 !Self::is_entry_empty(starts)
166 /// helper for binary operations that keeps Locs not present in rhs
167 fn bin_op_keep_helper(
170 mut f: impl FnMut(NonZeroU32, &mut EnumMap<LocKind, BigUint>, &EnumMap<LocKind, BigUint>),
172 rhs.starts_map.iter().for_each(|(®_len, rhs_starts)| {
173 match self.starts_map.entry(reg_len) {
174 Entry::Vacant(entry) => {
175 let mut lhs_starts = EnumMap::default();
176 f(reg_len, &mut lhs_starts, rhs_starts);
177 if !Self::is_entry_empty(&lhs_starts) {
178 entry.insert(lhs_starts);
181 Entry::Occupied(mut entry) => {
182 f(reg_len, entry.get_mut(), rhs_starts);
183 if Self::is_entry_empty(entry.get()) {
190 fn is_entry_empty(starts: &EnumMap<LocKind, BigUint>) -> bool {
191 starts.iter().all(|(_, starts)| starts.is_zero())
193 pub fn from_starts_map(
194 mut starts_map: BTreeMap<NonZeroU32, EnumMap<LocKind, BigUint>>,
196 let mut error = Ok(());
197 starts_map.retain(|®_len, starts| {
201 let mut any_locs = false;
202 for (kind, starts) in starts {
203 if !starts.is_zero() {
206 // bits() is one past max bit set, so use >= rather than >
207 if starts.bits() >= Loc::max_start(kind, reg_len)? as u64 {
208 return Err(Error::StartNotInValidRange);
219 Ok(Self { starts_map })
221 pub fn clear(&mut self) {
222 self.starts_map.clear();
224 pub fn contains_exact(&self, value: Loc) -> bool {
225 self.starts(value.reg_len, value.kind).bit(value.start as _)
227 pub fn insert(&mut self, value: Loc) -> bool {
228 let starts = match self.starts_map.entry(value.reg_len) {
229 Entry::Occupied(entry) => entry.into_mut(),
230 Entry::Vacant(entry) => {
231 entry.insert(Default::default())[value.kind].set_bit(value.start as u64, true);
235 let starts = &mut starts[value.kind];
236 let retval = !starts.bit(value.start as u64);
237 starts.set_bit(value.start as u64, true);
240 pub fn remove(&mut self, value: Loc) -> bool {
241 let Entry::Occupied(mut entry) = self.starts_map.entry(value.reg_len) else {
244 let starts = entry.get_mut();
245 if starts[value.kind].bit(value.start as u64) {
246 starts[value.kind].set_bit(value.start as u64, false);
247 if starts.values().all(BigUint::is_zero) {
255 pub fn is_empty(&self) -> bool {
256 self.starts_map.is_empty()
258 pub fn iter(&self) -> Iter<'_> {
260 internals: IterInternals::new(self.starts_map.iter()),
263 pub fn len(&self) -> usize {
264 let retval: u64 = self
267 .map(|starts| starts.values().map(BigUint::count_ones).sum::<u64>())
271 /// computes `self = &other - &self`
272 pub fn sub_reverse_assign(&mut self, other: impl Borrow<Self>) {
273 // TODO: make more efficient
274 let other: &Self = other.borrow();
275 *self = other - &*self;
279 #[derive(Clone, Debug)]
280 struct IterInternalsRest<StartsMapValueIter, Starts>
282 StartsMapValueIter: Iterator<Item = (LocKind, Starts)>,
283 Starts: Borrow<BigUint>,
286 starts_map_value_iter: StartsMapValueIter,
289 start_range: Range<u32>,
292 impl<StartsMapValueIter, Starts> IterInternalsRest<StartsMapValueIter, Starts>
294 StartsMapValueIter: Iterator<Item = (LocKind, Starts)>,
295 Starts: Borrow<BigUint>,
297 fn new(reg_len: NonZeroU32, mut starts_map_value_iter: StartsMapValueIter) -> Option<Self> {
299 let (kind, starts) = starts_map_value_iter.next()?;
300 let starts_ref: &BigUint = starts.borrow();
301 let Some(start) = starts_ref.trailing_zeros() else {
304 let start = start.try_into().expect("checked by LocSet constructors");
308 .expect("checked by LocSet constructors");
311 starts_map_value_iter,
314 start_range: start..end,
318 fn next(this: &mut Option<Self>) -> Option<Loc> {
319 while let Some(Self {
321 starts_map_value_iter: _,
327 let Some(start) = start_range.next() else {
328 *this = Self::new(reg_len, this.take().expect("known to be Some").starts_map_value_iter);
331 if starts.borrow().bit(start as u64) {
338 .expect("known to be valid"),
346 #[derive(Clone, Debug)]
347 struct IterInternals<StartsMapIter, RegLen, StartsMapValue, StartsMapValueIter, Starts>
349 StartsMapIter: Iterator<Item = (RegLen, StartsMapValue)>,
350 RegLen: Borrow<NonZeroU32>,
351 StartsMapValue: IntoIterator<IntoIter = StartsMapValueIter>,
352 StartsMapValueIter: Iterator<Item = (LocKind, Starts)>,
353 Starts: Borrow<BigUint>,
355 starts_map_iter: StartsMapIter,
356 rest: Option<IterInternalsRest<StartsMapValueIter, Starts>>,
359 impl<StartsMapIter, RegLen, StartsMapValue, StartsMapValueIter, Starts>
360 IterInternals<StartsMapIter, RegLen, StartsMapValue, StartsMapValueIter, Starts>
362 StartsMapIter: Iterator<Item = (RegLen, StartsMapValue)>,
363 RegLen: Borrow<NonZeroU32>,
364 StartsMapValue: IntoIterator<IntoIter = StartsMapValueIter>,
365 StartsMapValueIter: Iterator<Item = (LocKind, Starts)>,
366 Starts: Borrow<BigUint>,
368 fn new(starts_map_iter: StartsMapIter) -> Self {
374 fn next(&mut self) -> Option<Loc> {
376 while self.rest.is_none() {
377 let (reg_len, starts_map_value) = self.starts_map_iter.next()?;
378 self.rest = IterInternalsRest::new(*reg_len.borrow(), starts_map_value.into_iter());
380 if let Some(loc) = IterInternalsRest::next(&mut self.rest) {
387 #[derive(Clone, Debug)]
388 pub struct Iter<'a> {
389 internals: IterInternals<
390 btree_map::Iter<'a, NonZeroU32, EnumMap<LocKind, BigUint>>,
392 &'a EnumMap<LocKind, BigUint>,
393 enum_map::Iter<'a, LocKind, BigUint>,
398 impl Iterator for Iter<'_> {
401 fn next(&mut self) -> Option<Self::Item> {
402 self.internals.next()
406 impl FusedIterator for Iter<'_> {}
408 pub struct IntoIter {
409 internals: IterInternals<
410 btree_map::IntoIter<NonZeroU32, EnumMap<LocKind, BigUint>>,
412 EnumMap<LocKind, BigUint>,
413 enum_map::IntoIter<LocKind, BigUint>,
418 impl Iterator for IntoIter {
421 fn next(&mut self) -> Option<Self::Item> {
422 self.internals.next()
426 impl FusedIterator for IntoIter {}
428 impl IntoIterator for LocSet {
430 type IntoIter = IntoIter;
432 fn into_iter(self) -> Self::IntoIter {
434 internals: IterInternals::new(self.starts_map.into_iter()),
439 impl<'a> IntoIterator for &'a LocSet {
441 type IntoIter = Iter<'a>;
443 fn into_iter(self) -> Self::IntoIter {
448 impl Extend<Loc> for LocSet {
449 fn extend<T: IntoIterator<Item = Loc>>(&mut self, iter: T) {
450 iter.into_iter().for_each(|item| {
456 impl FromIterator<Loc> for LocSet {
457 fn from_iter<T: IntoIterator<Item = Loc>>(iter: T) -> Self {
458 let mut retval = LocSet::new();
464 struct HexBigUint<'a>(&'a BigUint);
466 impl fmt::Debug for HexBigUint<'_> {
467 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468 write!(f, "{:#x}", self.0)
472 struct LocSetStarts<'a>(&'a EnumMap<LocKind, BigUint>);
474 impl fmt::Debug for LocSetStarts<'_> {
475 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
477 .entries(self.0.iter().map(|(k, v)| (k, HexBigUint(v))))
482 struct LocSetStartsMap<'a>(&'a BTreeMap<NonZeroU32, EnumMap<LocKind, BigUint>>);
484 impl fmt::Debug for LocSetStartsMap<'_> {
485 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
487 .entries(self.0.iter().map(|(k, v)| (k, LocSetStarts(v))))
492 impl fmt::Debug for LocSet {
493 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
494 f.debug_struct("LocSet")
495 .field("starts_map", &LocSetStartsMap(&self.starts_map))
500 macro_rules! forward_bin_op {
502 $bin_op:ident::$bin_op_fn:ident(),
503 $bin_assign_op:ident::$bin_assign_op_fn:ident(),
504 $bin_assign_rev_op_fn:ident(),
506 impl $bin_assign_op<LocSet> for LocSet {
507 fn $bin_assign_op_fn(&mut self, rhs: LocSet) {
508 self.$bin_assign_op_fn(&rhs);
512 impl $bin_op<&'_ LocSet> for LocSet {
513 type Output = LocSet;
515 fn $bin_op_fn(mut self, rhs: &'_ LocSet) -> Self::Output {
516 self.$bin_assign_op_fn(rhs);
521 impl $bin_op<LocSet> for LocSet {
522 type Output = LocSet;
524 fn $bin_op_fn(mut self, rhs: LocSet) -> Self::Output {
525 self.$bin_assign_op_fn(rhs);
530 impl $bin_op<LocSet> for &'_ LocSet {
531 type Output = LocSet;
533 fn $bin_op_fn(self, mut rhs: LocSet) -> Self::Output {
534 rhs.$bin_assign_rev_op_fn(self);
542 for<'a> T: $bin_op<T> + $bin_op<&'a T> + $bin_assign_op<T> + $bin_assign_op<&'a T>,
543 for<'a, 'b> &'a T: $bin_op<T> + $bin_op<&'b T>,
551 impl BitAnd<&'_ LocSet> for &'_ LocSet {
552 type Output = LocSet;
554 fn bitand(self, rhs: &'_ LocSet) -> Self::Output {
555 LocSet::from_starts_map_iter_unchecked(self.starts_map.iter().map(|(®_len, starts)| {
558 enum_map! {kind => (&starts[kind]).bitand(rhs.starts(reg_len, kind))},
564 impl BitAndAssign<&'_ LocSet> for LocSet {
565 fn bitand_assign(&mut self, rhs: &'_ LocSet) {
566 self.for_each_reg_len_filtering_out_empty_entries(|reg_len, starts| {
567 for (kind, starts) in starts {
568 starts.bitand_assign(rhs.starts(reg_len, kind));
574 /// helper for binary operations that keeps Locs not present in rhs
575 macro_rules! impl_bin_op_keep {
577 $bin_op:ident::$bin_op_fn:ident(),
578 $bin_assign_op:ident::$bin_assign_op_fn:ident(),
580 impl $bin_op<&'_ LocSet> for &'_ LocSet {
581 type Output = LocSet;
583 fn $bin_op_fn(self, rhs: &'_ LocSet) -> Self::Output {
584 let mut retval: LocSet = self.clone();
585 retval.$bin_assign_op_fn(rhs);
590 impl $bin_assign_op<&'_ LocSet> for LocSet {
591 fn $bin_assign_op_fn(&mut self, rhs: &'_ LocSet) {
592 self.bin_op_keep_helper(rhs, |_reg_len, lhs_starts, rhs_starts| {
593 for (kind, rhs_starts) in rhs_starts {
594 lhs_starts[kind].$bin_assign_op_fn(rhs_starts);
604 BitAndAssign::bitand_assign(),
610 BitOrAssign::bitor_assign(),
615 BitOrAssign::bitor_assign(),
621 BitXorAssign::bitxor_assign(),
626 BitXorAssign::bitxor_assign(),
630 impl Sub<&'_ LocSet> for &'_ LocSet {
631 type Output = LocSet;
633 fn sub(self, rhs: &'_ LocSet) -> Self::Output {
634 LocSet::from_starts_map_iter_unchecked(self.starts_map.iter().map(|(®_len, starts)| {
637 enum_map! {kind => and_not(&starts[kind], rhs.starts(reg_len, kind))},
643 impl SubAssign<&'_ LocSet> for LocSet {
644 fn sub_assign(&mut self, rhs: &'_ LocSet) {
645 self.bin_op_keep_helper(rhs, |_reg_len, lhs_starts, rhs_starts| {
646 for (kind, lhs_starts) in lhs_starts {
647 let rhs_starts = &rhs_starts[kind];
648 if rhs_starts.is_zero() {
651 *lhs_starts = and_not(mem::take(lhs_starts), rhs_starts);
659 SubAssign::sub_assign(),
660 sub_reverse_assign(),
663 /// the largest number of Locs in `lhs` that a single Loc
664 /// from `rhs` can conflict with
666 pub struct LocSetMaxConflictsWith<Rhs> {
667 lhs: Interned<LocSet>,
669 // result is not included in equality or hash
670 result: Cell<Option<u32>>,
673 impl<Rhs: Hash> Hash for LocSetMaxConflictsWith<Rhs> {
674 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
675 self.lhs.hash(state);
676 self.rhs.hash(state);
680 impl<Rhs: Eq> Eq for LocSetMaxConflictsWith<Rhs> {}
682 impl<Rhs: PartialEq> PartialEq for LocSetMaxConflictsWith<Rhs> {
683 fn eq(&self, other: &Self) -> bool {
684 self.lhs == other.lhs && self.rhs == other.rhs
688 pub trait LocSetMaxConflictsWithTrait: Clone {
690 v: LocSetMaxConflictsWith<Self>,
691 global_state: &GlobalState,
692 ) -> Interned<LocSetMaxConflictsWith<Self>>;
693 fn compute_result(lhs: &Interned<LocSet>, rhs: &Self, global_state: &GlobalState) -> u32;
694 #[cfg(feature = "fuzzing")]
695 fn reference_compute_result(
696 lhs: &Interned<LocSet>,
698 global_state: &GlobalState,
702 impl LocSetMaxConflictsWithTrait for Loc {
703 fn compute_result(lhs: &Interned<LocSet>, rhs: &Self, _global_state: &GlobalState) -> u32 {
704 // now we do the equivalent of:
705 // return lhs.iter().map(|loc| rhs.conflicts(loc) as u32).sum()
707 for (&lhs_reg_len, lhs_starts) in lhs.starts_map() {
708 let lhs_starts = &lhs_starts[rhs.kind];
709 if lhs_starts.is_zero() {
712 // now we do the equivalent of:
713 // retval += sum(rhs.start < lhs_start + lhs_reg_len
714 // and lhs_start < rhs.start + rhs.reg_len
715 // for lhs_start in lhs_starts)
716 let lhs_stops = lhs_starts << lhs_reg_len.get();
718 // find all the bit indexes `i` where `i < rhs.start + 1`
719 let lt_rhs_start_plus_1 = (BigUint::from(1u32) << (rhs.start + 1)) - 1u32;
721 // find all the bit indexes `i` where
722 // `i < rhs.start + rhs.reg_len + lhs_reg_len`
723 let lt_rhs_start_plus_rhs_reg_len_plus_reg_len =
724 (BigUint::from(1u32) << (rhs.start + rhs.reg_len.get() + lhs_reg_len.get())) - 1u32;
725 let lhs_stops_and_lt_rhs_start_plus_1 = &lhs_stops & lt_rhs_start_plus_1;
726 let mut included = and_not(lhs_stops, lhs_stops_and_lt_rhs_start_plus_1);
727 included &= lt_rhs_start_plus_rhs_reg_len_plus_reg_len;
728 retval += included.count_ones() as u32;
733 #[cfg(feature = "fuzzing")]
734 fn reference_compute_result(
735 lhs: &Interned<LocSet>,
737 global_state: &GlobalState,
739 lhs.iter().map(|loc| rhs.conflicts(loc) as u32).sum::<u32>()
743 v: LocSetMaxConflictsWith<Self>,
744 global_state: &GlobalState,
745 ) -> Interned<LocSetMaxConflictsWith<Self>> {
746 v.into_interned(global_state)
750 impl LocSetMaxConflictsWithTrait for Interned<LocSet> {
751 fn compute_result(lhs: &Interned<LocSet>, rhs: &Self, global_state: &GlobalState) -> u32 {
753 .map(|loc| lhs.clone().max_conflicts_with(loc, global_state))
758 #[cfg(feature = "fuzzing")]
759 fn reference_compute_result(
760 lhs: &Interned<LocSet>,
762 global_state: &GlobalState,
765 .map(|loc| lhs.clone().reference_max_conflicts_with(loc, global_state))
771 v: LocSetMaxConflictsWith<Self>,
772 global_state: &GlobalState,
773 ) -> Interned<LocSetMaxConflictsWith<Self>> {
774 v.into_interned(global_state)
778 impl<Rhs: LocSetMaxConflictsWithTrait> LocSetMaxConflictsWith<Rhs> {
779 pub fn lhs(&self) -> &Interned<LocSet> {
782 pub fn rhs(&self) -> &Rhs {
785 pub fn result(&self, global_state: &GlobalState) -> u32 {
786 match self.result.get() {
789 let retval = Rhs::compute_result(&self.lhs, &self.rhs, global_state);
790 self.result.set(Some(retval));
795 #[cfg(feature = "fuzzing")]
796 pub fn reference_result(&self, global_state: &GlobalState) -> u32 {
797 match self.result.get() {
800 let retval = Rhs::reference_compute_result(&self.lhs, &self.rhs, global_state);
801 self.result.set(Some(retval));
808 impl Interned<LocSet> {
809 pub fn max_conflicts_with<Rhs>(self, rhs: Rhs, global_state: &GlobalState) -> u32
811 Rhs: LocSetMaxConflictsWithTrait,
812 LocSetMaxConflictsWith<Rhs>: InternTarget,
814 LocSetMaxConflictsWithTrait::intern(
815 LocSetMaxConflictsWith {
818 result: Cell::default(),
822 .result(global_state)
824 #[cfg(feature = "fuzzing")]
825 pub fn reference_max_conflicts_with<Rhs>(self, rhs: Rhs, global_state: &GlobalState) -> u32
827 Rhs: LocSetMaxConflictsWithTrait,
828 LocSetMaxConflictsWith<Rhs>: InternTarget,
830 LocSetMaxConflictsWithTrait::intern(
831 LocSetMaxConflictsWith {
834 result: Cell::default(),
838 .reference_result(global_state)
840 pub fn conflicts_with<Rhs>(self, rhs: Rhs, global_state: &GlobalState) -> bool
842 Rhs: LocSetMaxConflictsWithTrait,
843 LocSetMaxConflictsWith<Rhs>: InternTarget,
845 self.max_conflicts_with(rhs, global_state) != 0
855 for a in 0..0x10u32 {
858 and_not(&BigUint::from(a), BigUint::from(b)),