1use std::collections::{BTreeMap, HashMap, HashSet};
75
76use rucc_ir::{Block, Def, Extra, Func, Inst, IntPred, Opcode, Value};
77
78use super::ops::{self, Truth, Undo};
79use super::{PAIRS, Range};
80use crate::cfg::Cfg;
81use crate::dom::Dominators;
82
83const RELATIONS: usize = 16;
90
91const EXCLUSIONS: usize = PAIRS + 1;
97
98#[derive(Clone, Copy, Debug, PartialEq, Eq)]
100pub struct Options {
101 pub logical_depth: u32,
106 pub recompute_depth: u32,
112 pub refinements: usize,
116 pub budget: u64,
129}
130
131impl Default for Options {
132 fn default() -> Self {
133 Self { logical_depth: 6, recompute_depth: 5, refinements: 8, budget: 4096 }
134 }
135}
136
137#[derive(Clone, Debug, Default, PartialEq, Eq)]
143pub struct Counts {
144 queries: u64,
145 hits: u64,
146 fallbacks: u64,
147 full: u64,
148 assumed: u64,
149 exhausted: u64,
150 lost: BTreeMap<Opcode, u64>,
151}
152
153impl Counts {
154 #[must_use]
156 pub const fn queries(&self) -> u64 {
157 self.queries
158 }
159
160 #[must_use]
162 pub const fn hits(&self) -> u64 {
163 self.hits
164 }
165
166 #[must_use]
168 pub const fn fallbacks(&self) -> u64 {
169 self.fallbacks
170 }
171
172 #[must_use]
174 pub const fn full(&self) -> u64 {
175 self.full
176 }
177
178 #[must_use]
185 pub const fn exhausted(&self) -> u64 {
186 self.exhausted
187 }
188
189 #[must_use]
194 pub const fn assumed(&self) -> u64 {
195 self.assumed
196 }
197
198 #[must_use]
200 pub fn losses(&self) -> Vec<(Opcode, u64)> {
201 let mut losses: Vec<(Opcode, u64)> = self.lost.iter().map(|(&op, &n)| (op, n)).collect();
202 losses.sort_by_key(|&(opcode, count)| (std::cmp::Reverse(count), opcode));
203 losses
204 }
205}
206
207#[derive(Clone, Copy, Debug, PartialEq, Eq)]
212struct Relation {
213 left: Value,
214 pred: IntPred,
215 right: Value,
216}
217
218#[derive(Clone, Debug, Default)]
220struct Entry {
221 at_def: Option<Range>,
222 refined: HashMap<Block, Range>,
223}
224
225#[derive(Debug)]
231pub struct Ranges<'a> {
232 func: &'a Func,
233 cfg: &'a Cfg,
234 dom: &'a Dominators,
235 options: Options,
236 cache: HashMap<Value, Entry>,
237 relations: HashMap<Block, Vec<Relation>>,
238 counts: Counts,
239 active: HashSet<Value>,
244 cycles: u64,
247 spent: u64,
249}
250
251impl<'a> Ranges<'a> {
252 #[must_use]
254 pub fn new(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators) -> Self {
255 Self::with(func, cfg, dom, Options::default())
256 }
257
258 #[must_use]
260 pub fn with(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators, options: Options) -> Self {
261 Self {
262 func,
263 cfg,
264 dom,
265 options,
266 cache: HashMap::new(),
267 relations: HashMap::new(),
268 counts: Counts::default(),
269 active: HashSet::new(),
270 cycles: 0,
271 spent: 0,
272 }
273 }
274
275 #[must_use]
277 pub const fn counts(&self) -> &Counts {
278 &self.counts
279 }
280
281 pub fn of(&mut self, value: Value) -> Range {
283 self.counts.queries += 1;
284 self.at_def(value)
285 }
286
287 pub fn at(&mut self, value: Value, block: Block) -> Range {
293 self.counts.queries += 1;
294 self.refined(value, block)
295 }
296
297 pub fn at_inst(&mut self, value: Value, inst: Inst) -> Range {
303 match self.func.block_of(inst) {
304 Some(block) => self.at(value, block),
305 None => self.of(value),
306 }
307 }
308
309 pub fn compare(&mut self, pred: IntPred, a: Value, b: Value, block: Block) -> Truth {
315 let (left, right) = (self.at(a, block), self.at(b, block));
316 if left.width() != right.width() {
317 return Truth::Either;
318 }
319 match ops::compare(pred, left, right) {
320 Truth::Either => (),
321 settled => return settled,
322 }
323 match self.relation(a, b, block) {
324 Some(known) if implies(known, pred) => Truth::Always,
325 Some(known) if excludes(known, pred) => Truth::Never,
326 _ => Truth::Either,
327 }
328 }
329
330 pub fn relation(&mut self, a: Value, b: Value, block: Block) -> Option<IntPred> {
336 let facts = self.facts(block).clone();
337 if let Some(direct) = read(&facts, a, b) {
338 return Some(direct);
339 }
340 for step in &facts {
341 for middle in [step.left, step.right] {
342 if middle == a || middle == b {
343 continue;
344 }
345 let composed = read(&facts, a, middle)
346 .zip(read(&facts, middle, b))
347 .and_then(|(first, second)| compose(first, second));
348 if composed.is_some() {
349 return composed;
350 }
351 }
352 }
353 None
354 }
355
356 fn at_def(&mut self, value: Value) -> Range {
358 let ty = self.func[value].ty;
359 if !ty.is_int() || !ty.is_scalar() {
360 return Range::of(ty);
361 }
362 if let Some(cached) = self.cache.get(&value).and_then(|entry| entry.at_def) {
363 self.counts.hits += 1;
364 return cached;
365 }
366 if !self.active.insert(value) {
367 self.cycles += 1;
368 return Range::of(ty);
369 }
370 if self.spent >= self.options.budget {
374 self.active.remove(&value);
375 self.counts.exhausted += 1;
376 return Range::of(ty);
377 }
378 self.spent += 1;
379 let before = self.cycles;
380 let range = self.compute(value);
381 self.active.remove(&value);
382 if self.cycles == before {
383 self.cache.entry(value).or_default().at_def = Some(range);
384 }
385 range
386 }
387
388 fn compute(&mut self, value: Value) -> Range {
390 let ty = self.func[value].ty;
391 match self.func[value].def {
392 Def::Param { block, index } => self.of_param(value, block, index),
393 Def::Result { inst, .. } => {
394 let range = self.of_inst(value, inst);
395 if range.is_full() {
396 self.counts.full += 1;
397 *self.counts.lost.entry(self.func[inst].opcode).or_default() += 1;
398 }
399 debug_assert_eq!(range.width(), ty.bits(), "a range of the wrong width");
400 range
401 }
402 }
403 }
404
405 fn of_param(&mut self, value: Value, block: Block, index: u32) -> Range {
407 let ty = self.func[value].ty;
408 if self.cfg.entry() == Some(block) {
409 return Range::of(ty);
410 }
411 let preds: Vec<Block> = self.cfg.predecessors(block).to_vec();
412 if preds.is_empty() {
413 return Range::of(ty);
414 }
415 let mut range = Range::empty(ty.bits());
416 for pred in preds {
417 let Some(arg) = argument(self.func, pred, block, index as usize) else {
418 return Range::of(ty);
419 };
420 let incoming = self.refined(arg, pred);
421 let edge = self.edge_fact(pred, block, arg).unwrap_or_else(|| Range::of(ty));
422 range = range.union(incoming.intersect(edge));
423 if range.is_full() {
424 return range;
425 }
426 }
427 range
428 }
429
430 fn of_inst(&mut self, value: Value, inst: Inst) -> Range {
433 let ty = self.func[value].ty;
434 let width = ty.bits();
435 let data = self.func[inst];
436 let block = self.func.block_of(inst);
437 let args: Vec<Value> = self.func[data.args].to_vec();
438 let flags = data.flags;
439 let operand = |this: &mut Self, index: usize| match (args.get(index), block) {
440 (Some(&arg), Some(block)) => this.refined(arg, block),
441 (Some(&arg), None) => this.at_def(arg),
442 (None, _) => Range::of(ty),
443 };
444 match data.opcode {
445 Opcode::IConst => {
446 let Extra::Imm(at) = data.extra else { return Range::of(ty) };
447 Range::exactly(self.func[at].unsigned(), width)
448 }
449 Opcode::Add | Opcode::Sub | Opcode::Mul => {
450 let (a, b) = (operand(self, 0), operand(self, 1));
451 if a.width() != b.width() {
452 return Range::of(ty);
453 }
454 let apply = |flags| match data.opcode {
455 Opcode::Add => ops::add(a, b, flags),
456 Opcode::Sub => ops::sub(a, b, flags),
457 _ => ops::mul(a, b, flags),
458 };
459 self.assuming(apply, flags)
460 }
461 Opcode::And | Opcode::Or | Opcode::Xor => {
462 let (a, b) = (operand(self, 0), operand(self, 1));
463 if a.width() != b.width() {
464 return Range::of(ty);
465 }
466 match data.opcode {
467 Opcode::And => ops::and(a, b),
468 Opcode::Or => ops::or(a, b),
469 _ => ops::xor(a, b),
470 }
471 }
472 Opcode::Shl | Opcode::LShr | Opcode::AShr => {
473 let (a, count) = (operand(self, 0), operand(self, 1));
474 if a.width() != count.width() {
475 return Range::of(ty);
476 }
477 let apply = |flags| match data.opcode {
478 Opcode::Shl => ops::shl(a, count, flags),
479 Opcode::LShr => ops::lshr(a, count, flags),
480 _ => ops::ashr(a, count, flags),
481 };
482 self.assuming(apply, flags)
483 }
484 Opcode::Trunc => ops::trunc(operand(self, 0), width),
485 Opcode::ZExt => ops::zext(operand(self, 0), width),
486 Opcode::SExt => ops::sext(operand(self, 0), width),
487 Opcode::ICmp => {
488 let Extra::IntPred(pred) = data.extra else { return Range::of(ty) };
489 let (a, b) = (operand(self, 0), operand(self, 1));
490 if a.width() != b.width() {
491 return Range::of(ty);
492 }
493 match ops::compare(pred, a, b) {
494 Truth::Always => Range::exactly(1, width),
495 Truth::Never => Range::exactly(0, width),
496 Truth::Either => Range::of(ty),
497 }
498 }
499 Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop => {
502 let counted = args.first().map_or(width, |&arg| self.func[arg].ty.bits());
503 Range::between(0, u128::from(counted), width)
504 }
505 _ => Range::of(ty),
506 }
507 }
508
509 fn assuming(
516 &mut self,
517 apply: impl Fn(rucc_ir::Flags) -> Range,
518 flags: rucc_ir::Flags,
519 ) -> Range {
520 let range = apply(flags);
521 if !flags.is_empty() && range != apply(rucc_ir::Flags::NONE) {
522 self.counts.assumed += 1;
523 }
524 range
525 }
526
527 fn refined(&mut self, value: Value, block: Block) -> Range {
529 let ty = self.func[value].ty;
530 if !ty.is_int() || !ty.is_scalar() {
531 return Range::of(ty);
532 }
533 if let Some(&cached) = self.cache.get(&value).and_then(|e| e.refined.get(&block)) {
534 self.counts.hits += 1;
535 return cached;
536 }
537 let full = self
538 .cache
539 .get(&value)
540 .is_some_and(|entry| entry.refined.len() >= self.options.refinements);
541 if full {
542 self.counts.fallbacks += 1;
543 return self.at_def(value);
544 }
545 let before = self.cycles;
546 let range = self.walk(value, block);
547 if self.cycles == before {
548 let entry = self.cache.entry(value).or_default();
549 if entry.refined.len() < self.options.refinements {
550 entry.refined.insert(block, range);
551 }
552 }
553 range
554 }
555
556 fn walk(&mut self, value: Value, block: Block) -> Range {
562 let mut range = self.at_def(value);
563 let stop = defining_block(self.func, value);
564 let mut cursor = block;
565 let mut steps = 0;
566 while steps < self.options.recompute_depth && Some(cursor) != stop {
567 let Some(parent) = self.dom.immediate_dominator(cursor) else { break };
568 if self.cfg.predecessors(cursor) == [parent] {
569 if let Some(fact) = self.edge_fact(parent, cursor, value) {
570 range = range.intersect(fact);
571 }
572 }
573 cursor = parent;
574 steps += 1;
575 }
576 range
577 }
578
579 fn edge_fact(&mut self, from: Block, to: Block, value: Value) -> Option<Range> {
581 let term = self.func.terminator(from)?;
582 let depth = self.options.logical_depth;
583 match self.func[term].opcode {
584 Opcode::BrIf => {
585 let calls: Vec<_> = self.func.successors(term).collect();
586 let (then, other) = (calls.first()?, calls.get(1)?);
587 if then.block == other.block {
588 return None;
589 }
590 let taken = then.block == to;
591 let cond = *self.func[self.func[term].args].first()?;
592 self.condition_fact(cond, taken, value, from, depth)
593 }
594 Opcode::Switch => self.switch_fact(term, to, value, from, depth),
595 _ => None,
596 }
597 }
598
599 fn switch_fact(
602 &mut self,
603 term: Inst,
604 to: Block,
605 value: Value,
606 block: Block,
607 depth: u32,
608 ) -> Option<Range> {
609 if depth == 0 {
610 return None;
611 }
612 let Extra::Switch(info) = self.func[term].extra else { return None };
613 let info = self.func[info];
614 let calls: Vec<_> = self.func[info.targets].to_vec();
615 let cases: Vec<_> = self.func[info.cases].to_vec();
616 let subject = *self.func[self.func[term].args].first()?;
617 let width = self.func[subject].ty.bits();
618 let default = calls.first()?.block;
619 let hits: Vec<usize> = (1..calls.len()).filter(|&index| calls[index].block == to).collect();
620 let known = if default == to {
621 if !hits.is_empty() {
625 return None;
626 }
627 let mut range = Range::full(width);
628 for &case in cases.iter().take(EXCLUSIONS) {
629 range = range.intersect(Range::other_than(case.unsigned(), width));
630 }
631 range
632 } else {
633 let pairs: Vec<(u128, u128)> = hits
634 .iter()
635 .filter_map(|&index| cases.get(index - 1))
636 .map(|case| (case.unsigned(), case.unsigned()))
637 .collect();
638 if pairs.is_empty() {
639 return None;
640 }
641 Range::from_pairs(&pairs, width)
642 };
643 self.carry_back(subject, known, value, block, depth - 1)
644 }
645
646 fn condition_fact(
648 &mut self,
649 cond: Value,
650 taken: bool,
651 value: Value,
652 block: Block,
653 depth: u32,
654 ) -> Option<Range> {
655 if depth == 0 {
656 return None;
657 }
658 if cond == value {
659 let width = self.func[value].ty.bits();
660 return Some(Range::exactly(u128::from(taken), width));
661 }
662 let Def::Result { inst, .. } = self.func[cond].def else { return None };
663 let data = self.func[inst];
664 let args: Vec<Value> = self.func[data.args].to_vec();
665 match data.opcode {
666 Opcode::ICmp => {
667 let Extra::IntPred(pred) = data.extra else { return None };
668 let pred = if taken { pred } else { pred.inverse() };
669 let (&left, &right) = (args.first()?, args.get(1)?);
670 let (a, b) = (self.refined(left, block), self.refined(right, block));
671 if a.width() != b.width() {
672 return None;
673 }
674 let want = ops::narrow_for(pred, a, b);
675 if let Some(found) = self.carry_back(left, want, value, block, depth - 1) {
676 return Some(found);
677 }
678 let want = ops::narrow_for(pred.swapped(), b, a);
679 self.carry_back(right, want, value, block, depth - 1)
680 }
681 Opcode::And | Opcode::Or => {
686 let holds = data.opcode == Opcode::And;
687 if taken != holds {
688 return None;
689 }
690 let (&left, &right) = (args.first()?, args.get(1)?);
691 let a = self.condition_fact(left, taken, value, block, depth - 1);
692 let b = self.condition_fact(right, taken, value, block, depth - 1);
693 match (a, b) {
694 (Some(a), Some(b)) => Some(a.intersect(b)),
695 (found, None) | (None, found) => found,
696 }
697 }
698 Opcode::Xor => {
701 let (&left, &right) = (args.first()?, args.get(1)?);
702 let (cond, other) = match self.constant(right) {
703 Some(_) => (left, right),
704 None => (right, left),
705 };
706 let one = self.constant(other)? == 1 && self.func[other].ty.bits() == 1;
707 if !one {
708 return None;
709 }
710 self.condition_fact(cond, !taken, value, block, depth - 1)
711 }
712 _ => None,
713 }
714 }
715
716 fn carry_back(
723 &mut self,
724 subject: Value,
725 known: Range,
726 value: Value,
727 block: Block,
728 depth: u32,
729 ) -> Option<Range> {
730 if subject == value {
731 return Some(known);
732 }
733 if depth == 0 || known.is_full() {
734 return None;
735 }
736 let Def::Result { inst, .. } = self.func[subject].def else { return None };
737 let data = self.func[inst];
738 let args: Vec<Value> = self.func[data.args].to_vec();
739 let (&left, right) = (args.first()?, args.get(1).copied());
740 let steps: Vec<(Value, Undo, Option<Value>)> = match data.opcode {
741 Opcode::Add => vec![(left, Undo::AddLeft, right), (right?, Undo::AddLeft, Some(left))],
745 Opcode::Sub => vec![(left, Undo::SubLeft, right), (right?, Undo::SubRight, Some(left))],
746 Opcode::Xor => vec![(left, Undo::Xor, right), (right?, Undo::Xor, Some(left))],
747 Opcode::ZExt => vec![(left, Undo::Zext(self.func[left].ty.bits()), None)],
748 Opcode::SExt => vec![(left, Undo::Sext(self.func[left].ty.bits()), None)],
749 _ => return None,
750 };
751 for (operand, undo, other) in steps {
752 let other = match other {
753 Some(other) => self.refined(other, block),
754 None => Range::full(known.width()),
755 };
756 if other.width() != known.width() {
757 continue;
758 }
759 let back = ops::backward(undo, known, other);
760 if let Some(found) = self.carry_back(operand, back, value, block, depth - 1) {
761 return Some(found);
762 }
763 }
764 None
765 }
766
767 fn facts(&mut self, block: Block) -> &Vec<Relation> {
769 if !self.relations.contains_key(&block) {
770 let mut facts = match self.dom.immediate_dominator(block) {
771 Some(parent) => self.facts(parent).clone(),
772 None => Vec::new(),
773 };
774 if let Some(own) = self.own_relation(block) {
775 facts.push(own);
776 if facts.len() > RELATIONS {
777 facts.remove(0);
778 }
779 }
780 self.relations.insert(block, facts);
781 }
782 &self.relations[&block]
783 }
784
785 fn own_relation(&mut self, block: Block) -> Option<Relation> {
787 let [from] = *self.cfg.predecessors(block) else { return None };
788 let term = self.func.terminator(from)?;
789 if self.func[term].opcode != Opcode::BrIf {
790 return None;
791 }
792 let calls: Vec<_> = self.func.successors(term).collect();
793 let (then, other) = (calls.first()?, calls.get(1)?);
794 if then.block == other.block {
795 return None;
796 }
797 let taken = then.block == block;
798 let cond = *self.func[self.func[term].args].first()?;
799 let Def::Result { inst, .. } = self.func[cond].def else { return None };
800 if self.func[inst].opcode != Opcode::ICmp {
801 return None;
802 }
803 let Extra::IntPred(pred) = self.func[inst].extra else { return None };
804 let args = &self.func[self.func[inst].args];
805 let (&left, &right) = (args.first()?, args.get(1)?);
806 let pred = if taken { pred } else { pred.inverse() };
807 Some(Relation { left, pred, right })
808 }
809
810 fn constant(&self, value: Value) -> Option<u128> {
812 let Def::Result { inst, .. } = self.func[value].def else { return None };
813 if self.func[inst].opcode != Opcode::IConst {
814 return None;
815 }
816 let Extra::Imm(at) = self.func[inst].extra else { return None };
817 Some(self.func[at].unsigned())
818 }
819}
820
821fn defining_block(func: &Func, value: Value) -> Option<Block> {
823 match func[value].def {
824 Def::Param { block, .. } => Some(block),
825 Def::Result { inst, .. } => func.block_of(inst),
826 }
827}
828
829fn argument(func: &Func, pred: Block, block: Block, index: usize) -> Option<Value> {
835 let term = func.terminator(pred)?;
836 let mut found = None;
837 for call in func.successors(term) {
838 if call.block != block {
839 continue;
840 }
841 let arg = *func[call.args].get(index)?;
842 if found.replace(arg).is_some_and(|old| old != arg) {
843 return None;
844 }
845 }
846 found
847}
848
849fn read(facts: &[Relation], a: Value, b: Value) -> Option<IntPred> {
851 facts.iter().rev().find_map(|fact| {
852 if fact.left == a && fact.right == b {
853 Some(fact.pred)
854 } else if fact.left == b && fact.right == a {
855 Some(fact.pred.swapped())
856 } else {
857 None
858 }
859 })
860}
861
862const fn outcomes(pred: IntPred) -> u8 {
864 match pred {
865 IntPred::Eq => 0b010,
866 IntPred::Ne => 0b101,
867 IntPred::Slt | IntPred::Ult => 0b001,
868 IntPred::Sle | IntPred::Ule => 0b011,
869 IntPred::Sgt | IntPred::Ugt => 0b100,
870 IntPred::Sge | IntPred::Uge => 0b110,
871 }
872}
873
874const fn comparable(a: IntPred, b: IntPred) -> bool {
880 ordering_free(a) || ordering_free(b) || a.is_signed() == b.is_signed()
881}
882
883const fn ordering_free(pred: IntPred) -> bool {
885 matches!(pred, IntPred::Eq | IntPred::Ne)
886}
887
888fn implies(known: IntPred, pred: IntPred) -> bool {
890 comparable(known, pred) && outcomes(known) & !outcomes(pred) == 0
891}
892
893fn excludes(known: IntPred, pred: IntPred) -> bool {
895 comparable(known, pred) && outcomes(known) & outcomes(pred) == 0
896}
897
898fn compose(first: IntPred, second: IntPred) -> Option<IntPred> {
904 if !comparable(first, second) {
905 return None;
906 }
907 let strict = |pred| matches!(pred, IntPred::Slt | IntPred::Ult | IntPred::Sgt | IntPred::Ugt);
908 let direction = |pred| outcomes(pred) & 0b101;
909 match (first, second) {
910 (IntPred::Eq, other) | (other, IntPred::Eq) => Some(other),
911 (IntPred::Ne, _) | (_, IntPred::Ne) => None,
914 _ if direction(first) != direction(second) => None,
917 _ if strict(first) => Some(first),
918 _ => Some(second),
919 }
920}
921
922#[cfg(test)]
923mod tests {
924 use rucc_base::Interner;
925 use rucc_ir::{Block, Builder, Flags, Func, IntPred, Opcode, Signature, Type, Value};
926
927 use super::{Options, Ranges};
928 use crate::cfg::Cfg;
929 use crate::dom::Dominators;
930 use crate::range::Range;
931 use crate::range::ops::{self, Truth};
932
933 const I32: Type = Type::int(32);
934
935 fn shape(params: usize, blocks: usize) -> (Func, Vec<Value>, Vec<Block>) {
941 let mut names = Interner::new();
942 let types = vec![I32; params];
943 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&types));
944 let blocks: Vec<Block> = (0..blocks).map(|_| func.create_block()).collect();
945 let args = types.iter().map(|&ty| func.append_param(blocks[0], ty)).collect();
946 (func, args, blocks)
947 }
948
949 struct Asked {
951 cfg: Cfg,
952 dom: Dominators,
953 func: Func,
954 }
955
956 impl Asked {
957 fn new(func: Func) -> Self {
958 let cfg = Cfg::new(&func);
959 let dom = Dominators::new(&cfg);
960 Asked { cfg, dom, func }
961 }
962
963 fn ranges(&self) -> Ranges<'_> {
964 Ranges::new(&self.func, &self.cfg, &self.dom)
965 }
966
967 fn with(&self, options: Options) -> Ranges<'_> {
968 Ranges::with(&self.func, &self.cfg, &self.dom, options)
969 }
970 }
971
972 fn bounds(range: Range) -> Option<(i128, i128)> {
974 range.signed_bounds()
975 }
976
977 #[test]
978 fn a_constant_is_itself() {
979 let (mut func, _, blocks) = shape(0, 1);
980 let mut build = Builder::new(&mut func, blocks[0]);
981 let seven = build.iconst(I32, 7);
982 build.ret(&[]);
983 let asked = Asked::new(func);
984 assert_eq!(asked.ranges().of(seven).singleton(), Some(7));
985 }
986
987 #[test]
988 fn arithmetic_on_constants_is_the_arithmetic() {
989 let (mut func, _, blocks) = shape(0, 1);
990 let mut build = Builder::new(&mut func, blocks[0]);
991 let a = build.iconst(I32, 7);
992 let b = build.iconst(I32, 5);
993 let sum = build.binary(Opcode::Add, a, b, Flags::NONE);
994 build.ret(&[]);
995 let asked = Asked::new(func);
996 assert_eq!(asked.ranges().of(sum).singleton(), Some(12));
997 }
998
999 #[test]
1000 fn a_value_nothing_is_known_about_is_the_whole_of_its_type_and_says_which_opcode_lost_it() {
1001 let (mut func, args, blocks) = shape(1, 1);
1002 let mut build = Builder::new(&mut func, blocks[0]);
1003 let counted = build.unary(Opcode::Ctlz, args[0], I32);
1004 let squared = build.binary(Opcode::Mul, args[0], args[0], Flags::NONE);
1005 build.ret(&[]);
1006 let asked = Asked::new(func);
1007 let mut ranges = asked.ranges();
1008 assert!(ranges.of(args[0]).is_full(), "a parameter is anything");
1009 assert_eq!(bounds(ranges.of(counted)), Some((0, 32)));
1011 assert!(ranges.of(squared).is_full());
1012 assert_eq!(ranges.counts().losses(), vec![(Opcode::Mul, 1)]);
1013 }
1014
1015 fn guarded(pred: IntPred, bound: i128) -> (Func, Value, Block, Block) {
1017 let (mut func, args, blocks) = shape(1, 3);
1018 let mut build = Builder::new(&mut func, blocks[0]);
1019 let limit = build.iconst(I32, bound);
1020 let test = build.icmp(pred, args[0], limit);
1021 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1022 Builder::new(&mut func, blocks[1]).ret(&[]);
1023 Builder::new(&mut func, blocks[2]).ret(&[]);
1024 (func, args[0], blocks[1], blocks[2])
1025 }
1026
1027 #[test]
1028 fn a_branch_narrows_the_value_it_tested_on_both_of_its_edges() {
1029 let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1030 let asked = Asked::new(func);
1031 let mut ranges = asked.ranges();
1032 assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1033 assert_eq!(bounds(ranges.at(x, otherwise)), Some((10, i128::from(i32::MAX))));
1034 }
1035
1036 #[test]
1037 fn the_range_at_the_definition_is_not_the_range_at_the_use() {
1038 let (func, x, then, _) = guarded(IntPred::Ult, 64);
1039 let asked = Asked::new(func);
1040 let mut ranges = asked.ranges();
1041 assert!(ranges.of(x).is_full(), "nothing is known where it is defined");
1042 assert_eq!(ranges.at(x, then).unsigned_bounds(), Some((0, 63)));
1043 }
1044
1045 #[test]
1046 fn a_null_check_is_the_fact_a_single_interval_cannot_hold() {
1047 let (func, x, _, otherwise) = guarded(IntPred::Eq, 0);
1048 let asked = Asked::new(func);
1049 let mut ranges = asked.ranges();
1050 let range = ranges.at(x, otherwise);
1051 assert!(range.nonzero(), "the else edge of an equality with zero proves it");
1052 assert_eq!(range.pairs().len(), 1);
1056 }
1057
1058 fn through_arithmetic(offset: i128, bound: i128) -> (Func, Value, Block) {
1060 let (mut func, args, blocks) = shape(1, 3);
1061 let mut build = Builder::new(&mut func, blocks[0]);
1062 let by = build.iconst(I32, offset);
1063 let shifted = build.binary(Opcode::Add, args[0], by, Flags::NSW);
1064 let limit = build.iconst(I32, bound);
1065 let test = build.icmp(IntPred::Slt, shifted, limit);
1066 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1067 Builder::new(&mut func, blocks[1]).ret(&[]);
1068 Builder::new(&mut func, blocks[2]).ret(&[]);
1069 (func, args[0], blocks[1])
1070 }
1071
1072 #[test]
1073 fn the_condition_is_inverted_back_to_the_value_it_was_computed_from() {
1074 let (func, x, then) = through_arithmetic(3, 10);
1075 let asked = Asked::new(func);
1076 let mut ranges = asked.ranges();
1077 let (_, high) = bounds(ranges.at(x, then)).expect("not empty");
1078 assert!(high <= 6, "x + 3 < 10 makes x at most six, and this said {high}");
1079 }
1080
1081 #[test]
1082 fn the_inversion_stops_where_it_is_told_to() {
1083 let (func, x, then) = through_arithmetic(3, 10);
1084 let asked = Asked::new(func);
1085 let options = Options { logical_depth: 1, ..Options::default() };
1086 let mut ranges = asked.with(options);
1087 assert!(ranges.at(x, then).is_full(), "one step cannot reach past the comparison");
1088 }
1089
1090 #[test]
1091 fn a_value_carried_round_a_loop_is_not_pinned_down_and_the_branch_still_says_something() {
1092 let (mut func, _, blocks) = shape(0, 4);
1093 let counter = func.append_param(blocks[1], I32);
1094 let mut build = Builder::new(&mut func, blocks[0]);
1095 let start = build.iconst(I32, 0);
1096 build.jump(blocks[1], &[start]);
1097 let mut build = Builder::new(&mut func, blocks[1]);
1098 let limit = build.iconst(I32, 100);
1099 let test = build.icmp(IntPred::Slt, counter, limit);
1100 build.br_if(test, blocks[2], &[], blocks[3], &[]);
1101 let mut build = Builder::new(&mut func, blocks[2]);
1102 let one = build.iconst(I32, 1);
1103 let next = build.binary(Opcode::Add, counter, one, Flags::NSW);
1104 build.jump(blocks[1], &[next]);
1105 Builder::new(&mut func, blocks[3]).ret(&[]);
1106 let asked = Asked::new(func);
1107 let mut ranges = asked.ranges();
1108 let at_def = ranges.of(counter);
1113 assert!(at_def.contains(0) && at_def.contains(50) && at_def.contains(100));
1114 assert_eq!(bounds(at_def), Some((i128::from(i32::MIN) + 1, 100)));
1115 let (_, inside) = bounds(ranges.at(counter, blocks[2])).expect("not empty");
1117 assert_eq!(inside, 99);
1118 let (after, _) = bounds(ranges.at(counter, blocks[3])).expect("not empty");
1119 assert_eq!(after, 100);
1120 }
1121
1122 #[test]
1123 fn a_block_parameter_is_everything_its_predecessors_pass_to_it() {
1124 let (mut func, args, blocks) = shape(1, 4);
1125 let merged = func.append_param(blocks[3], I32);
1126 let mut build = Builder::new(&mut func, blocks[0]);
1127 let zero = build.iconst(I32, 0);
1128 let cond = build.icmp(IntPred::Slt, args[0], zero);
1129 build.br_if(cond, blocks[1], &[], blocks[2], &[]);
1130 let mut build = Builder::new(&mut func, blocks[1]);
1131 let five = build.iconst(I32, 5);
1132 build.jump(blocks[3], &[five]);
1133 let mut build = Builder::new(&mut func, blocks[2]);
1134 let nine = build.iconst(I32, 9);
1135 build.jump(blocks[3], &[nine]);
1136 Builder::new(&mut func, blocks[3]).ret(&[]);
1137 let asked = Asked::new(func);
1138 let mut ranges = asked.ranges();
1139 let range = ranges.of(merged);
1140 assert!(range.contains(5) && range.contains(9), "both arms are in it");
1141 assert!(!range.contains(7), "and nothing between them is");
1142 }
1143
1144 #[test]
1145 fn a_switch_edge_pins_its_cases_and_the_default_excludes_them() {
1146 let (mut func, args, blocks) = shape(1, 3);
1147 let mut build = Builder::new(&mut func, blocks[0]);
1148 build.switch(args[0], blocks[2], &[(4, blocks[1]), (7, blocks[1])]);
1149 Builder::new(&mut func, blocks[1]).ret(&[]);
1150 Builder::new(&mut func, blocks[2]).ret(&[]);
1151 let asked = Asked::new(func);
1152 let mut ranges = asked.ranges();
1153 assert_eq!(ranges.at(args[0], blocks[1]).list(4), Some(vec![4, 7]), "the two cases");
1154 let fell_through = ranges.at(args[0], blocks[2]);
1155 assert!(!fell_through.contains(4) && !fell_through.contains(7));
1156 assert!(fell_through.contains(5), "and everything else is still possible");
1157 }
1158
1159 #[test]
1160 fn both_arms_of_an_and_hold_where_it_is_true() {
1161 let (mut func, args, blocks) = shape(1, 3);
1162 let mut build = Builder::new(&mut func, blocks[0]);
1163 let low = build.iconst(I32, 10);
1164 let high = build.iconst(I32, 20);
1165 let above = build.icmp(IntPred::Sgt, args[0], low);
1166 let below = build.icmp(IntPred::Slt, args[0], high);
1167 let both = build.binary(Opcode::And, above, below, Flags::NONE);
1168 build.br_if(both, blocks[1], &[], blocks[2], &[]);
1169 Builder::new(&mut func, blocks[1]).ret(&[]);
1170 Builder::new(&mut func, blocks[2]).ret(&[]);
1171 let asked = Asked::new(func);
1172 let mut ranges = asked.ranges();
1173 assert_eq!(bounds(ranges.at(args[0], blocks[1])), Some((11, 19)));
1174 assert!(ranges.at(args[0], blocks[2]).is_full(), "the false edge says nothing");
1175 }
1176
1177 #[test]
1178 fn a_comparison_the_ranges_settle_is_settled() {
1179 let (func, x, then, _) = guarded(IntPred::Slt, 10);
1180 let mut asked = Asked::new(func);
1181 let ten = {
1182 let mut build = Builder::new(&mut asked.func, then);
1183 build.iconst(I32, 10)
1184 };
1185 let asked = Asked::new(asked.func);
1186 let mut ranges = asked.ranges();
1187 assert_eq!(ranges.compare(IntPred::Slt, x, ten, then), Truth::Always);
1188 assert_eq!(ranges.compare(IntPred::Sgt, x, ten, then), Truth::Never);
1189 }
1190
1191 fn related() -> (Func, Value, Value, Vec<Block>) {
1195 let (mut func, args, blocks) = shape(2, 4);
1196 let mut build = Builder::new(&mut func, blocks[0]);
1197 let test = build.icmp(IntPred::Slt, args[0], args[1]);
1198 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1199 Builder::new(&mut func, blocks[1]).jump(blocks[3], &[]);
1200 Builder::new(&mut func, blocks[2]).jump(blocks[3], &[]);
1201 Builder::new(&mut func, blocks[3]).ret(&[]);
1202 (func, args[0], args[1], blocks)
1203 }
1204
1205 #[test]
1206 fn a_relation_the_intervals_cannot_see_is_still_known() {
1207 let (func, a, b, blocks) = related();
1208 let asked = Asked::new(func);
1209 let mut ranges = asked.ranges();
1210 let (left, right) = (ranges.at(a, blocks[1]), ranges.at(b, blocks[1]));
1214 assert_eq!(ops::compare(IntPred::Slt, left, right), Truth::Either);
1215 assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1216 assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[1]), Truth::Always);
1217 assert_eq!(ranges.compare(IntPred::Sge, a, b, blocks[1]), Truth::Never);
1218 assert_eq!(ranges.compare(IntPred::Ne, a, b, blocks[1]), Truth::Always);
1219 assert_eq!(ranges.compare(IntPred::Ult, a, b, blocks[1]), Truth::Either);
1220 }
1221
1222 #[test]
1223 fn a_relation_belongs_to_the_block_the_edge_led_to() {
1224 let (func, a, b, blocks) = related();
1225 let asked = Asked::new(func);
1226 let mut ranges = asked.ranges();
1227 assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1228 assert_eq!(ranges.relation(a, b, blocks[2]), Some(IntPred::Sge), "the other edge");
1229 assert_eq!(ranges.relation(a, b, blocks[3]), None, "where they meet, neither holds");
1230 assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[3]), Truth::Either);
1231 }
1232
1233 #[test]
1234 fn one_step_of_composition_is_taken() {
1235 let (mut func, args, blocks) = shape(3, 4);
1236 let [a, b, c] = [args[0], args[1], args[2]];
1237 let mut build = Builder::new(&mut func, blocks[0]);
1238 let first = build.icmp(IntPred::Slt, a, b);
1239 build.br_if(first, blocks[1], &[], blocks[3], &[]);
1240 let mut build = Builder::new(&mut func, blocks[1]);
1241 let second = build.icmp(IntPred::Sle, b, c);
1242 build.br_if(second, blocks[2], &[], blocks[3], &[]);
1243 Builder::new(&mut func, blocks[2]).ret(&[]);
1244 Builder::new(&mut func, blocks[3]).ret(&[]);
1245 let asked = Asked::new(func);
1246 let mut ranges = asked.ranges();
1247 assert_eq!(ranges.relation(a, c, blocks[2]), Some(IntPred::Slt), "a < b and b <= c");
1248 assert_eq!(ranges.compare(IntPred::Slt, a, c, blocks[2]), Truth::Always);
1249 }
1250
1251 #[test]
1252 fn the_cache_gives_up_rather_than_growing_without_a_bound() {
1253 let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1254 let asked = Asked::new(func);
1255 let options = Options { refinements: 1, ..Options::default() };
1256 let mut ranges = asked.with(options);
1257 assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1258 assert!(ranges.at(x, otherwise).is_full(), "past the bound it is the definition range");
1259 assert_eq!(ranges.counts().fallbacks(), 1);
1260 }
1261
1262 #[test]
1263 fn asking_twice_asks_the_cache_the_second_time() {
1264 let (func, x, then, _) = guarded(IntPred::Slt, 10);
1265 let asked = Asked::new(func);
1266 let mut ranges = asked.ranges();
1267 let first = ranges.at(x, then);
1268 let hits = ranges.counts().hits();
1269 let second = ranges.at(x, then);
1270 assert_eq!(first, second);
1271 assert!(ranges.counts().hits() > hits, "the second query hit the cache");
1272 assert_eq!(ranges.counts().queries(), 2);
1273 }
1274
1275 #[test]
1276 fn a_range_that_is_only_true_because_overflow_is_undefined_is_counted() {
1277 let (mut func, args, blocks) = shape(1, 1);
1278 let mut build = Builder::new(&mut func, blocks[0]);
1279 let big = build.iconst(I32, i128::from(i32::MAX) - 4);
1280 let counted = build.unary(Opcode::Ctlz, args[0], I32);
1281 let sum = build.binary(Opcode::Add, counted, big, Flags::NSW);
1282 build.ret(&[]);
1283 let asked = Asked::new(func);
1284 let mut ranges = asked.ranges();
1285 assert!(!ranges.of(sum).is_full(), "the promise not to overflow bounds the sum");
1286 assert_eq!(ranges.counts().assumed(), 1);
1287 }
1288
1289 #[test]
1290 fn a_query_about_something_that_is_not_an_integer_answers_without_pretending() {
1291 let (mut func, _, blocks) = shape(0, 1);
1292 let mut build = Builder::new(&mut func, blocks[0]);
1293 let mem = build.mem_entry();
1294 build.ret(&[]);
1295 let asked = Asked::new(func);
1296 let mut ranges = asked.ranges();
1297 assert!(ranges.of(mem).is_full());
1298 assert_eq!(ranges.counts().full(), 0, "a memory value is not a lost integer");
1299 }
1300}