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::{Fuel, Pass, Preserved, Stats};
844
845 fn simplify(func: &mut Func) -> Stats {
847 SimplifyCfg.run(func, &mut crate::machine::fixtures::analyses(), &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 =
935 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
936 assert_eq!(terminator(&func, 0), Opcode::Jump);
937 assert_eq!(goes_to(&func, 0), [1]);
938 assert_eq!(blocks(&func), [0, 1, 3]);
939 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
940 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
943 }
944
945 #[test]
946 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
947 let cases: &[(IntPred, i128, i128, bool)] = &[
951 (IntPred::Eq, 7, 7, true),
952 (IntPred::Eq, 7, 8, false),
953 (IntPred::Ne, 7, 8, true),
954 (IntPred::Ne, 7, 7, false),
955 (IntPred::Slt, -1, 1, true),
956 (IntPred::Slt, 1, -1, false),
957 (IntPred::Sle, -1, -1, true),
958 (IntPred::Sle, 1, -1, false),
959 (IntPred::Sgt, 1, -1, true),
960 (IntPred::Sgt, -1, 1, false),
961 (IntPred::Sge, -1, -1, true),
962 (IntPred::Sge, -1, 1, false),
963 (IntPred::Ult, 1, -1, true),
964 (IntPred::Ult, -1, 1, false),
965 (IntPred::Ule, -1, -1, true),
966 (IntPred::Ule, -1, 1, false),
967 (IntPred::Ugt, -1, 1, true),
968 (IntPred::Ugt, 1, -1, false),
969 (IntPred::Uge, -1, -1, true),
970 (IntPred::Uge, 1, -1, false),
971 ];
972 for &(pred, lhs, rhs, taken) in cases {
973 let (mut func, marks) = diamond(|build| {
974 let lhs = build.iconst(Type::int(32), lhs);
975 let rhs = build.iconst(Type::int(32), rhs);
976 build.icmp(pred, lhs, rhs)
977 });
978 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
979 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
980 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
981 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
982 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
983 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
984 }
985 }
986
987 #[test]
988 fn a_branch_on_something_nobody_knows_is_left_alone() {
989 let mut names = Interner::new();
990 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
991 let entry = func.create_block();
992 let then_block = func.create_block();
993 let else_block = func.create_block();
994 let cond = func.append_param(entry, Type::int(1));
995 let mut build = Builder::new(&mut func, entry);
996 build.br_if(cond, then_block, &[], else_block, &[]);
997 for arm in [then_block, else_block] {
998 let mut build = Builder::new(&mut func, arm);
999 build.ret(&[]);
1000 }
1001 let stats = simplify(&mut func);
1002 assert!(!stats.changed());
1003 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1004 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1005 assert_eq!(blocks(&func), [0, 1, 2]);
1006 }
1007
1008 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1011 let mut names = Interner::new();
1012 let mut func = Func::new(names.intern("f"), Signature::new());
1013 let entry = func.create_block();
1014 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1015 let mut build = Builder::new(&mut func, entry);
1016 let value = build.iconst(Type::int(32), on);
1017 let pairs: Vec<(i128, Block)> =
1018 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1019 build.switch(value, arms[0], &pairs);
1020 let mut marks = Vec::new();
1021 for (at, &arm) in arms.iter().enumerate() {
1022 let mut build = Builder::new(&mut func, arm);
1023 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1024 build.ret(&[]);
1025 }
1026 (func, marks)
1027 }
1028
1029 #[test]
1030 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1031 let (mut func, marks) = switched(5, &[4, 5]);
1032 assert!(simplify(&mut func).changed());
1033 assert_eq!(lives_in(&func, marks[2]), Some(0));
1034 assert_eq!(lives_in(&func, marks[0]), None);
1035 assert_eq!(lives_in(&func, marks[1]), None);
1036 assert_eq!(blocks(&func), [0]);
1037 }
1038
1039 #[test]
1040 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1041 let (mut func, marks) = switched(9, &[4]);
1042 assert!(simplify(&mut func).changed());
1043 assert_eq!(lives_in(&func, marks[0]), Some(0));
1044 assert_eq!(lives_in(&func, marks[1]), None);
1045 assert_eq!(blocks(&func), [0]);
1046 }
1047
1048 #[test]
1049 fn the_arguments_travel_with_the_edge_that_survives() {
1050 let mut names = Interner::new();
1056 let mut func = Func::new(names.intern("f"), Signature::new());
1057 let entry = func.create_block();
1058 let join = func.create_block();
1059 let param = func.append_param(join, Type::int(32));
1060 let mut build = Builder::new(&mut func, entry);
1061 let cond = build.iconst(Type::int(1), 0);
1062 let taken = build.iconst(Type::int(32), 11);
1063 let other = build.iconst(Type::int(32), 22);
1064 build.br_if(cond, join, &[other], join, &[taken]);
1065 let mut build = Builder::new(&mut func, join);
1066 build.ret(&[param]);
1067 assert!(simplify(&mut func).changed());
1068 assert_eq!(blocks(&func), [0]);
1072 let term = func.terminator(entry).expect("the entry has one");
1073 assert_eq!(func[func[term].args], [taken]);
1074 assert_ne!(func[func[term].args], [param]);
1075 }
1076
1077 #[test]
1078 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1079 let mut names = Interner::new();
1083 let signature = Signature::new().with_params(&[Type::int(1)]);
1084 let mut func = Func::new(names.intern("f"), signature);
1085 let entry = func.create_block();
1086 let join = func.create_block();
1087 let cond = func.append_param(entry, Type::int(1));
1088 let mut build = Builder::new(&mut func, entry);
1089 build.br_if(cond, join, &[], join, &[]);
1090 let mut build = Builder::new(&mut func, join);
1091 build.ret(&[]);
1092 let stats = simplify(&mut func);
1093 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1094 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1095 assert_eq!(blocks(&func), [0]);
1096 assert_eq!(terminator(&func, 0), Opcode::Return);
1097 }
1098
1099 #[test]
1100 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1101 let mut names = Interner::new();
1102 let signature = Signature::new().with_params(&[Type::int(32)]);
1103 let mut func = Func::new(names.intern("f"), signature);
1104 let entry = func.create_block();
1105 let join = func.create_block();
1106 let value = func.append_param(entry, Type::int(32));
1107 let mut build = Builder::new(&mut func, entry);
1108 build.switch(value, join, &[(4, join), (5, join)]);
1109 let mut build = Builder::new(&mut func, join);
1110 build.ret(&[]);
1111 let stats = simplify(&mut func);
1112 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1113 assert_eq!(blocks(&func), [0]);
1114 }
1115
1116 #[test]
1117 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1118 let mut names = Interner::new();
1121 let signature = Signature::new().with_params(&[Type::int(1)]);
1122 let mut func = Func::new(names.intern("f"), signature);
1123 let entry = func.create_block();
1124 let join = func.create_block();
1125 let cond = func.append_param(entry, Type::int(1));
1126 func.append_param(join, Type::int(32));
1127 let mut build = Builder::new(&mut func, entry);
1128 let first = build.iconst(Type::int(32), 11);
1129 let second = build.iconst(Type::int(32), 22);
1130 build.br_if(cond, join, &[first], join, &[second]);
1131 let mut build = Builder::new(&mut func, join);
1132 build.ret(&[]);
1133 let stats = simplify(&mut func);
1134 assert!(!stats.changed());
1135 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1136 assert_eq!(blocks(&func), [0, 1]);
1137 }
1138
1139 #[test]
1140 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1141 let mut names = Interner::new();
1144 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1145 let entry = func.create_block();
1146 let dead = func.create_block();
1147 let shared = func.create_block();
1148 let exit = func.create_block();
1149 let x = func.append_param(entry, Type::int(32));
1150 let mut build = Builder::new(&mut func, entry);
1151 let never = build.iconst(Type::int(1), 0);
1152 build.switch(x, exit, &[(0, dead), (1, shared)]);
1153 let mut build = Builder::new(&mut func, dead);
1157 build.iconst(Type::int(32), 1);
1158 build.br_if(never, shared, &[], exit, &[]);
1159 for arm in [shared, exit] {
1160 let mut build = Builder::new(&mut func, arm);
1161 build.ret(&[]);
1162 }
1163 let stats = simplify(&mut func);
1164 assert!(stats.changed());
1165 assert_eq!(terminator(&func, 0), Opcode::Switch);
1168 assert_eq!(goes_to(&func, 1), [3]);
1169 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1170 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1171 }
1172
1173 #[test]
1174 fn a_block_whose_address_is_taken_is_not_removed() {
1175 let mut names = Interner::new();
1179 let mut func = Func::new(names.intern("f"), Signature::new());
1180 let entry = func.create_block();
1181 let labelled = func.create_block();
1182 let arm = func.create_block();
1183 let mut build = Builder::new(&mut func, entry);
1184 let cond = build.iconst(Type::int(1), 1);
1185 let addr = build.block_addr(labelled);
1186 build.br_if(cond, arm, &[], labelled, &[]);
1187 let mut build = Builder::new(&mut func, arm);
1188 build.indirect_br(addr, &[labelled]);
1189 let mut build = Builder::new(&mut func, labelled);
1190 build.ret(&[]);
1191 assert!(simplify(&mut func).changed());
1192 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1193 assert_eq!(blocks(&func), [0, 1]);
1196 assert_eq!(goes_to(&func, 0), [1]);
1197 }
1198
1199 #[test]
1200 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1201 let mut names = Interner::new();
1204 let mut func = Func::new(names.intern("f"), Signature::new());
1205 let entry = func.create_block();
1206 let dead = func.create_block();
1207 let labelled = func.create_block();
1208 let mut build = Builder::new(&mut func, entry);
1209 let cond = build.iconst(Type::int(1), 1);
1210 build.br_if(cond, entry, &[], dead, &[]);
1211 let mut build = Builder::new(&mut func, dead);
1212 let addr = build.block_addr(labelled);
1213 build.indirect_br(addr, &[labelled]);
1214 let mut build = Builder::new(&mut func, labelled);
1215 build.ret(&[]);
1216 assert!(simplify(&mut func).changed());
1217 assert_eq!(blocks(&func), [0]);
1218 }
1219
1220 #[test]
1221 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1222 let mut func = graph(&[&[], &[]]);
1227 let stats = simplify(&mut func);
1228 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1229 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1230 assert_eq!(blocks(&func), [0]);
1231 }
1232
1233 #[test]
1234 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1235 let mut names = Interner::new();
1238 let mut func = Func::new(names.intern("f"), Signature::new());
1239 let entry = func.create_block();
1240 let middle = func.create_block();
1241 let last = func.create_block();
1242 let mut build = Builder::new(&mut func, entry);
1243 build.iconst(Type::int(32), 1);
1244 build.jump(middle, &[]);
1245 let mut build = Builder::new(&mut func, middle);
1246 build.iconst(Type::int(32), 2);
1247 build.jump(last, &[]);
1248 let mut build = Builder::new(&mut func, last);
1249 build.ret(&[]);
1250 let stats = simplify(&mut func);
1251 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1254 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1255 assert_eq!(blocks(&func), [0]);
1256 assert_eq!(terminator(&func, 0), Opcode::Return);
1257 }
1258
1259 #[test]
1260 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1261 let mut names = Interner::new();
1264 let signature = Signature::new().with_params(&[Type::int(1)]);
1265 let mut func = Func::new(names.intern("f"), signature);
1266 let entry = func.create_block();
1267 let then_block = func.create_block();
1268 let else_block = func.create_block();
1269 let join = func.create_block();
1270 let cond = func.append_param(entry, Type::int(1));
1271 let mut build = Builder::new(&mut func, entry);
1272 build.br_if(cond, then_block, &[], else_block, &[]);
1273 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1274 let mut build = Builder::new(&mut func, arm);
1277 build.iconst(Type::int(32), mark);
1278 build.jump(join, &[]);
1279 }
1280 let mut build = Builder::new(&mut func, join);
1281 build.ret(&[]);
1282 let stats = simplify(&mut func);
1283 assert!(!stats.changed());
1284 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1285 }
1286
1287 #[test]
1288 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1289 let mut names = Interner::new();
1292 let signature = Signature::new().with_params(&[Type::int(1)]);
1293 let mut func = Func::new(names.intern("f"), signature);
1294 let entry = func.create_block();
1295 let arm = func.create_block();
1296 let exit = func.create_block();
1297 let cond = func.append_param(entry, Type::int(1));
1298 let mut build = Builder::new(&mut func, entry);
1299 build.br_if(cond, arm, &[], exit, &[]);
1300 for block in [arm, exit] {
1301 let mut build = Builder::new(&mut func, block);
1302 build.ret(&[]);
1303 }
1304 let stats = simplify(&mut func);
1305 assert!(!stats.changed());
1306 assert_eq!(blocks(&func), [0, 1, 2]);
1307 }
1308
1309 #[test]
1310 fn the_entry_block_is_never_the_one_that_moves() {
1311 let mut names = Interner::new();
1315 let signature = Signature::new().with_params(&[Type::int(1)]);
1316 let mut func = Func::new(names.intern("f"), signature);
1317 let entry = func.create_block();
1318 let latch = func.create_block();
1319 let exit = func.create_block();
1320 let cond = func.append_param(entry, Type::int(1));
1321 let mut build = Builder::new(&mut func, entry);
1322 build.br_if(cond, latch, &[], exit, &[]);
1323 let mut build = Builder::new(&mut func, latch);
1325 build.iconst(Type::int(32), 1);
1326 build.jump(entry, &[]);
1327 let mut build = Builder::new(&mut func, exit);
1328 build.ret(&[]);
1329 let stats = simplify(&mut func);
1330 assert!(!stats.changed());
1331 assert_eq!(blocks(&func), [0, 1, 2]);
1332 }
1333
1334 #[test]
1335 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1336 let mut names = Interner::new();
1339 let mut func = Func::new(names.intern("f"), Signature::new());
1340 let entry = func.create_block();
1341 let middle = func.create_block();
1342 let labelled = func.create_block();
1343 let mut build = Builder::new(&mut func, entry);
1344 build.block_addr(labelled);
1345 build.jump(middle, &[]);
1346 let mut build = Builder::new(&mut func, middle);
1349 build.iconst(Type::int(32), 1);
1350 build.jump(labelled, &[]);
1351 let mut build = Builder::new(&mut func, labelled);
1352 build.ret(&[]);
1353 let stats = simplify(&mut func);
1354 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1357 assert_eq!(blocks(&func), [0, 2]);
1358 }
1359
1360 #[test]
1361 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1362 let mut names = Interner::new();
1363 let mut func = Func::new(names.intern("f"), Signature::new());
1364 let entry = func.create_block();
1365 let below = func.create_block();
1366 let param = func.append_param(below, Type::int(32));
1367 let mut build = Builder::new(&mut func, entry);
1368 let arg = build.iconst(Type::int(32), 7);
1369 build.jump(below, &[arg]);
1370 let mut build = Builder::new(&mut func, below);
1371 build.ret(&[param]);
1372 assert!(simplify(&mut func).changed());
1373 assert_eq!(blocks(&func), [0]);
1374 let term = func.terminator(entry).expect("the entry has one");
1375 assert_eq!(func[func[term].args], [arg]);
1376 }
1377
1378 #[test]
1379 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1380 let mut names = Interner::new();
1384 let mut func = Func::new(names.intern("f"), Signature::new());
1385 let entry = func.create_block();
1386 let middle = func.create_block();
1387 let last = func.create_block();
1388 let carried = func.append_param(middle, Type::int(32));
1389 let arrived = func.append_param(last, Type::int(32));
1390 let mut build = Builder::new(&mut func, entry);
1391 let arg = build.iconst(Type::int(32), 7);
1392 build.jump(middle, &[arg]);
1393 let mut build = Builder::new(&mut func, middle);
1394 build.jump(last, &[carried]);
1395 let mut build = Builder::new(&mut func, last);
1396 build.ret(&[arrived]);
1397 assert!(simplify(&mut func).changed());
1398 assert_eq!(blocks(&func), [0]);
1399 let term = func.terminator(entry).expect("the entry has one");
1400 assert_eq!(func[func[term].args], [arg]);
1401 }
1402
1403 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1411 let entry = func.create_block();
1412 let first = func.create_block();
1413 let second = func.create_block();
1414 let cond = func.append_param(entry, Type::int(1));
1415 let mut build = Builder::new(func, entry);
1416 let carried = build.iconst(Type::int(32), 7);
1417 build.br_if(cond, first, &[], second, &[]);
1418 for (arm, mark) in [(first, 111), (second, 222)] {
1419 let mut build = Builder::new(func, arm);
1420 build.iconst(Type::int(32), mark);
1421 }
1422 (carried, [first, second])
1423 }
1424
1425 fn taking_a_condition() -> Func {
1427 let mut names = Interner::new();
1428 let signature = Signature::new().with_params(&[Type::int(1)]);
1429 Func::new(names.intern("f"), signature)
1430 }
1431
1432 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1434 let block = Block::from_usize(block);
1435 let term = func.terminator(block).expect("every block here has one");
1436 let call = func.successors(term).nth(edge).expect("the edge is there");
1437 func[call.args].to_vec()
1438 }
1439
1440 #[test]
1441 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1442 let mut func = taking_a_condition();
1445 let (_, arms) = arms(&mut func);
1446 let forwarder = func.create_block();
1447 let exit = func.create_block();
1448 for arm in arms {
1449 Builder::new(&mut func, arm).jump(forwarder, &[]);
1450 }
1451 Builder::new(&mut func, forwarder).jump(exit, &[]);
1452 Builder::new(&mut func, exit).ret(&[]);
1453 let stats = simplify(&mut func);
1454 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1455 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1456 assert_eq!(goes_to(&func, 1), [4]);
1457 assert_eq!(goes_to(&func, 2), [4]);
1458 }
1459
1460 #[test]
1461 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1462 let mut func = taking_a_condition();
1469 let (carried, [arm, above]) = arms(&mut func);
1470 let forwarder = func.create_block();
1471 let exit = func.create_block();
1472 let other = func.append_param(exit, Type::int(32));
1473 let mut build = Builder::new(&mut func, arm);
1474 let mine = build.iconst(Type::int(32), 9);
1475 build.jump(exit, &[mine]);
1476 Builder::new(&mut func, above).jump(forwarder, &[]);
1477 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1478 Builder::new(&mut func, exit).ret(&[other]);
1479 let stats = simplify(&mut func);
1480 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1481 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1482 assert_eq!(carries(&func, 2, 0), [carried]);
1485 assert_eq!(carries(&func, 1, 0), [mine]);
1486 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1488 }
1489
1490 #[test]
1491 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1492 let mut func = taking_a_condition();
1497 let (carried, [arm, forwarder]) = arms(&mut func);
1498 let exit = func.create_block();
1499 let other = func.append_param(exit, Type::int(32));
1500 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1502 func.remove_inst(inst);
1503 }
1504 let mut build = Builder::new(&mut func, arm);
1505 let mine = build.iconst(Type::int(32), 9);
1506 build.jump(exit, &[mine]);
1507 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1508 Builder::new(&mut func, exit).ret(&[other]);
1509 let stats = simplify(&mut func);
1510 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1511 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1512 }
1513
1514 #[test]
1515 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1516 let mut func = taking_a_condition();
1519 let (_, [arm, forwarder]) = arms(&mut func);
1520 let exit = func.create_block();
1521 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1522 func.remove_inst(inst);
1523 }
1524 Builder::new(&mut func, arm).jump(exit, &[]);
1525 Builder::new(&mut func, forwarder).jump(exit, &[]);
1526 Builder::new(&mut func, exit).ret(&[]);
1527 let stats = simplify(&mut func);
1528 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1529 assert_eq!(blocks(&func), [0, 1, 3]);
1530 }
1531
1532 #[test]
1533 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1534 let mut names = Interner::new();
1537 let mut func = Func::new(names.intern("f"), Signature::new());
1538 let entry = func.create_block();
1539 let spin = func.create_block();
1540 Builder::new(&mut func, entry).jump(spin, &[]);
1541 Builder::new(&mut func, spin).jump(spin, &[]);
1542 let stats = simplify(&mut func);
1543 assert!(!stats.changed());
1544 assert_eq!(blocks(&func), [0, 1]);
1545 }
1546
1547 #[test]
1548 fn the_entry_block_is_never_the_forwarder_that_goes() {
1549 let mut names = Interner::new();
1553 let mut func = Func::new(names.intern("f"), Signature::new());
1554 let entry = func.create_block();
1555 let below = func.create_block();
1556 Builder::new(&mut func, entry).jump(below, &[]);
1557 let mut build = Builder::new(&mut func, below);
1558 build.iconst(Type::int(32), 1);
1559 build.ret(&[]);
1560 let stats = simplify(&mut func);
1561 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1562 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1563 assert_eq!(blocks(&func), [0]);
1564 }
1565
1566 #[test]
1567 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1568 let mut names = Interner::new();
1572 let mut func = Func::new(names.intern("f"), Signature::new());
1573 let entry = func.create_block();
1574 let labelled = func.create_block();
1575 let exit = func.create_block();
1576 let mut build = Builder::new(&mut func, entry);
1577 let addr = build.block_addr(labelled);
1578 build.indirect_br(addr, &[labelled]);
1579 Builder::new(&mut func, labelled).jump(exit, &[]);
1580 let mut build = Builder::new(&mut func, exit);
1581 build.iconst(Type::int(32), 1);
1582 build.ret(&[]);
1583 let stats = simplify(&mut func);
1584 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1585 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1586 }
1587
1588 #[test]
1589 fn a_run_of_forwarders_comes_out_as_one_edge() {
1590 let mut func = taking_a_condition();
1591 let (_, arms) = arms(&mut func);
1592 let first = func.create_block();
1593 let second = func.create_block();
1594 let exit = func.create_block();
1595 for arm in arms {
1596 Builder::new(&mut func, arm).jump(first, &[]);
1597 }
1598 Builder::new(&mut func, first).jump(second, &[]);
1599 Builder::new(&mut func, second).jump(exit, &[]);
1600 Builder::new(&mut func, exit).ret(&[]);
1601 let stats = simplify(&mut func);
1602 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1603 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1604 assert_eq!(goes_to(&func, 1), [5]);
1605 assert_eq!(goes_to(&func, 2), [5]);
1606 }
1607
1608 #[test]
1609 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1610 let mut func = taking_a_condition();
1613 let (carried, arms) = arms(&mut func);
1614 let join = func.create_block();
1615 let param = func.append_param(join, Type::int(32));
1616 for arm in arms {
1617 Builder::new(&mut func, arm).jump(join, &[carried]);
1618 }
1619 Builder::new(&mut func, join).ret(&[param]);
1620 let stats = simplify(&mut func);
1621 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1622 assert!(func[Block::from_usize(3)].params.is_empty());
1623 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1625 assert_eq!(func[func[term].args], [carried]);
1626 assert!(carries(&func, 1, 0).is_empty());
1629 assert!(carries(&func, 2, 0).is_empty());
1630 }
1631
1632 #[test]
1633 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1634 let mut func = taking_a_condition();
1635 let (carried, arms) = arms(&mut func);
1636 let join = func.create_block();
1637 let param = func.append_param(join, Type::int(32));
1638 let mut build = Builder::new(&mut func, arms[0]);
1639 let mine = build.iconst(Type::int(32), 9);
1640 build.jump(join, &[mine]);
1641 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1642 Builder::new(&mut func, join).ret(&[param]);
1643 let stats = simplify(&mut func);
1644 assert!(!stats.changed());
1645 assert_eq!(func[Block::from_usize(3)].params, [param]);
1646 }
1647
1648 #[test]
1649 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1650 let mut names = Interner::new();
1654 let signature = Signature::new().with_params(&[Type::int(1)]);
1655 let mut func = Func::new(names.intern("f"), signature);
1656 let entry = func.create_block();
1657 let header = func.create_block();
1658 let latch = func.create_block();
1659 let exit = func.create_block();
1660 let cond = func.append_param(entry, Type::int(1));
1661 let param = func.append_param(header, Type::int(32));
1662 let mut build = Builder::new(&mut func, entry);
1663 let init = build.iconst(Type::int(32), 7);
1664 build.jump(header, &[init]);
1665 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1666 let mut build = Builder::new(&mut func, latch);
1667 build.iconst(Type::int(32), 1);
1668 build.jump(header, &[param]);
1669 Builder::new(&mut func, exit).ret(&[param]);
1670 let stats = simplify(&mut func);
1671 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1672 assert!(func[Block::from_usize(1)].params.is_empty());
1673 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1674 assert_eq!(func[func[term].args], [init]);
1675 }
1676
1677 #[test]
1678 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1679 let mut names = Interner::new();
1683 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1684 let mut func = Func::new(names.intern("f"), signature);
1685 let entry = func.create_block();
1686 let latch = func.create_block();
1687 let exit = func.create_block();
1688 let cond = func.append_param(entry, Type::int(1));
1689 let x = func.append_param(entry, Type::int(32));
1690 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1691 let mut build = Builder::new(&mut func, latch);
1692 let one = build.iconst(Type::int(1), 1);
1693 let seven = build.iconst(Type::int(32), 7);
1694 build.jump(entry, &[one, seven]);
1695 Builder::new(&mut func, exit).ret(&[x]);
1696 let stats = simplify(&mut func);
1697 assert!(!stats.changed());
1698 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1699 }
1700
1701 #[test]
1702 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1703 let mut func = taking_a_condition();
1707 let (carried, arms) = arms(&mut func);
1708 let join = func.create_block();
1709 let inner = func.append_param(join, Type::int(32));
1710 let left = func.create_block();
1711 let right = func.create_block();
1712 let last = func.create_block();
1713 let outer = func.append_param(last, Type::int(32));
1714 for arm in arms {
1715 Builder::new(&mut func, arm).jump(join, &[carried]);
1716 }
1717 let cond = func[Block::from_usize(0)].params[0];
1718 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1719 let mut build = Builder::new(&mut func, left);
1720 build.iconst(Type::int(32), 1);
1721 build.jump(last, &[inner]);
1722 let mut build = Builder::new(&mut func, right);
1723 build.iconst(Type::int(32), 2);
1724 build.jump(last, &[carried]);
1725 Builder::new(&mut func, last).ret(&[outer]);
1726 let stats = simplify(&mut func);
1727 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1728 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1729 assert_eq!(func[func[term].args], [carried]);
1730 }
1731
1732 #[test]
1733 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1734 let mut func = taking_a_condition();
1738 let (carried, arms) = arms(&mut func);
1739 let forwarder = func.create_block();
1740 let param = func.append_param(forwarder, Type::int(32));
1741 let exit = func.create_block();
1742 let arrived = func.append_param(exit, Type::int(32));
1743 for arm in arms {
1744 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1745 }
1746 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1747 Builder::new(&mut func, exit).ret(&[arrived]);
1748 let stats = simplify(&mut func);
1749 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1750 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1753 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1754 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
1755 assert_eq!(func[func[term].args], [carried]);
1756 }
1757
1758 #[test]
1759 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
1760 let mut func = taking_a_condition();
1763 let (carried, arms) = arms(&mut func);
1764 let forwarder = func.create_block();
1765 let param = func.append_param(forwarder, Type::int(32));
1766 let exit = func.create_block();
1767 for arm in arms {
1768 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1769 }
1770 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1771 Builder::new(&mut func, exit).ret(&[]);
1772 let stats =
1773 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1774 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1775 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1776 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
1777 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
1778 }
1779
1780 #[test]
1781 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
1782 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
1785 let mut names = Interner::new();
1786 let mut module = Module::new(names.intern("test.c"), &target);
1787 let mut func = taking_a_condition();
1788 let (carried, arms) = arms(&mut func);
1789 let forwarder = func.create_block();
1790 let param = func.append_param(forwarder, Type::int(32));
1791 let exit = func.create_block();
1792 let arrived = func.append_param(exit, Type::int(32));
1793 let mut build = Builder::new(&mut func, arms[0]);
1794 let mine = build.iconst(Type::int(32), 9);
1795 build.jump(exit, &[mine]);
1796 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
1797 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1798 let mut build = Builder::new(&mut func, exit);
1799 build.icmp(IntPred::Eq, arrived, arrived);
1802 build.ret(&[]);
1803 simplify(&mut func);
1804 module.add_func(func);
1805 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
1806 }
1807
1808 #[test]
1809 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
1810 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1811 let before = blocks(&func);
1812 let stats =
1813 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
1814 assert!(!stats.changed());
1815 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
1816 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1817 assert_eq!(blocks(&func), before);
1818 }
1819
1820 #[test]
1821 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
1822 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
1826 let stats =
1827 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1828 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1829 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
1830 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
1833 }
1834
1835 #[test]
1836 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
1837 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
1838 let mut names = Interner::new();
1839 let mut module = Module::new(names.intern("test.c"), &target);
1840 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
1841 simplify(&mut func);
1842 module.add_func(func);
1843 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
1844 }
1845
1846 #[test]
1847 fn the_pass_says_it_preserves_nothing() {
1848 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
1849 }
1850}