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
977pub(crate) fn merge_below(func: &mut Func, an: &mut Analyses, head: Block, into: Block) -> bool {
992 let cfg = an.cfg(func);
993 let Some(entry) = cfg.entry() else { return false };
994 if into == entry || into == head || cfg.predecessors(into) != [head] {
995 return false;
996 }
997 let Some(term) = func.terminator(head) else { return false };
998 if func[term].opcode != Opcode::Jump || addressed(func).contains(&into) {
999 return false;
1000 }
1001 let mut forward = HashMap::new();
1002 merge(func, head, into, &mut forward);
1003 uses::substitute(func, &forward);
1004 an.clear();
1005 true
1006}
1007
1008fn merge(func: &mut Func, head: Block, block: Block, forward: &mut HashMap<Value, Value>) {
1015 let term = func.terminator(head).expect("the head of a chain ends in a jump");
1016 let call = func.successors(term).next().expect("a jump goes somewhere");
1017 let args = func[call.args].to_vec();
1018 let params = func[block].params.clone();
1019 for (param, arg) in params.into_iter().zip(args) {
1020 let arg = uses::chase(forward, arg);
1024 forward.insert(param, arg);
1025 }
1026 func.remove_inst(term);
1027 for inst in func.insts(block).collect::<Vec<Inst>>() {
1028 func.remove_inst(inst);
1029 func.append_inst(head, inst);
1030 }
1031 func.remove_block(block);
1032}
1033
1034fn known(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1036 let value = resolve(subst, value);
1037 if let Some((imm, _)) = constant(func, value) {
1038 return Some(imm.unsigned() != 0);
1039 }
1040 compared(func, value, subst)
1041}
1042
1043fn compared(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1054 let Def::Result { inst, .. } = func[value].def else { return None };
1055 let data = &func[inst];
1056 if data.opcode != Opcode::ICmp {
1057 return None;
1058 }
1059 let Extra::IntPred(pred) = data.extra else { return None };
1060 let args = &func[data.args];
1061 let (lhs, ty) = constant(func, resolve(subst, *args.first()?))?;
1062 let (rhs, _) = constant(func, resolve(subst, *args.get(1)?))?;
1063 Some(crate::fold::compare(pred, lhs, rhs, ty))
1064}
1065
1066#[cfg(test)]
1067mod tests {
1068 use rucc_base::Interner;
1069 use rucc_ir::{
1070 Block, Builder, Def, Flags, Func, Inst, IntPred, MemInfo, MemOrder, Module, Opcode,
1071 Restrict, Signature, Type, Value,
1072 };
1073 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1074
1075 use super::SimplifyCfg;
1076 use crate::stats::Kind;
1077 use crate::testing::graph;
1078 use crate::{Fuel, Pass, Preserved, Stats};
1079
1080 fn simplify(func: &mut Func) -> Stats {
1082 SimplifyCfg.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1083 }
1084
1085 fn blocks(func: &Func) -> Vec<usize> {
1087 func.blocks().map(Block::index).collect()
1088 }
1089
1090 fn terminator(func: &Func, block: usize) -> Opcode {
1092 let block = Block::from_usize(block);
1093 func[func.terminator(block).expect("every block here has one")].opcode
1094 }
1095
1096 fn goes_to(func: &Func, block: usize) -> Vec<usize> {
1098 let block = Block::from_usize(block);
1099 let term = func.terminator(block).expect("every block here has one");
1100 func.successors(term).map(|call| call.block.index()).collect()
1101 }
1102
1103 fn lives_in(func: &Func, value: Value) -> Option<usize> {
1109 let Def::Result { inst, .. } = func[value].def else { return None };
1110 func.block_of(inst).map(Block::index)
1111 }
1112
1113 fn diamond(cond: impl FnOnce(&mut Builder<'_>) -> Value) -> (Func, [Value; 2]) {
1120 let mut names = Interner::new();
1121 let mut func = Func::new(names.intern("f"), Signature::new());
1122 let entry = func.create_block();
1123 let then_block = func.create_block();
1124 let else_block = func.create_block();
1125 let join = func.create_block();
1126 let mut build = Builder::new(&mut func, entry);
1127 let cond = cond(&mut build);
1128 build.br_if(cond, then_block, &[], else_block, &[]);
1129 let mut marks = Vec::new();
1130 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1131 let mut build = Builder::new(&mut func, arm);
1132 marks.push(build.iconst(Type::int(32), mark));
1133 build.jump(join, &[]);
1134 }
1135 let mut build = Builder::new(&mut func, join);
1136 build.ret(&[]);
1137 (func, [marks[0], marks[1]])
1138 }
1139
1140 #[test]
1141 fn a_branch_on_a_true_constant_becomes_a_jump_to_the_first_arm() {
1142 let (mut func, [taken, other]) = diamond(|build| build.iconst(Type::int(1), 1));
1143 let stats = simplify(&mut func);
1144 assert!(stats.changed());
1145 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1146 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1149 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1150 assert_eq!(lives_in(&func, taken), Some(0));
1151 assert_eq!(lives_in(&func, other), None);
1152 assert_eq!(blocks(&func), [0]);
1153 }
1154
1155 #[test]
1156 fn a_branch_on_a_false_constant_becomes_a_jump_to_the_second_arm() {
1157 let (mut func, [other, taken]) = diamond(|build| build.iconst(Type::int(1), 0));
1158 assert!(simplify(&mut func).changed());
1159 assert_eq!(lives_in(&func, taken), Some(0));
1160 assert_eq!(lives_in(&func, other), None);
1161 assert_eq!(blocks(&func), [0]);
1162 }
1163
1164 #[test]
1165 fn folding_a_branch_and_merging_what_it_leaves_are_two_things_fuel_buys_apart() {
1166 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1169 let stats =
1170 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1171 assert_eq!(terminator(&func, 0), Opcode::Jump);
1172 assert_eq!(goes_to(&func, 0), [1]);
1173 assert_eq!(blocks(&func), [0, 1, 3]);
1174 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
1175 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
1178 }
1179
1180 #[test]
1181 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
1182 let cases: &[(IntPred, i128, i128, bool)] = &[
1186 (IntPred::Eq, 7, 7, true),
1187 (IntPred::Eq, 7, 8, false),
1188 (IntPred::Ne, 7, 8, true),
1189 (IntPred::Ne, 7, 7, false),
1190 (IntPred::Slt, -1, 1, true),
1191 (IntPred::Slt, 1, -1, false),
1192 (IntPred::Sle, -1, -1, true),
1193 (IntPred::Sle, 1, -1, false),
1194 (IntPred::Sgt, 1, -1, true),
1195 (IntPred::Sgt, -1, 1, false),
1196 (IntPred::Sge, -1, -1, true),
1197 (IntPred::Sge, -1, 1, false),
1198 (IntPred::Ult, 1, -1, true),
1199 (IntPred::Ult, -1, 1, false),
1200 (IntPred::Ule, -1, -1, true),
1201 (IntPred::Ule, -1, 1, false),
1202 (IntPred::Ugt, -1, 1, true),
1203 (IntPred::Ugt, 1, -1, false),
1204 (IntPred::Uge, -1, -1, true),
1205 (IntPred::Uge, 1, -1, false),
1206 ];
1207 for &(pred, lhs, rhs, taken) in cases {
1208 let (mut func, marks) = diamond(|build| {
1209 let lhs = build.iconst(Type::int(32), lhs);
1210 let rhs = build.iconst(Type::int(32), rhs);
1211 build.icmp(pred, lhs, rhs)
1212 });
1213 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
1214 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
1215 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
1216 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
1217 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
1218 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
1219 }
1220 }
1221
1222 #[test]
1223 fn a_branch_on_something_nobody_knows_is_left_alone() {
1224 let mut names = Interner::new();
1225 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
1226 let entry = func.create_block();
1227 let then_block = func.create_block();
1228 let else_block = func.create_block();
1229 let cond = func.append_param(entry, Type::int(1));
1230 let mut build = Builder::new(&mut func, entry);
1231 build.br_if(cond, then_block, &[], else_block, &[]);
1232 for arm in [then_block, else_block] {
1233 let mut build = Builder::new(&mut func, arm);
1234 build.ret(&[]);
1235 }
1236 let stats = simplify(&mut func);
1237 assert!(!stats.changed());
1238 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1239 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1240 assert_eq!(blocks(&func), [0, 1, 2]);
1241 }
1242
1243 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1246 let mut names = Interner::new();
1247 let mut func = Func::new(names.intern("f"), Signature::new());
1248 let entry = func.create_block();
1249 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1250 let mut build = Builder::new(&mut func, entry);
1251 let value = build.iconst(Type::int(32), on);
1252 let pairs: Vec<(i128, Block)> =
1253 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1254 build.switch(value, arms[0], &pairs);
1255 let mut marks = Vec::new();
1256 for (at, &arm) in arms.iter().enumerate() {
1257 let mut build = Builder::new(&mut func, arm);
1258 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1259 build.ret(&[]);
1260 }
1261 (func, marks)
1262 }
1263
1264 #[test]
1265 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1266 let (mut func, marks) = switched(5, &[4, 5]);
1267 assert!(simplify(&mut func).changed());
1268 assert_eq!(lives_in(&func, marks[2]), Some(0));
1269 assert_eq!(lives_in(&func, marks[0]), None);
1270 assert_eq!(lives_in(&func, marks[1]), None);
1271 assert_eq!(blocks(&func), [0]);
1272 }
1273
1274 #[test]
1275 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1276 let (mut func, marks) = switched(9, &[4]);
1277 assert!(simplify(&mut func).changed());
1278 assert_eq!(lives_in(&func, marks[0]), Some(0));
1279 assert_eq!(lives_in(&func, marks[1]), None);
1280 assert_eq!(blocks(&func), [0]);
1281 }
1282
1283 #[test]
1284 fn the_arguments_travel_with_the_edge_that_survives() {
1285 let mut names = Interner::new();
1291 let mut func = Func::new(names.intern("f"), Signature::new());
1292 let entry = func.create_block();
1293 let join = func.create_block();
1294 let param = func.append_param(join, Type::int(32));
1295 let mut build = Builder::new(&mut func, entry);
1296 let cond = build.iconst(Type::int(1), 0);
1297 let taken = build.iconst(Type::int(32), 11);
1298 let other = build.iconst(Type::int(32), 22);
1299 build.br_if(cond, join, &[other], join, &[taken]);
1300 let mut build = Builder::new(&mut func, join);
1301 build.ret(&[param]);
1302 assert!(simplify(&mut func).changed());
1303 assert_eq!(blocks(&func), [0]);
1307 let term = func.terminator(entry).expect("the entry has one");
1308 assert_eq!(func[func[term].args], [taken]);
1309 assert_ne!(func[func[term].args], [param]);
1310 }
1311
1312 #[test]
1313 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1314 let mut names = Interner::new();
1318 let signature = Signature::new().with_params(&[Type::int(1)]);
1319 let mut func = Func::new(names.intern("f"), signature);
1320 let entry = func.create_block();
1321 let join = func.create_block();
1322 let cond = func.append_param(entry, Type::int(1));
1323 let mut build = Builder::new(&mut func, entry);
1324 build.br_if(cond, join, &[], join, &[]);
1325 let mut build = Builder::new(&mut func, join);
1326 build.ret(&[]);
1327 let stats = simplify(&mut func);
1328 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1329 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1330 assert_eq!(blocks(&func), [0]);
1331 assert_eq!(terminator(&func, 0), Opcode::Return);
1332 }
1333
1334 #[test]
1335 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1336 let mut names = Interner::new();
1337 let signature = Signature::new().with_params(&[Type::int(32)]);
1338 let mut func = Func::new(names.intern("f"), signature);
1339 let entry = func.create_block();
1340 let join = func.create_block();
1341 let value = func.append_param(entry, Type::int(32));
1342 let mut build = Builder::new(&mut func, entry);
1343 build.switch(value, join, &[(4, join), (5, join)]);
1344 let mut build = Builder::new(&mut func, join);
1345 build.ret(&[]);
1346 let stats = simplify(&mut func);
1347 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1348 assert_eq!(blocks(&func), [0]);
1349 }
1350
1351 #[test]
1352 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1353 let mut names = Interner::new();
1356 let signature = Signature::new().with_params(&[Type::int(1)]);
1357 let mut func = Func::new(names.intern("f"), signature);
1358 let entry = func.create_block();
1359 let join = func.create_block();
1360 let cond = func.append_param(entry, Type::int(1));
1361 let param = func.append_param(join, Type::int(32));
1362 let mut build = Builder::new(&mut func, entry);
1363 let first = build.iconst(Type::int(32), 11);
1364 let second = build.iconst(Type::int(32), 22);
1365 build.br_if(cond, join, &[first], join, &[second]);
1366 let mut build = Builder::new(&mut func, join);
1367 build.ret(&[param]);
1370 let stats = simplify(&mut func);
1371 assert!(!stats.changed());
1372 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1373 assert_eq!(blocks(&func), [0, 1]);
1374 }
1375
1376 #[test]
1377 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1378 let mut names = Interner::new();
1381 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1382 let entry = func.create_block();
1383 let dead = func.create_block();
1384 let shared = func.create_block();
1385 let exit = func.create_block();
1386 let x = func.append_param(entry, Type::int(32));
1387 let mut build = Builder::new(&mut func, entry);
1388 let never = build.iconst(Type::int(1), 0);
1389 build.switch(x, exit, &[(0, dead), (1, shared)]);
1390 let mut build = Builder::new(&mut func, dead);
1394 build.iconst(Type::int(32), 1);
1395 build.br_if(never, shared, &[], exit, &[]);
1396 for arm in [shared, exit] {
1397 let mut build = Builder::new(&mut func, arm);
1398 build.ret(&[]);
1399 }
1400 let stats = simplify(&mut func);
1401 assert!(stats.changed());
1402 assert_eq!(terminator(&func, 0), Opcode::Switch);
1405 assert_eq!(goes_to(&func, 1), [3]);
1406 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1407 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1408 }
1409
1410 #[test]
1411 fn a_block_whose_address_is_taken_is_not_removed() {
1412 let mut names = Interner::new();
1416 let mut func = Func::new(names.intern("f"), Signature::new());
1417 let entry = func.create_block();
1418 let labelled = func.create_block();
1419 let arm = func.create_block();
1420 let mut build = Builder::new(&mut func, entry);
1421 let cond = build.iconst(Type::int(1), 1);
1422 let addr = build.block_addr(labelled);
1423 build.br_if(cond, arm, &[], labelled, &[]);
1424 let mut build = Builder::new(&mut func, arm);
1425 build.indirect_br(addr, &[labelled]);
1426 let mut build = Builder::new(&mut func, labelled);
1427 build.ret(&[]);
1428 assert!(simplify(&mut func).changed());
1429 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1430 assert_eq!(blocks(&func), [0, 1]);
1433 assert_eq!(goes_to(&func, 0), [1]);
1434 }
1435
1436 #[test]
1437 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1438 let mut names = Interner::new();
1441 let mut func = Func::new(names.intern("f"), Signature::new());
1442 let entry = func.create_block();
1443 let dead = func.create_block();
1444 let labelled = func.create_block();
1445 let mut build = Builder::new(&mut func, entry);
1446 let cond = build.iconst(Type::int(1), 1);
1447 build.br_if(cond, entry, &[], dead, &[]);
1448 let mut build = Builder::new(&mut func, dead);
1449 let addr = build.block_addr(labelled);
1450 build.indirect_br(addr, &[labelled]);
1451 let mut build = Builder::new(&mut func, labelled);
1452 build.ret(&[]);
1453 assert!(simplify(&mut func).changed());
1454 assert_eq!(blocks(&func), [0]);
1455 }
1456
1457 #[test]
1458 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1459 let mut func = graph(&[&[], &[]]);
1464 let stats = simplify(&mut func);
1465 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1466 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1467 assert_eq!(blocks(&func), [0]);
1468 }
1469
1470 #[test]
1471 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1472 let mut names = Interner::new();
1475 let mut func = Func::new(names.intern("f"), Signature::new());
1476 let entry = func.create_block();
1477 let middle = func.create_block();
1478 let last = func.create_block();
1479 let mut build = Builder::new(&mut func, entry);
1480 build.iconst(Type::int(32), 1);
1481 build.jump(middle, &[]);
1482 let mut build = Builder::new(&mut func, middle);
1483 build.iconst(Type::int(32), 2);
1484 build.jump(last, &[]);
1485 let mut build = Builder::new(&mut func, last);
1486 build.ret(&[]);
1487 let stats = simplify(&mut func);
1488 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1491 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1492 assert_eq!(blocks(&func), [0]);
1493 assert_eq!(terminator(&func, 0), Opcode::Return);
1494 }
1495
1496 #[test]
1497 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1498 let mut names = Interner::new();
1501 let signature = Signature::new().with_params(&[Type::int(1)]);
1502 let mut func = Func::new(names.intern("f"), signature);
1503 let entry = func.create_block();
1504 let then_block = func.create_block();
1505 let else_block = func.create_block();
1506 let join = func.create_block();
1507 let cond = func.append_param(entry, Type::int(1));
1508 let mut build = Builder::new(&mut func, entry);
1509 build.br_if(cond, then_block, &[], else_block, &[]);
1510 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1511 let mut build = Builder::new(&mut func, arm);
1514 build.iconst(Type::int(32), mark);
1515 build.jump(join, &[]);
1516 }
1517 let mut build = Builder::new(&mut func, join);
1518 build.ret(&[]);
1519 let stats = simplify(&mut func);
1520 assert!(!stats.changed());
1521 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1522 }
1523
1524 #[test]
1525 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1526 let mut names = Interner::new();
1529 let signature = Signature::new().with_params(&[Type::int(1)]);
1530 let mut func = Func::new(names.intern("f"), signature);
1531 let entry = func.create_block();
1532 let arm = func.create_block();
1533 let exit = func.create_block();
1534 let cond = func.append_param(entry, Type::int(1));
1535 let mut build = Builder::new(&mut func, entry);
1536 build.br_if(cond, arm, &[], exit, &[]);
1537 for block in [arm, exit] {
1538 let mut build = Builder::new(&mut func, block);
1539 build.ret(&[]);
1540 }
1541 let stats = simplify(&mut func);
1542 assert!(!stats.changed());
1543 assert_eq!(blocks(&func), [0, 1, 2]);
1544 }
1545
1546 #[test]
1547 fn the_entry_block_is_never_the_one_that_moves() {
1548 let mut names = Interner::new();
1552 let signature = Signature::new().with_params(&[Type::int(1)]);
1553 let mut func = Func::new(names.intern("f"), signature);
1554 let entry = func.create_block();
1555 let latch = func.create_block();
1556 let exit = func.create_block();
1557 let cond = func.append_param(entry, Type::int(1));
1558 let mut build = Builder::new(&mut func, entry);
1559 build.br_if(cond, latch, &[], exit, &[]);
1560 let mut build = Builder::new(&mut func, latch);
1562 build.iconst(Type::int(32), 1);
1563 build.jump(entry, &[]);
1564 let mut build = Builder::new(&mut func, exit);
1565 build.ret(&[]);
1566 let stats = simplify(&mut func);
1567 assert!(!stats.changed());
1568 assert_eq!(blocks(&func), [0, 1, 2]);
1569 }
1570
1571 #[test]
1572 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1573 let mut names = Interner::new();
1576 let mut func = Func::new(names.intern("f"), Signature::new());
1577 let entry = func.create_block();
1578 let middle = func.create_block();
1579 let labelled = func.create_block();
1580 let mut build = Builder::new(&mut func, entry);
1581 build.block_addr(labelled);
1582 build.jump(middle, &[]);
1583 let mut build = Builder::new(&mut func, middle);
1586 build.iconst(Type::int(32), 1);
1587 build.jump(labelled, &[]);
1588 let mut build = Builder::new(&mut func, labelled);
1589 build.ret(&[]);
1590 let stats = simplify(&mut func);
1591 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1594 assert_eq!(blocks(&func), [0, 2]);
1595 }
1596
1597 #[test]
1598 fn a_block_an_image_names_is_not_merged_away_either() {
1599 let mut names = Interner::new();
1603 let mut func = Func::new(names.intern("f"), Signature::new());
1604 let entry = func.create_block();
1605 let middle = func.create_block();
1606 let labelled = func.create_block();
1607 Builder::new(&mut func, entry).jump(middle, &[]);
1608 let mut build = Builder::new(&mut func, middle);
1609 build.iconst(Type::int(32), 1);
1610 build.jump(labelled, &[]);
1611 Builder::new(&mut func, labelled).ret(&[]);
1612 func.name_block(labelled, names.intern(".Llbl.0"));
1613 let stats = simplify(&mut func);
1614 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1615 assert_eq!(blocks(&func), [0, 2]);
1616 assert_eq!(func.block_name(labelled), Some(names.intern(".Llbl.0")));
1617 }
1618
1619 #[test]
1620 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1621 let mut names = Interner::new();
1622 let mut func = Func::new(names.intern("f"), Signature::new());
1623 let entry = func.create_block();
1624 let below = func.create_block();
1625 let param = func.append_param(below, Type::int(32));
1626 let mut build = Builder::new(&mut func, entry);
1627 let arg = build.iconst(Type::int(32), 7);
1628 build.jump(below, &[arg]);
1629 let mut build = Builder::new(&mut func, below);
1630 build.ret(&[param]);
1631 assert!(simplify(&mut func).changed());
1632 assert_eq!(blocks(&func), [0]);
1633 let term = func.terminator(entry).expect("the entry has one");
1634 assert_eq!(func[func[term].args], [arg]);
1635 }
1636
1637 #[test]
1638 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1639 let mut names = Interner::new();
1643 let mut func = Func::new(names.intern("f"), Signature::new());
1644 let entry = func.create_block();
1645 let middle = func.create_block();
1646 let last = func.create_block();
1647 let carried = func.append_param(middle, Type::int(32));
1648 let arrived = func.append_param(last, Type::int(32));
1649 let mut build = Builder::new(&mut func, entry);
1650 let arg = build.iconst(Type::int(32), 7);
1651 build.jump(middle, &[arg]);
1652 let mut build = Builder::new(&mut func, middle);
1653 build.jump(last, &[carried]);
1654 let mut build = Builder::new(&mut func, last);
1655 build.ret(&[arrived]);
1656 assert!(simplify(&mut func).changed());
1657 assert_eq!(blocks(&func), [0]);
1658 let term = func.terminator(entry).expect("the entry has one");
1659 assert_eq!(func[func[term].args], [arg]);
1660 }
1661
1662 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1670 let entry = func.create_block();
1671 let first = func.create_block();
1672 let second = func.create_block();
1673 let cond = func.append_param(entry, Type::int(1));
1674 let mut build = Builder::new(func, entry);
1675 let carried = build.iconst(Type::int(32), 7);
1676 build.br_if(cond, first, &[], second, &[]);
1677 for (arm, mark) in [(first, 111), (second, 222)] {
1678 let mut build = Builder::new(func, arm);
1679 build.iconst(Type::int(32), mark);
1680 }
1681 (carried, [first, second])
1682 }
1683
1684 fn taking_a_condition() -> Func {
1686 let mut names = Interner::new();
1687 let signature = Signature::new().with_params(&[Type::int(1)]);
1688 Func::new(names.intern("f"), signature)
1689 }
1690
1691 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1693 let block = Block::from_usize(block);
1694 let term = func.terminator(block).expect("every block here has one");
1695 let call = func.successors(term).nth(edge).expect("the edge is there");
1696 func[call.args].to_vec()
1697 }
1698
1699 #[test]
1700 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1701 let mut func = taking_a_condition();
1704 let (_, arms) = arms(&mut func);
1705 let forwarder = func.create_block();
1706 let exit = func.create_block();
1707 for arm in arms {
1708 Builder::new(&mut func, arm).jump(forwarder, &[]);
1709 }
1710 Builder::new(&mut func, forwarder).jump(exit, &[]);
1711 Builder::new(&mut func, exit).ret(&[]);
1712 let stats = simplify(&mut func);
1713 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1714 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1715 assert_eq!(goes_to(&func, 1), [4]);
1716 assert_eq!(goes_to(&func, 2), [4]);
1717 }
1718
1719 #[test]
1720 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1721 let mut func = taking_a_condition();
1728 let (carried, [arm, above]) = arms(&mut func);
1729 let forwarder = func.create_block();
1730 let exit = func.create_block();
1731 let other = func.append_param(exit, Type::int(32));
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, above).jump(forwarder, &[]);
1736 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1737 Builder::new(&mut func, exit).ret(&[other]);
1738 let stats = simplify(&mut func);
1739 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1740 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1741 assert_eq!(carries(&func, 2, 0), [carried]);
1744 assert_eq!(carries(&func, 1, 0), [mine]);
1745 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1747 }
1748
1749 #[test]
1750 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1751 let mut func = taking_a_condition();
1756 let (carried, [arm, forwarder]) = arms(&mut func);
1757 let exit = func.create_block();
1758 let other = func.append_param(exit, Type::int(32));
1759 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1761 func.remove_inst(inst);
1762 }
1763 let mut build = Builder::new(&mut func, arm);
1764 let mine = build.iconst(Type::int(32), 9);
1765 build.jump(exit, &[mine]);
1766 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1767 Builder::new(&mut func, exit).ret(&[other]);
1768 let stats = simplify(&mut func);
1769 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1770 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1771 }
1772
1773 #[test]
1774 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1775 let mut func = taking_a_condition();
1778 let (_, [arm, forwarder]) = arms(&mut func);
1779 let exit = func.create_block();
1780 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1781 func.remove_inst(inst);
1782 }
1783 Builder::new(&mut func, arm).jump(exit, &[]);
1784 Builder::new(&mut func, forwarder).jump(exit, &[]);
1785 Builder::new(&mut func, exit).ret(&[]);
1786 let stats = simplify(&mut func);
1787 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1788 assert_eq!(blocks(&func), [0, 1, 3]);
1789 }
1790
1791 #[test]
1792 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1793 let mut names = Interner::new();
1796 let mut func = Func::new(names.intern("f"), Signature::new());
1797 let entry = func.create_block();
1798 let spin = func.create_block();
1799 Builder::new(&mut func, entry).jump(spin, &[]);
1800 Builder::new(&mut func, spin).jump(spin, &[]);
1801 let stats = simplify(&mut func);
1802 assert!(!stats.changed());
1803 assert_eq!(blocks(&func), [0, 1]);
1804 }
1805
1806 #[test]
1807 fn the_entry_block_is_never_the_forwarder_that_goes() {
1808 let mut names = Interner::new();
1812 let mut func = Func::new(names.intern("f"), Signature::new());
1813 let entry = func.create_block();
1814 let below = func.create_block();
1815 Builder::new(&mut func, entry).jump(below, &[]);
1816 let mut build = Builder::new(&mut func, below);
1817 build.iconst(Type::int(32), 1);
1818 build.ret(&[]);
1819 let stats = simplify(&mut func);
1820 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1821 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1822 assert_eq!(blocks(&func), [0]);
1823 }
1824
1825 #[test]
1826 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1827 let mut names = Interner::new();
1831 let mut func = Func::new(names.intern("f"), Signature::new());
1832 let entry = func.create_block();
1833 let labelled = func.create_block();
1834 let exit = func.create_block();
1835 let mut build = Builder::new(&mut func, entry);
1836 let addr = build.block_addr(labelled);
1837 build.indirect_br(addr, &[labelled]);
1838 Builder::new(&mut func, labelled).jump(exit, &[]);
1839 let mut build = Builder::new(&mut func, exit);
1840 build.iconst(Type::int(32), 1);
1841 build.ret(&[]);
1842 let stats = simplify(&mut func);
1843 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1844 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1845 }
1846
1847 #[test]
1848 fn a_run_of_forwarders_comes_out_as_one_edge() {
1849 let mut func = taking_a_condition();
1850 let (_, arms) = arms(&mut func);
1851 let first = func.create_block();
1852 let second = func.create_block();
1853 let exit = func.create_block();
1854 for arm in arms {
1855 Builder::new(&mut func, arm).jump(first, &[]);
1856 }
1857 Builder::new(&mut func, first).jump(second, &[]);
1858 Builder::new(&mut func, second).jump(exit, &[]);
1859 Builder::new(&mut func, exit).ret(&[]);
1860 let stats = simplify(&mut func);
1861 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1862 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1863 assert_eq!(goes_to(&func, 1), [5]);
1864 assert_eq!(goes_to(&func, 2), [5]);
1865 }
1866
1867 #[test]
1868 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1869 let mut func = taking_a_condition();
1872 let (carried, arms) = arms(&mut func);
1873 let join = func.create_block();
1874 let param = func.append_param(join, Type::int(32));
1875 for arm in arms {
1876 Builder::new(&mut func, arm).jump(join, &[carried]);
1877 }
1878 Builder::new(&mut func, join).ret(&[param]);
1879 let stats = simplify(&mut func);
1880 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1881 assert!(func[Block::from_usize(3)].params.is_empty());
1882 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1884 assert_eq!(func[func[term].args], [carried]);
1885 assert!(carries(&func, 1, 0).is_empty());
1888 assert!(carries(&func, 2, 0).is_empty());
1889 }
1890
1891 #[test]
1892 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1893 let mut func = taking_a_condition();
1894 let (carried, arms) = arms(&mut func);
1895 let join = func.create_block();
1896 let param = func.append_param(join, Type::int(32));
1897 let mut build = Builder::new(&mut func, arms[0]);
1898 let mine = build.iconst(Type::int(32), 9);
1899 build.jump(join, &[mine]);
1900 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1901 Builder::new(&mut func, join).ret(&[param]);
1902 let stats = simplify(&mut func);
1903 assert!(!stats.changed());
1904 assert_eq!(func[Block::from_usize(3)].params, [param]);
1905 }
1906
1907 #[test]
1908 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1909 let mut names = Interner::new();
1913 let signature = Signature::new().with_params(&[Type::int(1)]);
1914 let mut func = Func::new(names.intern("f"), signature);
1915 let entry = func.create_block();
1916 let header = func.create_block();
1917 let latch = func.create_block();
1918 let exit = func.create_block();
1919 let cond = func.append_param(entry, Type::int(1));
1920 let param = func.append_param(header, Type::int(32));
1921 let mut build = Builder::new(&mut func, entry);
1922 let init = build.iconst(Type::int(32), 7);
1923 build.jump(header, &[init]);
1924 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1925 let mut build = Builder::new(&mut func, latch);
1926 build.iconst(Type::int(32), 1);
1927 build.jump(header, &[param]);
1928 Builder::new(&mut func, exit).ret(&[param]);
1929 let stats = simplify(&mut func);
1930 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1931 assert!(func[Block::from_usize(1)].params.is_empty());
1932 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1933 assert_eq!(func[func[term].args], [init]);
1934 }
1935
1936 #[test]
1937 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1938 let mut names = Interner::new();
1942 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1943 let mut func = Func::new(names.intern("f"), signature);
1944 let entry = func.create_block();
1945 let latch = func.create_block();
1946 let exit = func.create_block();
1947 let cond = func.append_param(entry, Type::int(1));
1948 let x = func.append_param(entry, Type::int(32));
1949 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1950 let mut build = Builder::new(&mut func, latch);
1951 let one = build.iconst(Type::int(1), 1);
1952 let seven = build.iconst(Type::int(32), 7);
1953 build.jump(entry, &[one, seven]);
1954 Builder::new(&mut func, exit).ret(&[x]);
1955 let stats = simplify(&mut func);
1956 assert!(!stats.changed());
1957 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1958 }
1959
1960 #[test]
1961 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1962 let mut func = taking_a_condition();
1966 let (carried, arms) = arms(&mut func);
1967 let join = func.create_block();
1968 let inner = func.append_param(join, Type::int(32));
1969 let left = func.create_block();
1970 let right = func.create_block();
1971 let last = func.create_block();
1972 let outer = func.append_param(last, Type::int(32));
1973 for arm in arms {
1974 Builder::new(&mut func, arm).jump(join, &[carried]);
1975 }
1976 let cond = func[Block::from_usize(0)].params[0];
1977 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1978 let mut build = Builder::new(&mut func, left);
1979 build.iconst(Type::int(32), 1);
1980 build.jump(last, &[inner]);
1981 let mut build = Builder::new(&mut func, right);
1982 build.iconst(Type::int(32), 2);
1983 build.jump(last, &[carried]);
1984 Builder::new(&mut func, last).ret(&[outer]);
1985 let stats = simplify(&mut func);
1986 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1987 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1988 assert_eq!(func[func[term].args], [carried]);
1989 }
1990
1991 #[test]
1992 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1993 let mut func = taking_a_condition();
1997 let (carried, arms) = arms(&mut func);
1998 let forwarder = func.create_block();
1999 let param = func.append_param(forwarder, Type::int(32));
2000 let exit = func.create_block();
2001 let arrived = func.append_param(exit, Type::int(32));
2002 for arm in arms {
2003 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
2004 }
2005 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2006 Builder::new(&mut func, exit).ret(&[arrived]);
2007 let stats = simplify(&mut func);
2008 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
2009 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
2012 assert_eq!(blocks(&func), [0, 1, 2, 4]);
2013 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
2014 assert_eq!(func[func[term].args], [carried]);
2015 }
2016
2017 #[test]
2018 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
2019 let mut func = taking_a_condition();
2022 let (carried, arms) = arms(&mut func);
2023 let forwarder = func.create_block();
2024 let param = func.append_param(forwarder, Type::int(32));
2025 let exit = func.create_block();
2026 let arrived = func.append_param(exit, Type::int(32));
2029 for arm in arms {
2030 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
2031 }
2032 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2033 Builder::new(&mut func, exit).ret(&[arrived]);
2034 let stats =
2035 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2036 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
2037 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
2038 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
2039 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
2040 }
2041
2042 fn walking_a_pointer(on_counter: bool) -> Func {
2051 let mut names = Interner::new();
2052 let signature = Signature::new().with_params(&[Type::int(64)]);
2053 let mut func = Func::new(names.intern("f"), signature);
2054 let entry = func.create_block();
2055 let head = func.create_block();
2056 let out = func.create_block();
2057 let end = func.append_param(entry, Type::int(64));
2058 let counter = func.append_param(head, Type::int(32));
2059 let pointer = func.append_param(head, Type::int(64));
2060 let mut build = Builder::new(&mut func, entry);
2061 let from_zero = build.iconst(Type::int(32), 0);
2062 let from_start = build.iconst(Type::int(64), 0);
2063 build.jump(head, &[from_zero, from_start]);
2064 let mut build = Builder::new(&mut func, head);
2065 let one = build.iconst(Type::int(32), 1);
2066 let eight = build.iconst(Type::int(64), 8);
2067 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2068 let along = build.binary(Opcode::Add, pointer, eight, Flags::NONE);
2069 let address = build.unary(Opcode::IntToPtr, pointer, Type::PTR);
2072 let info = MemInfo {
2073 size: 8,
2074 align: 8,
2075 order: MemOrder::NotAtomic,
2076 tbaa: None,
2077 owns: 0,
2078 restrict: Restrict::NONE,
2079 };
2080 build.store(eight, address, info, Flags::NONE);
2081 let going = if on_counter {
2082 let limit = build.iconst(Type::int(32), 10);
2083 build.icmp(IntPred::Ne, next, limit)
2084 } else {
2085 build.icmp(IntPred::Ne, along, end)
2086 };
2087 build.br_if(going, head, &[next, along], out, &[]);
2088 Builder::new(&mut func, out).ret(&[]);
2089 func
2090 }
2091
2092 #[test]
2093 fn a_counter_the_loop_stopped_asking_about_stops_going_round() {
2094 let mut func = walking_a_pointer(false);
2095 let stats = simplify(&mut func);
2096 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2097 assert_eq!(func[Block::from_usize(1)].params.len(), 1);
2099 assert_eq!(carries(&func, 1, 0).len(), 1);
2101 assert_eq!(carries(&func, 0, 0).len(), 1);
2102 }
2103
2104 #[test]
2105 fn a_counter_the_loop_still_asks_about_goes_round_exactly_as_before() {
2106 let mut func = walking_a_pointer(true);
2107 let stats = simplify(&mut func);
2108 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2109 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2110 }
2111
2112 fn counting_into_nothing() -> (Func, Value, Value) {
2119 let mut names = Interner::new();
2120 let signature = Signature::new().with_params(&[Type::int(32)]);
2121 let mut func = Func::new(names.intern("f"), signature);
2122 let entry = func.create_block();
2123 let head = func.create_block();
2124 let out = func.create_block();
2125 let limit = func.append_param(entry, Type::int(32));
2126 let counter = func.append_param(head, Type::int(32));
2127 let mut build = Builder::new(&mut func, entry);
2128 let zero = build.iconst(Type::int(32), 0);
2129 build.jump(head, &[zero]);
2130 let mut build = Builder::new(&mut func, head);
2131 let one = build.iconst(Type::int(32), 1);
2132 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2133 let twice = build.binary(Opcode::Add, next, next, Flags::NONE);
2134 let going = build.icmp(IntPred::Ne, limit, one);
2135 build.br_if(going, head, &[next], out, &[]);
2136 Builder::new(&mut func, out).ret(&[]);
2137 (func, next, twice)
2138 }
2139
2140 #[test]
2141 fn what_was_reading_a_parameter_nothing_reads_goes_with_it() {
2142 let (mut func, next, twice) = counting_into_nothing();
2143 let stats = simplify(&mut func);
2144 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2145 assert_eq!(lives_in(&func, next), None);
2149 assert_eq!(lives_in(&func, twice), None);
2150 }
2151
2152 #[test]
2153 fn the_functions_own_parameters_stay_whether_or_not_anything_reads_them() {
2154 let mut names = Interner::new();
2157 let signature = Signature::new().with_params(&[Type::int(32)]);
2158 let mut func = Func::new(names.intern("f"), signature);
2159 let entry = func.create_block();
2160 func.append_param(entry, Type::int(32));
2161 Builder::new(&mut func, entry).ret(&[]);
2162 let stats = simplify(&mut func);
2163 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2164 assert_eq!(func[entry].params.len(), 1);
2165 }
2166
2167 #[test]
2168 fn a_parameter_nothing_reads_costs_one_unit_of_fuel_and_stays_without_it() {
2169 let mut func = walking_a_pointer(false);
2170 let stats =
2171 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2172 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2173 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_UNREAD), 1);
2174 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2175 }
2176
2177 #[test]
2178 fn the_counter_that_went_leaves_the_verifier_nothing_to_complain_about() {
2179 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2180 let mut names = Interner::new();
2181 let mut module = Module::new(names.intern("test.c"), &target);
2182 let mut func = walking_a_pointer(false);
2183 simplify(&mut func);
2184 module.add_func(func);
2185 rucc_ir::verify(&module, &names).expect("taking a parameter out left the function whole");
2186 }
2187
2188 #[test]
2189 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
2190 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2193 let mut names = Interner::new();
2194 let mut module = Module::new(names.intern("test.c"), &target);
2195 let mut func = taking_a_condition();
2196 let (carried, arms) = arms(&mut func);
2197 let forwarder = func.create_block();
2198 let param = func.append_param(forwarder, Type::int(32));
2199 let exit = func.create_block();
2200 let arrived = func.append_param(exit, Type::int(32));
2201 let mut build = Builder::new(&mut func, arms[0]);
2202 let mine = build.iconst(Type::int(32), 9);
2203 build.jump(exit, &[mine]);
2204 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
2205 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2206 let mut build = Builder::new(&mut func, exit);
2207 build.icmp(IntPred::Eq, arrived, arrived);
2210 build.ret(&[]);
2211 simplify(&mut func);
2212 module.add_func(func);
2213 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
2214 }
2215
2216 #[test]
2217 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
2218 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
2219 let before = blocks(&func);
2220 let stats =
2221 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2222 assert!(!stats.changed());
2223 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2224 assert_eq!(terminator(&func, 0), Opcode::BrIf);
2225 assert_eq!(blocks(&func), before);
2226 }
2227
2228 #[test]
2229 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
2230 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
2234 let stats =
2235 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2236 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
2237 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2238 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
2241 }
2242
2243 #[test]
2244 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2245 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2246 let mut names = Interner::new();
2247 let mut module = Module::new(names.intern("test.c"), &target);
2248 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
2249 simplify(&mut func);
2250 module.add_func(func);
2251 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2252 }
2253
2254 #[test]
2255 fn the_pass_says_it_preserves_nothing() {
2256 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
2257 }
2258}