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