1use std::collections::{HashMap, HashSet, VecDeque};
152
153use rucc_base::Idx;
154use rucc_ir::{Block, BlockCall, Def, Extra, Func, Inst, Opcode, Value};
155
156use crate::fold::constant;
157use crate::{Analyses, Fuel, Pass, Preserved, Stats, uses};
158
159const FOLDED: &str = "branch on a condition that is always the same way replaced by a jump";
161
162pub(crate) const REMOVED: &str = "block nothing reaches removed";
164
165const MERGED: &str = "block with one way into it merged into the block above it";
167
168const FORWARDED: &str = "block that only jumped somewhere else removed and its edges pointed past";
170
171const SAME_EVERY_WAY: &str = "block parameter that arrives as the same value every way in removed";
173
174const NO_FUEL: &str = "branch on a known condition left alone, the pass ran out of fuel";
176
177const NO_FUEL_MERGE: &str = "block with one way into it left alone, the pass ran out of fuel";
179
180const NO_FUEL_FORWARD: &str =
182 "block that only jumped somewhere else kept, the pass ran out of fuel";
183
184const NO_FUEL_PARAM: &str = "block parameter that is one value kept, the pass ran out of fuel";
186
187const NOTHING_READS_IT: &str = "block parameter nothing reads removed, and the argument on every \
189 edge that was feeding it";
190
191const NO_FUEL_UNREAD: &str = "block parameter nothing reads kept, the pass ran out of fuel";
193
194#[derive(Debug, Clone, Copy, PartialEq, Eq)]
196pub struct SimplifyCfg;
197
198impl Pass for SimplifyCfg {
199 fn name(&self) -> &'static str {
200 "simplify-cfg"
201 }
202
203 fn describe(&self) -> &'static str {
204 "unreachable blocks go, a branch that only goes one way becomes a jump, a block that only \
205 jumps stops being in the way, and a block with one way in is merged into the one above it"
206 }
207
208 fn preserves(&self) -> Preserved {
209 Preserved::NONE
212 }
213
214 fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
215 let mut stats = Stats::new();
216 sweep(func, an, &mut stats);
220 let mut folded = false;
221 let unbound = Bindings::new();
225 for block in func.blocks().collect::<Vec<Block>>() {
226 let Some(term) = func.terminator(block) else { continue };
227 let Some(taken) = taken(func, term, &unbound) else { continue };
228 if !fuel.take() {
229 stats.missed(NO_FUEL);
232 continue;
233 }
234 jump_to(func, term, taken);
235 stats.optimized(FOLDED);
236 folded = true;
237 }
238 if folded {
239 an.clear();
243 sweep(func, an, &mut stats);
244 }
245 let mut forward = HashMap::new();
246 let dropped = drop_unread(func, fuel, &mut stats);
252 if straighten(func, fuel, &mut stats, &mut forward) || dropped {
253 an.clear();
254 }
255 for chain in chains(func, an) {
259 for (at, &block) in chain.iter().enumerate().skip(1) {
260 if !fuel.take() {
261 for _ in at..chain.len() {
265 stats.missed(NO_FUEL_MERGE);
266 }
267 break;
268 }
269 merge(func, chain[0], block, &mut forward);
270 stats.optimized(MERGED);
271 }
272 }
273 if !forward.is_empty() {
274 uses::substitute(func, &forward);
277 }
278 stats
279 }
280}
281
282pub(crate) type Bindings = HashMap<Value, Value>;
289
290fn resolve(subst: &Bindings, value: Value) -> Value {
292 subst.get(&value).copied().unwrap_or(value)
293}
294
295pub(crate) fn taken(func: &Func, term: Inst, subst: &Bindings) -> Option<BlockCall> {
304 let data = &func[term];
305 let arg = *func[data.args].first()?;
306 match data.opcode {
307 Opcode::BrIf => {
308 let Extra::Targets(targets) = data.extra else { return None };
309 if let Some(call) = one_place(func, &func[targets]) {
310 return Some(call);
311 }
312 let arm = usize::from(!known(func, arg, subst)?);
315 func[targets].get(arm).copied()
316 }
317 Opcode::Switch => {
318 let Extra::Switch(at) = data.extra else { return None };
319 let info = func[at];
320 if let Some(call) = one_place(func, &func[info.targets]) {
321 return Some(call);
322 }
323 let (value, _) = constant(func, resolve(subst, arg))?;
324 let case = func[info.cases].iter().position(|it| *it == value);
327 func[info.targets].get(case.map_or(0, |case| case + 1)).copied()
328 }
329 _ => None,
330 }
331}
332
333fn one_place(func: &Func, calls: &[BlockCall]) -> Option<BlockCall> {
345 let &first = calls.first()?;
346 let same = |call: &BlockCall| call.block == first.block && func[call.args] == func[first.args];
347 calls[1..].iter().all(same).then_some(first)
348}
349
350pub(crate) fn jump_to(func: &mut Func, term: Inst, call: BlockCall) {
355 let targets = func.push_block_calls(&[call]);
356 let args = func.push_values(&[]);
357 let data = &mut func[term];
358 data.opcode = Opcode::Jump;
359 data.args = args;
360 data.extra = Extra::Targets(targets);
361}
362
363pub(crate) fn sweep(func: &mut Func, an: &mut Analyses, stats: &mut Stats) {
372 let gone = stranded(func, an);
373 if gone.is_empty() {
374 return;
375 }
376 for block in gone {
377 func.remove_block(block);
378 stats.optimized(REMOVED);
379 }
380 an.clear();
381}
382
383fn stranded(func: &Func, an: &mut Analyses) -> Vec<Block> {
392 let cfg = an.cfg(func);
393 let Some(entry) = cfg.entry() else { return Vec::new() };
394 let mut seen = vec![false; cfg.capacity()];
395 seen[entry.index()] = true;
396 let mut stack = vec![entry];
397 for (block, _) in func.named_blocks() {
401 if !seen[block.index()] {
402 seen[block.index()] = true;
403 stack.push(block);
404 }
405 }
406 let mut reached = Vec::new();
407 while let Some(block) = stack.pop() {
408 for &succ in cfg.successors(block) {
409 if !seen[succ.index()] {
410 seen[succ.index()] = true;
411 stack.push(succ);
412 }
413 }
414 reached.push(block);
415 }
416 let mut next = reached;
419 while !next.is_empty() {
420 let mut found = Vec::new();
421 for block in next {
422 for inst in func.insts(block) {
423 if func[inst].opcode != Opcode::BlockAddr {
424 continue;
425 }
426 for call in func.successors(inst) {
427 if !seen[call.block.index()] {
428 seen[call.block.index()] = true;
429 found.push(call.block);
430 }
431 }
432 }
433 }
434 let mut stack = found.clone();
437 while let Some(block) = stack.pop() {
438 for &succ in cfg.successors(block) {
439 if !seen[succ.index()] {
440 seen[succ.index()] = true;
441 stack.push(succ);
442 found.push(succ);
443 }
444 }
445 }
446 next = found;
447 }
448 func.blocks().filter(|block| !seen[block.index()]).collect()
449}
450
451pub(crate) type Edges = HashMap<Block, Vec<(Block, Idx<BlockCall>)>>;
458
459pub(crate) fn incoming(func: &Func) -> Edges {
467 let mut edges: Edges = HashMap::new();
468 for block in func.blocks() {
469 let Some(term) = func.terminator(block) else { continue };
470 for at in func.target_list(term).iter() {
471 edges.entry(func[at].block).or_default().push((block, at));
472 }
473 }
474 edges
475}
476
477fn drop_unread(func: &mut Func, fuel: &mut Fuel, stats: &mut Stats) -> bool {
490 let Some(entry) = func.entry() else { return false };
491 let live = live(func, entry, &addressed(func));
492 let edges = incoming(func);
493 let mut changed = false;
494 let mut gone: HashSet<Value> = HashSet::new();
495 for block in func.blocks().collect::<Vec<Block>>() {
496 let mut taking = Vec::new();
497 for (index, ¶m) in func[block].params.iter().enumerate() {
498 if live.contains(¶m) {
499 continue;
500 }
501 if !fuel.take() {
502 stats.missed(NO_FUEL_UNREAD);
503 continue;
504 }
505 taking.push(index);
506 }
507 if taking.is_empty() {
508 continue;
509 }
510 for _ in &taking {
511 stats.optimized(NOTHING_READS_IT);
512 }
513 gone.extend(taking.iter().map(|&index| func[block].params[index]));
514 take_params(func, block, &taking, edges.get(&block));
515 changed = true;
516 }
517 if !gone.is_empty() {
518 strand(func, gone);
519 }
520 changed
521}
522
523fn strand(func: &mut Func, mut gone: HashSet<Value>) {
545 loop {
546 let mut spread = false;
547 for block in func.blocks().collect::<Vec<Block>>() {
548 for inst in func.insts(block).collect::<Vec<Inst>>() {
549 if !func[func[inst].args].iter().any(|value| gone.contains(value)) {
550 continue;
551 }
552 let results: Vec<Value> = func[inst].results().collect();
553 for result in results {
554 spread |= gone.insert(result);
555 }
556 func.remove_inst(inst);
557 }
558 }
559 if !spread {
562 return;
563 }
564 }
565}
566
567fn live(func: &Func, entry: Block, addressed: &HashSet<Block>) -> HashSet<Value> {
582 let mut where_from: HashMap<Value, (Block, usize)> = HashMap::new();
583 let mut live: HashSet<Value> = HashSet::new();
584 let mut work: Vec<Value> = Vec::new();
585 let seed = |value: Value, live: &mut HashSet<Value>, work: &mut Vec<Value>| {
586 if live.insert(value) {
587 work.push(value);
588 }
589 };
590 for block in func.blocks() {
591 let held = block == entry || addressed.contains(&block);
592 for (index, ¶m) in func[block].params.iter().enumerate() {
593 where_from.insert(param, (block, index));
594 if held {
595 seed(param, &mut live, &mut work);
596 }
597 }
598 for inst in func.insts(block) {
599 if !func.is_terminator(inst) && !func[inst].opcode.has_effects() {
600 continue;
601 }
602 for &value in &func[func[inst].args] {
603 seed(value, &mut live, &mut work);
604 }
605 }
606 }
607
608 let edges = incoming(func);
609 while let Some(value) = work.pop() {
610 match func[value].def {
611 Def::Result { inst, .. } => {
612 for &operand in &func[func[inst].args] {
613 seed(operand, &mut live, &mut work);
614 }
615 }
616 Def::Param { .. } => {
617 let Some(&(block, index)) = where_from.get(&value) else { continue };
618 for &(_, at) in edges.get(&block).into_iter().flatten() {
619 let Some(&arg) = func[func[at].args].get(index) else { continue };
620 seed(arg, &mut live, &mut work);
621 }
622 }
623 }
624 }
625 live
626}
627
628fn straighten(
654 func: &mut Func,
655 fuel: &mut Fuel,
656 stats: &mut Stats,
657 forward: &mut HashMap<Value, Value>,
658) -> bool {
659 let Some(entry) = func.entry() else { return false };
660 let addressed = addressed(func);
661 let mut edges = incoming(func);
662 let mut work: VecDeque<Block> = func.blocks().collect();
663 let mut queued: HashSet<Block> = work.iter().copied().collect();
664 let mut gone: HashSet<Block> = HashSet::new();
665 let mut changed = false;
666 while let Some(block) = work.pop_front() {
667 queued.remove(&block);
668 if gone.contains(&block) {
669 continue;
670 }
671 let mut starved = false;
672 if block != entry {
673 let drop = redundant(func, block, edges.get(&block), forward);
674 let mut taking = Vec::new();
675 for (index, value) in drop {
676 if !fuel.take() {
677 stats.missed(NO_FUEL_PARAM);
678 starved = true;
679 break;
680 }
681 let value = uses::chase(forward, value);
684 forward.insert(func[block].params[index], value);
685 taking.push(index);
686 stats.optimized(SAME_EVERY_WAY);
687 }
688 if !taking.is_empty() {
689 take_params(func, block, &taking, edges.get(&block));
690 requeue(block, &mut work, &mut queued);
693 if let Some(term) = func.terminator(block) {
696 for call in func.successors(term).collect::<Vec<BlockCall>>() {
697 requeue(call.block, &mut work, &mut queued);
698 }
699 }
700 changed = true;
701 }
702 }
703 if starved {
706 break;
707 }
708 let Some((term, into, args)) = forwards(func, block, entry, &addressed, &edges) else {
709 continue;
710 };
711 if !fuel.take() {
712 stats.missed(NO_FUEL_FORWARD);
713 break;
714 }
715 let out = func.target_list(term).iter().next().expect("a jump has a target");
719 if let Some(list) = edges.get_mut(&into) {
720 list.retain(|&(_, at)| at != out);
721 }
722 let ins = edges.remove(&block).unwrap_or_default();
723 for &(_, at) in &ins {
724 let call = func[at];
728 let args = func.push_values(&args);
729 func.set_block_call(at, BlockCall { block: into, args, ..call });
730 }
731 edges.entry(into).or_default().extend(ins.iter().copied());
732 func.remove_block(block);
733 gone.insert(block);
734 stats.optimized(FORWARDED);
735 changed = true;
736 requeue(into, &mut work, &mut queued);
737 for &(from, _) in &ins {
738 requeue(from, &mut work, &mut queued);
739 }
740 }
741 changed
742}
743
744fn requeue(block: Block, work: &mut VecDeque<Block>, queued: &mut HashSet<Block>) {
746 if queued.insert(block) {
747 work.push_back(block);
748 }
749}
750
751fn redundant(
767 func: &Func,
768 block: Block,
769 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
770 forward: &HashMap<Value, Value>,
771) -> Vec<(usize, Value)> {
772 let Some(ins) = ins.filter(|ins| !ins.is_empty()) else { return Vec::new() };
773 let mut found = Vec::new();
774 for (index, ¶m) in func[block].params.iter().enumerate() {
775 let mut only = None;
776 let mut agree = true;
777 for &(_, at) in ins {
778 let list = func[at].args;
779 let Some(&arg) = func[list].get(index) else {
780 agree = false;
783 break;
784 };
785 let arg = uses::chase(forward, arg);
786 if arg == param {
787 continue;
788 }
789 match only {
790 None => only = Some(arg),
791 Some(seen) if seen == arg => {}
792 Some(_) => {
793 agree = false;
794 break;
795 }
796 }
797 }
798 if !agree {
799 continue;
800 }
801 if let Some(value) = only {
802 found.push((index, value));
803 }
804 }
805 found
806}
807
808fn take_params(
813 func: &mut Func,
814 block: Block,
815 taking: &[usize],
816 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
817) {
818 for &(_, at) in ins.into_iter().flatten() {
819 let call = func[at];
820 let kept: Vec<Value> = func[call.args]
821 .iter()
822 .enumerate()
823 .filter(|(index, _)| !taking.contains(index))
824 .map(|(_, &value)| value)
825 .collect();
826 let args = func.push_values(&kept);
827 func.set_block_call(at, BlockCall { args, ..call });
828 }
829 let mut index = 0;
830 func.retain_params(block, |_| {
831 let keep = !taking.contains(&index);
832 index += 1;
833 keep
834 });
835}
836
837fn forwards(
843 func: &Func,
844 block: Block,
845 entry: Block,
846 addressed: &HashSet<Block>,
847 edges: &Edges,
848) -> Option<(Inst, Block, Vec<Value>)> {
849 if block == entry || addressed.contains(&block) || !func[block].params.is_empty() {
850 return None;
851 }
852 let term = func.terminator(block)?;
853 if func[term].opcode != Opcode::Jump {
854 return None;
855 }
856 if func.insts(block).count() != 1 {
859 return None;
860 }
861 let call = func.successors(term).next()?;
862 if call.block == block {
863 return None;
864 }
865 if carrying(func, block, call.block, func[call.args].len(), edges) {
866 return None;
867 }
868 Some((term, call.block, func[call.args].to_vec()))
869}
870
871fn carrying(func: &Func, block: Block, into: Block, args: usize, edges: &Edges) -> bool {
885 if args == 0 {
886 return false;
887 }
888 let ins = edges.get(&block).map_or(0, Vec::len);
889 let after = edges.get(&into).map_or(0, Vec::len) - 1 + ins;
890 if after < 2 {
891 return false;
892 }
893 edges.get(&block).into_iter().flatten().any(|&(from, _)| {
894 let Some(term) = func.terminator(from) else { return false };
895 func.target_list(term).iter().count() >= 2
896 })
897}
898
899fn chains(func: &Func, an: &mut Analyses) -> Vec<Vec<Block>> {
920 let cfg = an.cfg(func);
921 let Some(entry) = cfg.entry() else { return Vec::new() };
922 let addressed = addressed(func);
923 let mut below = HashMap::new();
924 let mut is_below = HashSet::new();
925 for block in func.blocks() {
926 let Some(term) = func.terminator(block) else { continue };
927 if func[term].opcode != Opcode::Jump {
928 continue;
929 }
930 let Some(call) = func.successors(term).next() else { continue };
931 let into = call.block;
932 let preds = cfg.predecessors(into);
933 if into == entry || into == block || addressed.contains(&into) {
934 continue;
935 }
936 if preds.len() != 1 || preds[0] != block {
937 continue;
938 }
939 below.insert(block, into);
940 is_below.insert(into);
941 }
942 let heads = func.blocks().filter(|it| below.contains_key(it) && !is_below.contains(it));
943 heads
944 .map(|head| {
945 let mut chain = vec![head];
946 let mut at = head;
947 while let Some(&next) = below.get(&at) {
948 chain.push(next);
949 at = next;
950 }
951 chain
952 })
953 .collect()
954}
955
956fn addressed(func: &Func) -> HashSet<Block> {
963 let mut taken: HashSet<Block> = func.named_blocks().map(|(block, _)| block).collect();
964 for block in func.blocks() {
965 for inst in func.insts(block) {
966 if func[inst].opcode != Opcode::BlockAddr {
967 continue;
968 }
969 for call in func.successors(inst) {
970 taken.insert(call.block);
971 }
972 }
973 }
974 taken
975}
976
977fn merge(func: &mut Func, head: Block, block: Block, forward: &mut HashMap<Value, Value>) {
984 let term = func.terminator(head).expect("the head of a chain ends in a jump");
985 let call = func.successors(term).next().expect("a jump goes somewhere");
986 let args = func[call.args].to_vec();
987 let params = func[block].params.clone();
988 for (param, arg) in params.into_iter().zip(args) {
989 let arg = uses::chase(forward, arg);
993 forward.insert(param, arg);
994 }
995 func.remove_inst(term);
996 for inst in func.insts(block).collect::<Vec<Inst>>() {
997 func.remove_inst(inst);
998 func.append_inst(head, inst);
999 }
1000 func.remove_block(block);
1001}
1002
1003fn known(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1005 let value = resolve(subst, value);
1006 if let Some((imm, _)) = constant(func, value) {
1007 return Some(imm.unsigned() != 0);
1008 }
1009 compared(func, value, subst)
1010}
1011
1012fn compared(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1023 let Def::Result { inst, .. } = func[value].def else { return None };
1024 let data = &func[inst];
1025 if data.opcode != Opcode::ICmp {
1026 return None;
1027 }
1028 let Extra::IntPred(pred) = data.extra else { return None };
1029 let args = &func[data.args];
1030 let (lhs, ty) = constant(func, resolve(subst, *args.first()?))?;
1031 let (rhs, _) = constant(func, resolve(subst, *args.get(1)?))?;
1032 Some(crate::fold::compare(pred, lhs, rhs, ty))
1033}
1034
1035#[cfg(test)]
1036mod tests {
1037 use rucc_base::Interner;
1038 use rucc_ir::{
1039 Block, Builder, Def, Flags, Func, Inst, IntPred, MemInfo, MemOrder, Module, Opcode,
1040 Restrict, Signature, Type, Value,
1041 };
1042 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1043
1044 use super::SimplifyCfg;
1045 use crate::stats::Kind;
1046 use crate::testing::graph;
1047 use crate::{Fuel, Pass, Preserved, Stats};
1048
1049 fn simplify(func: &mut Func) -> Stats {
1051 SimplifyCfg.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1052 }
1053
1054 fn blocks(func: &Func) -> Vec<usize> {
1056 func.blocks().map(Block::index).collect()
1057 }
1058
1059 fn terminator(func: &Func, block: usize) -> Opcode {
1061 let block = Block::from_usize(block);
1062 func[func.terminator(block).expect("every block here has one")].opcode
1063 }
1064
1065 fn goes_to(func: &Func, block: usize) -> Vec<usize> {
1067 let block = Block::from_usize(block);
1068 let term = func.terminator(block).expect("every block here has one");
1069 func.successors(term).map(|call| call.block.index()).collect()
1070 }
1071
1072 fn lives_in(func: &Func, value: Value) -> Option<usize> {
1078 let Def::Result { inst, .. } = func[value].def else { return None };
1079 func.block_of(inst).map(Block::index)
1080 }
1081
1082 fn diamond(cond: impl FnOnce(&mut Builder<'_>) -> Value) -> (Func, [Value; 2]) {
1089 let mut names = Interner::new();
1090 let mut func = Func::new(names.intern("f"), Signature::new());
1091 let entry = func.create_block();
1092 let then_block = func.create_block();
1093 let else_block = func.create_block();
1094 let join = func.create_block();
1095 let mut build = Builder::new(&mut func, entry);
1096 let cond = cond(&mut build);
1097 build.br_if(cond, then_block, &[], else_block, &[]);
1098 let mut marks = Vec::new();
1099 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1100 let mut build = Builder::new(&mut func, arm);
1101 marks.push(build.iconst(Type::int(32), mark));
1102 build.jump(join, &[]);
1103 }
1104 let mut build = Builder::new(&mut func, join);
1105 build.ret(&[]);
1106 (func, [marks[0], marks[1]])
1107 }
1108
1109 #[test]
1110 fn a_branch_on_a_true_constant_becomes_a_jump_to_the_first_arm() {
1111 let (mut func, [taken, other]) = diamond(|build| build.iconst(Type::int(1), 1));
1112 let stats = simplify(&mut func);
1113 assert!(stats.changed());
1114 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1115 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1118 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1119 assert_eq!(lives_in(&func, taken), Some(0));
1120 assert_eq!(lives_in(&func, other), None);
1121 assert_eq!(blocks(&func), [0]);
1122 }
1123
1124 #[test]
1125 fn a_branch_on_a_false_constant_becomes_a_jump_to_the_second_arm() {
1126 let (mut func, [other, taken]) = diamond(|build| build.iconst(Type::int(1), 0));
1127 assert!(simplify(&mut func).changed());
1128 assert_eq!(lives_in(&func, taken), Some(0));
1129 assert_eq!(lives_in(&func, other), None);
1130 assert_eq!(blocks(&func), [0]);
1131 }
1132
1133 #[test]
1134 fn folding_a_branch_and_merging_what_it_leaves_are_two_things_fuel_buys_apart() {
1135 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1138 let stats =
1139 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1140 assert_eq!(terminator(&func, 0), Opcode::Jump);
1141 assert_eq!(goes_to(&func, 0), [1]);
1142 assert_eq!(blocks(&func), [0, 1, 3]);
1143 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
1144 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
1147 }
1148
1149 #[test]
1150 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
1151 let cases: &[(IntPred, i128, i128, bool)] = &[
1155 (IntPred::Eq, 7, 7, true),
1156 (IntPred::Eq, 7, 8, false),
1157 (IntPred::Ne, 7, 8, true),
1158 (IntPred::Ne, 7, 7, false),
1159 (IntPred::Slt, -1, 1, true),
1160 (IntPred::Slt, 1, -1, false),
1161 (IntPred::Sle, -1, -1, true),
1162 (IntPred::Sle, 1, -1, false),
1163 (IntPred::Sgt, 1, -1, true),
1164 (IntPred::Sgt, -1, 1, false),
1165 (IntPred::Sge, -1, -1, true),
1166 (IntPred::Sge, -1, 1, false),
1167 (IntPred::Ult, 1, -1, true),
1168 (IntPred::Ult, -1, 1, false),
1169 (IntPred::Ule, -1, -1, true),
1170 (IntPred::Ule, -1, 1, false),
1171 (IntPred::Ugt, -1, 1, true),
1172 (IntPred::Ugt, 1, -1, false),
1173 (IntPred::Uge, -1, -1, true),
1174 (IntPred::Uge, 1, -1, false),
1175 ];
1176 for &(pred, lhs, rhs, taken) in cases {
1177 let (mut func, marks) = diamond(|build| {
1178 let lhs = build.iconst(Type::int(32), lhs);
1179 let rhs = build.iconst(Type::int(32), rhs);
1180 build.icmp(pred, lhs, rhs)
1181 });
1182 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
1183 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
1184 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
1185 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
1186 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
1187 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
1188 }
1189 }
1190
1191 #[test]
1192 fn a_branch_on_something_nobody_knows_is_left_alone() {
1193 let mut names = Interner::new();
1194 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
1195 let entry = func.create_block();
1196 let then_block = func.create_block();
1197 let else_block = func.create_block();
1198 let cond = func.append_param(entry, Type::int(1));
1199 let mut build = Builder::new(&mut func, entry);
1200 build.br_if(cond, then_block, &[], else_block, &[]);
1201 for arm in [then_block, else_block] {
1202 let mut build = Builder::new(&mut func, arm);
1203 build.ret(&[]);
1204 }
1205 let stats = simplify(&mut func);
1206 assert!(!stats.changed());
1207 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1208 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1209 assert_eq!(blocks(&func), [0, 1, 2]);
1210 }
1211
1212 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1215 let mut names = Interner::new();
1216 let mut func = Func::new(names.intern("f"), Signature::new());
1217 let entry = func.create_block();
1218 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1219 let mut build = Builder::new(&mut func, entry);
1220 let value = build.iconst(Type::int(32), on);
1221 let pairs: Vec<(i128, Block)> =
1222 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1223 build.switch(value, arms[0], &pairs);
1224 let mut marks = Vec::new();
1225 for (at, &arm) in arms.iter().enumerate() {
1226 let mut build = Builder::new(&mut func, arm);
1227 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1228 build.ret(&[]);
1229 }
1230 (func, marks)
1231 }
1232
1233 #[test]
1234 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1235 let (mut func, marks) = switched(5, &[4, 5]);
1236 assert!(simplify(&mut func).changed());
1237 assert_eq!(lives_in(&func, marks[2]), Some(0));
1238 assert_eq!(lives_in(&func, marks[0]), None);
1239 assert_eq!(lives_in(&func, marks[1]), None);
1240 assert_eq!(blocks(&func), [0]);
1241 }
1242
1243 #[test]
1244 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1245 let (mut func, marks) = switched(9, &[4]);
1246 assert!(simplify(&mut func).changed());
1247 assert_eq!(lives_in(&func, marks[0]), Some(0));
1248 assert_eq!(lives_in(&func, marks[1]), None);
1249 assert_eq!(blocks(&func), [0]);
1250 }
1251
1252 #[test]
1253 fn the_arguments_travel_with_the_edge_that_survives() {
1254 let mut names = Interner::new();
1260 let mut func = Func::new(names.intern("f"), Signature::new());
1261 let entry = func.create_block();
1262 let join = func.create_block();
1263 let param = func.append_param(join, Type::int(32));
1264 let mut build = Builder::new(&mut func, entry);
1265 let cond = build.iconst(Type::int(1), 0);
1266 let taken = build.iconst(Type::int(32), 11);
1267 let other = build.iconst(Type::int(32), 22);
1268 build.br_if(cond, join, &[other], join, &[taken]);
1269 let mut build = Builder::new(&mut func, join);
1270 build.ret(&[param]);
1271 assert!(simplify(&mut func).changed());
1272 assert_eq!(blocks(&func), [0]);
1276 let term = func.terminator(entry).expect("the entry has one");
1277 assert_eq!(func[func[term].args], [taken]);
1278 assert_ne!(func[func[term].args], [param]);
1279 }
1280
1281 #[test]
1282 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1283 let mut names = Interner::new();
1287 let signature = Signature::new().with_params(&[Type::int(1)]);
1288 let mut func = Func::new(names.intern("f"), signature);
1289 let entry = func.create_block();
1290 let join = func.create_block();
1291 let cond = func.append_param(entry, Type::int(1));
1292 let mut build = Builder::new(&mut func, entry);
1293 build.br_if(cond, join, &[], join, &[]);
1294 let mut build = Builder::new(&mut func, join);
1295 build.ret(&[]);
1296 let stats = simplify(&mut func);
1297 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1298 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1299 assert_eq!(blocks(&func), [0]);
1300 assert_eq!(terminator(&func, 0), Opcode::Return);
1301 }
1302
1303 #[test]
1304 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1305 let mut names = Interner::new();
1306 let signature = Signature::new().with_params(&[Type::int(32)]);
1307 let mut func = Func::new(names.intern("f"), signature);
1308 let entry = func.create_block();
1309 let join = func.create_block();
1310 let value = func.append_param(entry, Type::int(32));
1311 let mut build = Builder::new(&mut func, entry);
1312 build.switch(value, join, &[(4, join), (5, join)]);
1313 let mut build = Builder::new(&mut func, join);
1314 build.ret(&[]);
1315 let stats = simplify(&mut func);
1316 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1317 assert_eq!(blocks(&func), [0]);
1318 }
1319
1320 #[test]
1321 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1322 let mut names = Interner::new();
1325 let signature = Signature::new().with_params(&[Type::int(1)]);
1326 let mut func = Func::new(names.intern("f"), signature);
1327 let entry = func.create_block();
1328 let join = func.create_block();
1329 let cond = func.append_param(entry, Type::int(1));
1330 let param = func.append_param(join, Type::int(32));
1331 let mut build = Builder::new(&mut func, entry);
1332 let first = build.iconst(Type::int(32), 11);
1333 let second = build.iconst(Type::int(32), 22);
1334 build.br_if(cond, join, &[first], join, &[second]);
1335 let mut build = Builder::new(&mut func, join);
1336 build.ret(&[param]);
1339 let stats = simplify(&mut func);
1340 assert!(!stats.changed());
1341 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1342 assert_eq!(blocks(&func), [0, 1]);
1343 }
1344
1345 #[test]
1346 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1347 let mut names = Interner::new();
1350 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1351 let entry = func.create_block();
1352 let dead = func.create_block();
1353 let shared = func.create_block();
1354 let exit = func.create_block();
1355 let x = func.append_param(entry, Type::int(32));
1356 let mut build = Builder::new(&mut func, entry);
1357 let never = build.iconst(Type::int(1), 0);
1358 build.switch(x, exit, &[(0, dead), (1, shared)]);
1359 let mut build = Builder::new(&mut func, dead);
1363 build.iconst(Type::int(32), 1);
1364 build.br_if(never, shared, &[], exit, &[]);
1365 for arm in [shared, exit] {
1366 let mut build = Builder::new(&mut func, arm);
1367 build.ret(&[]);
1368 }
1369 let stats = simplify(&mut func);
1370 assert!(stats.changed());
1371 assert_eq!(terminator(&func, 0), Opcode::Switch);
1374 assert_eq!(goes_to(&func, 1), [3]);
1375 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1376 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1377 }
1378
1379 #[test]
1380 fn a_block_whose_address_is_taken_is_not_removed() {
1381 let mut names = Interner::new();
1385 let mut func = Func::new(names.intern("f"), Signature::new());
1386 let entry = func.create_block();
1387 let labelled = func.create_block();
1388 let arm = func.create_block();
1389 let mut build = Builder::new(&mut func, entry);
1390 let cond = build.iconst(Type::int(1), 1);
1391 let addr = build.block_addr(labelled);
1392 build.br_if(cond, arm, &[], labelled, &[]);
1393 let mut build = Builder::new(&mut func, arm);
1394 build.indirect_br(addr, &[labelled]);
1395 let mut build = Builder::new(&mut func, labelled);
1396 build.ret(&[]);
1397 assert!(simplify(&mut func).changed());
1398 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1399 assert_eq!(blocks(&func), [0, 1]);
1402 assert_eq!(goes_to(&func, 0), [1]);
1403 }
1404
1405 #[test]
1406 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1407 let mut names = Interner::new();
1410 let mut func = Func::new(names.intern("f"), Signature::new());
1411 let entry = func.create_block();
1412 let dead = func.create_block();
1413 let labelled = func.create_block();
1414 let mut build = Builder::new(&mut func, entry);
1415 let cond = build.iconst(Type::int(1), 1);
1416 build.br_if(cond, entry, &[], dead, &[]);
1417 let mut build = Builder::new(&mut func, dead);
1418 let addr = build.block_addr(labelled);
1419 build.indirect_br(addr, &[labelled]);
1420 let mut build = Builder::new(&mut func, labelled);
1421 build.ret(&[]);
1422 assert!(simplify(&mut func).changed());
1423 assert_eq!(blocks(&func), [0]);
1424 }
1425
1426 #[test]
1427 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1428 let mut func = graph(&[&[], &[]]);
1433 let stats = simplify(&mut func);
1434 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1435 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1436 assert_eq!(blocks(&func), [0]);
1437 }
1438
1439 #[test]
1440 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1441 let mut names = Interner::new();
1444 let mut func = Func::new(names.intern("f"), Signature::new());
1445 let entry = func.create_block();
1446 let middle = func.create_block();
1447 let last = func.create_block();
1448 let mut build = Builder::new(&mut func, entry);
1449 build.iconst(Type::int(32), 1);
1450 build.jump(middle, &[]);
1451 let mut build = Builder::new(&mut func, middle);
1452 build.iconst(Type::int(32), 2);
1453 build.jump(last, &[]);
1454 let mut build = Builder::new(&mut func, last);
1455 build.ret(&[]);
1456 let stats = simplify(&mut func);
1457 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1460 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1461 assert_eq!(blocks(&func), [0]);
1462 assert_eq!(terminator(&func, 0), Opcode::Return);
1463 }
1464
1465 #[test]
1466 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1467 let mut names = Interner::new();
1470 let signature = Signature::new().with_params(&[Type::int(1)]);
1471 let mut func = Func::new(names.intern("f"), signature);
1472 let entry = func.create_block();
1473 let then_block = func.create_block();
1474 let else_block = func.create_block();
1475 let join = func.create_block();
1476 let cond = func.append_param(entry, Type::int(1));
1477 let mut build = Builder::new(&mut func, entry);
1478 build.br_if(cond, then_block, &[], else_block, &[]);
1479 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1480 let mut build = Builder::new(&mut func, arm);
1483 build.iconst(Type::int(32), mark);
1484 build.jump(join, &[]);
1485 }
1486 let mut build = Builder::new(&mut func, join);
1487 build.ret(&[]);
1488 let stats = simplify(&mut func);
1489 assert!(!stats.changed());
1490 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1491 }
1492
1493 #[test]
1494 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1495 let mut names = Interner::new();
1498 let signature = Signature::new().with_params(&[Type::int(1)]);
1499 let mut func = Func::new(names.intern("f"), signature);
1500 let entry = func.create_block();
1501 let arm = func.create_block();
1502 let exit = func.create_block();
1503 let cond = func.append_param(entry, Type::int(1));
1504 let mut build = Builder::new(&mut func, entry);
1505 build.br_if(cond, arm, &[], exit, &[]);
1506 for block in [arm, exit] {
1507 let mut build = Builder::new(&mut func, block);
1508 build.ret(&[]);
1509 }
1510 let stats = simplify(&mut func);
1511 assert!(!stats.changed());
1512 assert_eq!(blocks(&func), [0, 1, 2]);
1513 }
1514
1515 #[test]
1516 fn the_entry_block_is_never_the_one_that_moves() {
1517 let mut names = Interner::new();
1521 let signature = Signature::new().with_params(&[Type::int(1)]);
1522 let mut func = Func::new(names.intern("f"), signature);
1523 let entry = func.create_block();
1524 let latch = func.create_block();
1525 let exit = func.create_block();
1526 let cond = func.append_param(entry, Type::int(1));
1527 let mut build = Builder::new(&mut func, entry);
1528 build.br_if(cond, latch, &[], exit, &[]);
1529 let mut build = Builder::new(&mut func, latch);
1531 build.iconst(Type::int(32), 1);
1532 build.jump(entry, &[]);
1533 let mut build = Builder::new(&mut func, exit);
1534 build.ret(&[]);
1535 let stats = simplify(&mut func);
1536 assert!(!stats.changed());
1537 assert_eq!(blocks(&func), [0, 1, 2]);
1538 }
1539
1540 #[test]
1541 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1542 let mut names = Interner::new();
1545 let mut func = Func::new(names.intern("f"), Signature::new());
1546 let entry = func.create_block();
1547 let middle = func.create_block();
1548 let labelled = func.create_block();
1549 let mut build = Builder::new(&mut func, entry);
1550 build.block_addr(labelled);
1551 build.jump(middle, &[]);
1552 let mut build = Builder::new(&mut func, middle);
1555 build.iconst(Type::int(32), 1);
1556 build.jump(labelled, &[]);
1557 let mut build = Builder::new(&mut func, labelled);
1558 build.ret(&[]);
1559 let stats = simplify(&mut func);
1560 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1563 assert_eq!(blocks(&func), [0, 2]);
1564 }
1565
1566 #[test]
1567 fn a_block_an_image_names_is_not_merged_away_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 middle = func.create_block();
1575 let labelled = func.create_block();
1576 Builder::new(&mut func, entry).jump(middle, &[]);
1577 let mut build = Builder::new(&mut func, middle);
1578 build.iconst(Type::int(32), 1);
1579 build.jump(labelled, &[]);
1580 Builder::new(&mut func, labelled).ret(&[]);
1581 func.name_block(labelled, names.intern(".Llbl.0"));
1582 let stats = simplify(&mut func);
1583 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1584 assert_eq!(blocks(&func), [0, 2]);
1585 assert_eq!(func.block_name(labelled), Some(names.intern(".Llbl.0")));
1586 }
1587
1588 #[test]
1589 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1590 let mut names = Interner::new();
1591 let mut func = Func::new(names.intern("f"), Signature::new());
1592 let entry = func.create_block();
1593 let below = func.create_block();
1594 let param = func.append_param(below, Type::int(32));
1595 let mut build = Builder::new(&mut func, entry);
1596 let arg = build.iconst(Type::int(32), 7);
1597 build.jump(below, &[arg]);
1598 let mut build = Builder::new(&mut func, below);
1599 build.ret(&[param]);
1600 assert!(simplify(&mut func).changed());
1601 assert_eq!(blocks(&func), [0]);
1602 let term = func.terminator(entry).expect("the entry has one");
1603 assert_eq!(func[func[term].args], [arg]);
1604 }
1605
1606 #[test]
1607 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1608 let mut names = Interner::new();
1612 let mut func = Func::new(names.intern("f"), Signature::new());
1613 let entry = func.create_block();
1614 let middle = func.create_block();
1615 let last = func.create_block();
1616 let carried = func.append_param(middle, Type::int(32));
1617 let arrived = func.append_param(last, Type::int(32));
1618 let mut build = Builder::new(&mut func, entry);
1619 let arg = build.iconst(Type::int(32), 7);
1620 build.jump(middle, &[arg]);
1621 let mut build = Builder::new(&mut func, middle);
1622 build.jump(last, &[carried]);
1623 let mut build = Builder::new(&mut func, last);
1624 build.ret(&[arrived]);
1625 assert!(simplify(&mut func).changed());
1626 assert_eq!(blocks(&func), [0]);
1627 let term = func.terminator(entry).expect("the entry has one");
1628 assert_eq!(func[func[term].args], [arg]);
1629 }
1630
1631 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1639 let entry = func.create_block();
1640 let first = func.create_block();
1641 let second = func.create_block();
1642 let cond = func.append_param(entry, Type::int(1));
1643 let mut build = Builder::new(func, entry);
1644 let carried = build.iconst(Type::int(32), 7);
1645 build.br_if(cond, first, &[], second, &[]);
1646 for (arm, mark) in [(first, 111), (second, 222)] {
1647 let mut build = Builder::new(func, arm);
1648 build.iconst(Type::int(32), mark);
1649 }
1650 (carried, [first, second])
1651 }
1652
1653 fn taking_a_condition() -> Func {
1655 let mut names = Interner::new();
1656 let signature = Signature::new().with_params(&[Type::int(1)]);
1657 Func::new(names.intern("f"), signature)
1658 }
1659
1660 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1662 let block = Block::from_usize(block);
1663 let term = func.terminator(block).expect("every block here has one");
1664 let call = func.successors(term).nth(edge).expect("the edge is there");
1665 func[call.args].to_vec()
1666 }
1667
1668 #[test]
1669 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1670 let mut func = taking_a_condition();
1673 let (_, arms) = arms(&mut func);
1674 let forwarder = func.create_block();
1675 let exit = func.create_block();
1676 for arm in arms {
1677 Builder::new(&mut func, arm).jump(forwarder, &[]);
1678 }
1679 Builder::new(&mut func, forwarder).jump(exit, &[]);
1680 Builder::new(&mut func, exit).ret(&[]);
1681 let stats = simplify(&mut func);
1682 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1683 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1684 assert_eq!(goes_to(&func, 1), [4]);
1685 assert_eq!(goes_to(&func, 2), [4]);
1686 }
1687
1688 #[test]
1689 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1690 let mut func = taking_a_condition();
1697 let (carried, [arm, above]) = arms(&mut func);
1698 let forwarder = func.create_block();
1699 let exit = func.create_block();
1700 let other = func.append_param(exit, Type::int(32));
1701 let mut build = Builder::new(&mut func, arm);
1702 let mine = build.iconst(Type::int(32), 9);
1703 build.jump(exit, &[mine]);
1704 Builder::new(&mut func, above).jump(forwarder, &[]);
1705 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1706 Builder::new(&mut func, exit).ret(&[other]);
1707 let stats = simplify(&mut func);
1708 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1709 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1710 assert_eq!(carries(&func, 2, 0), [carried]);
1713 assert_eq!(carries(&func, 1, 0), [mine]);
1714 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1716 }
1717
1718 #[test]
1719 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1720 let mut func = taking_a_condition();
1725 let (carried, [arm, forwarder]) = arms(&mut func);
1726 let exit = func.create_block();
1727 let other = func.append_param(exit, Type::int(32));
1728 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1730 func.remove_inst(inst);
1731 }
1732 let mut build = Builder::new(&mut func, arm);
1733 let mine = build.iconst(Type::int(32), 9);
1734 build.jump(exit, &[mine]);
1735 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1736 Builder::new(&mut func, exit).ret(&[other]);
1737 let stats = simplify(&mut func);
1738 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1739 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1740 }
1741
1742 #[test]
1743 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1744 let mut func = taking_a_condition();
1747 let (_, [arm, forwarder]) = arms(&mut func);
1748 let exit = func.create_block();
1749 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1750 func.remove_inst(inst);
1751 }
1752 Builder::new(&mut func, arm).jump(exit, &[]);
1753 Builder::new(&mut func, forwarder).jump(exit, &[]);
1754 Builder::new(&mut func, exit).ret(&[]);
1755 let stats = simplify(&mut func);
1756 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1757 assert_eq!(blocks(&func), [0, 1, 3]);
1758 }
1759
1760 #[test]
1761 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1762 let mut names = Interner::new();
1765 let mut func = Func::new(names.intern("f"), Signature::new());
1766 let entry = func.create_block();
1767 let spin = func.create_block();
1768 Builder::new(&mut func, entry).jump(spin, &[]);
1769 Builder::new(&mut func, spin).jump(spin, &[]);
1770 let stats = simplify(&mut func);
1771 assert!(!stats.changed());
1772 assert_eq!(blocks(&func), [0, 1]);
1773 }
1774
1775 #[test]
1776 fn the_entry_block_is_never_the_forwarder_that_goes() {
1777 let mut names = Interner::new();
1781 let mut func = Func::new(names.intern("f"), Signature::new());
1782 let entry = func.create_block();
1783 let below = func.create_block();
1784 Builder::new(&mut func, entry).jump(below, &[]);
1785 let mut build = Builder::new(&mut func, below);
1786 build.iconst(Type::int(32), 1);
1787 build.ret(&[]);
1788 let stats = simplify(&mut func);
1789 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1790 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1791 assert_eq!(blocks(&func), [0]);
1792 }
1793
1794 #[test]
1795 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1796 let mut names = Interner::new();
1800 let mut func = Func::new(names.intern("f"), Signature::new());
1801 let entry = func.create_block();
1802 let labelled = func.create_block();
1803 let exit = func.create_block();
1804 let mut build = Builder::new(&mut func, entry);
1805 let addr = build.block_addr(labelled);
1806 build.indirect_br(addr, &[labelled]);
1807 Builder::new(&mut func, labelled).jump(exit, &[]);
1808 let mut build = Builder::new(&mut func, exit);
1809 build.iconst(Type::int(32), 1);
1810 build.ret(&[]);
1811 let stats = simplify(&mut func);
1812 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1813 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1814 }
1815
1816 #[test]
1817 fn a_run_of_forwarders_comes_out_as_one_edge() {
1818 let mut func = taking_a_condition();
1819 let (_, arms) = arms(&mut func);
1820 let first = func.create_block();
1821 let second = func.create_block();
1822 let exit = func.create_block();
1823 for arm in arms {
1824 Builder::new(&mut func, arm).jump(first, &[]);
1825 }
1826 Builder::new(&mut func, first).jump(second, &[]);
1827 Builder::new(&mut func, second).jump(exit, &[]);
1828 Builder::new(&mut func, exit).ret(&[]);
1829 let stats = simplify(&mut func);
1830 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1831 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1832 assert_eq!(goes_to(&func, 1), [5]);
1833 assert_eq!(goes_to(&func, 2), [5]);
1834 }
1835
1836 #[test]
1837 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1838 let mut func = taking_a_condition();
1841 let (carried, arms) = arms(&mut func);
1842 let join = func.create_block();
1843 let param = func.append_param(join, Type::int(32));
1844 for arm in arms {
1845 Builder::new(&mut func, arm).jump(join, &[carried]);
1846 }
1847 Builder::new(&mut func, join).ret(&[param]);
1848 let stats = simplify(&mut func);
1849 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1850 assert!(func[Block::from_usize(3)].params.is_empty());
1851 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1853 assert_eq!(func[func[term].args], [carried]);
1854 assert!(carries(&func, 1, 0).is_empty());
1857 assert!(carries(&func, 2, 0).is_empty());
1858 }
1859
1860 #[test]
1861 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1862 let mut func = taking_a_condition();
1863 let (carried, arms) = arms(&mut func);
1864 let join = func.create_block();
1865 let param = func.append_param(join, Type::int(32));
1866 let mut build = Builder::new(&mut func, arms[0]);
1867 let mine = build.iconst(Type::int(32), 9);
1868 build.jump(join, &[mine]);
1869 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1870 Builder::new(&mut func, join).ret(&[param]);
1871 let stats = simplify(&mut func);
1872 assert!(!stats.changed());
1873 assert_eq!(func[Block::from_usize(3)].params, [param]);
1874 }
1875
1876 #[test]
1877 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1878 let mut names = Interner::new();
1882 let signature = Signature::new().with_params(&[Type::int(1)]);
1883 let mut func = Func::new(names.intern("f"), signature);
1884 let entry = func.create_block();
1885 let header = func.create_block();
1886 let latch = func.create_block();
1887 let exit = func.create_block();
1888 let cond = func.append_param(entry, Type::int(1));
1889 let param = func.append_param(header, Type::int(32));
1890 let mut build = Builder::new(&mut func, entry);
1891 let init = build.iconst(Type::int(32), 7);
1892 build.jump(header, &[init]);
1893 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1894 let mut build = Builder::new(&mut func, latch);
1895 build.iconst(Type::int(32), 1);
1896 build.jump(header, &[param]);
1897 Builder::new(&mut func, exit).ret(&[param]);
1898 let stats = simplify(&mut func);
1899 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1900 assert!(func[Block::from_usize(1)].params.is_empty());
1901 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1902 assert_eq!(func[func[term].args], [init]);
1903 }
1904
1905 #[test]
1906 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1907 let mut names = Interner::new();
1911 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1912 let mut func = Func::new(names.intern("f"), signature);
1913 let entry = func.create_block();
1914 let latch = func.create_block();
1915 let exit = func.create_block();
1916 let cond = func.append_param(entry, Type::int(1));
1917 let x = func.append_param(entry, Type::int(32));
1918 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1919 let mut build = Builder::new(&mut func, latch);
1920 let one = build.iconst(Type::int(1), 1);
1921 let seven = build.iconst(Type::int(32), 7);
1922 build.jump(entry, &[one, seven]);
1923 Builder::new(&mut func, exit).ret(&[x]);
1924 let stats = simplify(&mut func);
1925 assert!(!stats.changed());
1926 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1927 }
1928
1929 #[test]
1930 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1931 let mut func = taking_a_condition();
1935 let (carried, arms) = arms(&mut func);
1936 let join = func.create_block();
1937 let inner = func.append_param(join, Type::int(32));
1938 let left = func.create_block();
1939 let right = func.create_block();
1940 let last = func.create_block();
1941 let outer = func.append_param(last, Type::int(32));
1942 for arm in arms {
1943 Builder::new(&mut func, arm).jump(join, &[carried]);
1944 }
1945 let cond = func[Block::from_usize(0)].params[0];
1946 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1947 let mut build = Builder::new(&mut func, left);
1948 build.iconst(Type::int(32), 1);
1949 build.jump(last, &[inner]);
1950 let mut build = Builder::new(&mut func, right);
1951 build.iconst(Type::int(32), 2);
1952 build.jump(last, &[carried]);
1953 Builder::new(&mut func, last).ret(&[outer]);
1954 let stats = simplify(&mut func);
1955 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1956 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1957 assert_eq!(func[func[term].args], [carried]);
1958 }
1959
1960 #[test]
1961 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1962 let mut func = taking_a_condition();
1966 let (carried, arms) = arms(&mut func);
1967 let forwarder = func.create_block();
1968 let param = func.append_param(forwarder, Type::int(32));
1969 let exit = func.create_block();
1970 let arrived = func.append_param(exit, Type::int(32));
1971 for arm in arms {
1972 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1973 }
1974 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1975 Builder::new(&mut func, exit).ret(&[arrived]);
1976 let stats = simplify(&mut func);
1977 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1978 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1981 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1982 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
1983 assert_eq!(func[func[term].args], [carried]);
1984 }
1985
1986 #[test]
1987 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
1988 let mut func = taking_a_condition();
1991 let (carried, arms) = arms(&mut func);
1992 let forwarder = func.create_block();
1993 let param = func.append_param(forwarder, Type::int(32));
1994 let exit = func.create_block();
1995 let arrived = func.append_param(exit, Type::int(32));
1998 for arm in arms {
1999 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
2000 }
2001 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2002 Builder::new(&mut func, exit).ret(&[arrived]);
2003 let stats =
2004 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2005 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
2006 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
2007 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
2008 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
2009 }
2010
2011 fn walking_a_pointer(on_counter: bool) -> Func {
2020 let mut names = Interner::new();
2021 let signature = Signature::new().with_params(&[Type::int(64)]);
2022 let mut func = Func::new(names.intern("f"), signature);
2023 let entry = func.create_block();
2024 let head = func.create_block();
2025 let out = func.create_block();
2026 let end = func.append_param(entry, Type::int(64));
2027 let counter = func.append_param(head, Type::int(32));
2028 let pointer = func.append_param(head, Type::int(64));
2029 let mut build = Builder::new(&mut func, entry);
2030 let from_zero = build.iconst(Type::int(32), 0);
2031 let from_start = build.iconst(Type::int(64), 0);
2032 build.jump(head, &[from_zero, from_start]);
2033 let mut build = Builder::new(&mut func, head);
2034 let one = build.iconst(Type::int(32), 1);
2035 let eight = build.iconst(Type::int(64), 8);
2036 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2037 let along = build.binary(Opcode::Add, pointer, eight, Flags::NONE);
2038 let address = build.unary(Opcode::IntToPtr, pointer, Type::PTR);
2041 let info = MemInfo {
2042 size: 8,
2043 align: 8,
2044 order: MemOrder::NotAtomic,
2045 tbaa: None,
2046 owns: 0,
2047 restrict: Restrict::NONE,
2048 };
2049 build.store(eight, address, info, Flags::NONE);
2050 let going = if on_counter {
2051 let limit = build.iconst(Type::int(32), 10);
2052 build.icmp(IntPred::Ne, next, limit)
2053 } else {
2054 build.icmp(IntPred::Ne, along, end)
2055 };
2056 build.br_if(going, head, &[next, along], out, &[]);
2057 Builder::new(&mut func, out).ret(&[]);
2058 func
2059 }
2060
2061 #[test]
2062 fn a_counter_the_loop_stopped_asking_about_stops_going_round() {
2063 let mut func = walking_a_pointer(false);
2064 let stats = simplify(&mut func);
2065 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2066 assert_eq!(func[Block::from_usize(1)].params.len(), 1);
2068 assert_eq!(carries(&func, 1, 0).len(), 1);
2070 assert_eq!(carries(&func, 0, 0).len(), 1);
2071 }
2072
2073 #[test]
2074 fn a_counter_the_loop_still_asks_about_goes_round_exactly_as_before() {
2075 let mut func = walking_a_pointer(true);
2076 let stats = simplify(&mut func);
2077 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2078 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2079 }
2080
2081 fn counting_into_nothing() -> (Func, Value, Value) {
2088 let mut names = Interner::new();
2089 let signature = Signature::new().with_params(&[Type::int(32)]);
2090 let mut func = Func::new(names.intern("f"), signature);
2091 let entry = func.create_block();
2092 let head = func.create_block();
2093 let out = func.create_block();
2094 let limit = func.append_param(entry, Type::int(32));
2095 let counter = func.append_param(head, Type::int(32));
2096 let mut build = Builder::new(&mut func, entry);
2097 let zero = build.iconst(Type::int(32), 0);
2098 build.jump(head, &[zero]);
2099 let mut build = Builder::new(&mut func, head);
2100 let one = build.iconst(Type::int(32), 1);
2101 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2102 let twice = build.binary(Opcode::Add, next, next, Flags::NONE);
2103 let going = build.icmp(IntPred::Ne, limit, one);
2104 build.br_if(going, head, &[next], out, &[]);
2105 Builder::new(&mut func, out).ret(&[]);
2106 (func, next, twice)
2107 }
2108
2109 #[test]
2110 fn what_was_reading_a_parameter_nothing_reads_goes_with_it() {
2111 let (mut func, next, twice) = counting_into_nothing();
2112 let stats = simplify(&mut func);
2113 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2114 assert_eq!(lives_in(&func, next), None);
2118 assert_eq!(lives_in(&func, twice), None);
2119 }
2120
2121 #[test]
2122 fn the_functions_own_parameters_stay_whether_or_not_anything_reads_them() {
2123 let mut names = Interner::new();
2126 let signature = Signature::new().with_params(&[Type::int(32)]);
2127 let mut func = Func::new(names.intern("f"), signature);
2128 let entry = func.create_block();
2129 func.append_param(entry, Type::int(32));
2130 Builder::new(&mut func, entry).ret(&[]);
2131 let stats = simplify(&mut func);
2132 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2133 assert_eq!(func[entry].params.len(), 1);
2134 }
2135
2136 #[test]
2137 fn a_parameter_nothing_reads_costs_one_unit_of_fuel_and_stays_without_it() {
2138 let mut func = walking_a_pointer(false);
2139 let stats =
2140 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2141 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2142 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_UNREAD), 1);
2143 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2144 }
2145
2146 #[test]
2147 fn the_counter_that_went_leaves_the_verifier_nothing_to_complain_about() {
2148 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2149 let mut names = Interner::new();
2150 let mut module = Module::new(names.intern("test.c"), &target);
2151 let mut func = walking_a_pointer(false);
2152 simplify(&mut func);
2153 module.add_func(func);
2154 rucc_ir::verify(&module, &names).expect("taking a parameter out left the function whole");
2155 }
2156
2157 #[test]
2158 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
2159 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2162 let mut names = Interner::new();
2163 let mut module = Module::new(names.intern("test.c"), &target);
2164 let mut func = taking_a_condition();
2165 let (carried, arms) = arms(&mut func);
2166 let forwarder = func.create_block();
2167 let param = func.append_param(forwarder, Type::int(32));
2168 let exit = func.create_block();
2169 let arrived = func.append_param(exit, Type::int(32));
2170 let mut build = Builder::new(&mut func, arms[0]);
2171 let mine = build.iconst(Type::int(32), 9);
2172 build.jump(exit, &[mine]);
2173 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
2174 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2175 let mut build = Builder::new(&mut func, exit);
2176 build.icmp(IntPred::Eq, arrived, arrived);
2179 build.ret(&[]);
2180 simplify(&mut func);
2181 module.add_func(func);
2182 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
2183 }
2184
2185 #[test]
2186 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
2187 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
2188 let before = blocks(&func);
2189 let stats =
2190 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2191 assert!(!stats.changed());
2192 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2193 assert_eq!(terminator(&func, 0), Opcode::BrIf);
2194 assert_eq!(blocks(&func), before);
2195 }
2196
2197 #[test]
2198 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
2199 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
2203 let stats =
2204 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2205 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
2206 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2207 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
2210 }
2211
2212 #[test]
2213 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2214 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2215 let mut names = Interner::new();
2216 let mut module = Module::new(names.intern("test.c"), &target);
2217 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
2218 simplify(&mut func);
2219 module.add_func(func);
2220 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2221 }
2222
2223 #[test]
2224 fn the_pass_says_it_preserves_nothing() {
2225 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
2226 }
2227}