1use std::collections::{BTreeMap, HashMap, HashSet};
78
79use rucc_ir::{Block, Def, Extra, Func, Inst, IntPred, Opcode, Value};
80
81use super::ops::{self, Truth, Undo};
82use super::{PAIRS, Range};
83use crate::cfg::Cfg;
84use crate::dom::Dominators;
85use crate::loops::Loops;
86use crate::scev::Scev;
87
88const RELATIONS: usize = 16;
95
96const EXCLUSIONS: usize = PAIRS + 1;
102
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
105pub struct Options {
106 pub logical_depth: u32,
111 pub recompute_depth: u32,
117 pub refinements: usize,
121 pub budget: u64,
134}
135
136impl Default for Options {
137 fn default() -> Self {
138 Self { logical_depth: 6, recompute_depth: 5, refinements: 8, budget: 4096 }
139 }
140}
141
142#[derive(Clone, Debug, Default, PartialEq, Eq)]
148pub struct Counts {
149 queries: u64,
150 hits: u64,
151 fallbacks: u64,
152 full: u64,
153 assumed: u64,
154 exhausted: u64,
155 counters: u64,
156 lost: BTreeMap<Opcode, u64>,
157}
158
159impl Counts {
160 #[must_use]
162 pub const fn queries(&self) -> u64 {
163 self.queries
164 }
165
166 #[must_use]
168 pub const fn hits(&self) -> u64 {
169 self.hits
170 }
171
172 #[must_use]
174 pub const fn fallbacks(&self) -> u64 {
175 self.fallbacks
176 }
177
178 #[must_use]
180 pub const fn full(&self) -> u64 {
181 self.full
182 }
183
184 #[must_use]
189 pub const fn counters(&self) -> u64 {
190 self.counters
191 }
192
193 #[must_use]
200 pub const fn exhausted(&self) -> u64 {
201 self.exhausted
202 }
203
204 #[must_use]
209 pub const fn assumed(&self) -> u64 {
210 self.assumed
211 }
212
213 #[must_use]
215 pub fn losses(&self) -> Vec<(Opcode, u64)> {
216 let mut losses: Vec<(Opcode, u64)> = self.lost.iter().map(|(&op, &n)| (op, n)).collect();
217 losses.sort_by_key(|&(opcode, count)| (std::cmp::Reverse(count), opcode));
218 losses
219 }
220}
221
222#[derive(Clone, Copy, Debug, PartialEq, Eq)]
227struct Relation {
228 left: Value,
229 pred: IntPred,
230 right: Value,
231}
232
233#[derive(Clone, Debug, Default)]
235struct Entry {
236 at_def: Option<Range>,
237 refined: HashMap<Block, Range>,
238}
239
240#[derive(Debug)]
246pub struct Ranges<'a> {
247 func: &'a Func,
248 cfg: &'a Cfg,
249 dom: &'a Dominators,
250 options: Options,
251 cache: HashMap<Value, Entry>,
252 relations: HashMap<Block, Vec<Relation>>,
253 counts: Counts,
254 active: HashSet<Value>,
259 cycles: u64,
262 spent: u64,
264 loops: Option<Loops>,
269}
270
271impl<'a> Ranges<'a> {
272 #[must_use]
274 pub fn new(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators) -> Self {
275 Self::with(func, cfg, dom, Options::default())
276 }
277
278 #[must_use]
280 pub fn with(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators, options: Options) -> Self {
281 Self {
282 func,
283 cfg,
284 dom,
285 options,
286 cache: HashMap::new(),
287 relations: HashMap::new(),
288 counts: Counts::default(),
289 active: HashSet::new(),
290 cycles: 0,
291 spent: 0,
292 loops: None,
293 }
294 }
295
296 #[must_use]
298 pub const fn counts(&self) -> &Counts {
299 &self.counts
300 }
301
302 pub fn of(&mut self, value: Value) -> Range {
304 self.counts.queries += 1;
305 self.at_def(value)
306 }
307
308 pub fn at(&mut self, value: Value, block: Block) -> Range {
314 self.counts.queries += 1;
315 self.refined(value, block)
316 }
317
318 pub fn at_inst(&mut self, value: Value, inst: Inst) -> Range {
324 match self.func.block_of(inst) {
325 Some(block) => self.at(value, block),
326 None => self.of(value),
327 }
328 }
329
330 pub fn compare(&mut self, pred: IntPred, a: Value, b: Value, block: Block) -> Truth {
336 let (left, right) = (self.at(a, block), self.at(b, block));
337 if left.width() != right.width() {
338 return Truth::Either;
339 }
340 match ops::compare(pred, left, right) {
341 Truth::Either => (),
342 settled => return settled,
343 }
344 match self.relation(a, b, block) {
345 Some(known) if implies(known, pred) => Truth::Always,
346 Some(known) if excludes(known, pred) => Truth::Never,
347 _ => Truth::Either,
348 }
349 }
350
351 pub fn relation(&mut self, a: Value, b: Value, block: Block) -> Option<IntPred> {
357 let facts = self.facts(block).clone();
358 if let Some(direct) = read(&facts, a, b) {
359 return Some(direct);
360 }
361 for step in &facts {
362 for middle in [step.left, step.right] {
363 if middle == a || middle == b {
364 continue;
365 }
366 let composed = read(&facts, a, middle)
367 .zip(read(&facts, middle, b))
368 .and_then(|(first, second)| compose(first, second));
369 if composed.is_some() {
370 return composed;
371 }
372 }
373 }
374 None
375 }
376
377 fn at_def(&mut self, value: Value) -> Range {
379 let ty = self.func[value].ty;
380 if !ty.is_int() || !ty.is_scalar() {
381 return Range::of(ty);
382 }
383 if let Some(cached) = self.cache.get(&value).and_then(|entry| entry.at_def) {
384 self.counts.hits += 1;
385 return cached;
386 }
387 if !self.active.insert(value) {
388 self.cycles += 1;
389 return Range::of(ty);
390 }
391 if self.spent >= self.options.budget {
395 self.active.remove(&value);
396 self.counts.exhausted += 1;
397 return Range::of(ty);
398 }
399 self.spent += 1;
400 let before = self.cycles;
401 let range = self.compute(value);
402 self.active.remove(&value);
403 if self.cycles == before {
404 self.cache.entry(value).or_default().at_def = Some(range);
405 }
406 range
407 }
408
409 fn compute(&mut self, value: Value) -> Range {
411 let ty = self.func[value].ty;
412 match self.func[value].def {
413 Def::Param { block, index } => self.of_param(value, block, index),
414 Def::Result { inst, .. } => {
415 let range = self.of_inst(value, inst);
416 if range.is_full() {
417 self.counts.full += 1;
418 *self.counts.lost.entry(self.func[inst].opcode).or_default() += 1;
419 }
420 debug_assert_eq!(range.width(), ty.bits(), "a range of the wrong width");
421 range
422 }
423 }
424 }
425
426 fn of_param(&mut self, value: Value, block: Block, index: u32) -> Range {
433 let ty = self.func[value].ty;
434 if self.cfg.entry() == Some(block) {
435 return Range::of(ty);
436 }
437 let preds: Vec<Block> = self.cfg.predecessors(block).to_vec();
438 if preds.is_empty() {
439 return Range::of(ty);
440 }
441 let mut range = Range::empty(ty.bits());
442 for pred in preds {
443 let Some(arg) = argument(self.func, pred, block, index as usize) else {
444 range = Range::of(ty);
445 break;
446 };
447 let incoming = self.refined(arg, pred);
448 let edge = self.edge_fact(pred, block, arg).unwrap_or_else(|| Range::of(ty));
449 range = range.union(incoming.intersect(edge));
450 if range.is_full() {
451 break;
452 }
453 }
454 match self.counter(value, block) {
455 Some(walked) => range.intersect(walked),
456 None => range,
457 }
458 }
459
460 fn counter(&mut self, value: Value, block: Block) -> Option<Range> {
488 let ty = self.func[value].ty;
489 if !ty.is_int() || !ty.is_scalar() {
490 return None;
491 }
492 let loops = self.loops.get_or_insert_with(|| Loops::new(self.cfg, self.dom));
493 let id = loops.innermost(block)?;
494 if loops.header(id) != block {
495 return None;
496 }
497 let chrec = Scev::new(self.func, self.cfg, loops).evolution(id, value).chrec()?;
502 if chrec.ty != ty || !chrec.does_not_wrap(true) {
503 return None;
504 }
505 let base = chrec.base.as_number()?;
506 let step = chrec.step.as_number()?;
507 let (least, most) = Range::of(ty).signed_bounds()?;
508 let (lo, hi) = if step < 0 { (least, base) } else { (base, most) };
509 let walked = Range::signed_between(lo, hi, ty.bits());
510 if walked.is_full() {
511 return None;
512 }
513 self.counts.counters += 1;
514 self.counts.assumed += 1;
515 Some(walked)
516 }
517
518 fn of_inst(&mut self, value: Value, inst: Inst) -> Range {
521 let ty = self.func[value].ty;
522 let width = ty.bits();
523 let data = self.func[inst];
524 let block = self.func.block_of(inst);
525 let args: Vec<Value> = self.func[data.args].to_vec();
526 let flags = data.flags;
527 let operand = |this: &mut Self, index: usize| match (args.get(index), block) {
528 (Some(&arg), Some(block)) => this.refined(arg, block),
529 (Some(&arg), None) => this.at_def(arg),
530 (None, _) => Range::of(ty),
531 };
532 match data.opcode {
533 Opcode::IConst => {
534 let Extra::Imm(at) = data.extra else { return Range::of(ty) };
535 Range::exactly(self.func[at].unsigned(), width)
536 }
537 Opcode::Add | Opcode::Sub | Opcode::Mul => {
538 let (a, b) = (operand(self, 0), operand(self, 1));
539 if a.width() != b.width() {
540 return Range::of(ty);
541 }
542 let apply = |flags| match data.opcode {
543 Opcode::Add => ops::add(a, b, flags),
544 Opcode::Sub => ops::sub(a, b, flags),
545 _ => ops::mul(a, b, flags),
546 };
547 self.assuming(apply, flags)
548 }
549 Opcode::And | Opcode::Or | Opcode::Xor => {
550 let (a, b) = (operand(self, 0), operand(self, 1));
551 if a.width() != b.width() {
552 return Range::of(ty);
553 }
554 match data.opcode {
555 Opcode::And => ops::and(a, b),
556 Opcode::Or => ops::or(a, b),
557 _ => ops::xor(a, b),
558 }
559 }
560 Opcode::Shl | Opcode::LShr | Opcode::AShr => {
561 let (a, count) = (operand(self, 0), operand(self, 1));
562 if a.width() != count.width() {
563 return Range::of(ty);
564 }
565 let apply = |flags| match data.opcode {
566 Opcode::Shl => ops::shl(a, count, flags),
567 Opcode::LShr => ops::lshr(a, count, flags),
568 _ => ops::ashr(a, count, flags),
569 };
570 self.assuming(apply, flags)
571 }
572 Opcode::Trunc => ops::trunc(operand(self, 0), width),
573 Opcode::ZExt => ops::zext(operand(self, 0), width),
574 Opcode::SExt => ops::sext(operand(self, 0), width),
575 Opcode::ICmp => {
576 let Extra::IntPred(pred) = data.extra else { return Range::of(ty) };
577 let (a, b) = (operand(self, 0), operand(self, 1));
578 if a.width() != b.width() {
579 return Range::of(ty);
580 }
581 match ops::compare(pred, a, b) {
582 Truth::Always => Range::exactly(1, width),
583 Truth::Never => Range::exactly(0, width),
584 Truth::Either => Range::of(ty),
585 }
586 }
587 Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop => {
590 let counted = args.first().map_or(width, |&arg| self.func[arg].ty.bits());
591 Range::between(0, u128::from(counted), width)
592 }
593 _ => Range::of(ty),
594 }
595 }
596
597 fn assuming(
604 &mut self,
605 apply: impl Fn(rucc_ir::Flags) -> Range,
606 flags: rucc_ir::Flags,
607 ) -> Range {
608 let range = apply(flags);
609 if !flags.is_empty() && range != apply(rucc_ir::Flags::NONE) {
610 self.counts.assumed += 1;
611 }
612 range
613 }
614
615 fn refined(&mut self, value: Value, block: Block) -> Range {
617 let ty = self.func[value].ty;
618 if !ty.is_int() || !ty.is_scalar() {
619 return Range::of(ty);
620 }
621 if let Some(&cached) = self.cache.get(&value).and_then(|e| e.refined.get(&block)) {
622 self.counts.hits += 1;
623 return cached;
624 }
625 let full = self
626 .cache
627 .get(&value)
628 .is_some_and(|entry| entry.refined.len() >= self.options.refinements);
629 if full {
630 self.counts.fallbacks += 1;
631 return self.at_def(value);
632 }
633 let before = self.cycles;
634 let range = self.walk(value, block);
635 if self.cycles == before {
636 let entry = self.cache.entry(value).or_default();
637 if entry.refined.len() < self.options.refinements {
638 entry.refined.insert(block, range);
639 }
640 }
641 range
642 }
643
644 fn walk(&mut self, value: Value, block: Block) -> Range {
650 let mut range = self.at_def(value);
651 let stop = defining_block(self.func, value);
652 let mut cursor = block;
653 let mut steps = 0;
654 while steps < self.options.recompute_depth && Some(cursor) != stop {
655 let Some(parent) = self.dom.immediate_dominator(cursor) else { break };
656 if self.cfg.predecessors(cursor) == [parent] {
657 if let Some(fact) = self.edge_fact(parent, cursor, value) {
658 range = range.intersect(fact);
659 }
660 }
661 cursor = parent;
662 steps += 1;
663 }
664 range
665 }
666
667 fn edge_fact(&mut self, from: Block, to: Block, value: Value) -> Option<Range> {
669 let term = self.func.terminator(from)?;
670 let depth = self.options.logical_depth;
671 match self.func[term].opcode {
672 Opcode::BrIf => {
673 let calls: Vec<_> = self.func.successors(term).collect();
674 let (then, other) = (calls.first()?, calls.get(1)?);
675 if then.block == other.block {
676 return None;
677 }
678 let taken = then.block == to;
679 let cond = *self.func[self.func[term].args].first()?;
680 self.condition_fact(cond, taken, value, from, depth)
681 }
682 Opcode::Switch => self.switch_fact(term, to, value, from, depth),
683 _ => None,
684 }
685 }
686
687 fn switch_fact(
690 &mut self,
691 term: Inst,
692 to: Block,
693 value: Value,
694 block: Block,
695 depth: u32,
696 ) -> Option<Range> {
697 if depth == 0 {
698 return None;
699 }
700 let Extra::Switch(info) = self.func[term].extra else { return None };
701 let info = self.func[info];
702 let calls: Vec<_> = self.func[info.targets].to_vec();
703 let cases: Vec<_> = self.func[info.cases].to_vec();
704 let subject = *self.func[self.func[term].args].first()?;
705 let width = self.func[subject].ty.bits();
706 let default = calls.first()?.block;
707 let hits: Vec<usize> = (1..calls.len()).filter(|&index| calls[index].block == to).collect();
708 let known = if default == to {
709 if !hits.is_empty() {
713 return None;
714 }
715 let mut range = Range::full(width);
716 for &case in cases.iter().take(EXCLUSIONS) {
717 range = range.intersect(Range::other_than(case.unsigned(), width));
718 }
719 range
720 } else {
721 let pairs: Vec<(u128, u128)> = hits
722 .iter()
723 .filter_map(|&index| cases.get(index - 1))
724 .map(|case| (case.unsigned(), case.unsigned()))
725 .collect();
726 if pairs.is_empty() {
727 return None;
728 }
729 Range::from_pairs(&pairs, width)
730 };
731 self.carry_back(subject, known, value, block, depth - 1)
732 }
733
734 fn condition_fact(
736 &mut self,
737 cond: Value,
738 taken: bool,
739 value: Value,
740 block: Block,
741 depth: u32,
742 ) -> Option<Range> {
743 if depth == 0 {
744 return None;
745 }
746 if cond == value {
747 let width = self.func[value].ty.bits();
748 return Some(Range::exactly(u128::from(taken), width));
749 }
750 let Def::Result { inst, .. } = self.func[cond].def else { return None };
751 let data = self.func[inst];
752 let args: Vec<Value> = self.func[data.args].to_vec();
753 match data.opcode {
754 Opcode::ICmp => {
755 let Extra::IntPred(pred) = data.extra else { return None };
756 let pred = if taken { pred } else { pred.inverse() };
757 let (&left, &right) = (args.first()?, args.get(1)?);
758 let (a, b) = (self.refined(left, block), self.refined(right, block));
759 if a.width() != b.width() {
760 return None;
761 }
762 let want = ops::narrow_for(pred, a, b);
763 if let Some(found) = self.carry_back(left, want, value, block, depth - 1) {
764 return Some(found);
765 }
766 let want = ops::narrow_for(pred.swapped(), b, a);
767 self.carry_back(right, want, value, block, depth - 1)
768 }
769 Opcode::And | Opcode::Or => {
774 let holds = data.opcode == Opcode::And;
775 if taken != holds {
776 return None;
777 }
778 let (&left, &right) = (args.first()?, args.get(1)?);
779 let a = self.condition_fact(left, taken, value, block, depth - 1);
780 let b = self.condition_fact(right, taken, value, block, depth - 1);
781 match (a, b) {
782 (Some(a), Some(b)) => Some(a.intersect(b)),
783 (found, None) | (None, found) => found,
784 }
785 }
786 Opcode::Xor => {
789 let (&left, &right) = (args.first()?, args.get(1)?);
790 let (cond, other) = match self.constant(right) {
791 Some(_) => (left, right),
792 None => (right, left),
793 };
794 let one = self.constant(other)? == 1 && self.func[other].ty.bits() == 1;
795 if !one {
796 return None;
797 }
798 self.condition_fact(cond, !taken, value, block, depth - 1)
799 }
800 _ => None,
801 }
802 }
803
804 fn carry_back(
811 &mut self,
812 subject: Value,
813 known: Range,
814 value: Value,
815 block: Block,
816 depth: u32,
817 ) -> Option<Range> {
818 if subject == value {
819 return Some(known);
820 }
821 if depth == 0 || known.is_full() {
822 return None;
823 }
824 let Def::Result { inst, .. } = self.func[subject].def else { return None };
825 let data = self.func[inst];
826 let args: Vec<Value> = self.func[data.args].to_vec();
827 let (&left, right) = (args.first()?, args.get(1).copied());
828 let steps: Vec<(Value, Undo, Option<Value>)> = match data.opcode {
829 Opcode::Add => vec![(left, Undo::AddLeft, right), (right?, Undo::AddLeft, Some(left))],
833 Opcode::Sub => vec![(left, Undo::SubLeft, right), (right?, Undo::SubRight, Some(left))],
834 Opcode::Xor => vec![(left, Undo::Xor, right), (right?, Undo::Xor, Some(left))],
835 Opcode::ZExt => vec![(left, Undo::Zext(self.func[left].ty.bits()), None)],
836 Opcode::SExt => vec![(left, Undo::Sext(self.func[left].ty.bits()), None)],
837 _ => return None,
838 };
839 for (operand, undo, other) in steps {
840 let other = match other {
841 Some(other) => self.refined(other, block),
842 None => Range::full(known.width()),
843 };
844 if other.width() != known.width() {
845 continue;
846 }
847 let back = ops::backward(undo, known, other);
848 if let Some(found) = self.carry_back(operand, back, value, block, depth - 1) {
849 return Some(found);
850 }
851 }
852 None
853 }
854
855 fn facts(&mut self, block: Block) -> &Vec<Relation> {
857 if !self.relations.contains_key(&block) {
858 let mut facts = match self.dom.immediate_dominator(block) {
859 Some(parent) => self.facts(parent).clone(),
860 None => Vec::new(),
861 };
862 if let Some(own) = self.own_relation(block) {
863 facts.push(own);
864 if facts.len() > RELATIONS {
865 facts.remove(0);
866 }
867 }
868 self.relations.insert(block, facts);
869 }
870 &self.relations[&block]
871 }
872
873 fn own_relation(&mut self, block: Block) -> Option<Relation> {
875 let [from] = *self.cfg.predecessors(block) else { return None };
876 let term = self.func.terminator(from)?;
877 if self.func[term].opcode != Opcode::BrIf {
878 return None;
879 }
880 let calls: Vec<_> = self.func.successors(term).collect();
881 let (then, other) = (calls.first()?, calls.get(1)?);
882 if then.block == other.block {
883 return None;
884 }
885 let taken = then.block == block;
886 let cond = *self.func[self.func[term].args].first()?;
887 let Def::Result { inst, .. } = self.func[cond].def else { return None };
888 if self.func[inst].opcode != Opcode::ICmp {
889 return None;
890 }
891 let Extra::IntPred(pred) = self.func[inst].extra else { return None };
892 let args = &self.func[self.func[inst].args];
893 let (&left, &right) = (args.first()?, args.get(1)?);
894 let pred = if taken { pred } else { pred.inverse() };
895 Some(Relation { left, pred, right })
896 }
897
898 fn constant(&self, value: Value) -> Option<u128> {
900 let Def::Result { inst, .. } = self.func[value].def else { return None };
901 if self.func[inst].opcode != Opcode::IConst {
902 return None;
903 }
904 let Extra::Imm(at) = self.func[inst].extra else { return None };
905 Some(self.func[at].unsigned())
906 }
907}
908
909fn defining_block(func: &Func, value: Value) -> Option<Block> {
911 match func[value].def {
912 Def::Param { block, .. } => Some(block),
913 Def::Result { inst, .. } => func.block_of(inst),
914 }
915}
916
917fn argument(func: &Func, pred: Block, block: Block, index: usize) -> Option<Value> {
923 let term = func.terminator(pred)?;
924 let mut found = None;
925 for call in func.successors(term) {
926 if call.block != block {
927 continue;
928 }
929 let arg = *func[call.args].get(index)?;
930 if found.replace(arg).is_some_and(|old| old != arg) {
931 return None;
932 }
933 }
934 found
935}
936
937fn read(facts: &[Relation], a: Value, b: Value) -> Option<IntPred> {
939 facts.iter().rev().find_map(|fact| {
940 if fact.left == a && fact.right == b {
941 Some(fact.pred)
942 } else if fact.left == b && fact.right == a {
943 Some(fact.pred.swapped())
944 } else {
945 None
946 }
947 })
948}
949
950const fn outcomes(pred: IntPred) -> u8 {
952 match pred {
953 IntPred::Eq => 0b010,
954 IntPred::Ne => 0b101,
955 IntPred::Slt | IntPred::Ult => 0b001,
956 IntPred::Sle | IntPred::Ule => 0b011,
957 IntPred::Sgt | IntPred::Ugt => 0b100,
958 IntPred::Sge | IntPred::Uge => 0b110,
959 }
960}
961
962const fn comparable(a: IntPred, b: IntPred) -> bool {
968 ordering_free(a) || ordering_free(b) || a.is_signed() == b.is_signed()
969}
970
971const fn ordering_free(pred: IntPred) -> bool {
973 matches!(pred, IntPred::Eq | IntPred::Ne)
974}
975
976fn implies(known: IntPred, pred: IntPred) -> bool {
978 comparable(known, pred) && outcomes(known) & !outcomes(pred) == 0
979}
980
981fn excludes(known: IntPred, pred: IntPred) -> bool {
983 comparable(known, pred) && outcomes(known) & outcomes(pred) == 0
984}
985
986fn compose(first: IntPred, second: IntPred) -> Option<IntPred> {
992 if !comparable(first, second) {
993 return None;
994 }
995 let strict = |pred| matches!(pred, IntPred::Slt | IntPred::Ult | IntPred::Sgt | IntPred::Ugt);
996 let direction = |pred| outcomes(pred) & 0b101;
997 match (first, second) {
998 (IntPred::Eq, other) | (other, IntPred::Eq) => Some(other),
999 (IntPred::Ne, _) | (_, IntPred::Ne) => None,
1002 _ if direction(first) != direction(second) => None,
1005 _ if strict(first) => Some(first),
1006 _ => Some(second),
1007 }
1008}
1009
1010#[cfg(test)]
1011mod tests {
1012 use rucc_base::Interner;
1013 use rucc_ir::{Block, Builder, Flags, Func, IntPred, Opcode, Signature, Type, Value};
1014
1015 use super::{Options, Ranges};
1016 use crate::cfg::Cfg;
1017 use crate::dom::Dominators;
1018 use crate::range::Range;
1019 use crate::range::ops::{self, Truth};
1020
1021 const I32: Type = Type::int(32);
1022
1023 fn shape(params: usize, blocks: usize) -> (Func, Vec<Value>, Vec<Block>) {
1029 let mut names = Interner::new();
1030 let types = vec![I32; params];
1031 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&types));
1032 let blocks: Vec<Block> = (0..blocks).map(|_| func.create_block()).collect();
1033 let args = types.iter().map(|&ty| func.append_param(blocks[0], ty)).collect();
1034 (func, args, blocks)
1035 }
1036
1037 struct Asked {
1039 cfg: Cfg,
1040 dom: Dominators,
1041 func: Func,
1042 }
1043
1044 impl Asked {
1045 fn new(func: Func) -> Self {
1046 let cfg = Cfg::new(&func);
1047 let dom = Dominators::new(&cfg);
1048 Asked { cfg, dom, func }
1049 }
1050
1051 fn ranges(&self) -> Ranges<'_> {
1052 Ranges::new(&self.func, &self.cfg, &self.dom)
1053 }
1054
1055 fn with(&self, options: Options) -> Ranges<'_> {
1056 Ranges::with(&self.func, &self.cfg, &self.dom, options)
1057 }
1058 }
1059
1060 fn bounds(range: Range) -> Option<(i128, i128)> {
1062 range.signed_bounds()
1063 }
1064
1065 #[test]
1066 fn a_constant_is_itself() {
1067 let (mut func, _, blocks) = shape(0, 1);
1068 let mut build = Builder::new(&mut func, blocks[0]);
1069 let seven = build.iconst(I32, 7);
1070 build.ret(&[]);
1071 let asked = Asked::new(func);
1072 assert_eq!(asked.ranges().of(seven).singleton(), Some(7));
1073 }
1074
1075 #[test]
1076 fn arithmetic_on_constants_is_the_arithmetic() {
1077 let (mut func, _, blocks) = shape(0, 1);
1078 let mut build = Builder::new(&mut func, blocks[0]);
1079 let a = build.iconst(I32, 7);
1080 let b = build.iconst(I32, 5);
1081 let sum = build.binary(Opcode::Add, a, b, Flags::NONE);
1082 build.ret(&[]);
1083 let asked = Asked::new(func);
1084 assert_eq!(asked.ranges().of(sum).singleton(), Some(12));
1085 }
1086
1087 #[test]
1088 fn a_value_nothing_is_known_about_is_the_whole_of_its_type_and_says_which_opcode_lost_it() {
1089 let (mut func, args, blocks) = shape(1, 1);
1090 let mut build = Builder::new(&mut func, blocks[0]);
1091 let counted = build.unary(Opcode::Ctlz, args[0], I32);
1092 let squared = build.binary(Opcode::Mul, args[0], args[0], Flags::NONE);
1093 build.ret(&[]);
1094 let asked = Asked::new(func);
1095 let mut ranges = asked.ranges();
1096 assert!(ranges.of(args[0]).is_full(), "a parameter is anything");
1097 assert_eq!(bounds(ranges.of(counted)), Some((0, 32)));
1099 assert!(ranges.of(squared).is_full());
1100 assert_eq!(ranges.counts().losses(), vec![(Opcode::Mul, 1)]);
1101 }
1102
1103 fn guarded(pred: IntPred, bound: i128) -> (Func, Value, Block, Block) {
1105 let (mut func, args, blocks) = shape(1, 3);
1106 let mut build = Builder::new(&mut func, blocks[0]);
1107 let limit = build.iconst(I32, bound);
1108 let test = build.icmp(pred, args[0], limit);
1109 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1110 Builder::new(&mut func, blocks[1]).ret(&[]);
1111 Builder::new(&mut func, blocks[2]).ret(&[]);
1112 (func, args[0], blocks[1], blocks[2])
1113 }
1114
1115 #[test]
1116 fn a_branch_narrows_the_value_it_tested_on_both_of_its_edges() {
1117 let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1118 let asked = Asked::new(func);
1119 let mut ranges = asked.ranges();
1120 assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1121 assert_eq!(bounds(ranges.at(x, otherwise)), Some((10, i128::from(i32::MAX))));
1122 }
1123
1124 #[test]
1125 fn the_range_at_the_definition_is_not_the_range_at_the_use() {
1126 let (func, x, then, _) = guarded(IntPred::Ult, 64);
1127 let asked = Asked::new(func);
1128 let mut ranges = asked.ranges();
1129 assert!(ranges.of(x).is_full(), "nothing is known where it is defined");
1130 assert_eq!(ranges.at(x, then).unsigned_bounds(), Some((0, 63)));
1131 }
1132
1133 #[test]
1134 fn a_null_check_is_the_fact_a_single_interval_cannot_hold() {
1135 let (func, x, _, otherwise) = guarded(IntPred::Eq, 0);
1136 let asked = Asked::new(func);
1137 let mut ranges = asked.ranges();
1138 let range = ranges.at(x, otherwise);
1139 assert!(range.nonzero(), "the else edge of an equality with zero proves it");
1140 assert_eq!(range.pairs().len(), 1);
1144 }
1145
1146 fn through_arithmetic(offset: i128, bound: i128) -> (Func, Value, Block) {
1148 let (mut func, args, blocks) = shape(1, 3);
1149 let mut build = Builder::new(&mut func, blocks[0]);
1150 let by = build.iconst(I32, offset);
1151 let shifted = build.binary(Opcode::Add, args[0], by, Flags::NSW);
1152 let limit = build.iconst(I32, bound);
1153 let test = build.icmp(IntPred::Slt, shifted, limit);
1154 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1155 Builder::new(&mut func, blocks[1]).ret(&[]);
1156 Builder::new(&mut func, blocks[2]).ret(&[]);
1157 (func, args[0], blocks[1])
1158 }
1159
1160 #[test]
1161 fn the_condition_is_inverted_back_to_the_value_it_was_computed_from() {
1162 let (func, x, then) = through_arithmetic(3, 10);
1163 let asked = Asked::new(func);
1164 let mut ranges = asked.ranges();
1165 let (_, high) = bounds(ranges.at(x, then)).expect("not empty");
1166 assert!(high <= 6, "x + 3 < 10 makes x at most six, and this said {high}");
1167 }
1168
1169 #[test]
1170 fn the_inversion_stops_where_it_is_told_to() {
1171 let (func, x, then) = through_arithmetic(3, 10);
1172 let asked = Asked::new(func);
1173 let options = Options { logical_depth: 1, ..Options::default() };
1174 let mut ranges = asked.with(options);
1175 assert!(ranges.at(x, then).is_full(), "one step cannot reach past the comparison");
1176 }
1177
1178 fn counting(start: i128, step: i128, flags: Flags) -> (Func, Value, Vec<Block>) {
1184 let (mut func, _, blocks) = shape(0, 4);
1185 let counter = func.append_param(blocks[1], I32);
1186 let mut build = Builder::new(&mut func, blocks[0]);
1187 let first = build.iconst(I32, start);
1188 build.jump(blocks[1], &[first]);
1189 let mut build = Builder::new(&mut func, blocks[1]);
1190 let limit = build.iconst(I32, 100);
1191 let test = build.icmp(IntPred::Slt, counter, limit);
1192 build.br_if(test, blocks[2], &[], blocks[3], &[]);
1193 let mut build = Builder::new(&mut func, blocks[2]);
1194 let by = build.iconst(I32, step);
1195 let next = build.binary(Opcode::Add, counter, by, flags);
1196 build.jump(blocks[1], &[next]);
1197 Builder::new(&mut func, blocks[3]).ret(&[]);
1198 (func, counter, blocks)
1199 }
1200
1201 #[test]
1202 fn a_counter_is_pinned_at_the_end_it_started_from_and_the_branch_says_the_other() {
1203 let (func, counter, blocks) = counting(0, 1, Flags::NSW);
1204 let asked = Asked::new(func);
1205 let mut ranges = asked.ranges();
1206 let at_def = ranges.of(counter);
1210 assert!(at_def.contains(0) && at_def.contains(50) && at_def.contains(100));
1211 assert_eq!(bounds(at_def), Some((0, 100)));
1212 assert_eq!(ranges.counts().counters(), 1, "one counter, read once");
1213 let (_, inside) = bounds(ranges.at(counter, blocks[2])).expect("not empty");
1215 assert_eq!(inside, 99);
1216 let (after, _) = bounds(ranges.at(counter, blocks[3])).expect("not empty");
1217 assert_eq!(after, 100);
1218 }
1219
1220 #[test]
1221 fn a_counter_that_walks_down_is_pinned_at_the_top() {
1222 let (func, counter, _) = counting(50, -1, Flags::NSW);
1225 let asked = Asked::new(func);
1226 let mut ranges = asked.ranges();
1227 assert_eq!(bounds(ranges.of(counter)), Some((i128::from(i32::MIN), 50)));
1228 }
1229
1230 #[test]
1231 fn a_counter_that_may_wrap_is_not_pinned_down() {
1232 let (func, counter, _) = counting(0, 1, Flags::NONE);
1236 let asked = Asked::new(func);
1237 let mut ranges = asked.ranges();
1238 let at_def = ranges.of(counter);
1239 assert!(at_def.contains(u128::from(u32::MAX)), "minus one is still in it");
1240 assert_eq!(ranges.counts().counters(), 0, "nothing was read off the recurrence");
1241 }
1242
1243 #[test]
1244 fn a_block_parameter_is_everything_its_predecessors_pass_to_it() {
1245 let (mut func, args, blocks) = shape(1, 4);
1246 let merged = func.append_param(blocks[3], I32);
1247 let mut build = Builder::new(&mut func, blocks[0]);
1248 let zero = build.iconst(I32, 0);
1249 let cond = build.icmp(IntPred::Slt, args[0], zero);
1250 build.br_if(cond, blocks[1], &[], blocks[2], &[]);
1251 let mut build = Builder::new(&mut func, blocks[1]);
1252 let five = build.iconst(I32, 5);
1253 build.jump(blocks[3], &[five]);
1254 let mut build = Builder::new(&mut func, blocks[2]);
1255 let nine = build.iconst(I32, 9);
1256 build.jump(blocks[3], &[nine]);
1257 Builder::new(&mut func, blocks[3]).ret(&[]);
1258 let asked = Asked::new(func);
1259 let mut ranges = asked.ranges();
1260 let range = ranges.of(merged);
1261 assert!(range.contains(5) && range.contains(9), "both arms are in it");
1262 assert!(!range.contains(7), "and nothing between them is");
1263 }
1264
1265 #[test]
1266 fn a_switch_edge_pins_its_cases_and_the_default_excludes_them() {
1267 let (mut func, args, blocks) = shape(1, 3);
1268 let mut build = Builder::new(&mut func, blocks[0]);
1269 build.switch(args[0], blocks[2], &[(4, blocks[1]), (7, blocks[1])]);
1270 Builder::new(&mut func, blocks[1]).ret(&[]);
1271 Builder::new(&mut func, blocks[2]).ret(&[]);
1272 let asked = Asked::new(func);
1273 let mut ranges = asked.ranges();
1274 assert_eq!(ranges.at(args[0], blocks[1]).list(4), Some(vec![4, 7]), "the two cases");
1275 let fell_through = ranges.at(args[0], blocks[2]);
1276 assert!(!fell_through.contains(4) && !fell_through.contains(7));
1277 assert!(fell_through.contains(5), "and everything else is still possible");
1278 }
1279
1280 #[test]
1281 fn both_arms_of_an_and_hold_where_it_is_true() {
1282 let (mut func, args, blocks) = shape(1, 3);
1283 let mut build = Builder::new(&mut func, blocks[0]);
1284 let low = build.iconst(I32, 10);
1285 let high = build.iconst(I32, 20);
1286 let above = build.icmp(IntPred::Sgt, args[0], low);
1287 let below = build.icmp(IntPred::Slt, args[0], high);
1288 let both = build.binary(Opcode::And, above, below, Flags::NONE);
1289 build.br_if(both, blocks[1], &[], blocks[2], &[]);
1290 Builder::new(&mut func, blocks[1]).ret(&[]);
1291 Builder::new(&mut func, blocks[2]).ret(&[]);
1292 let asked = Asked::new(func);
1293 let mut ranges = asked.ranges();
1294 assert_eq!(bounds(ranges.at(args[0], blocks[1])), Some((11, 19)));
1295 assert!(ranges.at(args[0], blocks[2]).is_full(), "the false edge says nothing");
1296 }
1297
1298 #[test]
1299 fn a_comparison_the_ranges_settle_is_settled() {
1300 let (func, x, then, _) = guarded(IntPred::Slt, 10);
1301 let mut asked = Asked::new(func);
1302 let ten = {
1303 let mut build = Builder::new(&mut asked.func, then);
1304 build.iconst(I32, 10)
1305 };
1306 let asked = Asked::new(asked.func);
1307 let mut ranges = asked.ranges();
1308 assert_eq!(ranges.compare(IntPred::Slt, x, ten, then), Truth::Always);
1309 assert_eq!(ranges.compare(IntPred::Sgt, x, ten, then), Truth::Never);
1310 }
1311
1312 fn related() -> (Func, Value, Value, Vec<Block>) {
1316 let (mut func, args, blocks) = shape(2, 4);
1317 let mut build = Builder::new(&mut func, blocks[0]);
1318 let test = build.icmp(IntPred::Slt, args[0], args[1]);
1319 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1320 Builder::new(&mut func, blocks[1]).jump(blocks[3], &[]);
1321 Builder::new(&mut func, blocks[2]).jump(blocks[3], &[]);
1322 Builder::new(&mut func, blocks[3]).ret(&[]);
1323 (func, args[0], args[1], blocks)
1324 }
1325
1326 #[test]
1327 fn a_relation_the_intervals_cannot_see_is_still_known() {
1328 let (func, a, b, blocks) = related();
1329 let asked = Asked::new(func);
1330 let mut ranges = asked.ranges();
1331 let (left, right) = (ranges.at(a, blocks[1]), ranges.at(b, blocks[1]));
1335 assert_eq!(ops::compare(IntPred::Slt, left, right), Truth::Either);
1336 assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1337 assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[1]), Truth::Always);
1338 assert_eq!(ranges.compare(IntPred::Sge, a, b, blocks[1]), Truth::Never);
1339 assert_eq!(ranges.compare(IntPred::Ne, a, b, blocks[1]), Truth::Always);
1340 assert_eq!(ranges.compare(IntPred::Ult, a, b, blocks[1]), Truth::Either);
1341 }
1342
1343 #[test]
1344 fn a_relation_belongs_to_the_block_the_edge_led_to() {
1345 let (func, a, b, blocks) = related();
1346 let asked = Asked::new(func);
1347 let mut ranges = asked.ranges();
1348 assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1349 assert_eq!(ranges.relation(a, b, blocks[2]), Some(IntPred::Sge), "the other edge");
1350 assert_eq!(ranges.relation(a, b, blocks[3]), None, "where they meet, neither holds");
1351 assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[3]), Truth::Either);
1352 }
1353
1354 #[test]
1355 fn one_step_of_composition_is_taken() {
1356 let (mut func, args, blocks) = shape(3, 4);
1357 let [a, b, c] = [args[0], args[1], args[2]];
1358 let mut build = Builder::new(&mut func, blocks[0]);
1359 let first = build.icmp(IntPred::Slt, a, b);
1360 build.br_if(first, blocks[1], &[], blocks[3], &[]);
1361 let mut build = Builder::new(&mut func, blocks[1]);
1362 let second = build.icmp(IntPred::Sle, b, c);
1363 build.br_if(second, blocks[2], &[], blocks[3], &[]);
1364 Builder::new(&mut func, blocks[2]).ret(&[]);
1365 Builder::new(&mut func, blocks[3]).ret(&[]);
1366 let asked = Asked::new(func);
1367 let mut ranges = asked.ranges();
1368 assert_eq!(ranges.relation(a, c, blocks[2]), Some(IntPred::Slt), "a < b and b <= c");
1369 assert_eq!(ranges.compare(IntPred::Slt, a, c, blocks[2]), Truth::Always);
1370 }
1371
1372 #[test]
1373 fn the_cache_gives_up_rather_than_growing_without_a_bound() {
1374 let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1375 let asked = Asked::new(func);
1376 let options = Options { refinements: 1, ..Options::default() };
1377 let mut ranges = asked.with(options);
1378 assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1379 assert!(ranges.at(x, otherwise).is_full(), "past the bound it is the definition range");
1380 assert_eq!(ranges.counts().fallbacks(), 1);
1381 }
1382
1383 #[test]
1384 fn asking_twice_asks_the_cache_the_second_time() {
1385 let (func, x, then, _) = guarded(IntPred::Slt, 10);
1386 let asked = Asked::new(func);
1387 let mut ranges = asked.ranges();
1388 let first = ranges.at(x, then);
1389 let hits = ranges.counts().hits();
1390 let second = ranges.at(x, then);
1391 assert_eq!(first, second);
1392 assert!(ranges.counts().hits() > hits, "the second query hit the cache");
1393 assert_eq!(ranges.counts().queries(), 2);
1394 }
1395
1396 #[test]
1397 fn a_range_that_is_only_true_because_overflow_is_undefined_is_counted() {
1398 let (mut func, args, blocks) = shape(1, 1);
1399 let mut build = Builder::new(&mut func, blocks[0]);
1400 let big = build.iconst(I32, i128::from(i32::MAX) - 4);
1401 let counted = build.unary(Opcode::Ctlz, args[0], I32);
1402 let sum = build.binary(Opcode::Add, counted, big, Flags::NSW);
1403 build.ret(&[]);
1404 let asked = Asked::new(func);
1405 let mut ranges = asked.ranges();
1406 assert!(!ranges.of(sum).is_full(), "the promise not to overflow bounds the sum");
1407 assert_eq!(ranges.counts().assumed(), 1);
1408 }
1409
1410 #[test]
1411 fn a_query_about_something_that_is_not_an_integer_answers_without_pretending() {
1412 let (mut func, _, blocks) = shape(0, 1);
1413 let mut build = Builder::new(&mut func, blocks[0]);
1414 let mem = build.mem_entry();
1415 build.ret(&[]);
1416 let asked = Asked::new(func);
1417 let mut ranges = asked.ranges();
1418 assert!(ranges.of(mem).is_full());
1419 assert_eq!(ranges.counts().full(), 0, "a memory value is not a lost integer");
1420 }
1421}