1use rucc_ir::{Flags, IntPred};
49
50use super::{Bits, PAIRS, Range, clamp, mask, sign_bit, signed_limits};
51
52const COUNTS: usize = 16;
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61pub enum Truth {
62 Always,
64 Never,
66 Either,
68}
69
70#[must_use]
76pub fn add(a: Range, b: Range, flags: Flags) -> Range {
77 assert_eq!(a.width(), b.width(), "these are ranges of different widths");
78 let width = a.width();
79 per_pair(a, b, |(al, ah), (bl, bh)| {
80 let wrapped = wrapping(al.wrapping_add(bl), span(ah - al, bh - bl, width), width);
81 clamped(
82 wrapped,
83 (al, ah),
84 (bl, bh),
85 flags,
86 width,
87 |(al, ah), (bl, bh)| (al.saturating_add(bl), ah.saturating_add(bh)),
88 |(al, ah), (bl, bh)| {
89 let lo = al.checked_add(bl).filter(|&lo| lo <= mask(width))?;
91 Some((lo, ah.saturating_add(bh)))
92 },
93 )
94 })
95}
96
97#[must_use]
103pub fn sub(a: Range, b: Range, flags: Flags) -> Range {
104 assert_eq!(a.width(), b.width(), "these are ranges of different widths");
105 let width = a.width();
106 per_pair(a, b, |(al, ah), (bl, bh)| {
107 let wrapped = wrapping(al.wrapping_sub(bh), span(ah - al, bh - bl, width), width);
108 clamped(
112 wrapped,
113 (al, ah),
114 (bl, bh),
115 flags,
116 width,
117 |(al, ah), (bl, bh)| (al.saturating_sub(bh), ah.saturating_sub(bl)),
118 |(al, ah), (bl, bh)| {
119 let hi = ah.checked_sub(bl)?;
123 Some((al.saturating_sub(bh), hi))
124 },
125 )
126 })
127}
128
129#[must_use]
131pub fn neg(a: Range, flags: Flags) -> Range {
132 sub(Range::exactly(0, a.width()), a, flags)
133}
134
135#[must_use]
146pub fn mul(a: Range, b: Range, flags: Flags) -> Range {
147 assert_eq!(a.width(), b.width(), "these are ranges of different widths");
148 let width = a.width();
149 if a.is_empty() || b.is_empty() {
150 return Range::empty(width);
151 }
152
153 let zeros = a.bits().low_zeros().saturating_add(b.bits().low_zeros()).min(width);
156 let low = if zeros >= width {
157 Bits::exactly(0, width)
158 } else {
159 Bits::from_parts(0, mask(width) << zeros, width)
160 };
161
162 per_pair(a, b, |(al, ah), (bl, bh)| {
163 let wrapped = if al == ah && bl == bh {
164 Range::exactly(al.wrapping_mul(bl), width)
167 } else {
168 match (al.checked_mul(bl), ah.checked_mul(bh)) {
169 (Some(low), Some(high)) if high <= mask(width) => Range::between(low, high, width),
170 _ => Range::full(width),
171 }
172 };
173 clamped(
174 wrapped,
175 (al, ah),
176 (bl, bh),
177 flags,
178 width,
179 |(al, ah), (bl, bh)| {
180 let corners = [
181 al.saturating_mul(bl),
182 al.saturating_mul(bh),
183 ah.saturating_mul(bl),
184 ah.saturating_mul(bh),
185 ];
186 let least = corners.into_iter().min().expect("four corners");
187 (least, corners.into_iter().max().expect("four corners"))
188 },
189 |(al, ah), (bl, bh)| {
190 let lo = al.checked_mul(bl).filter(|&lo| lo <= mask(width))?;
191 Some((lo, ah.saturating_mul(bh)))
192 },
193 )
194 })
195 .narrow(low)
196}
197
198#[must_use]
207pub fn and(a: Range, b: Range) -> Range {
208 assert_eq!(a.width(), b.width(), "these are ranges of different widths");
209 let width = a.width();
210 let (Some((_, ah)), Some((_, bh))) = (a.unsigned_bounds(), b.unsigned_bounds()) else {
211 return Range::empty(width);
212 };
213 let ones = ones(a) & ones(b);
214 let zeros = zeros(a, width) | zeros(b, width);
215 let bits = Bits::from_parts(ones, mask(width) & !ones & !zeros, width);
216 Range::between(0, ah.min(bh), width).narrow(bits)
217}
218
219#[must_use]
225pub fn or(a: Range, b: Range) -> Range {
226 assert_eq!(a.width(), b.width(), "these are ranges of different widths");
227 let width = a.width();
228 let (Some((al, _)), Some((bl, _))) = (a.unsigned_bounds(), b.unsigned_bounds()) else {
229 return Range::empty(width);
230 };
231 let ones = ones(a) | ones(b);
232 let zeros = zeros(a, width) & zeros(b, width);
233 let bits = Bits::from_parts(ones, mask(width) & !ones & !zeros, width);
234 Range::between(al.max(bl), mask(width), width).narrow(bits)
236}
237
238#[must_use]
247pub fn xor(a: Range, b: Range) -> Range {
248 assert_eq!(a.width(), b.width(), "these are ranges of different widths");
249 let width = a.width();
250 if a.is_empty() || b.is_empty() {
251 return Range::empty(width);
252 }
253 let known = !a.bits().unknown_bits() & !b.bits().unknown_bits();
254 let value = (a.bits().value() ^ b.bits().value()) & known;
255 Range::full(width).narrow(Bits::from_parts(value, mask(width) & !known, width))
256}
257
258#[must_use]
260pub fn not(a: Range) -> Range {
261 let width = a.width();
262 let pairs: Vec<(u128, u128)> =
263 a.pairs().iter().map(|&(lo, hi)| (mask(width) - hi, mask(width) - lo)).collect();
264 Range::from_pairs(&pairs, width)
265}
266
267#[must_use]
273pub fn shl(a: Range, count: Range, flags: Flags) -> Range {
274 shift(a, count, flags, Kind::Left)
275}
276
277#[must_use]
279pub fn lshr(a: Range, count: Range, flags: Flags) -> Range {
280 shift(a, count, flags, Kind::Logical)
281}
282
283#[must_use]
285pub fn ashr(a: Range, count: Range, flags: Flags) -> Range {
286 shift(a, count, flags, Kind::Arithmetic)
287}
288
289#[must_use]
295pub fn trunc(a: Range, to: u32) -> Range {
296 let to = clamp(to);
297 let mut pairs: Vec<(u128, u128)> = Vec::with_capacity(PAIRS * 2);
298 for &(lo, hi) in a.pairs() {
299 if hi - lo >= mask(to) {
300 return Range::full(to);
301 }
302 let (lo, hi) = (lo & mask(to), hi & mask(to));
303 if lo <= hi {
304 pairs.push((lo, hi));
305 } else {
306 pairs.push((0, hi));
307 pairs.push((lo, mask(to)));
308 }
309 }
310 Range::from_pairs(&pairs, to)
311}
312
313#[must_use]
315pub fn zext(a: Range, to: u32) -> Range {
316 let to = clamp(to);
317 if to <= a.width() {
318 return trunc(a, to);
319 }
320 Range::from_pairs(a.pairs(), to).narrow(Bits::from_parts(0, mask(a.width()), to))
321}
322
323#[must_use]
329pub fn sext(a: Range, to: u32) -> Range {
330 let to = clamp(to);
331 let from = a.width();
332 if to <= from {
333 return trunc(a, to);
334 }
335 let boundary = sign_bit(from);
336 let lift = mask(to) - mask(from);
337 let mut pairs: Vec<(u128, u128)> = Vec::with_capacity(PAIRS * 2);
338 for &(lo, hi) in a.pairs() {
339 if lo < boundary {
340 pairs.push((lo, hi.min(boundary - 1)));
341 }
342 if hi >= boundary {
343 pairs.push((lo.max(boundary) + lift, hi + lift));
344 }
345 }
346 Range::from_pairs(&pairs, to)
347}
348
349#[must_use]
355pub fn compare(pred: IntPred, a: Range, b: Range) -> Truth {
356 if a.is_empty() || b.is_empty() {
357 return Truth::Either;
358 }
359 match (possible(pred, a, b), possible(pred.inverse(), a, b)) {
360 (true, false) => Truth::Always,
361 (false, true) => Truth::Never,
362 _ => Truth::Either,
363 }
364}
365
366#[must_use]
377pub fn narrow_for(pred: IntPred, a: Range, b: Range) -> Range {
378 assert_eq!(a.width(), b.width(), "these are ranges of different widths");
379 let width = a.width();
380 if a.is_empty() || b.is_empty() {
381 return Range::empty(width);
382 }
383 let (ul, uh) = b.unsigned_bounds().expect("not empty");
384 let (sl, sh) = b.signed_bounds().expect("not empty");
385 let (low, high) = signed_limits(width);
386 let allowed = match pred {
387 IntPred::Eq => b,
388 IntPred::Ne => match b.singleton() {
391 Some(value) => Range::other_than(value, width),
392 None => return a,
393 },
394 IntPred::Ult if uh == 0 => Range::empty(width),
395 IntPred::Ult => Range::between(0, uh - 1, width),
396 IntPred::Ule => Range::between(0, uh, width),
397 IntPred::Ugt if ul == mask(width) => Range::empty(width),
398 IntPred::Ugt => Range::between(ul + 1, mask(width), width),
399 IntPred::Uge => Range::between(ul, mask(width), width),
400 IntPred::Slt => Range::signed_between(low, sh.saturating_sub(1), width),
401 IntPred::Sle => Range::signed_between(low, sh, width),
402 IntPred::Sgt => Range::signed_between(sl.saturating_add(1), high, width),
403 IntPred::Sge => Range::signed_between(sl, high, width),
404 };
405 a.intersect(allowed)
406}
407
408#[derive(Clone, Copy, Debug, PartialEq, Eq)]
415pub enum Undo {
416 AddLeft,
418 SubRight,
420 SubLeft,
422 Neg,
424 Not,
426 Xor,
428 Zext(u32),
430 Sext(u32),
432}
433
434#[must_use]
440pub fn backward(undo: Undo, result: Range, other: Range) -> Range {
441 let width = result.width();
442 match undo {
443 Undo::AddLeft => sub(result, other, Flags::NONE),
448 Undo::SubRight => sub(other, result, Flags::NONE),
451 Undo::SubLeft => add(result, other, Flags::NONE),
452 Undo::Neg => neg(result, Flags::NONE),
453 Undo::Not => not(result),
454 Undo::Xor => xor(result, other),
455 Undo::Zext(from) => trunc(result.intersect(zext(Range::full(from), width)), from),
458 Undo::Sext(from) => trunc(result.intersect(sext(Range::full(from), width)), from),
459 }
460}
461
462fn possible(pred: IntPred, a: Range, b: Range) -> bool {
464 let (Some((ul, uh)), Some((vl, vh))) = (a.unsigned_bounds(), b.unsigned_bounds()) else {
465 return false;
466 };
467 let (Some((sl, sh)), Some((tl, th))) = (a.signed_bounds(), b.signed_bounds()) else {
468 return false;
469 };
470 match pred {
471 IntPred::Eq => !a.intersect(b).is_empty(),
472 IntPred::Ne => !matches!((a.singleton(), b.singleton()), (Some(x), Some(y)) if x == y),
474 IntPred::Ult => ul < vh,
475 IntPred::Ule => ul <= vh,
476 IntPred::Ugt => uh > vl,
477 IntPred::Uge => uh >= vl,
478 IntPred::Slt => sl < th,
479 IntPred::Sle => sl <= th,
480 IntPred::Sgt => sh > tl,
481 IntPred::Sge => sh >= tl,
482 }
483}
484
485fn ones(a: Range) -> u128 {
487 a.bits().value()
488}
489
490fn zeros(a: Range, width: u32) -> u128 {
492 !a.bits().value() & !a.bits().unknown_bits() & mask(width)
493}
494
495fn span(a: u128, b: u128, width: u32) -> Option<u128> {
500 match a.checked_add(b) {
501 Some(span) if span < mask(width) => Some(span),
502 _ => None,
503 }
504}
505
506fn per_pair(a: Range, b: Range, each: impl Fn((u128, u128), (u128, u128)) -> Range) -> Range {
513 let width = a.width();
514 if a.is_empty() || b.is_empty() {
515 return Range::empty(width);
516 }
517 let mut out = Range::empty(width);
518 for &left in a.pairs() {
519 for &right in b.pairs() {
520 out = out.union(each(left, right));
521 }
522 }
523 out
524}
525
526fn wrapping(lo: u128, span: Option<u128>, width: u32) -> Range {
528 let Some(span) = span else {
529 return Range::full(width);
530 };
531 let lo = lo & mask(width);
532 Range::between(lo, lo.wrapping_add(span) & mask(width), width)
533}
534
535fn clamped(
542 wrapped: Range,
543 a: (u128, u128),
544 b: (u128, u128),
545 flags: Flags,
546 width: u32,
547 signed_window: impl Fn((i128, i128), (i128, i128)) -> (i128, i128),
548 unsigned_window: impl Fn((u128, u128), (u128, u128)) -> Option<(u128, u128)>,
549) -> Range {
550 let mut range = wrapped;
551 if flags.contains(Flags::NSW) {
552 let (lo, hi) = signed_window(as_signed(a, width), as_signed(b, width));
553 range = range.intersect(Range::signed_between(lo, hi, width));
554 }
555 if flags.contains(Flags::NUW) {
556 range = match unsigned_window(a, b) {
557 Some((lo, hi)) if lo <= mask(width) => {
560 range.intersect(Range::between(lo, hi.min(mask(width)), width))
561 }
562 _ => Range::empty(width),
563 };
564 }
565 range
566}
567
568fn as_signed(interval: (u128, u128), width: u32) -> (i128, i128) {
570 Range::between(interval.0, interval.1, width).signed_bounds().expect("not empty")
571}
572
573#[derive(Clone, Copy, PartialEq, Eq)]
575enum Kind {
576 Left,
577 Logical,
578 Arithmetic,
579}
580
581fn shift(a: Range, count: Range, flags: Flags, kind: Kind) -> Range {
583 let width = a.width();
584 if a.is_empty() || count.is_empty() {
585 return Range::empty(width);
586 }
587 let Some((low, _)) = count.unsigned_bounds() else {
588 return Range::empty(width);
589 };
590 if low >= u128::from(width) {
594 return Range::full(width);
595 }
596
597 match count.list(COUNTS) {
598 Some(counts) => {
599 let mut range = Range::empty(width);
600 for at in counts {
601 if at >= u128::from(width) {
602 return Range::full(width);
603 }
604 range = range.union(one_shift(a, at as u32, flags, kind, width));
605 }
606 range
607 }
608 None => coarse(a, low as u32, width, kind),
611 }
612}
613
614fn one_shift(a: Range, at: u32, flags: Flags, kind: Kind, width: u32) -> Range {
616 match kind {
617 Kind::Left => mul(a, Range::exactly(1u128 << at, width), flags),
620 Kind::Logical => {
621 let pairs: Vec<(u128, u128)> =
622 a.pairs().iter().map(|&(lo, hi)| (lo >> at, hi >> at)).collect();
623 Range::from_pairs(&pairs, width)
624 }
625 Kind::Arithmetic => {
626 let Some((lo, hi)) = a.signed_bounds() else {
628 return Range::empty(width);
629 };
630 Range::signed_between(lo >> at, hi >> at, width)
631 }
632 }
633}
634
635fn coarse(a: Range, low: u32, width: u32, kind: Kind) -> Range {
637 match kind {
638 Kind::Left => Range::full(width).narrow(Bits::from_parts(0, mask(width) << low, width)),
640 Kind::Logical => Range::between(0, mask(width) >> low, width),
642 Kind::Arithmetic => {
643 let Some((lo, hi)) = a.signed_bounds() else {
644 return Range::empty(width);
645 };
646 Range::signed_between(lo.min(0), hi.max(-1), width)
649 }
650 }
651}
652
653#[cfg(test)]
654mod tests {
655 use super::*;
656 use crate::range::signed;
657
658 type Forwards = Box<dyn Fn(u128, u128) -> u128>;
660
661 const W: u32 = 3;
668
669 fn all() -> Vec<Range> {
671 let mut ranges = Vec::new();
672 for subset in 0u32..1 << (1u32 << W) {
673 let values: Vec<u128> =
674 (0..=mask(W)).filter(|&value| subset & (1 << value) != 0).collect();
675 let mut pairs: Vec<(u128, u128)> = Vec::new();
676 for &value in &values {
677 match pairs.last_mut() {
678 Some(last) if last.1 + 1 == value => last.1 = value,
679 _ => pairs.push((value, value)),
680 }
681 }
682 if pairs.len() > PAIRS {
683 continue;
684 }
685 let range = Range::from_pairs(&pairs, W);
686 if held(range) == values {
687 ranges.push(range);
688 }
689 }
690 ranges
691 }
692
693 fn held(range: Range) -> Vec<u128> {
695 (0..=mask(range.width())).filter(|&value| range.contains(value)).collect()
696 }
697
698 fn check(got: Range, want: &[u128], what: &str, sharp: bool) {
709 for value in want {
710 assert!(got.contains(*value), "{what} lost {value:#x}, got {got:?}");
711 }
712 if !sharp {
713 return;
714 }
715 let listed: Vec<u128> = held(got);
716 assert_eq!(listed, want, "{what} is vaguer than it has any excuse to be");
717 }
718
719 fn binary(
724 name: &str,
725 op: impl Fn(Range, Range) -> Range,
726 truth: impl Fn(u128, u128) -> Option<u128>,
727 ) {
728 let ranges = all();
729 for &a in &ranges {
730 for &b in &ranges {
731 let mut want: Vec<u128> = Vec::new();
732 for x in held(a) {
733 for y in held(b) {
734 if let Some(value) = truth(x, y) {
735 if !want.contains(&value) {
736 want.push(value);
737 }
738 }
739 }
740 }
741 want.sort_unstable();
742 let sharp = a.singleton().is_some() && b.singleton().is_some();
743 check(op(a, b), &want, &format!("{name}({a:?}, {b:?})"), sharp);
744 }
745 }
746 }
747
748 fn unary(name: &str, op: impl Fn(Range) -> Range, truth: impl Fn(u128) -> u128) {
750 for a in all() {
751 let mut want: Vec<u128> = held(a).into_iter().map(&truth).collect();
752 want.sort_unstable();
753 want.dedup();
754 check(op(a), &want, &format!("{name}({a:?})"), a.singleton().is_some());
755 }
756 }
757
758 fn runs(values: &[u128]) -> usize {
761 let mut count = 0;
762 let mut previous: Option<u128> = None;
763 for &value in values {
764 match previous {
765 Some(last) if last + 1 == value => {}
766 _ => count += 1,
767 }
768 previous = Some(value);
769 }
770 count
771 }
772
773 fn as_signed(value: u128) -> i128 {
775 signed(value, W)
776 }
777
778 fn fits_signed(value: i128) -> bool {
780 let (low, high) = signed_limits(W);
781 (low..=high).contains(&value)
782 }
783
784 #[test]
785 fn addition_wraps_and_says_so() {
786 binary("add", |a, b| add(a, b, Flags::NONE), |x, y| Some(x.wrapping_add(y) & mask(W)));
787 }
788
789 #[test]
790 fn addition_that_promised_not_to_overflow_leaves_out_the_pairs_that_would_have() {
791 binary(
792 "add nsw",
793 |a, b| add(a, b, Flags::NSW),
794 |x, y| {
795 let sum = as_signed(x) + as_signed(y);
796 fits_signed(sum).then(|| x.wrapping_add(y) & mask(W))
797 },
798 );
799 binary("add nuw", |a, b| add(a, b, Flags::NUW), |x, y| (x + y <= mask(W)).then_some(x + y));
800 }
801
802 #[test]
803 fn subtraction_wraps_and_says_so() {
804 binary("sub", |a, b| sub(a, b, Flags::NONE), |x, y| Some(x.wrapping_sub(y) & mask(W)));
805 binary(
806 "sub nsw",
807 |a, b| sub(a, b, Flags::NSW),
808 |x, y| {
809 let difference = as_signed(x) - as_signed(y);
810 fits_signed(difference).then(|| x.wrapping_sub(y) & mask(W))
811 },
812 );
813 binary("sub nuw", |a, b| sub(a, b, Flags::NUW), |x, y| (x >= y).then(|| x - y));
814 }
815
816 #[test]
817 fn the_overflow_promise_is_checked_against_each_pairing_and_not_the_whole_range() {
818 let a = Range::from_pairs(&[(1, 1), (4, 4)], 3);
823 let b = Range::from_pairs(&[(3, 3), (6, 6)], 3);
824 assert_eq!(add(a, b, Flags::NSW).singleton(), Some(7));
825 assert_eq!(add(a, b, Flags::NONE).list(8), Some(vec![2, 4, 7]));
826 }
827
828 #[test]
829 fn a_promise_that_nothing_can_keep_proves_the_code_unreachable() {
830 let hundred = Range::exactly(100, 8);
833 assert!(add(hundred, hundred, Flags::NSW).is_empty());
834 assert_eq!(add(hundred, hundred, Flags::NONE).singleton(), Some(200));
837 assert!(add(Range::exactly(200, 8), hundred, Flags::NUW).is_empty());
839 }
840
841 #[test]
842 fn negation_is_zero_minus_it() {
843 unary("neg", |a| neg(a, Flags::NONE), |x| x.wrapping_neg() & mask(W));
844 }
845
846 #[test]
847 fn multiplication_wraps_and_says_so() {
848 binary("mul", |a, b| mul(a, b, Flags::NONE), |x, y| Some(x.wrapping_mul(y) & mask(W)));
849 binary(
850 "mul nsw",
851 |a, b| mul(a, b, Flags::NSW),
852 |x, y| {
853 let product = as_signed(x) * as_signed(y);
854 fits_signed(product).then(|| x.wrapping_mul(y) & mask(W))
855 },
856 );
857 binary("mul nuw", |a, b| mul(a, b, Flags::NUW), |x, y| (x * y <= mask(W)).then_some(x * y));
858 }
859
860 #[test]
861 fn a_product_of_even_numbers_is_known_to_be_a_multiple_of_four() {
862 let evens = Range::full(32).narrow(Bits::from_parts(0, mask(32) - 1, 32));
863 let product = mul(evens, evens, Flags::NONE);
864 assert_eq!(product.bits().low_zeros(), 2);
865 assert!(!product.contains(2));
866 assert!(product.contains(4));
867 }
868
869 #[test]
870 fn the_bitwise_operations_are_what_they_do_to_every_pair() {
871 binary("and", and, |x, y| Some(x & y));
872 binary("or", or, |x, y| Some(x | y));
873 binary("xor", xor, |x, y| Some(x ^ y));
874 unary("not", not, |x| !x & mask(W));
875 }
876
877 #[test]
878 fn the_shifts_are_what_they_do_to_every_pair() {
879 let counts = Range::between(0, u128::from(W) - 1, W);
880 for count in all() {
881 let count = count.intersect(counts);
882 if count.is_empty() {
883 continue;
884 }
885 for a in all() {
886 let sharp = a.singleton().is_some() && count.singleton().is_some();
887 for (name, got) in [
888 ("shl", shl(a, count, Flags::NONE)),
889 ("lshr", lshr(a, count, Flags::NONE)),
890 ("ashr", ashr(a, count, Flags::NONE)),
891 ] {
892 let mut want: Vec<u128> = Vec::new();
893 for x in held(a) {
894 for at in held(count) {
895 let at = at as u32;
896 let value = match name {
897 "shl" => (x << at) & mask(W),
898 "lshr" => x >> at,
899 _ => (as_signed(x) >> at) as u128 & mask(W),
900 };
901 if !want.contains(&value) {
902 want.push(value);
903 }
904 }
905 }
906 want.sort_unstable();
907 check(got, &want, &format!("{name}({a:?}, {count:?})"), sharp);
908 }
909 }
910 }
911 }
912
913 #[test]
914 fn a_shift_count_that_might_be_too_large_gives_up_rather_than_guessing() {
915 let a = Range::exactly(1, 8);
916 assert!(shl(a, Range::between(8, 9, 8), Flags::NONE).is_full());
917 assert!(shl(a, Range::between(7, 8, 8), Flags::NONE).is_full());
918 assert_eq!(shl(a, Range::exactly(7, 8), Flags::NONE).singleton(), Some(0x80));
919 }
920
921 #[test]
922 fn a_shift_count_with_more_values_than_are_worth_walking_still_says_something() {
923 let wide = Range::between(4, 31, 32);
926 let shifted = shl(Range::full(32), wide, Flags::NONE);
927 assert_eq!(shifted.bits().low_zeros(), 4);
928 assert_eq!(
930 lshr(Range::full(32), wide, Flags::NONE).unsigned_bounds(),
931 Some((0, 0x0fff_ffff))
932 );
933 }
934
935 #[test]
936 fn the_casts_are_what_they_do_to_every_value() {
937 for a in all() {
938 for to in 1..=6u32 {
939 let mut want: Vec<u128> =
940 held(a).into_iter().map(|x| x & mask(to)).collect::<Vec<_>>();
941 want.sort_unstable();
942 want.dedup();
943 let sharp = runs(&want) <= PAIRS;
944 check(trunc(a, to), &want, &format!("trunc({a:?}, {to})"), sharp);
945
946 let mut want: Vec<u128> = held(a).into_iter().map(|x| x & mask(W)).collect();
947 want.sort_unstable();
948 want.dedup();
949 let sharp = runs(&want) <= PAIRS;
950 check(zext(a, W + to), &want, &format!("zext({a:?}, {})", W + to), sharp);
951
952 let mut want: Vec<u128> =
953 held(a).into_iter().map(|x| as_signed(x) as u128 & mask(W + to)).collect();
954 want.sort_unstable();
955 want.dedup();
956 let sharp = runs(&want) <= PAIRS;
957 check(sext(a, W + to), &want, &format!("sext({a:?}, {})", W + to), sharp);
958 }
959 }
960 }
961
962 #[test]
963 fn truncating_a_run_that_wraps_round_is_still_exact() {
964 let range = Range::between(0xfe, 0x101, 32);
965 let low = trunc(range, 8);
966 assert_eq!(held(low), [0x00, 0x01, 0xfe, 0xff]);
967 }
968
969 #[test]
970 fn a_comparison_is_settled_only_when_every_pair_agrees() {
971 let ranges = all();
972 for &a in &ranges {
973 for &b in &ranges {
974 for pred in IntPred::all() {
975 let mut yes = false;
976 let mut no = false;
977 for x in held(a) {
978 for y in held(b) {
979 if holds(pred, x, y) {
980 yes = true;
981 } else {
982 no = true;
983 }
984 }
985 }
986 let want = match (yes, no) {
987 (true, false) => Truth::Always,
988 (false, true) => Truth::Never,
989 _ => Truth::Either,
990 };
991 let got = compare(pred, a, b);
992 if want == Truth::Either {
993 assert_eq!(got, Truth::Either, "{pred} {a:?} {b:?}");
994 } else {
995 assert!(
996 got == want || got == Truth::Either,
997 "{pred} {a:?} {b:?} said {got:?} and it is {want:?}"
998 );
999 }
1000 }
1001 }
1002 }
1003 }
1004
1005 #[test]
1006 fn narrowing_for_a_comparison_keeps_every_value_that_could_satisfy_it() {
1007 let ranges = all();
1008 for &a in &ranges {
1009 for &b in &ranges {
1010 for pred in IntPred::all() {
1011 let mut want: Vec<u128> = Vec::new();
1012 for x in held(a) {
1013 if held(b).into_iter().any(|y| holds(pred, x, y)) {
1014 want.push(x);
1015 }
1016 }
1017 let sharp = runs(&want) <= PAIRS && b.singleton().is_some();
1018 check(narrow_for(pred, a, b), &want, &format!("{pred} {a:?} {b:?}"), sharp);
1019 }
1020 }
1021 }
1022 }
1023
1024 #[test]
1025 fn a_branch_on_a_constant_bound_gives_the_range_the_bound_says() {
1026 let full = Range::full(32);
1027 let ten = Range::exactly(10, 32);
1028 assert_eq!(narrow_for(IntPred::Ult, full, ten).unsigned_bounds(), Some((0, 9)));
1029 assert_eq!(narrow_for(IntPred::Uge, full, ten).unsigned_bounds(), Some((10, 0xffff_ffff)));
1030 assert_eq!(
1031 narrow_for(IntPred::Slt, full, ten).signed_bounds(),
1032 Some((i128::from(i32::MIN), 9))
1033 );
1034 assert!(narrow_for(IntPred::Ne, full, Range::exactly(0, 32)).nonzero());
1035 }
1036
1037 #[test]
1038 fn the_inverses_take_a_result_back_to_an_operand_that_could_have_made_it() {
1039 let ranges = all();
1040 for &result in &ranges {
1041 for &other in &ranges {
1042 let cases: [(Undo, Forwards); 6] = [
1043 (Undo::AddLeft, Box::new(|r: u128, o: u128| r.wrapping_sub(o) & mask(W))),
1044 (Undo::SubRight, Box::new(|r: u128, o: u128| o.wrapping_sub(r) & mask(W))),
1045 (Undo::SubLeft, Box::new(|r: u128, o: u128| r.wrapping_add(o) & mask(W))),
1046 (Undo::Neg, Box::new(|r: u128, _| r.wrapping_neg() & mask(W))),
1047 (Undo::Not, Box::new(|r: u128, _| !r & mask(W))),
1048 (Undo::Xor, Box::new(|r: u128, o: u128| r ^ o)),
1049 ];
1050 for (undo, forwards) in cases {
1051 let mut want: Vec<u128> = Vec::new();
1055 for r in held(result) {
1056 for o in held(other) {
1057 let value = forwards(r, o);
1058 if !want.contains(&value) {
1059 want.push(value);
1060 }
1061 }
1062 }
1063 want.sort_unstable();
1064 let got = backward(undo, result, other);
1065 for value in &want {
1066 assert!(
1067 got.contains(*value),
1068 "{undo:?} of {result:?} and {other:?} lost {value:#x}"
1069 );
1070 }
1071 }
1072 }
1073 }
1074 }
1075
1076 #[test]
1077 fn undoing_an_extension_narrows_and_can_prove_a_path_dead() {
1078 let result = Range::between(0x0f0, 0x1ff, 32);
1081 assert_eq!(
1082 backward(Undo::Zext(8), result, Range::full(32)).unsigned_bounds(),
1083 Some((0xf0, 0xff))
1084 );
1085 let impossible = Range::between(0x100, 0x1ff, 32);
1087 assert!(backward(Undo::Sext(8), impossible, Range::full(32)).is_empty());
1088 }
1089
1090 fn holds(pred: IntPred, x: u128, y: u128) -> bool {
1092 let (sx, sy) = (as_signed(x), as_signed(y));
1093 match pred {
1094 IntPred::Eq => x == y,
1095 IntPred::Ne => x != y,
1096 IntPred::Ult => x < y,
1097 IntPred::Ule => x <= y,
1098 IntPred::Ugt => x > y,
1099 IntPred::Uge => x >= y,
1100 IntPred::Slt => sx < sy,
1101 IntPred::Sle => sx <= sy,
1102 IntPred::Sgt => sx > sy,
1103 IntPred::Sge => sx >= sy,
1104 }
1105 }
1106}