Skip to main content

rucc_opt/range/
ops.rs

1//! What an operation does to a range, forwards and backwards.
2//!
3//! Design: `spec/optimizer/10-value-ranges.md`, sections 10.4 and 10.7. Section 10.4 calls this a
4//! table with an entry per opcode, and says the M4 subset is addition, subtraction,
5//! multiplication, the bitwise operations, the shifts, the comparisons, truncation, sign and zero
6//! extension, and negation. Not division, not remainder, not the overflow builtins, not the
7//! intrinsics: those are cheap to add later against the same tests and expensive to get subtly
8//! wrong now.
9//!
10//! # Forwards and backwards
11//!
12//! Forwards is the easy direction and the one everything else is built on: given what the
13//! operands can be, what can the result be. [`add`] and the rest of the free functions here are
14//! that.
15//!
16//! Backwards is the direction the on-demand query needs, and it is the whole reason a branch
17//! teaches the analysis anything. On the true edge of `if (x < 10)`, the fact is not about the
18//! comparison's result, it is about `x`, and getting there means running the comparison inverse:
19//! given that `x < y` holds and given what `y` can be, what can `x` be. [`narrow_for`] is that,
20//! and it is the one inverse that pays for itself on nearly every branch in nearly every
21//! function. [`backward`] is the rest, for the operations whose inverse is exact and cheap, and
22//! it says so when there is no inverse worth having rather than pretending.
23//!
24//! # Wrapping is an argument and not a check somewhere else
25//!
26//! Section 10.7 names this as the way a range implementation gets a program wrong. `[100, 200] +
27//! [100, 200]` in eight bits is not `[200, 400]`, and in a signed type without `-fwrapv` the
28//! optimizer may assume the overflow did not happen, which is a stronger fact and a different
29//! answer. So every operation that can overflow takes [`Flags`], the same `NSW` and `NUW` the
30//! instruction carries, and there is no way to call one of these and forget. A range computed
31//! under one assumption and used under the other is a miscompilation, and the only defence
32//! against that is not having a version of the function that does not ask.
33//!
34//! What the flags buy is the clamp. Without them the answer is the wrapping one, exact modulo
35//! `2^width`. With `NSW` the sums that do not fit cannot have happened, so the answer is
36//! intersected with the ones that do, and if none of them fit the range is empty, which is the
37//! analysis proving the code is unreachable.
38//!
39//! # Sound, and then as sharp as there is room for
40//!
41//! Every function here returns a range that holds every value the operation can actually produce.
42//! That is the property the tests check exhaustively at width three and four, and it is the one
43//! whose failure is a wrong program. Holding more than that is precision loss, which costs speed
44//! and not correctness, and it happens for two reasons: an answer that needs more than
45//! [`super::PAIRS`] intervals, and an operation whose exact answer is not worth computing. Both
46//! are marked where they happen.
47
48use rucc_ir::{Flags, IntPred};
49
50use super::{Bits, PAIRS, Range, clamp, mask, sign_bit, signed_limits};
51
52/// How many values of a shift count are worth walking one at a time.
53///
54/// A constant shift is one value and most of the rest are a handful, and walking them gives the
55/// exact answer where reasoning about the bounds would round the whole thing off. Past this the
56/// coarse answer is used, which is the bits a shift by the smallest count is bound to produce.
57const COUNTS: usize = 16;
58
59/// Whether a comparison is settled, and which way.
60#[derive(Clone, Copy, Debug, PartialEq, Eq)]
61pub enum Truth {
62    /// It holds however the operands come out, so the comparison folds to one.
63    Always,
64    /// It never holds, so it folds to zero.
65    Never,
66    /// The ranges do not settle it.
67    Either,
68}
69
70/// The sum, modulo the width, and narrowed by whatever the flags promise.
71///
72/// # Panics
73///
74/// Panics if the operands are of different widths.
75#[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                // The smallest sum already leaving the type means every sum does.
90                let lo = al.checked_add(bl).filter(|&lo| lo <= mask(width))?;
91                Some((lo, ah.saturating_add(bh)))
92            },
93        )
94    })
95}
96
97/// The difference, modulo the width, and narrowed by whatever the flags promise.
98///
99/// # Panics
100///
101/// Panics if the operands are of different widths.
102#[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        // The window of a difference runs from the smallest minus the largest to the largest
109        // minus the smallest, which is why on the unsigned side it is the low bound that can
110        // prove the whole pairing impossible.
111        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                // Only the largest difference going below zero rules the pairing out. The
120                // smallest going below zero rules out those particular values and leaves the
121                // rest, which is what the clamp to zero says.
122                let hi = ah.checked_sub(bl)?;
123                Some((al.saturating_sub(bh), hi))
124            },
125        )
126    })
127}
128
129/// Zero minus it, modulo the width, and narrowed by whatever the flags promise.
130#[must_use]
131pub fn neg(a: Range, flags: Flags) -> Range {
132    sub(Range::exactly(0, a.width()), a, flags)
133}
134
135/// The product, modulo the width, and narrowed by whatever the flags promise.
136///
137/// Exact when the largest product fits without wrapping, which is the case that matters, since a
138/// multiply whose operands are known small is the one an index calculation produces. When it does
139/// wrap the answer is everything, refined by the low zero bits, because the low bits of a product
140/// are the low bits of the product whatever the top of it did.
141///
142/// # Panics
143///
144/// Panics if the operands are of different widths.
145#[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    // The low zero bits of a product are the low zero bits of the two added, and that holds
154    // whether or not the top wrapped, so it is the one thing worth knowing in every case.
155    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            // One value times one value is one value, whatever the top of it did. This is worth a
165            // case of its own because a shift by a constant comes through here.
166            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/// The bitwise and.
199///
200/// Worked out a bit at a time, which is exact whenever the operands' bits are known, plus the one
201/// interval fact that holds for every pair: an and is no larger than either of them.
202///
203/// # Panics
204///
205/// Panics if the operands are of different widths.
206#[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/// The bitwise or.
220///
221/// # Panics
222///
223/// Panics if the operands are of different widths.
224#[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    // An or is no smaller than either of them, which is the mirror of the bound on an and.
235    Range::between(al.max(bl), mask(width), width).narrow(bits)
236}
237
238/// The bitwise exclusive or.
239///
240/// A bit of the result is known only where both operands know theirs, so there is nothing here
241/// but the bits. There is no interval bound on an exclusive or that is worth the line.
242///
243/// # Panics
244///
245/// Panics if the operands are of different widths.
246#[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/// The bitwise complement, which is exact: it reverses each interval and nothing else.
259#[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/// The value shifted left by the count, modulo the width.
268///
269/// A count at or above the width has no defined answer, so this says everything rather than
270/// picking one. The count range may be of a different width from the value, since the IR does not
271/// require them to match.
272#[must_use]
273pub fn shl(a: Range, count: Range, flags: Flags) -> Range {
274    shift(a, count, flags, Kind::Left)
275}
276
277/// The value shifted right by the count with zeroes coming in.
278#[must_use]
279pub fn lshr(a: Range, count: Range, flags: Flags) -> Range {
280    shift(a, count, flags, Kind::Logical)
281}
282
283/// The value shifted right by the count with the sign bit coming in.
284#[must_use]
285pub fn ashr(a: Range, count: Range, flags: Flags) -> Range {
286    shift(a, count, flags, Kind::Arithmetic)
287}
288
289/// The low bits of it, at the narrower width.
290///
291/// Exact, including the case nobody expects: a run of consecutive values whose low bits wrap
292/// round is still one wrapping interval at the narrower width, so truncating `[0xfe, 0x101]` to
293/// eight bits gives `[0xfe, 0x01]` and not everything.
294#[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/// It at the wider width with zeroes on top, which keeps every interval as it was.
314#[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/// It at the wider width with the sign bit on top.
324///
325/// An interval that straddles the sign boundary is two intervals afterwards, since the values
326/// just below the boundary stay where they are and the ones at and above it move to the top of
327/// the wider type. Splitting at the boundary first is what makes the rest of it arithmetic.
328#[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/// Whether the ranges settle the comparison.
350///
351/// An empty operand means the comparison is on a path that is never taken, and this answers
352/// [`Truth::Either`] for it rather than picking a side, because folding an unreachable comparison
353/// to a constant is work spent on code that is about to be deleted anyway.
354#[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/// What the left operand can be given that the comparison holds.
367///
368/// This is the inverse the on-demand query runs at every branch, and it is where a range comes
369/// from in the first place: on the true edge of `if (x < 10)` this turns what was known about `x`
370/// into what is known about `x` there. For the other operand, call it with the predicate
371/// [`IntPred::swapped`] and the ranges the other way round.
372///
373/// # Panics
374///
375/// Panics if the operands are of different widths.
376#[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        // Only a value known exactly rules anything out, since `x != y` with `y` in `[1, 2]`
389        // leaves every `x` possible: whichever one it is, the other value of `y` is still there.
390        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/// Which operation an inverse is being asked for.
409///
410/// Only the ones whose inverse is exact and cheap are here. An and, an or and a multiply have
411/// inverses that are either everything or an expensive approximation of everything, and section
412/// 10.4's advice about the ones not worth having applies to them: a wrong answer is a
413/// miscompilation and a vague answer is a slow program, so the vague one is what this gives.
414#[derive(Clone, Copy, Debug, PartialEq, Eq)]
415pub enum Undo {
416    /// The left operand of an addition, given the other one.
417    AddLeft,
418    /// The right operand of a subtraction, given the left one.
419    SubRight,
420    /// The left operand of a subtraction, given the right one.
421    SubLeft,
422    /// The operand of a negation.
423    Neg,
424    /// The operand of a complement.
425    Not,
426    /// The operand of an exclusive or, given the other one.
427    Xor,
428    /// The operand of a zero extension, at the narrower width.
429    Zext(u32),
430    /// The operand of a sign extension, at the narrower width.
431    Sext(u32),
432}
433
434/// What the operand must have been for the operation to have produced this.
435///
436/// The `other` range is the operation's second operand where it has one, at the result's width,
437/// and is ignored where it does not. The answer is at the operand's width, which is the result's
438/// width except for the two extensions.
439#[must_use]
440pub fn backward(undo: Undo, result: Range, other: Range) -> Range {
441    let width = result.width();
442    match undo {
443        // Wrapping addition and subtraction are exactly reversible, and that stays true whatever
444        // the original carried, so the flags do not come into it: the inverse takes the result
445        // back to an operand that could have produced it, and any narrowing the flags allow was
446        // already done on the way forward.
447        Undo::AddLeft => sub(result, other, Flags::NONE),
448        // The right operand of a subtraction is the left one less the result, which is the other
449        // way round from the left operand of an addition however alike the two look.
450        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        // A result outside what the extension can produce means the operand cannot exist, which
456        // is the intersection coming back empty, and that is the analysis proving a path dead.
457        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
462/// Whether some pair of values, one from each, satisfies the comparison.
463fn 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        // Two ranges have a differing pair unless both are the same one value.
473        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
485/// The bits known to be one.
486fn ones(a: Range) -> u128 {
487    a.bits().value()
488}
489
490/// The bits known to be zero.
491fn zeros(a: Range, width: u32) -> u128 {
492    !a.bits().value() & !a.bits().unknown_bits() & mask(width)
493}
494
495/// How many values wide a result is, or `None` when that is all of them.
496///
497/// The number of values a wrapping interval covers is one more than this, so a span equal to the
498/// largest value already covers everything and there is no interval to write down.
499fn 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
506/// Every interval of one against every interval of the other, unioned.
507///
508/// Doing it per pairing rather than once on the two hulls is what keeps the overflow promises
509/// sharp. `[1, 1] u [4, 4]` plus `[3, 3] u [6, 6]` in three signed bits has exactly one pairing
510/// whose sum fits, and a version of this that worked on the hulls would look at `[-4, 1]` and
511/// `[-2, 3]`, see a window that fits, and learn nothing.
512fn 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
526/// The interval that starts here and runs that far, wrapping round if it has to.
527fn 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
535/// The wrapping answer for one pairing, narrowed by whichever overflow promises are made.
536///
537/// A window is the operation done in the integers rather than in the type. `NSW` says the signed
538/// one did not leave the type, so anything outside it did not happen, and if none of it fits the
539/// pairing contributes nothing at all, which is the analysis proving that pairing impossible.
540/// `NUW` says the same of the unsigned one.
541fn 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            // `None` from the window means the pairing overflowed however it came out, and the
558            // promise says it did not, so there is nothing left of it.
559            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
568/// The least and greatest signed values in that interval of bit patterns.
569fn 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/// Which way a shift goes and what comes in behind it.
574#[derive(Clone, Copy, PartialEq, Eq)]
575enum Kind {
576    Left,
577    Logical,
578    Arithmetic,
579}
580
581/// The shift, walking the counts one at a time where there are few enough of them.
582fn 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    // A count at or above the width is undefined, and an analysis that answered anything but
591    // everything here would be exploiting the undefinedness, which is a decision for the pass
592    // that wants it and not for the table.
593    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        // Too many counts to walk, so what is left is the part of the answer that holds for every
609        // count in the range at once.
610        None => coarse(a, low as u32, width, kind),
611    }
612}
613
614/// The shift by one count.
615fn one_shift(a: Range, at: u32, flags: Flags, kind: Kind, width: u32) -> Range {
616    match kind {
617        // A shift left is a multiply by a power of two and gets that entry's exactness for free,
618        // including what the flags say about it.
619        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            // An arithmetic shift is monotone on the signed reading, so the ends stay the ends.
627            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
635/// What holds for every count from this one up.
636fn coarse(a: Range, low: u32, width: u32, kind: Kind) -> Range {
637    match kind {
638        // Shifting left by at least this much leaves that many low zeroes whatever the count was.
639        Kind::Left => Range::full(width).narrow(Bits::from_parts(0, mask(width) << low, width)),
640        // Shifting right by at least this much leaves the top clear.
641        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            // Shifting right moves a value towards zero and stops at zero for a positive one and
647            // at minus one for a negative one, so neither end can pass where it started.
648            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    /// An operation run the way round the inverse claims to undo.
659    type Forwards = Box<dyn Fn(u128, u128) -> u128>;
660
661    /// The width the exhaustive checks run at.
662    ///
663    /// Three, because these walk every range against every range and every value against every
664    /// value, and section 10.4 says a claim about ranges at a small width is either exhaustively
665    /// checkable or not worth stating. Eight values is one hundred and twenty seven ranges, and
666    /// the whole table goes past in a second.
667    const W: u32 = 3;
668
669    /// Every range this width describes exactly.
670    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    /// The values a range says it holds.
694    fn held(range: Range) -> Vec<u128> {
695        (0..=mask(range.width())).filter(|&value| range.contains(value)).collect()
696    }
697
698    /// The result holds every value the operation can produce, and where the operands were one
699    /// value each it holds nothing else.
700    ///
701    /// Soundness is checked always, since losing a value is a wrong program. Sharpness is checked
702    /// on operands that are single values, because an implementation that answered everything
703    /// from every entry would pass a soundness check on its own and be worth nothing, and because
704    /// that is the one case where every entry in the table can be exact. Wider operands are
705    /// allowed to be vague: an exact wrapping product of two intervals is not an interval, and
706    /// section 10.4 is clear that a vague answer is a slow program while a wrong one is a wrong
707    /// program.
708    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    /// Check a binary operation against every value in every pair of ranges.
720    ///
721    /// `truth` gives the value the operation produces, or `None` when the flags say that pairing
722    /// cannot have happened, which is how an overflow promise is expressed as ground truth.
723    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    /// Check a unary operation against every value in every range.
749    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    /// How many runs of consecutive values these are, which is how many intervals it takes to say
759    /// them exactly.
760    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    /// That bit pattern read as a signed number at the test width.
774    fn as_signed(value: u128) -> i128 {
775        signed(value, W)
776    }
777
778    /// Whether that mathematical value fits in a signed number at the test width.
779    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        // In three signed bits, one and minus four against three and minus two. Only two of the
819        // four pairings have a sum that fits, and both come to minus one. Looking at the two
820        // ranges as `[-4, 1]` and `[-2, 3]` would give a window of `[-6, 4]`, which overlaps what
821        // fits, and the answer would have been the three values the wrapping sum allows.
822        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        // A hundred plus a hundred does not fit in a signed byte, and the promise says it did not
831        // overflow, so there is no pair of values this could have been.
832        let hundred = Range::exactly(100, 8);
833        assert!(add(hundred, hundred, Flags::NSW).is_empty());
834        // And the same addition without the promise is just the wrapping answer, which reads as
835        // two hundred unsigned and as minus fifty six signed.
836        assert_eq!(add(hundred, hundred, Flags::NONE).singleton(), Some(200));
837        // The unsigned promise says the same of a sum that goes past two hundred and fifty five.
838        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        // Every count from four up, which is past the walking limit, so this takes the coarse
924        // answer: whatever it shifted, the low four bits came out zero.
925        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        // And the same going the other way leaves the top four clear.
929        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                    // Every operand that could have produced a value in the result has to be in
1052                    // the answer, and for these operations that set is what running the operation
1053                    // the other way round gives.
1054                    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        // A zero extension of a byte cannot have produced anything above 255, and the part of the
1079        // result that it could have produced is the answer.
1080        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        // Nothing a sign extension of a byte produces is in here, so the operand cannot exist.
1086        let impossible = Range::between(0x100, 0x1ff, 32);
1087        assert!(backward(Undo::Sext(8), impossible, Range::full(32)).is_empty());
1088    }
1089
1090    /// Whether the comparison holds of these two values at the test width.
1091    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}