1use std::collections::{HashMap, HashSet, VecDeque};
117
118use rucc_base::Idx;
119use rucc_ir::{Block, BlockCall, Def, Extra, Func, Inst, IntPred, Opcode, Value};
120
121use crate::fold::constant;
122use crate::{Analyses, Fuel, Pass, Preserved, Stats, uses};
123
124const FOLDED: &str = "branch on a condition that is always the same way replaced by a jump";
126
127pub(crate) const REMOVED: &str = "block nothing reaches removed";
129
130const MERGED: &str = "block with one way into it merged into the block above it";
132
133const FORWARDED: &str = "block that only jumped somewhere else removed and its edges pointed past";
135
136const SAME_EVERY_WAY: &str = "block parameter that arrives as the same value every way in removed";
138
139const NO_FUEL: &str = "branch on a known condition left alone, the pass ran out of fuel";
141
142const NO_FUEL_MERGE: &str = "block with one way into it left alone, the pass ran out of fuel";
144
145const NO_FUEL_FORWARD: &str =
147 "block that only jumped somewhere else kept, the pass ran out of fuel";
148
149const NO_FUEL_PARAM: &str = "block parameter that is one value kept, the pass ran out of fuel";
151
152#[derive(Debug, Clone, Copy, PartialEq, Eq)]
154pub struct SimplifyCfg;
155
156impl Pass for SimplifyCfg {
157 fn name(&self) -> &'static str {
158 "simplify-cfg"
159 }
160
161 fn describe(&self) -> &'static str {
162 "unreachable blocks go, a branch that only goes one way becomes a jump, a block that only \
163 jumps stops being in the way, and a block with one way in is merged into the one above it"
164 }
165
166 fn preserves(&self) -> Preserved {
167 Preserved::NONE
170 }
171
172 fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
173 let mut stats = Stats::new();
174 sweep(func, an, &mut stats);
178 let mut folded = false;
179 let unbound = Bindings::new();
183 for block in func.blocks().collect::<Vec<Block>>() {
184 let Some(term) = func.terminator(block) else { continue };
185 let Some(taken) = taken(func, term, &unbound) else { continue };
186 if !fuel.take() {
187 stats.missed(NO_FUEL);
190 continue;
191 }
192 jump_to(func, term, taken);
193 stats.optimized(FOLDED);
194 folded = true;
195 }
196 if folded {
197 an.clear();
201 sweep(func, an, &mut stats);
202 }
203 let mut forward = HashMap::new();
204 if straighten(func, fuel, &mut stats, &mut forward) {
208 an.clear();
209 }
210 for chain in chains(func, an) {
214 for (at, &block) in chain.iter().enumerate().skip(1) {
215 if !fuel.take() {
216 for _ in at..chain.len() {
220 stats.missed(NO_FUEL_MERGE);
221 }
222 break;
223 }
224 merge(func, chain[0], block, &mut forward);
225 stats.optimized(MERGED);
226 }
227 }
228 if !forward.is_empty() {
229 uses::substitute(func, &forward);
232 }
233 stats
234 }
235}
236
237pub(crate) type Bindings = HashMap<Value, Value>;
244
245fn resolve(subst: &Bindings, value: Value) -> Value {
247 subst.get(&value).copied().unwrap_or(value)
248}
249
250pub(crate) fn taken(func: &Func, term: Inst, subst: &Bindings) -> Option<BlockCall> {
259 let data = &func[term];
260 let arg = *func[data.args].first()?;
261 match data.opcode {
262 Opcode::BrIf => {
263 let Extra::Targets(targets) = data.extra else { return None };
264 if let Some(call) = one_place(func, &func[targets]) {
265 return Some(call);
266 }
267 let arm = usize::from(!known(func, arg, subst)?);
270 func[targets].get(arm).copied()
271 }
272 Opcode::Switch => {
273 let Extra::Switch(at) = data.extra else { return None };
274 let info = func[at];
275 if let Some(call) = one_place(func, &func[info.targets]) {
276 return Some(call);
277 }
278 let (value, _) = constant(func, resolve(subst, arg))?;
279 let case = func[info.cases].iter().position(|it| *it == value);
282 func[info.targets].get(case.map_or(0, |case| case + 1)).copied()
283 }
284 _ => None,
285 }
286}
287
288fn one_place(func: &Func, calls: &[BlockCall]) -> Option<BlockCall> {
300 let &first = calls.first()?;
301 let same = |call: &BlockCall| call.block == first.block && func[call.args] == func[first.args];
302 calls[1..].iter().all(same).then_some(first)
303}
304
305pub(crate) fn jump_to(func: &mut Func, term: Inst, call: BlockCall) {
310 let targets = func.push_block_calls(&[call]);
311 let args = func.push_values(&[]);
312 let data = &mut func[term];
313 data.opcode = Opcode::Jump;
314 data.args = args;
315 data.extra = Extra::Targets(targets);
316}
317
318pub(crate) fn sweep(func: &mut Func, an: &mut Analyses, stats: &mut Stats) {
327 let gone = stranded(func, an);
328 if gone.is_empty() {
329 return;
330 }
331 for block in gone {
332 func.remove_block(block);
333 stats.optimized(REMOVED);
334 }
335 an.clear();
336}
337
338fn stranded(func: &Func, an: &mut Analyses) -> Vec<Block> {
347 let cfg = an.cfg(func);
348 let Some(entry) = cfg.entry() else { return Vec::new() };
349 let mut seen = vec![false; cfg.capacity()];
350 seen[entry.index()] = true;
351 let mut stack = vec![entry];
352 let mut reached = Vec::new();
353 while let Some(block) = stack.pop() {
354 for &succ in cfg.successors(block) {
355 if !seen[succ.index()] {
356 seen[succ.index()] = true;
357 stack.push(succ);
358 }
359 }
360 reached.push(block);
361 }
362 let mut next = reached;
365 while !next.is_empty() {
366 let mut found = Vec::new();
367 for block in next {
368 for inst in func.insts(block) {
369 if func[inst].opcode != Opcode::BlockAddr {
370 continue;
371 }
372 for call in func.successors(inst) {
373 if !seen[call.block.index()] {
374 seen[call.block.index()] = true;
375 found.push(call.block);
376 }
377 }
378 }
379 }
380 let mut stack = found.clone();
383 while let Some(block) = stack.pop() {
384 for &succ in cfg.successors(block) {
385 if !seen[succ.index()] {
386 seen[succ.index()] = true;
387 stack.push(succ);
388 found.push(succ);
389 }
390 }
391 }
392 next = found;
393 }
394 func.blocks().filter(|block| !seen[block.index()]).collect()
395}
396
397pub(crate) type Edges = HashMap<Block, Vec<(Block, Idx<BlockCall>)>>;
404
405pub(crate) fn incoming(func: &Func) -> Edges {
413 let mut edges: Edges = HashMap::new();
414 for block in func.blocks() {
415 let Some(term) = func.terminator(block) else { continue };
416 for at in func.target_list(term).iter() {
417 edges.entry(func[at].block).or_default().push((block, at));
418 }
419 }
420 edges
421}
422
423fn straighten(
449 func: &mut Func,
450 fuel: &mut Fuel,
451 stats: &mut Stats,
452 forward: &mut HashMap<Value, Value>,
453) -> bool {
454 let Some(entry) = func.entry() else { return false };
455 let addressed = addressed(func);
456 let mut edges = incoming(func);
457 let mut work: VecDeque<Block> = func.blocks().collect();
458 let mut queued: HashSet<Block> = work.iter().copied().collect();
459 let mut gone: HashSet<Block> = HashSet::new();
460 let mut changed = false;
461 while let Some(block) = work.pop_front() {
462 queued.remove(&block);
463 if gone.contains(&block) {
464 continue;
465 }
466 let mut starved = false;
467 if block != entry {
468 let drop = redundant(func, block, edges.get(&block), forward);
469 let mut taking = Vec::new();
470 for (index, value) in drop {
471 if !fuel.take() {
472 stats.missed(NO_FUEL_PARAM);
473 starved = true;
474 break;
475 }
476 let value = uses::chase(forward, value);
479 forward.insert(func[block].params[index], value);
480 taking.push(index);
481 stats.optimized(SAME_EVERY_WAY);
482 }
483 if !taking.is_empty() {
484 take_params(func, block, &taking, edges.get(&block));
485 requeue(block, &mut work, &mut queued);
488 if let Some(term) = func.terminator(block) {
491 for call in func.successors(term).collect::<Vec<BlockCall>>() {
492 requeue(call.block, &mut work, &mut queued);
493 }
494 }
495 changed = true;
496 }
497 }
498 if starved {
501 break;
502 }
503 let Some((term, into, args)) = forwards(func, block, entry, &addressed, &edges) else {
504 continue;
505 };
506 if !fuel.take() {
507 stats.missed(NO_FUEL_FORWARD);
508 break;
509 }
510 let out = func.target_list(term).iter().next().expect("a jump has a target");
514 if let Some(list) = edges.get_mut(&into) {
515 list.retain(|&(_, at)| at != out);
516 }
517 let ins = edges.remove(&block).unwrap_or_default();
518 for &(_, at) in &ins {
519 let args = func.push_values(&args);
523 func.set_block_call(at, BlockCall { block: into, args });
524 }
525 edges.entry(into).or_default().extend(ins.iter().copied());
526 func.remove_block(block);
527 gone.insert(block);
528 stats.optimized(FORWARDED);
529 changed = true;
530 requeue(into, &mut work, &mut queued);
531 for &(from, _) in &ins {
532 requeue(from, &mut work, &mut queued);
533 }
534 }
535 changed
536}
537
538fn requeue(block: Block, work: &mut VecDeque<Block>, queued: &mut HashSet<Block>) {
540 if queued.insert(block) {
541 work.push_back(block);
542 }
543}
544
545fn redundant(
561 func: &Func,
562 block: Block,
563 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
564 forward: &HashMap<Value, Value>,
565) -> Vec<(usize, Value)> {
566 let Some(ins) = ins.filter(|ins| !ins.is_empty()) else { return Vec::new() };
567 let mut found = Vec::new();
568 for (index, ¶m) in func[block].params.iter().enumerate() {
569 let mut only = None;
570 let mut agree = true;
571 for &(_, at) in ins {
572 let list = func[at].args;
573 let Some(&arg) = func[list].get(index) else {
574 agree = false;
577 break;
578 };
579 let arg = uses::chase(forward, arg);
580 if arg == param {
581 continue;
582 }
583 match only {
584 None => only = Some(arg),
585 Some(seen) if seen == arg => {}
586 Some(_) => {
587 agree = false;
588 break;
589 }
590 }
591 }
592 if !agree {
593 continue;
594 }
595 if let Some(value) = only {
596 found.push((index, value));
597 }
598 }
599 found
600}
601
602fn take_params(
607 func: &mut Func,
608 block: Block,
609 taking: &[usize],
610 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
611) {
612 for &(_, at) in ins.into_iter().flatten() {
613 let call = func[at];
614 let kept: Vec<Value> = func[call.args]
615 .iter()
616 .enumerate()
617 .filter(|(index, _)| !taking.contains(index))
618 .map(|(_, &value)| value)
619 .collect();
620 let args = func.push_values(&kept);
621 func.set_block_call(at, BlockCall { block: call.block, args });
622 }
623 let mut index = 0;
624 func.retain_params(block, |_| {
625 let keep = !taking.contains(&index);
626 index += 1;
627 keep
628 });
629}
630
631fn forwards(
637 func: &Func,
638 block: Block,
639 entry: Block,
640 addressed: &HashSet<Block>,
641 edges: &Edges,
642) -> Option<(Inst, Block, Vec<Value>)> {
643 if block == entry || addressed.contains(&block) || !func[block].params.is_empty() {
644 return None;
645 }
646 let term = func.terminator(block)?;
647 if func[term].opcode != Opcode::Jump {
648 return None;
649 }
650 if func.insts(block).count() != 1 {
653 return None;
654 }
655 let call = func.successors(term).next()?;
656 if call.block == block {
657 return None;
658 }
659 if carrying(func, block, call.block, func[call.args].len(), edges) {
660 return None;
661 }
662 Some((term, call.block, func[call.args].to_vec()))
663}
664
665fn carrying(func: &Func, block: Block, into: Block, args: usize, edges: &Edges) -> bool {
679 if args == 0 {
680 return false;
681 }
682 let ins = edges.get(&block).map_or(0, Vec::len);
683 let after = edges.get(&into).map_or(0, Vec::len) - 1 + ins;
684 if after < 2 {
685 return false;
686 }
687 edges.get(&block).into_iter().flatten().any(|&(from, _)| {
688 let Some(term) = func.terminator(from) else { return false };
689 func.target_list(term).iter().count() >= 2
690 })
691}
692
693fn chains(func: &Func, an: &mut Analyses) -> Vec<Vec<Block>> {
714 let cfg = an.cfg(func);
715 let Some(entry) = cfg.entry() else { return Vec::new() };
716 let addressed = addressed(func);
717 let mut below = HashMap::new();
718 let mut is_below = HashSet::new();
719 for block in func.blocks() {
720 let Some(term) = func.terminator(block) else { continue };
721 if func[term].opcode != Opcode::Jump {
722 continue;
723 }
724 let Some(call) = func.successors(term).next() else { continue };
725 let into = call.block;
726 let preds = cfg.predecessors(into);
727 if into == entry || into == block || addressed.contains(&into) {
728 continue;
729 }
730 if preds.len() != 1 || preds[0] != block {
731 continue;
732 }
733 below.insert(block, into);
734 is_below.insert(into);
735 }
736 let heads = func.blocks().filter(|it| below.contains_key(it) && !is_below.contains(it));
737 heads
738 .map(|head| {
739 let mut chain = vec![head];
740 let mut at = head;
741 while let Some(&next) = below.get(&at) {
742 chain.push(next);
743 at = next;
744 }
745 chain
746 })
747 .collect()
748}
749
750fn addressed(func: &Func) -> HashSet<Block> {
752 let mut taken = HashSet::new();
753 for block in func.blocks() {
754 for inst in func.insts(block) {
755 if func[inst].opcode != Opcode::BlockAddr {
756 continue;
757 }
758 for call in func.successors(inst) {
759 taken.insert(call.block);
760 }
761 }
762 }
763 taken
764}
765
766fn merge(func: &mut Func, head: Block, block: Block, forward: &mut HashMap<Value, Value>) {
773 let term = func.terminator(head).expect("the head of a chain ends in a jump");
774 let call = func.successors(term).next().expect("a jump goes somewhere");
775 let args = func[call.args].to_vec();
776 let params = func[block].params.clone();
777 for (param, arg) in params.into_iter().zip(args) {
778 let arg = uses::chase(forward, arg);
782 forward.insert(param, arg);
783 }
784 func.remove_inst(term);
785 for inst in func.insts(block).collect::<Vec<Inst>>() {
786 func.remove_inst(inst);
787 func.append_inst(head, inst);
788 }
789 func.remove_block(block);
790}
791
792fn known(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
794 let value = resolve(subst, value);
795 if let Some((imm, _)) = constant(func, value) {
796 return Some(imm.unsigned() != 0);
797 }
798 compared(func, value, subst)
799}
800
801fn compared(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
809 let Def::Result { inst, .. } = func[value].def else { return None };
810 let data = &func[inst];
811 if data.opcode != Opcode::ICmp {
812 return None;
813 }
814 let Extra::IntPred(pred) = data.extra else { return None };
815 let args = &func[data.args];
816 let (lhs, ty) = constant(func, resolve(subst, *args.first()?))?;
817 let (rhs, _) = constant(func, resolve(subst, *args.get(1)?))?;
818 Some(match pred {
819 IntPred::Eq => lhs == rhs,
820 IntPred::Ne => lhs != rhs,
821 IntPred::Slt => lhs.signed(ty) < rhs.signed(ty),
822 IntPred::Sle => lhs.signed(ty) <= rhs.signed(ty),
823 IntPred::Sgt => lhs.signed(ty) > rhs.signed(ty),
824 IntPred::Sge => lhs.signed(ty) >= rhs.signed(ty),
825 IntPred::Ult => lhs.unsigned() < rhs.unsigned(),
826 IntPred::Ule => lhs.unsigned() <= rhs.unsigned(),
827 IntPred::Ugt => lhs.unsigned() > rhs.unsigned(),
828 IntPred::Uge => lhs.unsigned() >= rhs.unsigned(),
829 })
830}
831
832#[cfg(test)]
833mod tests {
834 use rucc_base::Interner;
835 use rucc_ir::{
836 Block, Builder, Def, Func, Inst, IntPred, Module, Opcode, Signature, Type, Value,
837 };
838 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
839
840 use super::SimplifyCfg;
841 use crate::stats::Kind;
842 use crate::testing::graph;
843 use crate::{Analyses, Fuel, Pass, Preserved, Stats};
844
845 fn simplify(func: &mut Func) -> Stats {
847 SimplifyCfg.run(func, &mut Analyses::new(), &mut Fuel::unlimited())
848 }
849
850 fn blocks(func: &Func) -> Vec<usize> {
852 func.blocks().map(Block::index).collect()
853 }
854
855 fn terminator(func: &Func, block: usize) -> Opcode {
857 let block = Block::from_usize(block);
858 func[func.terminator(block).expect("every block here has one")].opcode
859 }
860
861 fn goes_to(func: &Func, block: usize) -> Vec<usize> {
863 let block = Block::from_usize(block);
864 let term = func.terminator(block).expect("every block here has one");
865 func.successors(term).map(|call| call.block.index()).collect()
866 }
867
868 fn lives_in(func: &Func, value: Value) -> Option<usize> {
874 let Def::Result { inst, .. } = func[value].def else { return None };
875 func.block_of(inst).map(Block::index)
876 }
877
878 fn diamond(cond: impl FnOnce(&mut Builder<'_>) -> Value) -> (Func, [Value; 2]) {
885 let mut names = Interner::new();
886 let mut func = Func::new(names.intern("f"), Signature::new());
887 let entry = func.create_block();
888 let then_block = func.create_block();
889 let else_block = func.create_block();
890 let join = func.create_block();
891 let mut build = Builder::new(&mut func, entry);
892 let cond = cond(&mut build);
893 build.br_if(cond, then_block, &[], else_block, &[]);
894 let mut marks = Vec::new();
895 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
896 let mut build = Builder::new(&mut func, arm);
897 marks.push(build.iconst(Type::int(32), mark));
898 build.jump(join, &[]);
899 }
900 let mut build = Builder::new(&mut func, join);
901 build.ret(&[]);
902 (func, [marks[0], marks[1]])
903 }
904
905 #[test]
906 fn a_branch_on_a_true_constant_becomes_a_jump_to_the_first_arm() {
907 let (mut func, [taken, other]) = diamond(|build| build.iconst(Type::int(1), 1));
908 let stats = simplify(&mut func);
909 assert!(stats.changed());
910 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
911 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
914 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
915 assert_eq!(lives_in(&func, taken), Some(0));
916 assert_eq!(lives_in(&func, other), None);
917 assert_eq!(blocks(&func), [0]);
918 }
919
920 #[test]
921 fn a_branch_on_a_false_constant_becomes_a_jump_to_the_second_arm() {
922 let (mut func, [other, taken]) = diamond(|build| build.iconst(Type::int(1), 0));
923 assert!(simplify(&mut func).changed());
924 assert_eq!(lives_in(&func, taken), Some(0));
925 assert_eq!(lives_in(&func, other), None);
926 assert_eq!(blocks(&func), [0]);
927 }
928
929 #[test]
930 fn folding_a_branch_and_merging_what_it_leaves_are_two_things_fuel_buys_apart() {
931 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
934 let stats = SimplifyCfg.run(&mut func, &mut Analyses::new(), &mut Fuel::of(1));
935 assert_eq!(terminator(&func, 0), Opcode::Jump);
936 assert_eq!(goes_to(&func, 0), [1]);
937 assert_eq!(blocks(&func), [0, 1, 3]);
938 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
939 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
942 }
943
944 #[test]
945 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
946 let cases: &[(IntPred, i128, i128, bool)] = &[
950 (IntPred::Eq, 7, 7, true),
951 (IntPred::Eq, 7, 8, false),
952 (IntPred::Ne, 7, 8, true),
953 (IntPred::Ne, 7, 7, false),
954 (IntPred::Slt, -1, 1, true),
955 (IntPred::Slt, 1, -1, false),
956 (IntPred::Sle, -1, -1, true),
957 (IntPred::Sle, 1, -1, false),
958 (IntPred::Sgt, 1, -1, true),
959 (IntPred::Sgt, -1, 1, false),
960 (IntPred::Sge, -1, -1, true),
961 (IntPred::Sge, -1, 1, false),
962 (IntPred::Ult, 1, -1, true),
963 (IntPred::Ult, -1, 1, false),
964 (IntPred::Ule, -1, -1, true),
965 (IntPred::Ule, -1, 1, false),
966 (IntPred::Ugt, -1, 1, true),
967 (IntPred::Ugt, 1, -1, false),
968 (IntPred::Uge, -1, -1, true),
969 (IntPred::Uge, 1, -1, false),
970 ];
971 for &(pred, lhs, rhs, taken) in cases {
972 let (mut func, marks) = diamond(|build| {
973 let lhs = build.iconst(Type::int(32), lhs);
974 let rhs = build.iconst(Type::int(32), rhs);
975 build.icmp(pred, lhs, rhs)
976 });
977 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
978 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
979 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
980 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
981 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
982 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
983 }
984 }
985
986 #[test]
987 fn a_branch_on_something_nobody_knows_is_left_alone() {
988 let mut names = Interner::new();
989 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
990 let entry = func.create_block();
991 let then_block = func.create_block();
992 let else_block = func.create_block();
993 let cond = func.append_param(entry, Type::int(1));
994 let mut build = Builder::new(&mut func, entry);
995 build.br_if(cond, then_block, &[], else_block, &[]);
996 for arm in [then_block, else_block] {
997 let mut build = Builder::new(&mut func, arm);
998 build.ret(&[]);
999 }
1000 let stats = simplify(&mut func);
1001 assert!(!stats.changed());
1002 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1003 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1004 assert_eq!(blocks(&func), [0, 1, 2]);
1005 }
1006
1007 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1010 let mut names = Interner::new();
1011 let mut func = Func::new(names.intern("f"), Signature::new());
1012 let entry = func.create_block();
1013 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1014 let mut build = Builder::new(&mut func, entry);
1015 let value = build.iconst(Type::int(32), on);
1016 let pairs: Vec<(i128, Block)> =
1017 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1018 build.switch(value, arms[0], &pairs);
1019 let mut marks = Vec::new();
1020 for (at, &arm) in arms.iter().enumerate() {
1021 let mut build = Builder::new(&mut func, arm);
1022 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1023 build.ret(&[]);
1024 }
1025 (func, marks)
1026 }
1027
1028 #[test]
1029 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1030 let (mut func, marks) = switched(5, &[4, 5]);
1031 assert!(simplify(&mut func).changed());
1032 assert_eq!(lives_in(&func, marks[2]), Some(0));
1033 assert_eq!(lives_in(&func, marks[0]), None);
1034 assert_eq!(lives_in(&func, marks[1]), None);
1035 assert_eq!(blocks(&func), [0]);
1036 }
1037
1038 #[test]
1039 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1040 let (mut func, marks) = switched(9, &[4]);
1041 assert!(simplify(&mut func).changed());
1042 assert_eq!(lives_in(&func, marks[0]), Some(0));
1043 assert_eq!(lives_in(&func, marks[1]), None);
1044 assert_eq!(blocks(&func), [0]);
1045 }
1046
1047 #[test]
1048 fn the_arguments_travel_with_the_edge_that_survives() {
1049 let mut names = Interner::new();
1055 let mut func = Func::new(names.intern("f"), Signature::new());
1056 let entry = func.create_block();
1057 let join = func.create_block();
1058 let param = func.append_param(join, Type::int(32));
1059 let mut build = Builder::new(&mut func, entry);
1060 let cond = build.iconst(Type::int(1), 0);
1061 let taken = build.iconst(Type::int(32), 11);
1062 let other = build.iconst(Type::int(32), 22);
1063 build.br_if(cond, join, &[other], join, &[taken]);
1064 let mut build = Builder::new(&mut func, join);
1065 build.ret(&[param]);
1066 assert!(simplify(&mut func).changed());
1067 assert_eq!(blocks(&func), [0]);
1071 let term = func.terminator(entry).expect("the entry has one");
1072 assert_eq!(func[func[term].args], [taken]);
1073 assert_ne!(func[func[term].args], [param]);
1074 }
1075
1076 #[test]
1077 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1078 let mut names = Interner::new();
1082 let signature = Signature::new().with_params(&[Type::int(1)]);
1083 let mut func = Func::new(names.intern("f"), signature);
1084 let entry = func.create_block();
1085 let join = func.create_block();
1086 let cond = func.append_param(entry, Type::int(1));
1087 let mut build = Builder::new(&mut func, entry);
1088 build.br_if(cond, join, &[], join, &[]);
1089 let mut build = Builder::new(&mut func, join);
1090 build.ret(&[]);
1091 let stats = simplify(&mut func);
1092 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1093 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1094 assert_eq!(blocks(&func), [0]);
1095 assert_eq!(terminator(&func, 0), Opcode::Return);
1096 }
1097
1098 #[test]
1099 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1100 let mut names = Interner::new();
1101 let signature = Signature::new().with_params(&[Type::int(32)]);
1102 let mut func = Func::new(names.intern("f"), signature);
1103 let entry = func.create_block();
1104 let join = func.create_block();
1105 let value = func.append_param(entry, Type::int(32));
1106 let mut build = Builder::new(&mut func, entry);
1107 build.switch(value, join, &[(4, join), (5, join)]);
1108 let mut build = Builder::new(&mut func, join);
1109 build.ret(&[]);
1110 let stats = simplify(&mut func);
1111 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1112 assert_eq!(blocks(&func), [0]);
1113 }
1114
1115 #[test]
1116 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1117 let mut names = Interner::new();
1120 let signature = Signature::new().with_params(&[Type::int(1)]);
1121 let mut func = Func::new(names.intern("f"), signature);
1122 let entry = func.create_block();
1123 let join = func.create_block();
1124 let cond = func.append_param(entry, Type::int(1));
1125 func.append_param(join, Type::int(32));
1126 let mut build = Builder::new(&mut func, entry);
1127 let first = build.iconst(Type::int(32), 11);
1128 let second = build.iconst(Type::int(32), 22);
1129 build.br_if(cond, join, &[first], join, &[second]);
1130 let mut build = Builder::new(&mut func, join);
1131 build.ret(&[]);
1132 let stats = simplify(&mut func);
1133 assert!(!stats.changed());
1134 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1135 assert_eq!(blocks(&func), [0, 1]);
1136 }
1137
1138 #[test]
1139 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1140 let mut names = Interner::new();
1143 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1144 let entry = func.create_block();
1145 let dead = func.create_block();
1146 let shared = func.create_block();
1147 let exit = func.create_block();
1148 let x = func.append_param(entry, Type::int(32));
1149 let mut build = Builder::new(&mut func, entry);
1150 let never = build.iconst(Type::int(1), 0);
1151 build.switch(x, exit, &[(0, dead), (1, shared)]);
1152 let mut build = Builder::new(&mut func, dead);
1156 build.iconst(Type::int(32), 1);
1157 build.br_if(never, shared, &[], exit, &[]);
1158 for arm in [shared, exit] {
1159 let mut build = Builder::new(&mut func, arm);
1160 build.ret(&[]);
1161 }
1162 let stats = simplify(&mut func);
1163 assert!(stats.changed());
1164 assert_eq!(terminator(&func, 0), Opcode::Switch);
1167 assert_eq!(goes_to(&func, 1), [3]);
1168 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1169 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1170 }
1171
1172 #[test]
1173 fn a_block_whose_address_is_taken_is_not_removed() {
1174 let mut names = Interner::new();
1178 let mut func = Func::new(names.intern("f"), Signature::new());
1179 let entry = func.create_block();
1180 let labelled = func.create_block();
1181 let arm = func.create_block();
1182 let mut build = Builder::new(&mut func, entry);
1183 let cond = build.iconst(Type::int(1), 1);
1184 let addr = build.block_addr(labelled);
1185 build.br_if(cond, arm, &[], labelled, &[]);
1186 let mut build = Builder::new(&mut func, arm);
1187 build.indirect_br(addr, &[labelled]);
1188 let mut build = Builder::new(&mut func, labelled);
1189 build.ret(&[]);
1190 assert!(simplify(&mut func).changed());
1191 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1192 assert_eq!(blocks(&func), [0, 1]);
1195 assert_eq!(goes_to(&func, 0), [1]);
1196 }
1197
1198 #[test]
1199 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1200 let mut names = Interner::new();
1203 let mut func = Func::new(names.intern("f"), Signature::new());
1204 let entry = func.create_block();
1205 let dead = func.create_block();
1206 let labelled = func.create_block();
1207 let mut build = Builder::new(&mut func, entry);
1208 let cond = build.iconst(Type::int(1), 1);
1209 build.br_if(cond, entry, &[], dead, &[]);
1210 let mut build = Builder::new(&mut func, dead);
1211 let addr = build.block_addr(labelled);
1212 build.indirect_br(addr, &[labelled]);
1213 let mut build = Builder::new(&mut func, labelled);
1214 build.ret(&[]);
1215 assert!(simplify(&mut func).changed());
1216 assert_eq!(blocks(&func), [0]);
1217 }
1218
1219 #[test]
1220 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1221 let mut func = graph(&[&[], &[]]);
1226 let stats = simplify(&mut func);
1227 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1228 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1229 assert_eq!(blocks(&func), [0]);
1230 }
1231
1232 #[test]
1233 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1234 let mut names = Interner::new();
1237 let mut func = Func::new(names.intern("f"), Signature::new());
1238 let entry = func.create_block();
1239 let middle = func.create_block();
1240 let last = func.create_block();
1241 let mut build = Builder::new(&mut func, entry);
1242 build.iconst(Type::int(32), 1);
1243 build.jump(middle, &[]);
1244 let mut build = Builder::new(&mut func, middle);
1245 build.iconst(Type::int(32), 2);
1246 build.jump(last, &[]);
1247 let mut build = Builder::new(&mut func, last);
1248 build.ret(&[]);
1249 let stats = simplify(&mut func);
1250 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1253 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1254 assert_eq!(blocks(&func), [0]);
1255 assert_eq!(terminator(&func, 0), Opcode::Return);
1256 }
1257
1258 #[test]
1259 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1260 let mut names = Interner::new();
1263 let signature = Signature::new().with_params(&[Type::int(1)]);
1264 let mut func = Func::new(names.intern("f"), signature);
1265 let entry = func.create_block();
1266 let then_block = func.create_block();
1267 let else_block = func.create_block();
1268 let join = func.create_block();
1269 let cond = func.append_param(entry, Type::int(1));
1270 let mut build = Builder::new(&mut func, entry);
1271 build.br_if(cond, then_block, &[], else_block, &[]);
1272 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1273 let mut build = Builder::new(&mut func, arm);
1276 build.iconst(Type::int(32), mark);
1277 build.jump(join, &[]);
1278 }
1279 let mut build = Builder::new(&mut func, join);
1280 build.ret(&[]);
1281 let stats = simplify(&mut func);
1282 assert!(!stats.changed());
1283 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1284 }
1285
1286 #[test]
1287 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1288 let mut names = Interner::new();
1291 let signature = Signature::new().with_params(&[Type::int(1)]);
1292 let mut func = Func::new(names.intern("f"), signature);
1293 let entry = func.create_block();
1294 let arm = func.create_block();
1295 let exit = func.create_block();
1296 let cond = func.append_param(entry, Type::int(1));
1297 let mut build = Builder::new(&mut func, entry);
1298 build.br_if(cond, arm, &[], exit, &[]);
1299 for block in [arm, exit] {
1300 let mut build = Builder::new(&mut func, block);
1301 build.ret(&[]);
1302 }
1303 let stats = simplify(&mut func);
1304 assert!(!stats.changed());
1305 assert_eq!(blocks(&func), [0, 1, 2]);
1306 }
1307
1308 #[test]
1309 fn the_entry_block_is_never_the_one_that_moves() {
1310 let mut names = Interner::new();
1314 let signature = Signature::new().with_params(&[Type::int(1)]);
1315 let mut func = Func::new(names.intern("f"), signature);
1316 let entry = func.create_block();
1317 let latch = func.create_block();
1318 let exit = func.create_block();
1319 let cond = func.append_param(entry, Type::int(1));
1320 let mut build = Builder::new(&mut func, entry);
1321 build.br_if(cond, latch, &[], exit, &[]);
1322 let mut build = Builder::new(&mut func, latch);
1324 build.iconst(Type::int(32), 1);
1325 build.jump(entry, &[]);
1326 let mut build = Builder::new(&mut func, exit);
1327 build.ret(&[]);
1328 let stats = simplify(&mut func);
1329 assert!(!stats.changed());
1330 assert_eq!(blocks(&func), [0, 1, 2]);
1331 }
1332
1333 #[test]
1334 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1335 let mut names = Interner::new();
1338 let mut func = Func::new(names.intern("f"), Signature::new());
1339 let entry = func.create_block();
1340 let middle = func.create_block();
1341 let labelled = func.create_block();
1342 let mut build = Builder::new(&mut func, entry);
1343 build.block_addr(labelled);
1344 build.jump(middle, &[]);
1345 let mut build = Builder::new(&mut func, middle);
1348 build.iconst(Type::int(32), 1);
1349 build.jump(labelled, &[]);
1350 let mut build = Builder::new(&mut func, labelled);
1351 build.ret(&[]);
1352 let stats = simplify(&mut func);
1353 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1356 assert_eq!(blocks(&func), [0, 2]);
1357 }
1358
1359 #[test]
1360 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1361 let mut names = Interner::new();
1362 let mut func = Func::new(names.intern("f"), Signature::new());
1363 let entry = func.create_block();
1364 let below = func.create_block();
1365 let param = func.append_param(below, Type::int(32));
1366 let mut build = Builder::new(&mut func, entry);
1367 let arg = build.iconst(Type::int(32), 7);
1368 build.jump(below, &[arg]);
1369 let mut build = Builder::new(&mut func, below);
1370 build.ret(&[param]);
1371 assert!(simplify(&mut func).changed());
1372 assert_eq!(blocks(&func), [0]);
1373 let term = func.terminator(entry).expect("the entry has one");
1374 assert_eq!(func[func[term].args], [arg]);
1375 }
1376
1377 #[test]
1378 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1379 let mut names = Interner::new();
1383 let mut func = Func::new(names.intern("f"), Signature::new());
1384 let entry = func.create_block();
1385 let middle = func.create_block();
1386 let last = func.create_block();
1387 let carried = func.append_param(middle, Type::int(32));
1388 let arrived = func.append_param(last, Type::int(32));
1389 let mut build = Builder::new(&mut func, entry);
1390 let arg = build.iconst(Type::int(32), 7);
1391 build.jump(middle, &[arg]);
1392 let mut build = Builder::new(&mut func, middle);
1393 build.jump(last, &[carried]);
1394 let mut build = Builder::new(&mut func, last);
1395 build.ret(&[arrived]);
1396 assert!(simplify(&mut func).changed());
1397 assert_eq!(blocks(&func), [0]);
1398 let term = func.terminator(entry).expect("the entry has one");
1399 assert_eq!(func[func[term].args], [arg]);
1400 }
1401
1402 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1410 let entry = func.create_block();
1411 let first = func.create_block();
1412 let second = func.create_block();
1413 let cond = func.append_param(entry, Type::int(1));
1414 let mut build = Builder::new(func, entry);
1415 let carried = build.iconst(Type::int(32), 7);
1416 build.br_if(cond, first, &[], second, &[]);
1417 for (arm, mark) in [(first, 111), (second, 222)] {
1418 let mut build = Builder::new(func, arm);
1419 build.iconst(Type::int(32), mark);
1420 }
1421 (carried, [first, second])
1422 }
1423
1424 fn taking_a_condition() -> Func {
1426 let mut names = Interner::new();
1427 let signature = Signature::new().with_params(&[Type::int(1)]);
1428 Func::new(names.intern("f"), signature)
1429 }
1430
1431 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1433 let block = Block::from_usize(block);
1434 let term = func.terminator(block).expect("every block here has one");
1435 let call = func.successors(term).nth(edge).expect("the edge is there");
1436 func[call.args].to_vec()
1437 }
1438
1439 #[test]
1440 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1441 let mut func = taking_a_condition();
1444 let (_, arms) = arms(&mut func);
1445 let forwarder = func.create_block();
1446 let exit = func.create_block();
1447 for arm in arms {
1448 Builder::new(&mut func, arm).jump(forwarder, &[]);
1449 }
1450 Builder::new(&mut func, forwarder).jump(exit, &[]);
1451 Builder::new(&mut func, exit).ret(&[]);
1452 let stats = simplify(&mut func);
1453 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1454 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1455 assert_eq!(goes_to(&func, 1), [4]);
1456 assert_eq!(goes_to(&func, 2), [4]);
1457 }
1458
1459 #[test]
1460 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1461 let mut func = taking_a_condition();
1468 let (carried, [arm, above]) = arms(&mut func);
1469 let forwarder = func.create_block();
1470 let exit = func.create_block();
1471 let other = func.append_param(exit, Type::int(32));
1472 let mut build = Builder::new(&mut func, arm);
1473 let mine = build.iconst(Type::int(32), 9);
1474 build.jump(exit, &[mine]);
1475 Builder::new(&mut func, above).jump(forwarder, &[]);
1476 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1477 Builder::new(&mut func, exit).ret(&[other]);
1478 let stats = simplify(&mut func);
1479 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1480 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1481 assert_eq!(carries(&func, 2, 0), [carried]);
1484 assert_eq!(carries(&func, 1, 0), [mine]);
1485 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1487 }
1488
1489 #[test]
1490 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1491 let mut func = taking_a_condition();
1496 let (carried, [arm, forwarder]) = arms(&mut func);
1497 let exit = func.create_block();
1498 let other = func.append_param(exit, Type::int(32));
1499 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1501 func.remove_inst(inst);
1502 }
1503 let mut build = Builder::new(&mut func, arm);
1504 let mine = build.iconst(Type::int(32), 9);
1505 build.jump(exit, &[mine]);
1506 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1507 Builder::new(&mut func, exit).ret(&[other]);
1508 let stats = simplify(&mut func);
1509 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1510 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1511 }
1512
1513 #[test]
1514 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1515 let mut func = taking_a_condition();
1518 let (_, [arm, forwarder]) = arms(&mut func);
1519 let exit = func.create_block();
1520 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1521 func.remove_inst(inst);
1522 }
1523 Builder::new(&mut func, arm).jump(exit, &[]);
1524 Builder::new(&mut func, forwarder).jump(exit, &[]);
1525 Builder::new(&mut func, exit).ret(&[]);
1526 let stats = simplify(&mut func);
1527 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1528 assert_eq!(blocks(&func), [0, 1, 3]);
1529 }
1530
1531 #[test]
1532 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1533 let mut names = Interner::new();
1536 let mut func = Func::new(names.intern("f"), Signature::new());
1537 let entry = func.create_block();
1538 let spin = func.create_block();
1539 Builder::new(&mut func, entry).jump(spin, &[]);
1540 Builder::new(&mut func, spin).jump(spin, &[]);
1541 let stats = simplify(&mut func);
1542 assert!(!stats.changed());
1543 assert_eq!(blocks(&func), [0, 1]);
1544 }
1545
1546 #[test]
1547 fn the_entry_block_is_never_the_forwarder_that_goes() {
1548 let mut names = Interner::new();
1552 let mut func = Func::new(names.intern("f"), Signature::new());
1553 let entry = func.create_block();
1554 let below = func.create_block();
1555 Builder::new(&mut func, entry).jump(below, &[]);
1556 let mut build = Builder::new(&mut func, below);
1557 build.iconst(Type::int(32), 1);
1558 build.ret(&[]);
1559 let stats = simplify(&mut func);
1560 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1561 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1562 assert_eq!(blocks(&func), [0]);
1563 }
1564
1565 #[test]
1566 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1567 let mut names = Interner::new();
1571 let mut func = Func::new(names.intern("f"), Signature::new());
1572 let entry = func.create_block();
1573 let labelled = func.create_block();
1574 let exit = func.create_block();
1575 let mut build = Builder::new(&mut func, entry);
1576 let addr = build.block_addr(labelled);
1577 build.indirect_br(addr, &[labelled]);
1578 Builder::new(&mut func, labelled).jump(exit, &[]);
1579 let mut build = Builder::new(&mut func, exit);
1580 build.iconst(Type::int(32), 1);
1581 build.ret(&[]);
1582 let stats = simplify(&mut func);
1583 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1584 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1585 }
1586
1587 #[test]
1588 fn a_run_of_forwarders_comes_out_as_one_edge() {
1589 let mut func = taking_a_condition();
1590 let (_, arms) = arms(&mut func);
1591 let first = func.create_block();
1592 let second = func.create_block();
1593 let exit = func.create_block();
1594 for arm in arms {
1595 Builder::new(&mut func, arm).jump(first, &[]);
1596 }
1597 Builder::new(&mut func, first).jump(second, &[]);
1598 Builder::new(&mut func, second).jump(exit, &[]);
1599 Builder::new(&mut func, exit).ret(&[]);
1600 let stats = simplify(&mut func);
1601 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1602 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1603 assert_eq!(goes_to(&func, 1), [5]);
1604 assert_eq!(goes_to(&func, 2), [5]);
1605 }
1606
1607 #[test]
1608 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1609 let mut func = taking_a_condition();
1612 let (carried, arms) = arms(&mut func);
1613 let join = func.create_block();
1614 let param = func.append_param(join, Type::int(32));
1615 for arm in arms {
1616 Builder::new(&mut func, arm).jump(join, &[carried]);
1617 }
1618 Builder::new(&mut func, join).ret(&[param]);
1619 let stats = simplify(&mut func);
1620 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1621 assert!(func[Block::from_usize(3)].params.is_empty());
1622 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1624 assert_eq!(func[func[term].args], [carried]);
1625 assert!(carries(&func, 1, 0).is_empty());
1628 assert!(carries(&func, 2, 0).is_empty());
1629 }
1630
1631 #[test]
1632 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1633 let mut func = taking_a_condition();
1634 let (carried, arms) = arms(&mut func);
1635 let join = func.create_block();
1636 let param = func.append_param(join, Type::int(32));
1637 let mut build = Builder::new(&mut func, arms[0]);
1638 let mine = build.iconst(Type::int(32), 9);
1639 build.jump(join, &[mine]);
1640 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1641 Builder::new(&mut func, join).ret(&[param]);
1642 let stats = simplify(&mut func);
1643 assert!(!stats.changed());
1644 assert_eq!(func[Block::from_usize(3)].params, [param]);
1645 }
1646
1647 #[test]
1648 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1649 let mut names = Interner::new();
1653 let signature = Signature::new().with_params(&[Type::int(1)]);
1654 let mut func = Func::new(names.intern("f"), signature);
1655 let entry = func.create_block();
1656 let header = func.create_block();
1657 let latch = func.create_block();
1658 let exit = func.create_block();
1659 let cond = func.append_param(entry, Type::int(1));
1660 let param = func.append_param(header, Type::int(32));
1661 let mut build = Builder::new(&mut func, entry);
1662 let init = build.iconst(Type::int(32), 7);
1663 build.jump(header, &[init]);
1664 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1665 let mut build = Builder::new(&mut func, latch);
1666 build.iconst(Type::int(32), 1);
1667 build.jump(header, &[param]);
1668 Builder::new(&mut func, exit).ret(&[param]);
1669 let stats = simplify(&mut func);
1670 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1671 assert!(func[Block::from_usize(1)].params.is_empty());
1672 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1673 assert_eq!(func[func[term].args], [init]);
1674 }
1675
1676 #[test]
1677 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1678 let mut names = Interner::new();
1682 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1683 let mut func = Func::new(names.intern("f"), signature);
1684 let entry = func.create_block();
1685 let latch = func.create_block();
1686 let exit = func.create_block();
1687 let cond = func.append_param(entry, Type::int(1));
1688 let x = func.append_param(entry, Type::int(32));
1689 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1690 let mut build = Builder::new(&mut func, latch);
1691 let one = build.iconst(Type::int(1), 1);
1692 let seven = build.iconst(Type::int(32), 7);
1693 build.jump(entry, &[one, seven]);
1694 Builder::new(&mut func, exit).ret(&[x]);
1695 let stats = simplify(&mut func);
1696 assert!(!stats.changed());
1697 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1698 }
1699
1700 #[test]
1701 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1702 let mut func = taking_a_condition();
1706 let (carried, arms) = arms(&mut func);
1707 let join = func.create_block();
1708 let inner = func.append_param(join, Type::int(32));
1709 let left = func.create_block();
1710 let right = func.create_block();
1711 let last = func.create_block();
1712 let outer = func.append_param(last, Type::int(32));
1713 for arm in arms {
1714 Builder::new(&mut func, arm).jump(join, &[carried]);
1715 }
1716 let cond = func[Block::from_usize(0)].params[0];
1717 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1718 let mut build = Builder::new(&mut func, left);
1719 build.iconst(Type::int(32), 1);
1720 build.jump(last, &[inner]);
1721 let mut build = Builder::new(&mut func, right);
1722 build.iconst(Type::int(32), 2);
1723 build.jump(last, &[carried]);
1724 Builder::new(&mut func, last).ret(&[outer]);
1725 let stats = simplify(&mut func);
1726 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1727 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1728 assert_eq!(func[func[term].args], [carried]);
1729 }
1730
1731 #[test]
1732 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1733 let mut func = taking_a_condition();
1737 let (carried, arms) = arms(&mut func);
1738 let forwarder = func.create_block();
1739 let param = func.append_param(forwarder, Type::int(32));
1740 let exit = func.create_block();
1741 let arrived = func.append_param(exit, Type::int(32));
1742 for arm in arms {
1743 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1744 }
1745 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1746 Builder::new(&mut func, exit).ret(&[arrived]);
1747 let stats = simplify(&mut func);
1748 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1749 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1752 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1753 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
1754 assert_eq!(func[func[term].args], [carried]);
1755 }
1756
1757 #[test]
1758 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
1759 let mut func = taking_a_condition();
1762 let (carried, arms) = arms(&mut func);
1763 let forwarder = func.create_block();
1764 let param = func.append_param(forwarder, Type::int(32));
1765 let exit = func.create_block();
1766 for arm in arms {
1767 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1768 }
1769 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1770 Builder::new(&mut func, exit).ret(&[]);
1771 let stats = SimplifyCfg.run(&mut func, &mut Analyses::new(), &mut Fuel::of(1));
1772 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1773 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1774 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
1775 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
1776 }
1777
1778 #[test]
1779 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
1780 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
1783 let mut names = Interner::new();
1784 let mut module = Module::new(names.intern("test.c"), &target);
1785 let mut func = taking_a_condition();
1786 let (carried, arms) = arms(&mut func);
1787 let forwarder = func.create_block();
1788 let param = func.append_param(forwarder, Type::int(32));
1789 let exit = func.create_block();
1790 let arrived = func.append_param(exit, Type::int(32));
1791 let mut build = Builder::new(&mut func, arms[0]);
1792 let mine = build.iconst(Type::int(32), 9);
1793 build.jump(exit, &[mine]);
1794 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
1795 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1796 let mut build = Builder::new(&mut func, exit);
1797 build.icmp(IntPred::Eq, arrived, arrived);
1800 build.ret(&[]);
1801 simplify(&mut func);
1802 module.add_func(func);
1803 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
1804 }
1805
1806 #[test]
1807 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
1808 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1809 let before = blocks(&func);
1810 let stats = SimplifyCfg.run(&mut func, &mut Analyses::new(), &mut Fuel::of(0));
1811 assert!(!stats.changed());
1812 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
1813 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1814 assert_eq!(blocks(&func), before);
1815 }
1816
1817 #[test]
1818 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
1819 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
1823 let stats = SimplifyCfg.run(&mut func, &mut Analyses::new(), &mut Fuel::of(1));
1824 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1825 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
1826 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
1829 }
1830
1831 #[test]
1832 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
1833 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
1834 let mut names = Interner::new();
1835 let mut module = Module::new(names.intern("test.c"), &target);
1836 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
1837 simplify(&mut func);
1838 module.add_func(func);
1839 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
1840 }
1841
1842 #[test]
1843 fn the_pass_says_it_preserves_nothing() {
1844 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
1845 }
1846}