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 given: Option<&'a Loops>,
271}
272
273impl<'a> Ranges<'a> {
274 #[must_use]
276 pub fn new(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators) -> Self {
277 Self::with(func, cfg, dom, Options::default())
278 }
279
280 #[must_use]
282 pub fn with(func: &'a Func, cfg: &'a Cfg, dom: &'a Dominators, options: Options) -> Self {
283 Self {
284 func,
285 cfg,
286 dom,
287 options,
288 cache: HashMap::new(),
289 relations: HashMap::new(),
290 counts: Counts::default(),
291 active: HashSet::new(),
292 cycles: 0,
293 spent: 0,
294 loops: None,
295 given: None,
296 }
297 }
298
299 #[must_use]
308 pub fn knowing(mut self, loops: &'a Loops) -> Self {
309 self.given = Some(loops);
310 self
311 }
312
313 #[must_use]
315 pub const fn counts(&self) -> &Counts {
316 &self.counts
317 }
318
319 pub fn of(&mut self, value: Value) -> Range {
321 self.counts.queries += 1;
322 self.at_def(value)
323 }
324
325 pub fn at(&mut self, value: Value, block: Block) -> Range {
331 self.counts.queries += 1;
332 self.refined(value, block)
333 }
334
335 pub fn at_inst(&mut self, value: Value, inst: Inst) -> Range {
341 match self.func.block_of(inst) {
342 Some(block) => self.at(value, block),
343 None => self.of(value),
344 }
345 }
346
347 pub fn compare(&mut self, pred: IntPred, a: Value, b: Value, block: Block) -> Truth {
353 let (left, right) = (self.at(a, block), self.at(b, block));
354 if left.width() != right.width() {
355 return Truth::Either;
356 }
357 match ops::compare(pred, left, right) {
358 Truth::Either => (),
359 settled => return settled,
360 }
361 match self.relation(a, b, block) {
362 Some(known) if implies(known, pred) => Truth::Always,
363 Some(known) if excludes(known, pred) => Truth::Never,
364 _ => Truth::Either,
365 }
366 }
367
368 pub fn relation(&mut self, a: Value, b: Value, block: Block) -> Option<IntPred> {
374 let facts = self.facts(block).clone();
375 if let Some(direct) = read(&facts, a, b) {
376 return Some(direct);
377 }
378 for step in &facts {
379 for middle in [step.left, step.right] {
380 if middle == a || middle == b {
381 continue;
382 }
383 let composed = read(&facts, a, middle)
384 .zip(read(&facts, middle, b))
385 .and_then(|(first, second)| compose(first, second));
386 if composed.is_some() {
387 return composed;
388 }
389 }
390 }
391 None
392 }
393
394 fn at_def(&mut self, value: Value) -> Range {
396 let ty = self.func[value].ty;
397 if !ty.is_int() || !ty.is_scalar() {
398 return Range::of(ty);
399 }
400 if let Some(cached) = self.cache.get(&value).and_then(|entry| entry.at_def) {
401 self.counts.hits += 1;
402 return cached;
403 }
404 if !self.active.insert(value) {
405 self.cycles += 1;
406 return Range::of(ty);
407 }
408 if self.spent >= self.options.budget {
412 self.active.remove(&value);
413 self.counts.exhausted += 1;
414 return Range::of(ty);
415 }
416 self.spent += 1;
417 let before = self.cycles;
418 let range = self.compute(value);
419 self.active.remove(&value);
420 if self.cycles == before {
421 self.cache.entry(value).or_default().at_def = Some(range);
422 }
423 range
424 }
425
426 fn compute(&mut self, value: Value) -> Range {
428 let ty = self.func[value].ty;
429 match self.func[value].def {
430 Def::Param { block, index } => self.of_param(value, block, index),
431 Def::Result { inst, .. } => {
432 let range = self.of_inst(value, inst);
433 if range.is_full() {
434 self.counts.full += 1;
435 *self.counts.lost.entry(self.func[inst].opcode).or_default() += 1;
436 }
437 debug_assert_eq!(range.width(), ty.bits(), "a range of the wrong width");
438 range
439 }
440 }
441 }
442
443 fn of_param(&mut self, value: Value, block: Block, index: u32) -> Range {
450 let ty = self.func[value].ty;
451 if self.cfg.entry() == Some(block) {
452 return Range::of(ty);
453 }
454 let preds: Vec<Block> = self.cfg.predecessors(block).to_vec();
455 if preds.is_empty() {
456 return Range::of(ty);
457 }
458 let mut range = Range::empty(ty.bits());
459 for pred in preds {
460 let Some(arg) = argument(self.func, pred, block, index as usize) else {
461 range = Range::of(ty);
462 break;
463 };
464 let incoming = self.refined(arg, pred);
465 let edge = self.edge_fact(pred, block, arg).unwrap_or_else(|| Range::of(ty));
466 range = range.union(incoming.intersect(edge));
467 if range.is_full() {
468 break;
469 }
470 }
471 match self.counter(value, block) {
472 Some(walked) => range.intersect(walked),
473 None => range,
474 }
475 }
476
477 fn counter(&mut self, value: Value, block: Block) -> Option<Range> {
505 let ty = self.func[value].ty;
506 if !ty.is_int() || !ty.is_scalar() {
507 return None;
508 }
509 let loops = match self.given {
510 Some(loops) => loops,
511 None => &*self.loops.get_or_insert_with(|| Loops::new(self.cfg, self.dom)),
512 };
513 let id = loops.innermost(block)?;
514 if loops.header(id) != block {
515 return None;
516 }
517 let chrec = Scev::new(self.func, self.cfg, loops).evolution(id, value).chrec()?;
522 if chrec.ty != ty || !chrec.does_not_wrap(true) {
523 return None;
524 }
525 let base = chrec.base.as_number()?;
526 let step = chrec.step.as_number()?;
527 let (least, most) = Range::of(ty).signed_bounds()?;
528 let (lo, hi) = if step < 0 { (least, base) } else { (base, most) };
529 let walked = Range::signed_between(lo, hi, ty.bits());
530 if walked.is_full() {
531 return None;
532 }
533 self.counts.counters += 1;
534 self.counts.assumed += 1;
535 Some(walked)
536 }
537
538 fn of_inst(&mut self, value: Value, inst: Inst) -> Range {
541 let ty = self.func[value].ty;
542 let width = ty.bits();
543 let data = self.func[inst];
544 let block = self.func.block_of(inst);
545 let args: Vec<Value> = self.func[data.args].to_vec();
546 let flags = data.flags;
547 let operand = |this: &mut Self, index: usize| match (args.get(index), block) {
548 (Some(&arg), Some(block)) => this.refined(arg, block),
549 (Some(&arg), None) => this.at_def(arg),
550 (None, _) => Range::of(ty),
551 };
552 match data.opcode {
553 Opcode::IConst => {
554 let Extra::Imm(at) = data.extra else { return Range::of(ty) };
555 Range::exactly(self.func[at].unsigned(), width)
556 }
557 Opcode::Add | Opcode::Sub | Opcode::Mul => {
558 let (a, b) = (operand(self, 0), operand(self, 1));
559 if a.width() != b.width() {
560 return Range::of(ty);
561 }
562 let apply = |flags| match data.opcode {
563 Opcode::Add => ops::add(a, b, flags),
564 Opcode::Sub => ops::sub(a, b, flags),
565 _ => ops::mul(a, b, flags),
566 };
567 self.assuming(apply, flags)
568 }
569 Opcode::And | Opcode::Or | Opcode::Xor => {
570 let (a, b) = (operand(self, 0), operand(self, 1));
571 if a.width() != b.width() {
572 return Range::of(ty);
573 }
574 match data.opcode {
575 Opcode::And => ops::and(a, b),
576 Opcode::Or => ops::or(a, b),
577 _ => ops::xor(a, b),
578 }
579 }
580 Opcode::Shl | Opcode::LShr | Opcode::AShr => {
581 let (a, count) = (operand(self, 0), operand(self, 1));
582 if a.width() != count.width() {
583 return Range::of(ty);
584 }
585 let apply = |flags| match data.opcode {
586 Opcode::Shl => ops::shl(a, count, flags),
587 Opcode::LShr => ops::lshr(a, count, flags),
588 _ => ops::ashr(a, count, flags),
589 };
590 self.assuming(apply, flags)
591 }
592 Opcode::Trunc => ops::trunc(operand(self, 0), width),
593 Opcode::ZExt => ops::zext(operand(self, 0), width),
594 Opcode::SExt => ops::sext(operand(self, 0), width),
595 Opcode::ICmp => {
596 let Extra::IntPred(pred) = data.extra else { return Range::of(ty) };
597 let (a, b) = (operand(self, 0), operand(self, 1));
598 if a.width() != b.width() {
599 return Range::of(ty);
600 }
601 match ops::compare(pred, a, b) {
602 Truth::Always => Range::exactly(1, width),
603 Truth::Never => Range::exactly(0, width),
604 Truth::Either => Range::of(ty),
605 }
606 }
607 Opcode::Ctlz | Opcode::Cttz | Opcode::Ctpop => {
610 let counted = args.first().map_or(width, |&arg| self.func[arg].ty.bits());
611 Range::between(0, u128::from(counted), width)
612 }
613 _ => Range::of(ty),
614 }
615 }
616
617 fn assuming(
624 &mut self,
625 apply: impl Fn(rucc_ir::Flags) -> Range,
626 flags: rucc_ir::Flags,
627 ) -> Range {
628 let range = apply(flags);
629 if !flags.is_empty() && range != apply(rucc_ir::Flags::NONE) {
630 self.counts.assumed += 1;
631 }
632 range
633 }
634
635 fn refined(&mut self, value: Value, block: Block) -> Range {
637 let ty = self.func[value].ty;
638 if !ty.is_int() || !ty.is_scalar() {
639 return Range::of(ty);
640 }
641 if let Some(&cached) = self.cache.get(&value).and_then(|e| e.refined.get(&block)) {
642 self.counts.hits += 1;
643 return cached;
644 }
645 let full = self
646 .cache
647 .get(&value)
648 .is_some_and(|entry| entry.refined.len() >= self.options.refinements);
649 if full {
650 self.counts.fallbacks += 1;
651 return self.at_def(value);
652 }
653 let before = self.cycles;
654 let range = self.walk(value, block);
655 if self.cycles == before {
656 let entry = self.cache.entry(value).or_default();
657 if entry.refined.len() < self.options.refinements {
658 entry.refined.insert(block, range);
659 }
660 }
661 range
662 }
663
664 fn walk(&mut self, value: Value, block: Block) -> Range {
670 let mut range = self.at_def(value);
671 let stop = defining_block(self.func, value);
672 let mut cursor = block;
673 let mut steps = 0;
674 while steps < self.options.recompute_depth && Some(cursor) != stop {
675 let Some(parent) = self.dom.immediate_dominator(cursor) else { break };
676 if self.cfg.predecessors(cursor) == [parent] {
677 if let Some(fact) = self.edge_fact(parent, cursor, value) {
678 range = range.intersect(fact);
679 }
680 }
681 cursor = parent;
682 steps += 1;
683 }
684 range
685 }
686
687 fn edge_fact(&mut self, from: Block, to: Block, value: Value) -> Option<Range> {
689 let term = self.func.terminator(from)?;
690 let depth = self.options.logical_depth;
691 match self.func[term].opcode {
692 Opcode::BrIf => {
693 let calls: Vec<_> = self.func.successors(term).collect();
694 let (then, other) = (calls.first()?, calls.get(1)?);
695 if then.block == other.block {
696 return None;
697 }
698 let taken = then.block == to;
699 let cond = *self.func[self.func[term].args].first()?;
700 self.condition_fact(cond, taken, value, from, depth)
701 }
702 Opcode::Switch => self.switch_fact(term, to, value, from, depth),
703 _ => None,
704 }
705 }
706
707 fn switch_fact(
710 &mut self,
711 term: Inst,
712 to: Block,
713 value: Value,
714 block: Block,
715 depth: u32,
716 ) -> Option<Range> {
717 if depth == 0 {
718 return None;
719 }
720 let Extra::Switch(info) = self.func[term].extra else { return None };
721 let info = self.func[info];
722 let calls: Vec<_> = self.func[info.targets].to_vec();
723 let cases: Vec<_> = self.func[info.cases].to_vec();
724 let subject = *self.func[self.func[term].args].first()?;
725 let width = self.func[subject].ty.bits();
726 let default = calls.first()?.block;
727 let hits: Vec<usize> = (1..calls.len()).filter(|&index| calls[index].block == to).collect();
728 let known = if default == to {
729 if !hits.is_empty() {
733 return None;
734 }
735 let mut range = Range::full(width);
736 for &case in cases.iter().take(EXCLUSIONS) {
737 range = range.intersect(Range::other_than(case.unsigned(), width));
738 }
739 range
740 } else {
741 let pairs: Vec<(u128, u128)> = hits
742 .iter()
743 .filter_map(|&index| cases.get(index - 1))
744 .map(|case| (case.unsigned(), case.unsigned()))
745 .collect();
746 if pairs.is_empty() {
747 return None;
748 }
749 Range::from_pairs(&pairs, width)
750 };
751 self.carry_back(subject, known, value, block, depth - 1)
752 }
753
754 fn condition_fact(
756 &mut self,
757 cond: Value,
758 taken: bool,
759 value: Value,
760 block: Block,
761 depth: u32,
762 ) -> Option<Range> {
763 if depth == 0 {
764 return None;
765 }
766 if cond == value {
767 let width = self.func[value].ty.bits();
768 return Some(Range::exactly(u128::from(taken), width));
769 }
770 let Def::Result { inst, .. } = self.func[cond].def else { return None };
771 let data = self.func[inst];
772 let args: Vec<Value> = self.func[data.args].to_vec();
773 match data.opcode {
774 Opcode::ICmp => {
775 let Extra::IntPred(pred) = data.extra else { return None };
776 let pred = if taken { pred } else { pred.inverse() };
777 let (&left, &right) = (args.first()?, args.get(1)?);
778 let (a, b) = (self.refined(left, block), self.refined(right, block));
779 if a.width() != b.width() {
780 return None;
781 }
782 let want = ops::narrow_for(pred, a, b);
783 if let Some(found) = self.carry_back(left, want, value, block, depth - 1) {
784 return Some(found);
785 }
786 let want = ops::narrow_for(pred.swapped(), b, a);
787 self.carry_back(right, want, value, block, depth - 1)
788 }
789 Opcode::And | Opcode::Or => {
794 let holds = data.opcode == Opcode::And;
795 if taken != holds {
796 return None;
797 }
798 let (&left, &right) = (args.first()?, args.get(1)?);
799 let a = self.condition_fact(left, taken, value, block, depth - 1);
800 let b = self.condition_fact(right, taken, value, block, depth - 1);
801 match (a, b) {
802 (Some(a), Some(b)) => Some(a.intersect(b)),
803 (found, None) | (None, found) => found,
804 }
805 }
806 Opcode::Xor => {
809 let (&left, &right) = (args.first()?, args.get(1)?);
810 let (cond, other) = match self.constant(right) {
811 Some(_) => (left, right),
812 None => (right, left),
813 };
814 let one = self.constant(other)? == 1 && self.func[other].ty.bits() == 1;
815 if !one {
816 return None;
817 }
818 self.condition_fact(cond, !taken, value, block, depth - 1)
819 }
820 _ => None,
821 }
822 }
823
824 fn carry_back(
831 &mut self,
832 subject: Value,
833 known: Range,
834 value: Value,
835 block: Block,
836 depth: u32,
837 ) -> Option<Range> {
838 if subject == value {
839 return Some(known);
840 }
841 if depth == 0 || known.is_full() {
842 return None;
843 }
844 let Def::Result { inst, .. } = self.func[subject].def else { return None };
845 let data = self.func[inst];
846 let args: Vec<Value> = self.func[data.args].to_vec();
847 let (&left, right) = (args.first()?, args.get(1).copied());
848 let steps: Vec<(Value, Undo, Option<Value>)> = match data.opcode {
849 Opcode::Add => vec![(left, Undo::AddLeft, right), (right?, Undo::AddLeft, Some(left))],
853 Opcode::Sub => vec![(left, Undo::SubLeft, right), (right?, Undo::SubRight, Some(left))],
854 Opcode::Xor => vec![(left, Undo::Xor, right), (right?, Undo::Xor, Some(left))],
855 Opcode::ZExt => vec![(left, Undo::Zext(self.func[left].ty.bits()), None)],
856 Opcode::SExt => vec![(left, Undo::Sext(self.func[left].ty.bits()), None)],
857 _ => return None,
858 };
859 for (operand, undo, other) in steps {
860 let other = match other {
861 Some(other) => self.refined(other, block),
862 None => Range::full(known.width()),
863 };
864 if other.width() != known.width() {
865 continue;
866 }
867 let back = ops::backward(undo, known, other);
868 if let Some(found) = self.carry_back(operand, back, value, block, depth - 1) {
869 return Some(found);
870 }
871 }
872 None
873 }
874
875 fn facts(&mut self, block: Block) -> &Vec<Relation> {
877 if !self.relations.contains_key(&block) {
878 let mut facts = match self.dom.immediate_dominator(block) {
879 Some(parent) => self.facts(parent).clone(),
880 None => Vec::new(),
881 };
882 if let Some(own) = self.own_relation(block) {
883 facts.push(own);
884 if facts.len() > RELATIONS {
885 facts.remove(0);
886 }
887 }
888 self.relations.insert(block, facts);
889 }
890 &self.relations[&block]
891 }
892
893 fn own_relation(&mut self, block: Block) -> Option<Relation> {
895 let [from] = *self.cfg.predecessors(block) else { return None };
896 let term = self.func.terminator(from)?;
897 if self.func[term].opcode != Opcode::BrIf {
898 return None;
899 }
900 let calls: Vec<_> = self.func.successors(term).collect();
901 let (then, other) = (calls.first()?, calls.get(1)?);
902 if then.block == other.block {
903 return None;
904 }
905 let taken = then.block == block;
906 let cond = *self.func[self.func[term].args].first()?;
907 let Def::Result { inst, .. } = self.func[cond].def else { return None };
908 if self.func[inst].opcode != Opcode::ICmp {
909 return None;
910 }
911 let Extra::IntPred(pred) = self.func[inst].extra else { return None };
912 let args = &self.func[self.func[inst].args];
913 let (&left, &right) = (args.first()?, args.get(1)?);
914 let pred = if taken { pred } else { pred.inverse() };
915 Some(Relation { left, pred, right })
916 }
917
918 fn constant(&self, value: Value) -> Option<u128> {
920 let Def::Result { inst, .. } = self.func[value].def else { return None };
921 if self.func[inst].opcode != Opcode::IConst {
922 return None;
923 }
924 let Extra::Imm(at) = self.func[inst].extra else { return None };
925 Some(self.func[at].unsigned())
926 }
927}
928
929fn defining_block(func: &Func, value: Value) -> Option<Block> {
931 match func[value].def {
932 Def::Param { block, .. } => Some(block),
933 Def::Result { inst, .. } => func.block_of(inst),
934 }
935}
936
937fn argument(func: &Func, pred: Block, block: Block, index: usize) -> Option<Value> {
943 let term = func.terminator(pred)?;
944 let mut found = None;
945 for call in func.successors(term) {
946 if call.block != block {
947 continue;
948 }
949 let arg = *func[call.args].get(index)?;
950 if found.replace(arg).is_some_and(|old| old != arg) {
951 return None;
952 }
953 }
954 found
955}
956
957fn read(facts: &[Relation], a: Value, b: Value) -> Option<IntPred> {
959 facts.iter().rev().find_map(|fact| {
960 if fact.left == a && fact.right == b {
961 Some(fact.pred)
962 } else if fact.left == b && fact.right == a {
963 Some(fact.pred.swapped())
964 } else {
965 None
966 }
967 })
968}
969
970const fn outcomes(pred: IntPred) -> u8 {
972 match pred {
973 IntPred::Eq => 0b010,
974 IntPred::Ne => 0b101,
975 IntPred::Slt | IntPred::Ult => 0b001,
976 IntPred::Sle | IntPred::Ule => 0b011,
977 IntPred::Sgt | IntPred::Ugt => 0b100,
978 IntPred::Sge | IntPred::Uge => 0b110,
979 }
980}
981
982const fn comparable(a: IntPred, b: IntPred) -> bool {
988 ordering_free(a) || ordering_free(b) || a.is_signed() == b.is_signed()
989}
990
991const fn ordering_free(pred: IntPred) -> bool {
993 matches!(pred, IntPred::Eq | IntPred::Ne)
994}
995
996fn implies(known: IntPred, pred: IntPred) -> bool {
998 comparable(known, pred) && outcomes(known) & !outcomes(pred) == 0
999}
1000
1001fn excludes(known: IntPred, pred: IntPred) -> bool {
1003 comparable(known, pred) && outcomes(known) & outcomes(pred) == 0
1004}
1005
1006fn compose(first: IntPred, second: IntPred) -> Option<IntPred> {
1012 if !comparable(first, second) {
1013 return None;
1014 }
1015 let strict = |pred| matches!(pred, IntPred::Slt | IntPred::Ult | IntPred::Sgt | IntPred::Ugt);
1016 let direction = |pred| outcomes(pred) & 0b101;
1017 match (first, second) {
1018 (IntPred::Eq, other) | (other, IntPred::Eq) => Some(other),
1019 (IntPred::Ne, _) | (_, IntPred::Ne) => None,
1022 _ if direction(first) != direction(second) => None,
1025 _ if strict(first) => Some(first),
1026 _ => Some(second),
1027 }
1028}
1029
1030#[cfg(test)]
1031mod tests {
1032 use rucc_base::Interner;
1033 use rucc_ir::{Block, Builder, Flags, Func, IntPred, Opcode, Signature, Type, Value};
1034
1035 use super::{Options, Ranges};
1036 use crate::cfg::Cfg;
1037 use crate::dom::Dominators;
1038 use crate::range::Range;
1039 use crate::range::ops::{self, Truth};
1040
1041 const I32: Type = Type::int(32);
1042
1043 fn shape(params: usize, blocks: usize) -> (Func, Vec<Value>, Vec<Block>) {
1049 let mut names = Interner::new();
1050 let types = vec![I32; params];
1051 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&types));
1052 let blocks: Vec<Block> = (0..blocks).map(|_| func.create_block()).collect();
1053 let args = types.iter().map(|&ty| func.append_param(blocks[0], ty)).collect();
1054 (func, args, blocks)
1055 }
1056
1057 struct Asked {
1059 cfg: Cfg,
1060 dom: Dominators,
1061 func: Func,
1062 }
1063
1064 impl Asked {
1065 fn new(func: Func) -> Self {
1066 let cfg = Cfg::new(&func);
1067 let dom = Dominators::new(&cfg);
1068 Asked { cfg, dom, func }
1069 }
1070
1071 fn ranges(&self) -> Ranges<'_> {
1072 Ranges::new(&self.func, &self.cfg, &self.dom)
1073 }
1074
1075 fn with(&self, options: Options) -> Ranges<'_> {
1076 Ranges::with(&self.func, &self.cfg, &self.dom, options)
1077 }
1078 }
1079
1080 fn bounds(range: Range) -> Option<(i128, i128)> {
1082 range.signed_bounds()
1083 }
1084
1085 #[test]
1086 fn a_constant_is_itself() {
1087 let (mut func, _, blocks) = shape(0, 1);
1088 let mut build = Builder::new(&mut func, blocks[0]);
1089 let seven = build.iconst(I32, 7);
1090 build.ret(&[]);
1091 let asked = Asked::new(func);
1092 assert_eq!(asked.ranges().of(seven).singleton(), Some(7));
1093 }
1094
1095 #[test]
1096 fn arithmetic_on_constants_is_the_arithmetic() {
1097 let (mut func, _, blocks) = shape(0, 1);
1098 let mut build = Builder::new(&mut func, blocks[0]);
1099 let a = build.iconst(I32, 7);
1100 let b = build.iconst(I32, 5);
1101 let sum = build.binary(Opcode::Add, a, b, Flags::NONE);
1102 build.ret(&[]);
1103 let asked = Asked::new(func);
1104 assert_eq!(asked.ranges().of(sum).singleton(), Some(12));
1105 }
1106
1107 #[test]
1108 fn a_value_nothing_is_known_about_is_the_whole_of_its_type_and_says_which_opcode_lost_it() {
1109 let (mut func, args, blocks) = shape(1, 1);
1110 let mut build = Builder::new(&mut func, blocks[0]);
1111 let counted = build.unary(Opcode::Ctlz, args[0], I32);
1112 let squared = build.binary(Opcode::Mul, args[0], args[0], Flags::NONE);
1113 build.ret(&[]);
1114 let asked = Asked::new(func);
1115 let mut ranges = asked.ranges();
1116 assert!(ranges.of(args[0]).is_full(), "a parameter is anything");
1117 assert_eq!(bounds(ranges.of(counted)), Some((0, 32)));
1119 assert!(ranges.of(squared).is_full());
1120 assert_eq!(ranges.counts().losses(), vec![(Opcode::Mul, 1)]);
1121 }
1122
1123 fn guarded(pred: IntPred, bound: i128) -> (Func, Value, Block, Block) {
1125 let (mut func, args, blocks) = shape(1, 3);
1126 let mut build = Builder::new(&mut func, blocks[0]);
1127 let limit = build.iconst(I32, bound);
1128 let test = build.icmp(pred, args[0], limit);
1129 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1130 Builder::new(&mut func, blocks[1]).ret(&[]);
1131 Builder::new(&mut func, blocks[2]).ret(&[]);
1132 (func, args[0], blocks[1], blocks[2])
1133 }
1134
1135 #[test]
1136 fn a_branch_narrows_the_value_it_tested_on_both_of_its_edges() {
1137 let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1138 let asked = Asked::new(func);
1139 let mut ranges = asked.ranges();
1140 assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1141 assert_eq!(bounds(ranges.at(x, otherwise)), Some((10, i128::from(i32::MAX))));
1142 }
1143
1144 #[test]
1145 fn the_range_at_the_definition_is_not_the_range_at_the_use() {
1146 let (func, x, then, _) = guarded(IntPred::Ult, 64);
1147 let asked = Asked::new(func);
1148 let mut ranges = asked.ranges();
1149 assert!(ranges.of(x).is_full(), "nothing is known where it is defined");
1150 assert_eq!(ranges.at(x, then).unsigned_bounds(), Some((0, 63)));
1151 }
1152
1153 #[test]
1154 fn a_null_check_is_the_fact_a_single_interval_cannot_hold() {
1155 let (func, x, _, otherwise) = guarded(IntPred::Eq, 0);
1156 let asked = Asked::new(func);
1157 let mut ranges = asked.ranges();
1158 let range = ranges.at(x, otherwise);
1159 assert!(range.nonzero(), "the else edge of an equality with zero proves it");
1160 assert_eq!(range.pairs().len(), 1);
1164 }
1165
1166 fn through_arithmetic(offset: i128, bound: i128) -> (Func, Value, Block) {
1168 let (mut func, args, blocks) = shape(1, 3);
1169 let mut build = Builder::new(&mut func, blocks[0]);
1170 let by = build.iconst(I32, offset);
1171 let shifted = build.binary(Opcode::Add, args[0], by, Flags::NSW);
1172 let limit = build.iconst(I32, bound);
1173 let test = build.icmp(IntPred::Slt, shifted, limit);
1174 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1175 Builder::new(&mut func, blocks[1]).ret(&[]);
1176 Builder::new(&mut func, blocks[2]).ret(&[]);
1177 (func, args[0], blocks[1])
1178 }
1179
1180 #[test]
1181 fn the_condition_is_inverted_back_to_the_value_it_was_computed_from() {
1182 let (func, x, then) = through_arithmetic(3, 10);
1183 let asked = Asked::new(func);
1184 let mut ranges = asked.ranges();
1185 let (_, high) = bounds(ranges.at(x, then)).expect("not empty");
1186 assert!(high <= 6, "x + 3 < 10 makes x at most six, and this said {high}");
1187 }
1188
1189 #[test]
1190 fn the_inversion_stops_where_it_is_told_to() {
1191 let (func, x, then) = through_arithmetic(3, 10);
1192 let asked = Asked::new(func);
1193 let options = Options { logical_depth: 1, ..Options::default() };
1194 let mut ranges = asked.with(options);
1195 assert!(ranges.at(x, then).is_full(), "one step cannot reach past the comparison");
1196 }
1197
1198 fn counting(start: i128, step: i128, flags: Flags) -> (Func, Value, Vec<Block>) {
1204 let (mut func, _, blocks) = shape(0, 4);
1205 let counter = func.append_param(blocks[1], I32);
1206 let mut build = Builder::new(&mut func, blocks[0]);
1207 let first = build.iconst(I32, start);
1208 build.jump(blocks[1], &[first]);
1209 let mut build = Builder::new(&mut func, blocks[1]);
1210 let limit = build.iconst(I32, 100);
1211 let test = build.icmp(IntPred::Slt, counter, limit);
1212 build.br_if(test, blocks[2], &[], blocks[3], &[]);
1213 let mut build = Builder::new(&mut func, blocks[2]);
1214 let by = build.iconst(I32, step);
1215 let next = build.binary(Opcode::Add, counter, by, flags);
1216 build.jump(blocks[1], &[next]);
1217 Builder::new(&mut func, blocks[3]).ret(&[]);
1218 (func, counter, blocks)
1219 }
1220
1221 #[test]
1222 fn a_counter_is_pinned_at_the_end_it_started_from_and_the_branch_says_the_other() {
1223 let (func, counter, blocks) = counting(0, 1, Flags::NSW);
1224 let asked = Asked::new(func);
1225 let mut ranges = asked.ranges();
1226 let at_def = ranges.of(counter);
1230 assert!(at_def.contains(0) && at_def.contains(50) && at_def.contains(100));
1231 assert_eq!(bounds(at_def), Some((0, 100)));
1232 assert_eq!(ranges.counts().counters(), 1, "one counter, read once");
1233 let (_, inside) = bounds(ranges.at(counter, blocks[2])).expect("not empty");
1235 assert_eq!(inside, 99);
1236 let (after, _) = bounds(ranges.at(counter, blocks[3])).expect("not empty");
1237 assert_eq!(after, 100);
1238 }
1239
1240 #[test]
1241 fn a_counter_that_walks_down_is_pinned_at_the_top() {
1242 let (func, counter, _) = counting(50, -1, Flags::NSW);
1245 let asked = Asked::new(func);
1246 let mut ranges = asked.ranges();
1247 assert_eq!(bounds(ranges.of(counter)), Some((i128::from(i32::MIN), 50)));
1248 }
1249
1250 #[test]
1251 fn a_counter_that_may_wrap_is_not_pinned_down() {
1252 let (func, counter, _) = counting(0, 1, Flags::NONE);
1256 let asked = Asked::new(func);
1257 let mut ranges = asked.ranges();
1258 let at_def = ranges.of(counter);
1259 assert!(at_def.contains(u128::from(u32::MAX)), "minus one is still in it");
1260 assert_eq!(ranges.counts().counters(), 0, "nothing was read off the recurrence");
1261 }
1262
1263 #[test]
1264 fn a_block_parameter_is_everything_its_predecessors_pass_to_it() {
1265 let (mut func, args, blocks) = shape(1, 4);
1266 let merged = func.append_param(blocks[3], I32);
1267 let mut build = Builder::new(&mut func, blocks[0]);
1268 let zero = build.iconst(I32, 0);
1269 let cond = build.icmp(IntPred::Slt, args[0], zero);
1270 build.br_if(cond, blocks[1], &[], blocks[2], &[]);
1271 let mut build = Builder::new(&mut func, blocks[1]);
1272 let five = build.iconst(I32, 5);
1273 build.jump(blocks[3], &[five]);
1274 let mut build = Builder::new(&mut func, blocks[2]);
1275 let nine = build.iconst(I32, 9);
1276 build.jump(blocks[3], &[nine]);
1277 Builder::new(&mut func, blocks[3]).ret(&[]);
1278 let asked = Asked::new(func);
1279 let mut ranges = asked.ranges();
1280 let range = ranges.of(merged);
1281 assert!(range.contains(5) && range.contains(9), "both arms are in it");
1282 assert!(!range.contains(7), "and nothing between them is");
1283 }
1284
1285 #[test]
1286 fn a_switch_edge_pins_its_cases_and_the_default_excludes_them() {
1287 let (mut func, args, blocks) = shape(1, 3);
1288 let mut build = Builder::new(&mut func, blocks[0]);
1289 build.switch(args[0], blocks[2], &[(4, blocks[1]), (7, blocks[1])]);
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!(ranges.at(args[0], blocks[1]).list(4), Some(vec![4, 7]), "the two cases");
1295 let fell_through = ranges.at(args[0], blocks[2]);
1296 assert!(!fell_through.contains(4) && !fell_through.contains(7));
1297 assert!(fell_through.contains(5), "and everything else is still possible");
1298 }
1299
1300 #[test]
1301 fn both_arms_of_an_and_hold_where_it_is_true() {
1302 let (mut func, args, blocks) = shape(1, 3);
1303 let mut build = Builder::new(&mut func, blocks[0]);
1304 let low = build.iconst(I32, 10);
1305 let high = build.iconst(I32, 20);
1306 let above = build.icmp(IntPred::Sgt, args[0], low);
1307 let below = build.icmp(IntPred::Slt, args[0], high);
1308 let both = build.binary(Opcode::And, above, below, Flags::NONE);
1309 build.br_if(both, blocks[1], &[], blocks[2], &[]);
1310 Builder::new(&mut func, blocks[1]).ret(&[]);
1311 Builder::new(&mut func, blocks[2]).ret(&[]);
1312 let asked = Asked::new(func);
1313 let mut ranges = asked.ranges();
1314 assert_eq!(bounds(ranges.at(args[0], blocks[1])), Some((11, 19)));
1315 assert!(ranges.at(args[0], blocks[2]).is_full(), "the false edge says nothing");
1316 }
1317
1318 #[test]
1319 fn a_comparison_the_ranges_settle_is_settled() {
1320 let (func, x, then, _) = guarded(IntPred::Slt, 10);
1321 let mut asked = Asked::new(func);
1322 let ten = {
1323 let mut build = Builder::new(&mut asked.func, then);
1324 build.iconst(I32, 10)
1325 };
1326 let asked = Asked::new(asked.func);
1327 let mut ranges = asked.ranges();
1328 assert_eq!(ranges.compare(IntPred::Slt, x, ten, then), Truth::Always);
1329 assert_eq!(ranges.compare(IntPred::Sgt, x, ten, then), Truth::Never);
1330 }
1331
1332 fn related() -> (Func, Value, Value, Vec<Block>) {
1336 let (mut func, args, blocks) = shape(2, 4);
1337 let mut build = Builder::new(&mut func, blocks[0]);
1338 let test = build.icmp(IntPred::Slt, args[0], args[1]);
1339 build.br_if(test, blocks[1], &[], blocks[2], &[]);
1340 Builder::new(&mut func, blocks[1]).jump(blocks[3], &[]);
1341 Builder::new(&mut func, blocks[2]).jump(blocks[3], &[]);
1342 Builder::new(&mut func, blocks[3]).ret(&[]);
1343 (func, args[0], args[1], blocks)
1344 }
1345
1346 #[test]
1347 fn a_relation_the_intervals_cannot_see_is_still_known() {
1348 let (func, a, b, blocks) = related();
1349 let asked = Asked::new(func);
1350 let mut ranges = asked.ranges();
1351 let (left, right) = (ranges.at(a, blocks[1]), ranges.at(b, blocks[1]));
1355 assert_eq!(ops::compare(IntPred::Slt, left, right), Truth::Either);
1356 assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1357 assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[1]), Truth::Always);
1358 assert_eq!(ranges.compare(IntPred::Sge, a, b, blocks[1]), Truth::Never);
1359 assert_eq!(ranges.compare(IntPred::Ne, a, b, blocks[1]), Truth::Always);
1360 assert_eq!(ranges.compare(IntPred::Ult, a, b, blocks[1]), Truth::Either);
1361 }
1362
1363 #[test]
1364 fn a_relation_belongs_to_the_block_the_edge_led_to() {
1365 let (func, a, b, blocks) = related();
1366 let asked = Asked::new(func);
1367 let mut ranges = asked.ranges();
1368 assert_eq!(ranges.relation(a, b, blocks[1]), Some(IntPred::Slt));
1369 assert_eq!(ranges.relation(a, b, blocks[2]), Some(IntPred::Sge), "the other edge");
1370 assert_eq!(ranges.relation(a, b, blocks[3]), None, "where they meet, neither holds");
1371 assert_eq!(ranges.compare(IntPred::Slt, a, b, blocks[3]), Truth::Either);
1372 }
1373
1374 #[test]
1375 fn one_step_of_composition_is_taken() {
1376 let (mut func, args, blocks) = shape(3, 4);
1377 let [a, b, c] = [args[0], args[1], args[2]];
1378 let mut build = Builder::new(&mut func, blocks[0]);
1379 let first = build.icmp(IntPred::Slt, a, b);
1380 build.br_if(first, blocks[1], &[], blocks[3], &[]);
1381 let mut build = Builder::new(&mut func, blocks[1]);
1382 let second = build.icmp(IntPred::Sle, b, c);
1383 build.br_if(second, blocks[2], &[], blocks[3], &[]);
1384 Builder::new(&mut func, blocks[2]).ret(&[]);
1385 Builder::new(&mut func, blocks[3]).ret(&[]);
1386 let asked = Asked::new(func);
1387 let mut ranges = asked.ranges();
1388 assert_eq!(ranges.relation(a, c, blocks[2]), Some(IntPred::Slt), "a < b and b <= c");
1389 assert_eq!(ranges.compare(IntPred::Slt, a, c, blocks[2]), Truth::Always);
1390 }
1391
1392 #[test]
1393 fn the_cache_gives_up_rather_than_growing_without_a_bound() {
1394 let (func, x, then, otherwise) = guarded(IntPred::Slt, 10);
1395 let asked = Asked::new(func);
1396 let options = Options { refinements: 1, ..Options::default() };
1397 let mut ranges = asked.with(options);
1398 assert_eq!(bounds(ranges.at(x, then)), Some((i128::from(i32::MIN), 9)));
1399 assert!(ranges.at(x, otherwise).is_full(), "past the bound it is the definition range");
1400 assert_eq!(ranges.counts().fallbacks(), 1);
1401 }
1402
1403 #[test]
1404 fn asking_twice_asks_the_cache_the_second_time() {
1405 let (func, x, then, _) = guarded(IntPred::Slt, 10);
1406 let asked = Asked::new(func);
1407 let mut ranges = asked.ranges();
1408 let first = ranges.at(x, then);
1409 let hits = ranges.counts().hits();
1410 let second = ranges.at(x, then);
1411 assert_eq!(first, second);
1412 assert!(ranges.counts().hits() > hits, "the second query hit the cache");
1413 assert_eq!(ranges.counts().queries(), 2);
1414 }
1415
1416 #[test]
1417 fn a_range_that_is_only_true_because_overflow_is_undefined_is_counted() {
1418 let (mut func, args, blocks) = shape(1, 1);
1419 let mut build = Builder::new(&mut func, blocks[0]);
1420 let big = build.iconst(I32, i128::from(i32::MAX) - 4);
1421 let counted = build.unary(Opcode::Ctlz, args[0], I32);
1422 let sum = build.binary(Opcode::Add, counted, big, Flags::NSW);
1423 build.ret(&[]);
1424 let asked = Asked::new(func);
1425 let mut ranges = asked.ranges();
1426 assert!(!ranges.of(sum).is_full(), "the promise not to overflow bounds the sum");
1427 assert_eq!(ranges.counts().assumed(), 1);
1428 }
1429
1430 #[test]
1431 fn a_query_about_something_that_is_not_an_integer_answers_without_pretending() {
1432 let (mut func, _, blocks) = shape(0, 1);
1433 let mut build = Builder::new(&mut func, blocks[0]);
1434 let mem = build.mem_entry();
1435 build.ret(&[]);
1436 let asked = Asked::new(func);
1437 let mut ranges = asked.ranges();
1438 assert!(ranges.of(mem).is_full());
1439 assert_eq!(ranges.counts().full(), 0, "a memory value is not a lost integer");
1440 }
1441}