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::copy::addressed;
157use crate::fold::constant;
158use crate::{Analyses, Fuel, Pass, Preserved, Stats, uses};
159
160const FOLDED: &str = "branch on a condition that is always the same way replaced by a jump";
162
163pub(crate) const REMOVED: &str = "block nothing reaches removed";
165
166const MERGED: &str = "block with one way into it merged into the block above it";
168
169const FORWARDED: &str = "block that only jumped somewhere else removed and its edges pointed past";
171
172const SAME_EVERY_WAY: &str = "block parameter that arrives as the same value every way in removed";
174
175const NO_FUEL: &str = "branch on a known condition left alone, the pass ran out of fuel";
177
178const NO_FUEL_MERGE: &str = "block with one way into it left alone, the pass ran out of fuel";
180
181const NO_FUEL_FORWARD: &str =
183 "block that only jumped somewhere else kept, the pass ran out of fuel";
184
185const NO_FUEL_PARAM: &str = "block parameter that is one value kept, the pass ran out of fuel";
187
188const NOTHING_READS_IT: &str = "block parameter nothing reads removed, and the argument on every \
190 edge that was feeding it";
191
192const NO_FUEL_UNREAD: &str = "block parameter nothing reads kept, the pass ran out of fuel";
194
195#[derive(Debug, Clone, Copy, PartialEq, Eq)]
197pub struct SimplifyCfg;
198
199impl Pass for SimplifyCfg {
200 fn name(&self) -> &'static str {
201 "simplify-cfg"
202 }
203
204 fn describe(&self) -> &'static str {
205 "unreachable blocks go, a branch that only goes one way becomes a jump, a block that only \
206 jumps stops being in the way, and a block with one way in is merged into the one above it"
207 }
208
209 fn preserves(&self) -> Preserved {
210 Preserved::NONE
213 }
214
215 fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
216 let mut stats = Stats::new();
217 sweep(func, an, &mut stats);
221 let mut folded = false;
222 let unbound = Bindings::new();
226 for block in func.blocks().collect::<Vec<Block>>() {
227 let Some(term) = func.terminator(block) else { continue };
228 let Some(taken) = taken(func, term, &unbound) else { continue };
229 if !fuel.take() {
230 stats.missed(NO_FUEL);
233 continue;
234 }
235 jump_to(func, term, taken);
236 stats.optimized(FOLDED);
237 folded = true;
238 }
239 if folded {
240 an.clear();
244 sweep(func, an, &mut stats);
245 }
246 let mut forward = HashMap::new();
247 let dropped = drop_unread(func, fuel, &mut stats);
253 if straighten(func, fuel, &mut stats, &mut forward) || dropped {
254 an.clear();
255 }
256 for chain in chains(func, an) {
260 for (at, &block) in chain.iter().enumerate().skip(1) {
261 if !fuel.take() {
262 for _ in at..chain.len() {
266 stats.missed(NO_FUEL_MERGE);
267 }
268 break;
269 }
270 merge(func, chain[0], block, &mut forward);
271 stats.optimized(MERGED);
272 }
273 }
274 if !forward.is_empty() {
275 uses::substitute(func, &forward);
278 }
279 stats
280 }
281}
282
283pub(crate) type Bindings = HashMap<Value, Value>;
290
291fn resolve(subst: &Bindings, value: Value) -> Value {
293 subst.get(&value).copied().unwrap_or(value)
294}
295
296pub(crate) fn taken(func: &Func, term: Inst, subst: &Bindings) -> Option<BlockCall> {
305 let data = &func[term];
306 let arg = *func[data.args].first()?;
307 match data.opcode {
308 Opcode::BrIf => {
309 let Extra::Targets(targets) = data.extra else { return None };
310 if let Some(call) = one_place(func, &func[targets]) {
311 return Some(call);
312 }
313 let arm = usize::from(!known(func, arg, subst)?);
316 func[targets].get(arm).copied()
317 }
318 Opcode::Switch => {
319 let Extra::Switch(at) = data.extra else { return None };
320 let info = func[at];
321 if let Some(call) = one_place(func, &func[info.targets]) {
322 return Some(call);
323 }
324 let (value, _) = constant(func, resolve(subst, arg))?;
325 let case = func[info.cases].iter().position(|it| *it == value);
328 func[info.targets].get(case.map_or(0, |case| case + 1)).copied()
329 }
330 _ => None,
331 }
332}
333
334fn one_place(func: &Func, calls: &[BlockCall]) -> Option<BlockCall> {
346 let &first = calls.first()?;
347 let same = |call: &BlockCall| call.block == first.block && func[call.args] == func[first.args];
348 calls[1..].iter().all(same).then_some(first)
349}
350
351pub(crate) fn jump_to(func: &mut Func, term: Inst, call: BlockCall) {
356 let targets = func.push_block_calls(&[call]);
357 let args = func.push_values(&[]);
358 let data = &mut func[term];
359 data.opcode = Opcode::Jump;
360 data.args = args;
361 data.extra = Extra::Targets(targets);
362}
363
364pub(crate) fn sweep(func: &mut Func, an: &mut Analyses, stats: &mut Stats) {
373 let gone = stranded(func, an);
374 if gone.is_empty() {
375 return;
376 }
377 for block in gone {
378 func.remove_block(block);
379 stats.optimized(REMOVED);
380 }
381 an.clear();
382}
383
384fn stranded(func: &Func, an: &mut Analyses) -> Vec<Block> {
393 let cfg = an.cfg(func);
394 let Some(entry) = cfg.entry() else { return Vec::new() };
395 let mut seen = vec![false; cfg.capacity()];
396 seen[entry.index()] = true;
397 let mut stack = vec![entry];
398 for (block, _) in func.named_blocks() {
402 if !seen[block.index()] {
403 seen[block.index()] = true;
404 stack.push(block);
405 }
406 }
407 let mut reached = Vec::new();
408 while let Some(block) = stack.pop() {
409 for &succ in cfg.successors(block) {
410 if !seen[succ.index()] {
411 seen[succ.index()] = true;
412 stack.push(succ);
413 }
414 }
415 reached.push(block);
416 }
417 let mut next = reached;
420 while !next.is_empty() {
421 let mut found = Vec::new();
422 for block in next {
423 for inst in func.insts(block) {
424 if func[inst].opcode != Opcode::BlockAddr {
425 continue;
426 }
427 for call in func.successors(inst) {
428 if !seen[call.block.index()] {
429 seen[call.block.index()] = true;
430 found.push(call.block);
431 }
432 }
433 }
434 }
435 let mut stack = found.clone();
438 while let Some(block) = stack.pop() {
439 for &succ in cfg.successors(block) {
440 if !seen[succ.index()] {
441 seen[succ.index()] = true;
442 stack.push(succ);
443 found.push(succ);
444 }
445 }
446 }
447 next = found;
448 }
449 func.blocks().filter(|block| !seen[block.index()]).collect()
450}
451
452pub(crate) type Edges = HashMap<Block, Vec<(Block, Idx<BlockCall>)>>;
459
460pub(crate) fn incoming(func: &Func) -> Edges {
468 let mut edges: Edges = HashMap::new();
469 for block in func.blocks() {
470 let Some(term) = func.terminator(block) else { continue };
471 for at in func.target_list(term).iter() {
472 edges.entry(func[at].block).or_default().push((block, at));
473 }
474 }
475 edges
476}
477
478fn drop_unread(func: &mut Func, fuel: &mut Fuel, stats: &mut Stats) -> bool {
491 let Some(entry) = func.entry() else { return false };
492 let live = live(func, entry, &addressed(func));
493 let edges = incoming(func);
494 let mut changed = false;
495 let mut gone: HashSet<Value> = HashSet::new();
496 for block in func.blocks().collect::<Vec<Block>>() {
497 let mut taking = Vec::new();
498 for (index, ¶m) in func[block].params.iter().enumerate() {
499 if live.contains(¶m) {
500 continue;
501 }
502 if !fuel.take() {
503 stats.missed(NO_FUEL_UNREAD);
504 continue;
505 }
506 taking.push(index);
507 }
508 if taking.is_empty() {
509 continue;
510 }
511 for _ in &taking {
512 stats.optimized(NOTHING_READS_IT);
513 }
514 gone.extend(taking.iter().map(|&index| func[block].params[index]));
515 take_params(func, block, &taking, edges.get(&block));
516 changed = true;
517 }
518 if !gone.is_empty() {
519 strand(func, gone);
520 }
521 changed
522}
523
524fn strand(func: &mut Func, mut gone: HashSet<Value>) {
546 loop {
547 let mut spread = false;
548 for block in func.blocks().collect::<Vec<Block>>() {
549 for inst in func.insts(block).collect::<Vec<Inst>>() {
550 if !func[func[inst].args].iter().any(|value| gone.contains(value)) {
551 continue;
552 }
553 let results: Vec<Value> = func[inst].results().collect();
554 for result in results {
555 spread |= gone.insert(result);
556 }
557 func.remove_inst(inst);
558 }
559 }
560 if !spread {
563 return;
564 }
565 }
566}
567
568fn live(func: &Func, entry: Block, addressed: &HashSet<Block>) -> HashSet<Value> {
583 let mut where_from: HashMap<Value, (Block, usize)> = HashMap::new();
584 let mut live: HashSet<Value> = HashSet::new();
585 let mut work: Vec<Value> = Vec::new();
586 let seed = |value: Value, live: &mut HashSet<Value>, work: &mut Vec<Value>| {
587 if live.insert(value) {
588 work.push(value);
589 }
590 };
591 for block in func.blocks() {
592 let held = block == entry || addressed.contains(&block);
593 for (index, ¶m) in func[block].params.iter().enumerate() {
594 where_from.insert(param, (block, index));
595 if held {
596 seed(param, &mut live, &mut work);
597 }
598 }
599 for inst in func.insts(block) {
600 if !func.is_terminator(inst) && !func[inst].opcode.has_effects() {
601 continue;
602 }
603 for &value in &func[func[inst].args] {
604 seed(value, &mut live, &mut work);
605 }
606 }
607 }
608
609 let edges = incoming(func);
610 while let Some(value) = work.pop() {
611 match func[value].def {
612 Def::Result { inst, .. } => {
613 for &operand in &func[func[inst].args] {
614 seed(operand, &mut live, &mut work);
615 }
616 }
617 Def::Param { .. } => {
618 let Some(&(block, index)) = where_from.get(&value) else { continue };
619 for &(_, at) in edges.get(&block).into_iter().flatten() {
620 let Some(&arg) = func[func[at].args].get(index) else { continue };
621 seed(arg, &mut live, &mut work);
622 }
623 }
624 }
625 }
626 live
627}
628
629fn straighten(
655 func: &mut Func,
656 fuel: &mut Fuel,
657 stats: &mut Stats,
658 forward: &mut HashMap<Value, Value>,
659) -> bool {
660 let Some(entry) = func.entry() else { return false };
661 let addressed = addressed(func);
662 let mut edges = incoming(func);
663 let mut work: VecDeque<Block> = func.blocks().collect();
664 let mut queued: HashSet<Block> = work.iter().copied().collect();
665 let mut gone: HashSet<Block> = HashSet::new();
666 let mut changed = false;
667 while let Some(block) = work.pop_front() {
668 queued.remove(&block);
669 if gone.contains(&block) {
670 continue;
671 }
672 let mut starved = false;
673 if block != entry {
674 let drop = redundant(func, block, edges.get(&block), forward);
675 let mut taking = Vec::new();
676 for (index, value) in drop {
677 if !fuel.take() {
678 stats.missed(NO_FUEL_PARAM);
679 starved = true;
680 break;
681 }
682 let value = uses::chase(forward, value);
685 forward.insert(func[block].params[index], value);
686 taking.push(index);
687 stats.optimized(SAME_EVERY_WAY);
688 }
689 if !taking.is_empty() {
690 take_params(func, block, &taking, edges.get(&block));
691 requeue(block, &mut work, &mut queued);
694 if let Some(term) = func.terminator(block) {
697 for call in func.successors(term).collect::<Vec<BlockCall>>() {
698 requeue(call.block, &mut work, &mut queued);
699 }
700 }
701 changed = true;
702 }
703 }
704 if starved {
707 break;
708 }
709 let Some((term, into, args)) = forwards(func, block, entry, &addressed, &edges) else {
710 continue;
711 };
712 if !fuel.take() {
713 stats.missed(NO_FUEL_FORWARD);
714 break;
715 }
716 let out = func.target_list(term).iter().next().expect("a jump has a target");
720 if let Some(list) = edges.get_mut(&into) {
721 list.retain(|&(_, at)| at != out);
722 }
723 let ins = edges.remove(&block).unwrap_or_default();
724 for &(_, at) in &ins {
725 let call = func[at];
729 let args = func.push_values(&args);
730 func.set_block_call(at, BlockCall { block: into, args, ..call });
731 }
732 edges.entry(into).or_default().extend(ins.iter().copied());
733 func.remove_block(block);
734 gone.insert(block);
735 stats.optimized(FORWARDED);
736 changed = true;
737 requeue(into, &mut work, &mut queued);
738 for &(from, _) in &ins {
739 requeue(from, &mut work, &mut queued);
740 }
741 }
742 changed
743}
744
745fn requeue(block: Block, work: &mut VecDeque<Block>, queued: &mut HashSet<Block>) {
747 if queued.insert(block) {
748 work.push_back(block);
749 }
750}
751
752fn redundant(
768 func: &Func,
769 block: Block,
770 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
771 forward: &HashMap<Value, Value>,
772) -> Vec<(usize, Value)> {
773 let Some(ins) = ins.filter(|ins| !ins.is_empty()) else { return Vec::new() };
774 let mut found = Vec::new();
775 for (index, ¶m) in func[block].params.iter().enumerate() {
776 let mut only = None;
777 let mut agree = true;
778 for &(_, at) in ins {
779 let list = func[at].args;
780 let Some(&arg) = func[list].get(index) else {
781 agree = false;
784 break;
785 };
786 let arg = uses::chase(forward, arg);
787 if arg == param {
788 continue;
789 }
790 match only {
791 None => only = Some(arg),
792 Some(seen) if seen == arg => {}
793 Some(_) => {
794 agree = false;
795 break;
796 }
797 }
798 }
799 if !agree {
800 continue;
801 }
802 if let Some(value) = only {
803 found.push((index, value));
804 }
805 }
806 found
807}
808
809fn take_params(
814 func: &mut Func,
815 block: Block,
816 taking: &[usize],
817 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
818) {
819 for &(_, at) in ins.into_iter().flatten() {
820 let call = func[at];
821 let kept: Vec<Value> = func[call.args]
822 .iter()
823 .enumerate()
824 .filter(|(index, _)| !taking.contains(index))
825 .map(|(_, &value)| value)
826 .collect();
827 let args = func.push_values(&kept);
828 func.set_block_call(at, BlockCall { args, ..call });
829 }
830 let mut index = 0;
831 func.retain_params(block, |_| {
832 let keep = !taking.contains(&index);
833 index += 1;
834 keep
835 });
836}
837
838fn forwards(
844 func: &Func,
845 block: Block,
846 entry: Block,
847 addressed: &HashSet<Block>,
848 edges: &Edges,
849) -> Option<(Inst, Block, Vec<Value>)> {
850 if block == entry || addressed.contains(&block) || !func[block].params.is_empty() {
851 return None;
852 }
853 let term = func.terminator(block)?;
854 if func[term].opcode != Opcode::Jump {
855 return None;
856 }
857 if func.insts(block).count() != 1 {
860 return None;
861 }
862 let call = func.successors(term).next()?;
863 if call.block == block {
864 return None;
865 }
866 if carrying(func, block, call.block, func[call.args].len(), edges) {
867 return None;
868 }
869 Some((term, call.block, func[call.args].to_vec()))
870}
871
872fn carrying(func: &Func, block: Block, into: Block, args: usize, edges: &Edges) -> bool {
886 if args == 0 {
887 return false;
888 }
889 let ins = edges.get(&block).map_or(0, Vec::len);
890 let after = edges.get(&into).map_or(0, Vec::len) - 1 + ins;
891 if after < 2 {
892 return false;
893 }
894 edges.get(&block).into_iter().flatten().any(|&(from, _)| {
895 let Some(term) = func.terminator(from) else { return false };
896 func.target_list(term).iter().count() >= 2
897 })
898}
899
900fn chains(func: &Func, an: &mut Analyses) -> Vec<Vec<Block>> {
921 let cfg = an.cfg(func);
922 let Some(entry) = cfg.entry() else { return Vec::new() };
923 let addressed = addressed(func);
924 let mut below = HashMap::new();
925 let mut is_below = HashSet::new();
926 for block in func.blocks() {
927 let Some(term) = func.terminator(block) else { continue };
928 if func[term].opcode != Opcode::Jump {
929 continue;
930 }
931 let Some(call) = func.successors(term).next() else { continue };
932 let into = call.block;
933 let preds = cfg.predecessors(into);
934 if into == entry || into == block || addressed.contains(&into) {
935 continue;
936 }
937 if preds.len() != 1 || preds[0] != block {
938 continue;
939 }
940 below.insert(block, into);
941 is_below.insert(into);
942 }
943 let heads = func.blocks().filter(|it| below.contains_key(it) && !is_below.contains(it));
944 heads
945 .map(|head| {
946 let mut chain = vec![head];
947 let mut at = head;
948 while let Some(&next) = below.get(&at) {
949 chain.push(next);
950 at = next;
951 }
952 chain
953 })
954 .collect()
955}
956
957pub(crate) fn merge_below(func: &mut Func, an: &mut Analyses, head: Block, into: Block) -> bool {
972 let cfg = an.cfg(func);
973 let Some(entry) = cfg.entry() else { return false };
974 if into == entry || into == head || cfg.predecessors(into) != [head] {
975 return false;
976 }
977 let Some(term) = func.terminator(head) else { return false };
978 if func[term].opcode != Opcode::Jump || addressed(func).contains(&into) {
979 return false;
980 }
981 let mut forward = HashMap::new();
982 merge(func, head, into, &mut forward);
983 uses::substitute(func, &forward);
984 an.clear();
985 true
986}
987
988fn merge(func: &mut Func, head: Block, block: Block, forward: &mut HashMap<Value, Value>) {
995 let term = func.terminator(head).expect("the head of a chain ends in a jump");
996 let call = func.successors(term).next().expect("a jump goes somewhere");
997 let args = func[call.args].to_vec();
998 let params = func[block].params.clone();
999 for (param, arg) in params.into_iter().zip(args) {
1000 let arg = uses::chase(forward, arg);
1004 forward.insert(param, arg);
1005 }
1006 func.remove_inst(term);
1007 let top = func.insts_backwards(head).next();
1010 for inst in func.insts(block).collect::<Vec<Inst>>() {
1011 func.remove_inst(inst);
1012 func.append_inst(head, inst);
1013 }
1014 func.carry_starts(block, head, top);
1015 func.remove_block(block);
1016}
1017
1018fn known(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1020 let value = resolve(subst, value);
1021 if let Some((imm, _)) = constant(func, value) {
1022 return Some(imm.unsigned() != 0);
1023 }
1024 compared(func, value, subst)
1025}
1026
1027fn compared(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1038 let Def::Result { inst, .. } = func[value].def else { return None };
1039 let data = &func[inst];
1040 if data.opcode != Opcode::ICmp {
1041 return None;
1042 }
1043 let Extra::IntPred(pred) = data.extra else { return None };
1044 let args = &func[data.args];
1045 let (lhs, ty) = constant(func, resolve(subst, *args.first()?))?;
1046 let (rhs, _) = constant(func, resolve(subst, *args.get(1)?))?;
1047 Some(crate::fold::compare(pred, lhs, rhs, ty))
1048}
1049
1050#[cfg(test)]
1051mod tests {
1052 use rucc_base::Interner;
1053 use rucc_ir::{
1054 Block, Builder, Def, Flags, Func, Inst, IntPred, MemInfo, MemOrder, Module, Opcode,
1055 Restrict, Signature, Type, Value,
1056 };
1057 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1058
1059 use super::SimplifyCfg;
1060 use crate::stats::Kind;
1061 use crate::testing::graph;
1062 use crate::{Fuel, Pass, Preserved, Stats};
1063
1064 fn simplify(func: &mut Func) -> Stats {
1066 SimplifyCfg.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1067 }
1068
1069 fn blocks(func: &Func) -> Vec<usize> {
1071 func.blocks().map(Block::index).collect()
1072 }
1073
1074 fn terminator(func: &Func, block: usize) -> Opcode {
1076 let block = Block::from_usize(block);
1077 func[func.terminator(block).expect("every block here has one")].opcode
1078 }
1079
1080 fn goes_to(func: &Func, block: usize) -> Vec<usize> {
1082 let block = Block::from_usize(block);
1083 let term = func.terminator(block).expect("every block here has one");
1084 func.successors(term).map(|call| call.block.index()).collect()
1085 }
1086
1087 fn lives_in(func: &Func, value: Value) -> Option<usize> {
1093 let Def::Result { inst, .. } = func[value].def else { return None };
1094 func.block_of(inst).map(Block::index)
1095 }
1096
1097 fn diamond(cond: impl FnOnce(&mut Builder<'_>) -> Value) -> (Func, [Value; 2]) {
1104 let mut names = Interner::new();
1105 let mut func = Func::new(names.intern("f"), Signature::new());
1106 let entry = func.create_block();
1107 let then_block = func.create_block();
1108 let else_block = func.create_block();
1109 let join = func.create_block();
1110 let mut build = Builder::new(&mut func, entry);
1111 let cond = cond(&mut build);
1112 build.br_if(cond, then_block, &[], else_block, &[]);
1113 let mut marks = Vec::new();
1114 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1115 let mut build = Builder::new(&mut func, arm);
1116 marks.push(build.iconst(Type::int(32), mark));
1117 build.jump(join, &[]);
1118 }
1119 let mut build = Builder::new(&mut func, join);
1120 build.ret(&[]);
1121 (func, [marks[0], marks[1]])
1122 }
1123
1124 #[test]
1125 fn a_branch_on_a_true_constant_becomes_a_jump_to_the_first_arm() {
1126 let (mut func, [taken, other]) = diamond(|build| build.iconst(Type::int(1), 1));
1127 let stats = simplify(&mut func);
1128 assert!(stats.changed());
1129 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1130 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1133 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1134 assert_eq!(lives_in(&func, taken), Some(0));
1135 assert_eq!(lives_in(&func, other), None);
1136 assert_eq!(blocks(&func), [0]);
1137 }
1138
1139 #[test]
1140 fn a_branch_on_a_false_constant_becomes_a_jump_to_the_second_arm() {
1141 let (mut func, [other, taken]) = diamond(|build| build.iconst(Type::int(1), 0));
1142 assert!(simplify(&mut func).changed());
1143 assert_eq!(lives_in(&func, taken), Some(0));
1144 assert_eq!(lives_in(&func, other), None);
1145 assert_eq!(blocks(&func), [0]);
1146 }
1147
1148 #[test]
1149 fn folding_a_branch_and_merging_what_it_leaves_are_two_things_fuel_buys_apart() {
1150 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1153 let stats =
1154 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1155 assert_eq!(terminator(&func, 0), Opcode::Jump);
1156 assert_eq!(goes_to(&func, 0), [1]);
1157 assert_eq!(blocks(&func), [0, 1, 3]);
1158 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
1159 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
1162 }
1163
1164 #[test]
1165 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
1166 let cases: &[(IntPred, i128, i128, bool)] = &[
1170 (IntPred::Eq, 7, 7, true),
1171 (IntPred::Eq, 7, 8, false),
1172 (IntPred::Ne, 7, 8, true),
1173 (IntPred::Ne, 7, 7, false),
1174 (IntPred::Slt, -1, 1, true),
1175 (IntPred::Slt, 1, -1, false),
1176 (IntPred::Sle, -1, -1, true),
1177 (IntPred::Sle, 1, -1, false),
1178 (IntPred::Sgt, 1, -1, true),
1179 (IntPred::Sgt, -1, 1, false),
1180 (IntPred::Sge, -1, -1, true),
1181 (IntPred::Sge, -1, 1, false),
1182 (IntPred::Ult, 1, -1, true),
1183 (IntPred::Ult, -1, 1, false),
1184 (IntPred::Ule, -1, -1, true),
1185 (IntPred::Ule, -1, 1, false),
1186 (IntPred::Ugt, -1, 1, true),
1187 (IntPred::Ugt, 1, -1, false),
1188 (IntPred::Uge, -1, -1, true),
1189 (IntPred::Uge, 1, -1, false),
1190 ];
1191 for &(pred, lhs, rhs, taken) in cases {
1192 let (mut func, marks) = diamond(|build| {
1193 let lhs = build.iconst(Type::int(32), lhs);
1194 let rhs = build.iconst(Type::int(32), rhs);
1195 build.icmp(pred, lhs, rhs)
1196 });
1197 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
1198 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
1199 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
1200 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
1201 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
1202 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
1203 }
1204 }
1205
1206 #[test]
1207 fn a_branch_on_something_nobody_knows_is_left_alone() {
1208 let mut names = Interner::new();
1209 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
1210 let entry = func.create_block();
1211 let then_block = func.create_block();
1212 let else_block = func.create_block();
1213 let cond = func.append_param(entry, Type::int(1));
1214 let mut build = Builder::new(&mut func, entry);
1215 build.br_if(cond, then_block, &[], else_block, &[]);
1216 for arm in [then_block, else_block] {
1217 let mut build = Builder::new(&mut func, arm);
1218 build.ret(&[]);
1219 }
1220 let stats = simplify(&mut func);
1221 assert!(!stats.changed());
1222 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1223 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1224 assert_eq!(blocks(&func), [0, 1, 2]);
1225 }
1226
1227 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1230 let mut names = Interner::new();
1231 let mut func = Func::new(names.intern("f"), Signature::new());
1232 let entry = func.create_block();
1233 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1234 let mut build = Builder::new(&mut func, entry);
1235 let value = build.iconst(Type::int(32), on);
1236 let pairs: Vec<(i128, Block)> =
1237 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1238 build.switch(value, arms[0], &pairs);
1239 let mut marks = Vec::new();
1240 for (at, &arm) in arms.iter().enumerate() {
1241 let mut build = Builder::new(&mut func, arm);
1242 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1243 build.ret(&[]);
1244 }
1245 (func, marks)
1246 }
1247
1248 #[test]
1249 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1250 let (mut func, marks) = switched(5, &[4, 5]);
1251 assert!(simplify(&mut func).changed());
1252 assert_eq!(lives_in(&func, marks[2]), Some(0));
1253 assert_eq!(lives_in(&func, marks[0]), None);
1254 assert_eq!(lives_in(&func, marks[1]), None);
1255 assert_eq!(blocks(&func), [0]);
1256 }
1257
1258 #[test]
1259 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1260 let (mut func, marks) = switched(9, &[4]);
1261 assert!(simplify(&mut func).changed());
1262 assert_eq!(lives_in(&func, marks[0]), Some(0));
1263 assert_eq!(lives_in(&func, marks[1]), None);
1264 assert_eq!(blocks(&func), [0]);
1265 }
1266
1267 #[test]
1268 fn the_arguments_travel_with_the_edge_that_survives() {
1269 let mut names = Interner::new();
1275 let mut func = Func::new(names.intern("f"), Signature::new());
1276 let entry = func.create_block();
1277 let join = func.create_block();
1278 let param = func.append_param(join, Type::int(32));
1279 let mut build = Builder::new(&mut func, entry);
1280 let cond = build.iconst(Type::int(1), 0);
1281 let taken = build.iconst(Type::int(32), 11);
1282 let other = build.iconst(Type::int(32), 22);
1283 build.br_if(cond, join, &[other], join, &[taken]);
1284 let mut build = Builder::new(&mut func, join);
1285 build.ret(&[param]);
1286 assert!(simplify(&mut func).changed());
1287 assert_eq!(blocks(&func), [0]);
1291 let term = func.terminator(entry).expect("the entry has one");
1292 assert_eq!(func[func[term].args], [taken]);
1293 assert_ne!(func[func[term].args], [param]);
1294 }
1295
1296 #[test]
1297 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1298 let mut names = Interner::new();
1302 let signature = Signature::new().with_params(&[Type::int(1)]);
1303 let mut func = Func::new(names.intern("f"), signature);
1304 let entry = func.create_block();
1305 let join = func.create_block();
1306 let cond = func.append_param(entry, Type::int(1));
1307 let mut build = Builder::new(&mut func, entry);
1308 build.br_if(cond, join, &[], join, &[]);
1309 let mut build = Builder::new(&mut func, join);
1310 build.ret(&[]);
1311 let stats = simplify(&mut func);
1312 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1313 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1314 assert_eq!(blocks(&func), [0]);
1315 assert_eq!(terminator(&func, 0), Opcode::Return);
1316 }
1317
1318 #[test]
1319 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1320 let mut names = Interner::new();
1321 let signature = Signature::new().with_params(&[Type::int(32)]);
1322 let mut func = Func::new(names.intern("f"), signature);
1323 let entry = func.create_block();
1324 let join = func.create_block();
1325 let value = func.append_param(entry, Type::int(32));
1326 let mut build = Builder::new(&mut func, entry);
1327 build.switch(value, join, &[(4, join), (5, join)]);
1328 let mut build = Builder::new(&mut func, join);
1329 build.ret(&[]);
1330 let stats = simplify(&mut func);
1331 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1332 assert_eq!(blocks(&func), [0]);
1333 }
1334
1335 #[test]
1336 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1337 let mut names = Interner::new();
1340 let signature = Signature::new().with_params(&[Type::int(1)]);
1341 let mut func = Func::new(names.intern("f"), signature);
1342 let entry = func.create_block();
1343 let join = func.create_block();
1344 let cond = func.append_param(entry, Type::int(1));
1345 let param = func.append_param(join, Type::int(32));
1346 let mut build = Builder::new(&mut func, entry);
1347 let first = build.iconst(Type::int(32), 11);
1348 let second = build.iconst(Type::int(32), 22);
1349 build.br_if(cond, join, &[first], join, &[second]);
1350 let mut build = Builder::new(&mut func, join);
1351 build.ret(&[param]);
1354 let stats = simplify(&mut func);
1355 assert!(!stats.changed());
1356 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1357 assert_eq!(blocks(&func), [0, 1]);
1358 }
1359
1360 #[test]
1361 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1362 let mut names = Interner::new();
1365 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1366 let entry = func.create_block();
1367 let dead = func.create_block();
1368 let shared = func.create_block();
1369 let exit = func.create_block();
1370 let x = func.append_param(entry, Type::int(32));
1371 let mut build = Builder::new(&mut func, entry);
1372 let never = build.iconst(Type::int(1), 0);
1373 build.switch(x, exit, &[(0, dead), (1, shared)]);
1374 let mut build = Builder::new(&mut func, dead);
1378 build.iconst(Type::int(32), 1);
1379 build.br_if(never, shared, &[], exit, &[]);
1380 for arm in [shared, exit] {
1381 let mut build = Builder::new(&mut func, arm);
1382 build.ret(&[]);
1383 }
1384 let stats = simplify(&mut func);
1385 assert!(stats.changed());
1386 assert_eq!(terminator(&func, 0), Opcode::Switch);
1389 assert_eq!(goes_to(&func, 1), [3]);
1390 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1391 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1392 }
1393
1394 #[test]
1395 fn a_block_whose_address_is_taken_is_not_removed() {
1396 let mut names = Interner::new();
1400 let mut func = Func::new(names.intern("f"), Signature::new());
1401 let entry = func.create_block();
1402 let labelled = func.create_block();
1403 let arm = func.create_block();
1404 let mut build = Builder::new(&mut func, entry);
1405 let cond = build.iconst(Type::int(1), 1);
1406 let addr = build.block_addr(labelled);
1407 build.br_if(cond, arm, &[], labelled, &[]);
1408 let mut build = Builder::new(&mut func, arm);
1409 build.indirect_br(addr, &[labelled]);
1410 let mut build = Builder::new(&mut func, labelled);
1411 build.ret(&[]);
1412 assert!(simplify(&mut func).changed());
1413 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1414 assert_eq!(blocks(&func), [0, 1]);
1417 assert_eq!(goes_to(&func, 0), [1]);
1418 }
1419
1420 #[test]
1421 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1422 let mut names = Interner::new();
1425 let mut func = Func::new(names.intern("f"), Signature::new());
1426 let entry = func.create_block();
1427 let dead = func.create_block();
1428 let labelled = func.create_block();
1429 let mut build = Builder::new(&mut func, entry);
1430 let cond = build.iconst(Type::int(1), 1);
1431 build.br_if(cond, entry, &[], dead, &[]);
1432 let mut build = Builder::new(&mut func, dead);
1433 let addr = build.block_addr(labelled);
1434 build.indirect_br(addr, &[labelled]);
1435 let mut build = Builder::new(&mut func, labelled);
1436 build.ret(&[]);
1437 assert!(simplify(&mut func).changed());
1438 assert_eq!(blocks(&func), [0]);
1439 }
1440
1441 #[test]
1442 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1443 let mut func = graph(&[&[], &[]]);
1448 let stats = simplify(&mut func);
1449 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1450 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1451 assert_eq!(blocks(&func), [0]);
1452 }
1453
1454 #[test]
1455 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1456 let mut names = Interner::new();
1459 let mut func = Func::new(names.intern("f"), Signature::new());
1460 let entry = func.create_block();
1461 let middle = func.create_block();
1462 let last = func.create_block();
1463 let mut build = Builder::new(&mut func, entry);
1464 build.iconst(Type::int(32), 1);
1465 build.jump(middle, &[]);
1466 let mut build = Builder::new(&mut func, middle);
1467 build.iconst(Type::int(32), 2);
1468 build.jump(last, &[]);
1469 let mut build = Builder::new(&mut func, last);
1470 build.ret(&[]);
1471 let stats = simplify(&mut func);
1472 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1475 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1476 assert_eq!(blocks(&func), [0]);
1477 assert_eq!(terminator(&func, 0), Opcode::Return);
1478 }
1479
1480 #[test]
1481 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1482 let mut names = Interner::new();
1485 let signature = Signature::new().with_params(&[Type::int(1)]);
1486 let mut func = Func::new(names.intern("f"), signature);
1487 let entry = func.create_block();
1488 let then_block = func.create_block();
1489 let else_block = func.create_block();
1490 let join = func.create_block();
1491 let cond = func.append_param(entry, Type::int(1));
1492 let mut build = Builder::new(&mut func, entry);
1493 build.br_if(cond, then_block, &[], else_block, &[]);
1494 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1495 let mut build = Builder::new(&mut func, arm);
1498 build.iconst(Type::int(32), mark);
1499 build.jump(join, &[]);
1500 }
1501 let mut build = Builder::new(&mut func, join);
1502 build.ret(&[]);
1503 let stats = simplify(&mut func);
1504 assert!(!stats.changed());
1505 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1506 }
1507
1508 #[test]
1509 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1510 let mut names = Interner::new();
1513 let signature = Signature::new().with_params(&[Type::int(1)]);
1514 let mut func = Func::new(names.intern("f"), signature);
1515 let entry = func.create_block();
1516 let arm = func.create_block();
1517 let exit = func.create_block();
1518 let cond = func.append_param(entry, Type::int(1));
1519 let mut build = Builder::new(&mut func, entry);
1520 build.br_if(cond, arm, &[], exit, &[]);
1521 for block in [arm, exit] {
1522 let mut build = Builder::new(&mut func, block);
1523 build.ret(&[]);
1524 }
1525 let stats = simplify(&mut func);
1526 assert!(!stats.changed());
1527 assert_eq!(blocks(&func), [0, 1, 2]);
1528 }
1529
1530 #[test]
1531 fn the_entry_block_is_never_the_one_that_moves() {
1532 let mut names = Interner::new();
1536 let signature = Signature::new().with_params(&[Type::int(1)]);
1537 let mut func = Func::new(names.intern("f"), signature);
1538 let entry = func.create_block();
1539 let latch = func.create_block();
1540 let exit = func.create_block();
1541 let cond = func.append_param(entry, Type::int(1));
1542 let mut build = Builder::new(&mut func, entry);
1543 build.br_if(cond, latch, &[], exit, &[]);
1544 let mut build = Builder::new(&mut func, latch);
1546 build.iconst(Type::int(32), 1);
1547 build.jump(entry, &[]);
1548 let mut build = Builder::new(&mut func, exit);
1549 build.ret(&[]);
1550 let stats = simplify(&mut func);
1551 assert!(!stats.changed());
1552 assert_eq!(blocks(&func), [0, 1, 2]);
1553 }
1554
1555 #[test]
1556 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1557 let mut names = Interner::new();
1560 let mut func = Func::new(names.intern("f"), Signature::new());
1561 let entry = func.create_block();
1562 let middle = func.create_block();
1563 let labelled = func.create_block();
1564 let mut build = Builder::new(&mut func, entry);
1565 build.block_addr(labelled);
1566 build.jump(middle, &[]);
1567 let mut build = Builder::new(&mut func, middle);
1570 build.iconst(Type::int(32), 1);
1571 build.jump(labelled, &[]);
1572 let mut build = Builder::new(&mut func, labelled);
1573 build.ret(&[]);
1574 let stats = simplify(&mut func);
1575 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1578 assert_eq!(blocks(&func), [0, 2]);
1579 }
1580
1581 #[test]
1582 fn a_block_an_image_names_is_not_merged_away_either() {
1583 let mut names = Interner::new();
1587 let mut func = Func::new(names.intern("f"), Signature::new());
1588 let entry = func.create_block();
1589 let middle = func.create_block();
1590 let labelled = func.create_block();
1591 Builder::new(&mut func, entry).jump(middle, &[]);
1592 let mut build = Builder::new(&mut func, middle);
1593 build.iconst(Type::int(32), 1);
1594 build.jump(labelled, &[]);
1595 Builder::new(&mut func, labelled).ret(&[]);
1596 func.name_block(labelled, names.intern(".Llbl.0"));
1597 let stats = simplify(&mut func);
1598 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1599 assert_eq!(blocks(&func), [0, 2]);
1600 assert_eq!(func.block_name(labelled), Some(names.intern(".Llbl.0")));
1601 }
1602
1603 #[test]
1604 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1605 let mut names = Interner::new();
1606 let mut func = Func::new(names.intern("f"), Signature::new());
1607 let entry = func.create_block();
1608 let below = func.create_block();
1609 let param = func.append_param(below, Type::int(32));
1610 let mut build = Builder::new(&mut func, entry);
1611 let arg = build.iconst(Type::int(32), 7);
1612 build.jump(below, &[arg]);
1613 let mut build = Builder::new(&mut func, below);
1614 build.ret(&[param]);
1615 assert!(simplify(&mut func).changed());
1616 assert_eq!(blocks(&func), [0]);
1617 let term = func.terminator(entry).expect("the entry has one");
1618 assert_eq!(func[func[term].args], [arg]);
1619 }
1620
1621 #[test]
1622 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1623 let mut names = Interner::new();
1627 let mut func = Func::new(names.intern("f"), Signature::new());
1628 let entry = func.create_block();
1629 let middle = func.create_block();
1630 let last = func.create_block();
1631 let carried = func.append_param(middle, Type::int(32));
1632 let arrived = func.append_param(last, Type::int(32));
1633 let mut build = Builder::new(&mut func, entry);
1634 let arg = build.iconst(Type::int(32), 7);
1635 build.jump(middle, &[arg]);
1636 let mut build = Builder::new(&mut func, middle);
1637 build.jump(last, &[carried]);
1638 let mut build = Builder::new(&mut func, last);
1639 build.ret(&[arrived]);
1640 assert!(simplify(&mut func).changed());
1641 assert_eq!(blocks(&func), [0]);
1642 let term = func.terminator(entry).expect("the entry has one");
1643 assert_eq!(func[func[term].args], [arg]);
1644 }
1645
1646 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1654 let entry = func.create_block();
1655 let first = func.create_block();
1656 let second = func.create_block();
1657 let cond = func.append_param(entry, Type::int(1));
1658 let mut build = Builder::new(func, entry);
1659 let carried = build.iconst(Type::int(32), 7);
1660 build.br_if(cond, first, &[], second, &[]);
1661 for (arm, mark) in [(first, 111), (second, 222)] {
1662 let mut build = Builder::new(func, arm);
1663 build.iconst(Type::int(32), mark);
1664 }
1665 (carried, [first, second])
1666 }
1667
1668 fn taking_a_condition() -> Func {
1670 let mut names = Interner::new();
1671 let signature = Signature::new().with_params(&[Type::int(1)]);
1672 Func::new(names.intern("f"), signature)
1673 }
1674
1675 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1677 let block = Block::from_usize(block);
1678 let term = func.terminator(block).expect("every block here has one");
1679 let call = func.successors(term).nth(edge).expect("the edge is there");
1680 func[call.args].to_vec()
1681 }
1682
1683 #[test]
1684 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1685 let mut func = taking_a_condition();
1688 let (_, arms) = arms(&mut func);
1689 let forwarder = func.create_block();
1690 let exit = func.create_block();
1691 for arm in arms {
1692 Builder::new(&mut func, arm).jump(forwarder, &[]);
1693 }
1694 Builder::new(&mut func, forwarder).jump(exit, &[]);
1695 Builder::new(&mut func, exit).ret(&[]);
1696 let stats = simplify(&mut func);
1697 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1698 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1699 assert_eq!(goes_to(&func, 1), [4]);
1700 assert_eq!(goes_to(&func, 2), [4]);
1701 }
1702
1703 #[test]
1704 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1705 let mut func = taking_a_condition();
1712 let (carried, [arm, above]) = arms(&mut func);
1713 let forwarder = func.create_block();
1714 let exit = func.create_block();
1715 let other = func.append_param(exit, Type::int(32));
1716 let mut build = Builder::new(&mut func, arm);
1717 let mine = build.iconst(Type::int(32), 9);
1718 build.jump(exit, &[mine]);
1719 Builder::new(&mut func, above).jump(forwarder, &[]);
1720 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1721 Builder::new(&mut func, exit).ret(&[other]);
1722 let stats = simplify(&mut func);
1723 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1724 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1725 assert_eq!(carries(&func, 2, 0), [carried]);
1728 assert_eq!(carries(&func, 1, 0), [mine]);
1729 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1731 }
1732
1733 #[test]
1734 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1735 let mut func = taking_a_condition();
1740 let (carried, [arm, forwarder]) = arms(&mut func);
1741 let exit = func.create_block();
1742 let other = func.append_param(exit, Type::int(32));
1743 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1745 func.remove_inst(inst);
1746 }
1747 let mut build = Builder::new(&mut func, arm);
1748 let mine = build.iconst(Type::int(32), 9);
1749 build.jump(exit, &[mine]);
1750 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1751 Builder::new(&mut func, exit).ret(&[other]);
1752 let stats = simplify(&mut func);
1753 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1754 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1755 }
1756
1757 #[test]
1758 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1759 let mut func = taking_a_condition();
1762 let (_, [arm, forwarder]) = arms(&mut func);
1763 let exit = func.create_block();
1764 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1765 func.remove_inst(inst);
1766 }
1767 Builder::new(&mut func, arm).jump(exit, &[]);
1768 Builder::new(&mut func, forwarder).jump(exit, &[]);
1769 Builder::new(&mut func, exit).ret(&[]);
1770 let stats = simplify(&mut func);
1771 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1772 assert_eq!(blocks(&func), [0, 1, 3]);
1773 }
1774
1775 #[test]
1776 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1777 let mut names = Interner::new();
1780 let mut func = Func::new(names.intern("f"), Signature::new());
1781 let entry = func.create_block();
1782 let spin = func.create_block();
1783 Builder::new(&mut func, entry).jump(spin, &[]);
1784 Builder::new(&mut func, spin).jump(spin, &[]);
1785 let stats = simplify(&mut func);
1786 assert!(!stats.changed());
1787 assert_eq!(blocks(&func), [0, 1]);
1788 }
1789
1790 #[test]
1791 fn the_entry_block_is_never_the_forwarder_that_goes() {
1792 let mut names = Interner::new();
1796 let mut func = Func::new(names.intern("f"), Signature::new());
1797 let entry = func.create_block();
1798 let below = func.create_block();
1799 Builder::new(&mut func, entry).jump(below, &[]);
1800 let mut build = Builder::new(&mut func, below);
1801 build.iconst(Type::int(32), 1);
1802 build.ret(&[]);
1803 let stats = simplify(&mut func);
1804 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1805 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1806 assert_eq!(blocks(&func), [0]);
1807 }
1808
1809 #[test]
1810 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1811 let mut names = Interner::new();
1815 let mut func = Func::new(names.intern("f"), Signature::new());
1816 let entry = func.create_block();
1817 let labelled = func.create_block();
1818 let exit = func.create_block();
1819 let mut build = Builder::new(&mut func, entry);
1820 let addr = build.block_addr(labelled);
1821 build.indirect_br(addr, &[labelled]);
1822 Builder::new(&mut func, labelled).jump(exit, &[]);
1823 let mut build = Builder::new(&mut func, exit);
1824 build.iconst(Type::int(32), 1);
1825 build.ret(&[]);
1826 let stats = simplify(&mut func);
1827 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1828 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1829 }
1830
1831 #[test]
1832 fn a_run_of_forwarders_comes_out_as_one_edge() {
1833 let mut func = taking_a_condition();
1834 let (_, arms) = arms(&mut func);
1835 let first = func.create_block();
1836 let second = func.create_block();
1837 let exit = func.create_block();
1838 for arm in arms {
1839 Builder::new(&mut func, arm).jump(first, &[]);
1840 }
1841 Builder::new(&mut func, first).jump(second, &[]);
1842 Builder::new(&mut func, second).jump(exit, &[]);
1843 Builder::new(&mut func, exit).ret(&[]);
1844 let stats = simplify(&mut func);
1845 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1846 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1847 assert_eq!(goes_to(&func, 1), [5]);
1848 assert_eq!(goes_to(&func, 2), [5]);
1849 }
1850
1851 #[test]
1852 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1853 let mut func = taking_a_condition();
1856 let (carried, arms) = arms(&mut func);
1857 let join = func.create_block();
1858 let param = func.append_param(join, Type::int(32));
1859 for arm in arms {
1860 Builder::new(&mut func, arm).jump(join, &[carried]);
1861 }
1862 Builder::new(&mut func, join).ret(&[param]);
1863 let stats = simplify(&mut func);
1864 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1865 assert!(func[Block::from_usize(3)].params.is_empty());
1866 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1868 assert_eq!(func[func[term].args], [carried]);
1869 assert!(carries(&func, 1, 0).is_empty());
1872 assert!(carries(&func, 2, 0).is_empty());
1873 }
1874
1875 #[test]
1876 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1877 let mut func = taking_a_condition();
1878 let (carried, arms) = arms(&mut func);
1879 let join = func.create_block();
1880 let param = func.append_param(join, Type::int(32));
1881 let mut build = Builder::new(&mut func, arms[0]);
1882 let mine = build.iconst(Type::int(32), 9);
1883 build.jump(join, &[mine]);
1884 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1885 Builder::new(&mut func, join).ret(&[param]);
1886 let stats = simplify(&mut func);
1887 assert!(!stats.changed());
1888 assert_eq!(func[Block::from_usize(3)].params, [param]);
1889 }
1890
1891 #[test]
1892 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1893 let mut names = Interner::new();
1897 let signature = Signature::new().with_params(&[Type::int(1)]);
1898 let mut func = Func::new(names.intern("f"), signature);
1899 let entry = func.create_block();
1900 let header = func.create_block();
1901 let latch = func.create_block();
1902 let exit = func.create_block();
1903 let cond = func.append_param(entry, Type::int(1));
1904 let param = func.append_param(header, Type::int(32));
1905 let mut build = Builder::new(&mut func, entry);
1906 let init = build.iconst(Type::int(32), 7);
1907 build.jump(header, &[init]);
1908 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1909 let mut build = Builder::new(&mut func, latch);
1910 build.iconst(Type::int(32), 1);
1911 build.jump(header, &[param]);
1912 Builder::new(&mut func, exit).ret(&[param]);
1913 let stats = simplify(&mut func);
1914 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1915 assert!(func[Block::from_usize(1)].params.is_empty());
1916 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1917 assert_eq!(func[func[term].args], [init]);
1918 }
1919
1920 #[test]
1921 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1922 let mut names = Interner::new();
1926 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1927 let mut func = Func::new(names.intern("f"), signature);
1928 let entry = func.create_block();
1929 let latch = func.create_block();
1930 let exit = func.create_block();
1931 let cond = func.append_param(entry, Type::int(1));
1932 let x = func.append_param(entry, Type::int(32));
1933 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1934 let mut build = Builder::new(&mut func, latch);
1935 let one = build.iconst(Type::int(1), 1);
1936 let seven = build.iconst(Type::int(32), 7);
1937 build.jump(entry, &[one, seven]);
1938 Builder::new(&mut func, exit).ret(&[x]);
1939 let stats = simplify(&mut func);
1940 assert!(!stats.changed());
1941 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1942 }
1943
1944 #[test]
1945 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1946 let mut func = taking_a_condition();
1950 let (carried, arms) = arms(&mut func);
1951 let join = func.create_block();
1952 let inner = func.append_param(join, Type::int(32));
1953 let left = func.create_block();
1954 let right = func.create_block();
1955 let last = func.create_block();
1956 let outer = func.append_param(last, Type::int(32));
1957 for arm in arms {
1958 Builder::new(&mut func, arm).jump(join, &[carried]);
1959 }
1960 let cond = func[Block::from_usize(0)].params[0];
1961 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1962 let mut build = Builder::new(&mut func, left);
1963 build.iconst(Type::int(32), 1);
1964 build.jump(last, &[inner]);
1965 let mut build = Builder::new(&mut func, right);
1966 build.iconst(Type::int(32), 2);
1967 build.jump(last, &[carried]);
1968 Builder::new(&mut func, last).ret(&[outer]);
1969 let stats = simplify(&mut func);
1970 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1971 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1972 assert_eq!(func[func[term].args], [carried]);
1973 }
1974
1975 #[test]
1976 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1977 let mut func = taking_a_condition();
1981 let (carried, arms) = arms(&mut func);
1982 let forwarder = func.create_block();
1983 let param = func.append_param(forwarder, Type::int(32));
1984 let exit = func.create_block();
1985 let arrived = func.append_param(exit, Type::int(32));
1986 for arm in arms {
1987 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1988 }
1989 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1990 Builder::new(&mut func, exit).ret(&[arrived]);
1991 let stats = simplify(&mut func);
1992 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1993 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1996 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1997 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
1998 assert_eq!(func[func[term].args], [carried]);
1999 }
2000
2001 #[test]
2002 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
2003 let mut func = taking_a_condition();
2006 let (carried, arms) = arms(&mut func);
2007 let forwarder = func.create_block();
2008 let param = func.append_param(forwarder, Type::int(32));
2009 let exit = func.create_block();
2010 let arrived = func.append_param(exit, Type::int(32));
2013 for arm in arms {
2014 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
2015 }
2016 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2017 Builder::new(&mut func, exit).ret(&[arrived]);
2018 let stats =
2019 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2020 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
2021 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
2022 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
2023 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
2024 }
2025
2026 fn walking_a_pointer(on_counter: bool) -> Func {
2035 let mut names = Interner::new();
2036 let signature = Signature::new().with_params(&[Type::int(64)]);
2037 let mut func = Func::new(names.intern("f"), signature);
2038 let entry = func.create_block();
2039 let head = func.create_block();
2040 let out = func.create_block();
2041 let end = func.append_param(entry, Type::int(64));
2042 let counter = func.append_param(head, Type::int(32));
2043 let pointer = func.append_param(head, Type::int(64));
2044 let mut build = Builder::new(&mut func, entry);
2045 let from_zero = build.iconst(Type::int(32), 0);
2046 let from_start = build.iconst(Type::int(64), 0);
2047 build.jump(head, &[from_zero, from_start]);
2048 let mut build = Builder::new(&mut func, head);
2049 let one = build.iconst(Type::int(32), 1);
2050 let eight = build.iconst(Type::int(64), 8);
2051 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2052 let along = build.binary(Opcode::Add, pointer, eight, Flags::NONE);
2053 let address = build.unary(Opcode::IntToPtr, pointer, Type::PTR);
2056 let info = MemInfo {
2057 size: 8,
2058 align: 8,
2059 order: MemOrder::NotAtomic,
2060 tbaa: None,
2061 owns: 0,
2062 restrict: Restrict::NONE,
2063 };
2064 build.store(eight, address, info, Flags::NONE);
2065 let going = if on_counter {
2066 let limit = build.iconst(Type::int(32), 10);
2067 build.icmp(IntPred::Ne, next, limit)
2068 } else {
2069 build.icmp(IntPred::Ne, along, end)
2070 };
2071 build.br_if(going, head, &[next, along], out, &[]);
2072 Builder::new(&mut func, out).ret(&[]);
2073 func
2074 }
2075
2076 #[test]
2077 fn a_counter_the_loop_stopped_asking_about_stops_going_round() {
2078 let mut func = walking_a_pointer(false);
2079 let stats = simplify(&mut func);
2080 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2081 assert_eq!(func[Block::from_usize(1)].params.len(), 1);
2083 assert_eq!(carries(&func, 1, 0).len(), 1);
2085 assert_eq!(carries(&func, 0, 0).len(), 1);
2086 }
2087
2088 #[test]
2089 fn a_counter_the_loop_still_asks_about_goes_round_exactly_as_before() {
2090 let mut func = walking_a_pointer(true);
2091 let stats = simplify(&mut func);
2092 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2093 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2094 }
2095
2096 fn counting_into_nothing() -> (Func, Value, Value) {
2103 let mut names = Interner::new();
2104 let signature = Signature::new().with_params(&[Type::int(32)]);
2105 let mut func = Func::new(names.intern("f"), signature);
2106 let entry = func.create_block();
2107 let head = func.create_block();
2108 let out = func.create_block();
2109 let limit = func.append_param(entry, Type::int(32));
2110 let counter = func.append_param(head, Type::int(32));
2111 let mut build = Builder::new(&mut func, entry);
2112 let zero = build.iconst(Type::int(32), 0);
2113 build.jump(head, &[zero]);
2114 let mut build = Builder::new(&mut func, head);
2115 let one = build.iconst(Type::int(32), 1);
2116 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2117 let twice = build.binary(Opcode::Add, next, next, Flags::NONE);
2118 let going = build.icmp(IntPred::Ne, limit, one);
2119 build.br_if(going, head, &[next], out, &[]);
2120 Builder::new(&mut func, out).ret(&[]);
2121 (func, next, twice)
2122 }
2123
2124 #[test]
2125 fn what_was_reading_a_parameter_nothing_reads_goes_with_it() {
2126 let (mut func, next, twice) = counting_into_nothing();
2127 let stats = simplify(&mut func);
2128 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2129 assert_eq!(lives_in(&func, next), None);
2133 assert_eq!(lives_in(&func, twice), None);
2134 }
2135
2136 #[test]
2137 fn the_functions_own_parameters_stay_whether_or_not_anything_reads_them() {
2138 let mut names = Interner::new();
2141 let signature = Signature::new().with_params(&[Type::int(32)]);
2142 let mut func = Func::new(names.intern("f"), signature);
2143 let entry = func.create_block();
2144 func.append_param(entry, Type::int(32));
2145 Builder::new(&mut func, entry).ret(&[]);
2146 let stats = simplify(&mut func);
2147 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2148 assert_eq!(func[entry].params.len(), 1);
2149 }
2150
2151 #[test]
2152 fn a_parameter_nothing_reads_costs_one_unit_of_fuel_and_stays_without_it() {
2153 let mut func = walking_a_pointer(false);
2154 let stats =
2155 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2156 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2157 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_UNREAD), 1);
2158 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2159 }
2160
2161 #[test]
2162 fn the_counter_that_went_leaves_the_verifier_nothing_to_complain_about() {
2163 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2164 let mut names = Interner::new();
2165 let mut module = Module::new(names.intern("test.c"), &target);
2166 let mut func = walking_a_pointer(false);
2167 simplify(&mut func);
2168 module.add_func(func);
2169 rucc_ir::verify(&module, &names).expect("taking a parameter out left the function whole");
2170 }
2171
2172 #[test]
2173 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
2174 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2177 let mut names = Interner::new();
2178 let mut module = Module::new(names.intern("test.c"), &target);
2179 let mut func = taking_a_condition();
2180 let (carried, arms) = arms(&mut func);
2181 let forwarder = func.create_block();
2182 let param = func.append_param(forwarder, Type::int(32));
2183 let exit = func.create_block();
2184 let arrived = func.append_param(exit, Type::int(32));
2185 let mut build = Builder::new(&mut func, arms[0]);
2186 let mine = build.iconst(Type::int(32), 9);
2187 build.jump(exit, &[mine]);
2188 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
2189 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2190 let mut build = Builder::new(&mut func, exit);
2191 build.icmp(IntPred::Eq, arrived, arrived);
2194 build.ret(&[]);
2195 simplify(&mut func);
2196 module.add_func(func);
2197 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
2198 }
2199
2200 #[test]
2201 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
2202 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
2203 let before = blocks(&func);
2204 let stats =
2205 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2206 assert!(!stats.changed());
2207 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2208 assert_eq!(terminator(&func, 0), Opcode::BrIf);
2209 assert_eq!(blocks(&func), before);
2210 }
2211
2212 #[test]
2213 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
2214 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
2218 let stats =
2219 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2220 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
2221 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2222 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
2225 }
2226
2227 #[test]
2228 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2229 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2230 let mut names = Interner::new();
2231 let mut module = Module::new(names.intern("test.c"), &target);
2232 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
2233 simplify(&mut func);
2234 module.add_func(func);
2235 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2236 }
2237
2238 #[test]
2239 fn the_pass_says_it_preserves_nothing() {
2240 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
2241 }
2242}