use crate::{
function::ProgPoint,
index::{LiveRangeIdx, SSAValIdx},
+ live_range::sealed::Sealed,
loc::Loc,
};
-use std::{cmp::Ordering, collections::BTreeSet, iter::FusedIterator, ops::Range};
+use std::{
+ cmp::Ordering,
+ collections::BTreeSet,
+ fmt,
+ hash::{Hash, Hasher},
+ iter::FusedIterator,
+ marker::PhantomData,
+ ops::Range,
+};
+
+mod sealed {
+ pub trait Sealed {}
+}
+
+pub trait ProgRangeCmpKind: Sealed + Copy {
+ const OVERLAPPING_IS_EQ: bool;
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
+pub struct OverlappingIsEq;
+
+impl Sealed for OverlappingIsEq {}
+
+impl ProgRangeCmpKind for OverlappingIsEq {
+ const OVERLAPPING_IS_EQ: bool = true;
+}
+
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
+pub struct Lexicographic;
+
+impl Sealed for Lexicographic {}
-#[derive(Copy, Clone, Debug)]
-/// a Range<ProgPoint> except that any overlapping ranges compare equal
-pub struct ProgRange {
+impl ProgRangeCmpKind for Lexicographic {
+ const OVERLAPPING_IS_EQ: bool = false;
+}
+
+#[derive(Copy, Clone)]
+/// a Range<ProgPoint> except that if `CmpKind` is `OverlappingIsEq` and two
+/// ranges overlap, then they compare equal, otherwise they are
+/// lexicographically ordered.
+///
+/// only `ProgRange<Lexicographic>` can impl `Eq`, `ProgRange<OverlappingIsEq>`
+/// can't impl `Eq` because `Eq` requires equality to be transitive, but
+/// `ProgRange<OverlappingIsEq>` fails to be:
+/// ```
+/// # use bigint_presentation_code_register_allocator::live_range::{ProgRange, OverlappingIsEq};
+/// let a = ProgRange::<OverlappingIsEq>::from_usize_range(1..2);
+/// let b = ProgRange::<OverlappingIsEq>::from_usize_range(1..10);
+/// let c = ProgRange::<OverlappingIsEq>::from_usize_range(9..10);
+/// // Eq requires a == b && b == c implies a == c
+/// assert_eq!(a, b);
+/// assert_eq!(b, c);
+/// assert_ne!(a, c); // equality not transitive here
+/// ```
+pub struct ProgRange<CmpKind: ProgRangeCmpKind> {
pub start: ProgPoint,
pub end: ProgPoint,
+ _phantom: PhantomData<fn(CmpKind)>,
+}
+
+impl<CmpKind: ProgRangeCmpKind> fmt::Debug for ProgRange<CmpKind> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("ProgRange")
+ .field("start", &self.start)
+ .field("end", &self.end)
+ .finish()
+ }
}
-impl ProgRange {
+impl<CmpKind: ProgRangeCmpKind> ProgRange<CmpKind> {
+ pub const fn new(range: Range<ProgPoint>) -> Self {
+ let Range { start, end } = range;
+ Self {
+ start,
+ end,
+ _phantom: PhantomData,
+ }
+ }
+ pub const fn as_range(self) -> Range<ProgPoint> {
+ self.start..self.end
+ }
+ pub const fn into<CmpKind2: ProgRangeCmpKind>(self) -> ProgRange<CmpKind2> {
+ let Self {
+ start,
+ end,
+ _phantom: _,
+ } = self;
+ ProgRange {
+ start,
+ end,
+ _phantom: PhantomData,
+ }
+ }
pub const fn is_empty(self) -> bool {
self.len() == 0
}
pub const fn len(self) -> usize {
self.end.as_usize().saturating_sub(self.start.as_usize())
}
- pub fn overlaps(self, other: Self) -> bool {
- self.start < other.end && other.start < self.end
+ pub const fn overlaps(self, other: Self) -> bool {
+ self.start.as_usize() < other.end.as_usize() && other.start.as_usize() < self.end.as_usize()
}
pub const fn as_usize_range(self) -> Range<usize> {
self.start.as_usize()..self.end.as_usize()
Self {
start: ProgPoint::from_usize(value.start),
end: ProgPoint::from_usize(value.end),
+ _phantom: PhantomData,
}
}
pub fn with_self_as_usize_range<R, F: FnOnce(&mut Range<usize>) -> R>(&mut self, f: F) -> R {
*self = Self::from_usize_range(range);
retval
}
+ pub const fn eq(&self, other: &Self) -> bool {
+ if CmpKind::OVERLAPPING_IS_EQ && self.overlaps(*other) {
+ true
+ } else {
+ self.start.as_usize() == other.start.as_usize()
+ && self.end.as_usize() == other.end.as_usize()
+ }
+ }
+ pub const fn cmp(&self, other: &Self) -> Ordering {
+ if CmpKind::OVERLAPPING_IS_EQ && self.overlaps(*other) {
+ Ordering::Equal
+ } else if self.start.as_usize() < other.start.as_usize() {
+ Ordering::Less
+ } else if self.start.as_usize() > other.start.as_usize() {
+ Ordering::Greater
+ } else if self.end.as_usize() < other.end.as_usize() {
+ Ordering::Less
+ } else if self.end.as_usize() > other.end.as_usize() {
+ Ordering::Greater
+ } else {
+ Ordering::Equal
+ }
+ }
}
-impl Eq for ProgRange {}
+impl Eq for ProgRange<Lexicographic> {}
-impl PartialEq for ProgRange {
+impl<CmpKind: ProgRangeCmpKind> Hash for ProgRange<CmpKind> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.start.hash(state);
+ self.end.hash(state);
+ }
+}
+
+impl<CmpKind: ProgRangeCmpKind> PartialEq for ProgRange<CmpKind> {
fn eq(&self, other: &Self) -> bool {
- self.cmp(other) == Ordering::Equal
+ self.eq(other)
}
}
-impl PartialOrd for ProgRange {
+impl<CmpKind: ProgRangeCmpKind> PartialOrd for ProgRange<CmpKind> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
-impl Ord for ProgRange {
+impl<CmpKind: ProgRangeCmpKind> Ord for ProgRange<CmpKind>
+where
+ Self: Eq,
+{
fn cmp(&self, other: &Self) -> Ordering {
- if self.overlaps(*other) {
- Ordering::Equal
- } else {
- (self.start, self.end).cmp(&(other.start, other.end))
- }
+ self.cmp(other)
}
}
-impl IntoIterator for ProgRange {
+impl<CmpKind: ProgRangeCmpKind> IntoIterator for ProgRange<CmpKind> {
type Item = ProgPoint;
type IntoIter = ProgRangeIter;
fn into_iter(self) -> Self::IntoIter {
- ProgRangeIter { range: self }
+ ProgRangeIter { range: self.into() }
}
}
#[derive(Clone, Debug)]
pub struct ProgRangeIter {
- pub range: ProgRange,
+ pub range: ProgRange<Lexicographic>,
}
impl Iterator for ProgRangeIter {
}
}
-/// ensure Hash isn't implemented for ProgRange,
-/// since afaict there isn't a way to implement it consistently with Ord
-fn _check_prog_range_is_not_hashable() {
- struct Hashable<const HASHABLE: bool>;
- trait ProgRangeHashCheck: Sized {
- fn _f(self) -> Hashable<false> {
- Hashable
- }
- }
- struct S<T>([T; 0]);
- impl<T: std::hash::Hash> S<T> {
- fn _f(self) -> Hashable<true> {
- Hashable
- }
- }
- impl<T> ProgRangeHashCheck for S<T> {}
- let _: Hashable<false> = S::<ProgRange>([])._f();
-}
-
#[derive(Clone, Debug)]
pub struct LiveRange {
- pub range: ProgRange,
+ pub range: ProgRange<Lexicographic>,
pub ssa_val: SSAValIdx,
pub allocation: Option<Loc>,
}