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 let mut reached = Vec::new();
398 while let Some(block) = stack.pop() {
399 for &succ in cfg.successors(block) {
400 if !seen[succ.index()] {
401 seen[succ.index()] = true;
402 stack.push(succ);
403 }
404 }
405 reached.push(block);
406 }
407 let mut next = reached;
410 while !next.is_empty() {
411 let mut found = Vec::new();
412 for block in next {
413 for inst in func.insts(block) {
414 if func[inst].opcode != Opcode::BlockAddr {
415 continue;
416 }
417 for call in func.successors(inst) {
418 if !seen[call.block.index()] {
419 seen[call.block.index()] = true;
420 found.push(call.block);
421 }
422 }
423 }
424 }
425 let mut stack = found.clone();
428 while let Some(block) = stack.pop() {
429 for &succ in cfg.successors(block) {
430 if !seen[succ.index()] {
431 seen[succ.index()] = true;
432 stack.push(succ);
433 found.push(succ);
434 }
435 }
436 }
437 next = found;
438 }
439 func.blocks().filter(|block| !seen[block.index()]).collect()
440}
441
442pub(crate) type Edges = HashMap<Block, Vec<(Block, Idx<BlockCall>)>>;
449
450pub(crate) fn incoming(func: &Func) -> Edges {
458 let mut edges: Edges = HashMap::new();
459 for block in func.blocks() {
460 let Some(term) = func.terminator(block) else { continue };
461 for at in func.target_list(term).iter() {
462 edges.entry(func[at].block).or_default().push((block, at));
463 }
464 }
465 edges
466}
467
468fn drop_unread(func: &mut Func, fuel: &mut Fuel, stats: &mut Stats) -> bool {
481 let Some(entry) = func.entry() else { return false };
482 let live = live(func, entry, &addressed(func));
483 let edges = incoming(func);
484 let mut changed = false;
485 let mut gone: HashSet<Value> = HashSet::new();
486 for block in func.blocks().collect::<Vec<Block>>() {
487 let mut taking = Vec::new();
488 for (index, ¶m) in func[block].params.iter().enumerate() {
489 if live.contains(¶m) {
490 continue;
491 }
492 if !fuel.take() {
493 stats.missed(NO_FUEL_UNREAD);
494 continue;
495 }
496 taking.push(index);
497 }
498 if taking.is_empty() {
499 continue;
500 }
501 for _ in &taking {
502 stats.optimized(NOTHING_READS_IT);
503 }
504 gone.extend(taking.iter().map(|&index| func[block].params[index]));
505 take_params(func, block, &taking, edges.get(&block));
506 changed = true;
507 }
508 if !gone.is_empty() {
509 strand(func, gone);
510 }
511 changed
512}
513
514fn strand(func: &mut Func, mut gone: HashSet<Value>) {
536 loop {
537 let mut spread = false;
538 for block in func.blocks().collect::<Vec<Block>>() {
539 for inst in func.insts(block).collect::<Vec<Inst>>() {
540 if !func[func[inst].args].iter().any(|value| gone.contains(value)) {
541 continue;
542 }
543 let results: Vec<Value> = func[inst].results().collect();
544 for result in results {
545 spread |= gone.insert(result);
546 }
547 func.remove_inst(inst);
548 }
549 }
550 if !spread {
553 return;
554 }
555 }
556}
557
558fn live(func: &Func, entry: Block, addressed: &HashSet<Block>) -> HashSet<Value> {
573 let mut where_from: HashMap<Value, (Block, usize)> = HashMap::new();
574 let mut live: HashSet<Value> = HashSet::new();
575 let mut work: Vec<Value> = Vec::new();
576 let seed = |value: Value, live: &mut HashSet<Value>, work: &mut Vec<Value>| {
577 if live.insert(value) {
578 work.push(value);
579 }
580 };
581 for block in func.blocks() {
582 let held = block == entry || addressed.contains(&block);
583 for (index, ¶m) in func[block].params.iter().enumerate() {
584 where_from.insert(param, (block, index));
585 if held {
586 seed(param, &mut live, &mut work);
587 }
588 }
589 for inst in func.insts(block) {
590 if !func.is_terminator(inst) && !func[inst].opcode.has_effects() {
591 continue;
592 }
593 for &value in &func[func[inst].args] {
594 seed(value, &mut live, &mut work);
595 }
596 }
597 }
598
599 let edges = incoming(func);
600 while let Some(value) = work.pop() {
601 match func[value].def {
602 Def::Result { inst, .. } => {
603 for &operand in &func[func[inst].args] {
604 seed(operand, &mut live, &mut work);
605 }
606 }
607 Def::Param { .. } => {
608 let Some(&(block, index)) = where_from.get(&value) else { continue };
609 for &(_, at) in edges.get(&block).into_iter().flatten() {
610 let Some(&arg) = func[func[at].args].get(index) else { continue };
611 seed(arg, &mut live, &mut work);
612 }
613 }
614 }
615 }
616 live
617}
618
619fn straighten(
645 func: &mut Func,
646 fuel: &mut Fuel,
647 stats: &mut Stats,
648 forward: &mut HashMap<Value, Value>,
649) -> bool {
650 let Some(entry) = func.entry() else { return false };
651 let addressed = addressed(func);
652 let mut edges = incoming(func);
653 let mut work: VecDeque<Block> = func.blocks().collect();
654 let mut queued: HashSet<Block> = work.iter().copied().collect();
655 let mut gone: HashSet<Block> = HashSet::new();
656 let mut changed = false;
657 while let Some(block) = work.pop_front() {
658 queued.remove(&block);
659 if gone.contains(&block) {
660 continue;
661 }
662 let mut starved = false;
663 if block != entry {
664 let drop = redundant(func, block, edges.get(&block), forward);
665 let mut taking = Vec::new();
666 for (index, value) in drop {
667 if !fuel.take() {
668 stats.missed(NO_FUEL_PARAM);
669 starved = true;
670 break;
671 }
672 let value = uses::chase(forward, value);
675 forward.insert(func[block].params[index], value);
676 taking.push(index);
677 stats.optimized(SAME_EVERY_WAY);
678 }
679 if !taking.is_empty() {
680 take_params(func, block, &taking, edges.get(&block));
681 requeue(block, &mut work, &mut queued);
684 if let Some(term) = func.terminator(block) {
687 for call in func.successors(term).collect::<Vec<BlockCall>>() {
688 requeue(call.block, &mut work, &mut queued);
689 }
690 }
691 changed = true;
692 }
693 }
694 if starved {
697 break;
698 }
699 let Some((term, into, args)) = forwards(func, block, entry, &addressed, &edges) else {
700 continue;
701 };
702 if !fuel.take() {
703 stats.missed(NO_FUEL_FORWARD);
704 break;
705 }
706 let out = func.target_list(term).iter().next().expect("a jump has a target");
710 if let Some(list) = edges.get_mut(&into) {
711 list.retain(|&(_, at)| at != out);
712 }
713 let ins = edges.remove(&block).unwrap_or_default();
714 for &(_, at) in &ins {
715 let call = func[at];
719 let args = func.push_values(&args);
720 func.set_block_call(at, BlockCall { block: into, args, ..call });
721 }
722 edges.entry(into).or_default().extend(ins.iter().copied());
723 func.remove_block(block);
724 gone.insert(block);
725 stats.optimized(FORWARDED);
726 changed = true;
727 requeue(into, &mut work, &mut queued);
728 for &(from, _) in &ins {
729 requeue(from, &mut work, &mut queued);
730 }
731 }
732 changed
733}
734
735fn requeue(block: Block, work: &mut VecDeque<Block>, queued: &mut HashSet<Block>) {
737 if queued.insert(block) {
738 work.push_back(block);
739 }
740}
741
742fn redundant(
758 func: &Func,
759 block: Block,
760 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
761 forward: &HashMap<Value, Value>,
762) -> Vec<(usize, Value)> {
763 let Some(ins) = ins.filter(|ins| !ins.is_empty()) else { return Vec::new() };
764 let mut found = Vec::new();
765 for (index, ¶m) in func[block].params.iter().enumerate() {
766 let mut only = None;
767 let mut agree = true;
768 for &(_, at) in ins {
769 let list = func[at].args;
770 let Some(&arg) = func[list].get(index) else {
771 agree = false;
774 break;
775 };
776 let arg = uses::chase(forward, arg);
777 if arg == param {
778 continue;
779 }
780 match only {
781 None => only = Some(arg),
782 Some(seen) if seen == arg => {}
783 Some(_) => {
784 agree = false;
785 break;
786 }
787 }
788 }
789 if !agree {
790 continue;
791 }
792 if let Some(value) = only {
793 found.push((index, value));
794 }
795 }
796 found
797}
798
799fn take_params(
804 func: &mut Func,
805 block: Block,
806 taking: &[usize],
807 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
808) {
809 for &(_, at) in ins.into_iter().flatten() {
810 let call = func[at];
811 let kept: Vec<Value> = func[call.args]
812 .iter()
813 .enumerate()
814 .filter(|(index, _)| !taking.contains(index))
815 .map(|(_, &value)| value)
816 .collect();
817 let args = func.push_values(&kept);
818 func.set_block_call(at, BlockCall { args, ..call });
819 }
820 let mut index = 0;
821 func.retain_params(block, |_| {
822 let keep = !taking.contains(&index);
823 index += 1;
824 keep
825 });
826}
827
828fn forwards(
834 func: &Func,
835 block: Block,
836 entry: Block,
837 addressed: &HashSet<Block>,
838 edges: &Edges,
839) -> Option<(Inst, Block, Vec<Value>)> {
840 if block == entry || addressed.contains(&block) || !func[block].params.is_empty() {
841 return None;
842 }
843 let term = func.terminator(block)?;
844 if func[term].opcode != Opcode::Jump {
845 return None;
846 }
847 if func.insts(block).count() != 1 {
850 return None;
851 }
852 let call = func.successors(term).next()?;
853 if call.block == block {
854 return None;
855 }
856 if carrying(func, block, call.block, func[call.args].len(), edges) {
857 return None;
858 }
859 Some((term, call.block, func[call.args].to_vec()))
860}
861
862fn carrying(func: &Func, block: Block, into: Block, args: usize, edges: &Edges) -> bool {
876 if args == 0 {
877 return false;
878 }
879 let ins = edges.get(&block).map_or(0, Vec::len);
880 let after = edges.get(&into).map_or(0, Vec::len) - 1 + ins;
881 if after < 2 {
882 return false;
883 }
884 edges.get(&block).into_iter().flatten().any(|&(from, _)| {
885 let Some(term) = func.terminator(from) else { return false };
886 func.target_list(term).iter().count() >= 2
887 })
888}
889
890fn chains(func: &Func, an: &mut Analyses) -> Vec<Vec<Block>> {
911 let cfg = an.cfg(func);
912 let Some(entry) = cfg.entry() else { return Vec::new() };
913 let addressed = addressed(func);
914 let mut below = HashMap::new();
915 let mut is_below = HashSet::new();
916 for block in func.blocks() {
917 let Some(term) = func.terminator(block) else { continue };
918 if func[term].opcode != Opcode::Jump {
919 continue;
920 }
921 let Some(call) = func.successors(term).next() else { continue };
922 let into = call.block;
923 let preds = cfg.predecessors(into);
924 if into == entry || into == block || addressed.contains(&into) {
925 continue;
926 }
927 if preds.len() != 1 || preds[0] != block {
928 continue;
929 }
930 below.insert(block, into);
931 is_below.insert(into);
932 }
933 let heads = func.blocks().filter(|it| below.contains_key(it) && !is_below.contains(it));
934 heads
935 .map(|head| {
936 let mut chain = vec![head];
937 let mut at = head;
938 while let Some(&next) = below.get(&at) {
939 chain.push(next);
940 at = next;
941 }
942 chain
943 })
944 .collect()
945}
946
947fn addressed(func: &Func) -> HashSet<Block> {
949 let mut taken = HashSet::new();
950 for block in func.blocks() {
951 for inst in func.insts(block) {
952 if func[inst].opcode != Opcode::BlockAddr {
953 continue;
954 }
955 for call in func.successors(inst) {
956 taken.insert(call.block);
957 }
958 }
959 }
960 taken
961}
962
963fn merge(func: &mut Func, head: Block, block: Block, forward: &mut HashMap<Value, Value>) {
970 let term = func.terminator(head).expect("the head of a chain ends in a jump");
971 let call = func.successors(term).next().expect("a jump goes somewhere");
972 let args = func[call.args].to_vec();
973 let params = func[block].params.clone();
974 for (param, arg) in params.into_iter().zip(args) {
975 let arg = uses::chase(forward, arg);
979 forward.insert(param, arg);
980 }
981 func.remove_inst(term);
982 for inst in func.insts(block).collect::<Vec<Inst>>() {
983 func.remove_inst(inst);
984 func.append_inst(head, inst);
985 }
986 func.remove_block(block);
987}
988
989fn known(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
991 let value = resolve(subst, value);
992 if let Some((imm, _)) = constant(func, value) {
993 return Some(imm.unsigned() != 0);
994 }
995 compared(func, value, subst)
996}
997
998fn compared(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1009 let Def::Result { inst, .. } = func[value].def else { return None };
1010 let data = &func[inst];
1011 if data.opcode != Opcode::ICmp {
1012 return None;
1013 }
1014 let Extra::IntPred(pred) = data.extra else { return None };
1015 let args = &func[data.args];
1016 let (lhs, ty) = constant(func, resolve(subst, *args.first()?))?;
1017 let (rhs, _) = constant(func, resolve(subst, *args.get(1)?))?;
1018 Some(crate::fold::compare(pred, lhs, rhs, ty))
1019}
1020
1021#[cfg(test)]
1022mod tests {
1023 use rucc_base::Interner;
1024 use rucc_ir::{
1025 Block, Builder, Def, Flags, Func, Inst, IntPred, MemInfo, MemOrder, Module, Opcode,
1026 Restrict, Signature, Type, Value,
1027 };
1028 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1029
1030 use super::SimplifyCfg;
1031 use crate::stats::Kind;
1032 use crate::testing::graph;
1033 use crate::{Fuel, Pass, Preserved, Stats};
1034
1035 fn simplify(func: &mut Func) -> Stats {
1037 SimplifyCfg.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1038 }
1039
1040 fn blocks(func: &Func) -> Vec<usize> {
1042 func.blocks().map(Block::index).collect()
1043 }
1044
1045 fn terminator(func: &Func, block: usize) -> Opcode {
1047 let block = Block::from_usize(block);
1048 func[func.terminator(block).expect("every block here has one")].opcode
1049 }
1050
1051 fn goes_to(func: &Func, block: usize) -> Vec<usize> {
1053 let block = Block::from_usize(block);
1054 let term = func.terminator(block).expect("every block here has one");
1055 func.successors(term).map(|call| call.block.index()).collect()
1056 }
1057
1058 fn lives_in(func: &Func, value: Value) -> Option<usize> {
1064 let Def::Result { inst, .. } = func[value].def else { return None };
1065 func.block_of(inst).map(Block::index)
1066 }
1067
1068 fn diamond(cond: impl FnOnce(&mut Builder<'_>) -> Value) -> (Func, [Value; 2]) {
1075 let mut names = Interner::new();
1076 let mut func = Func::new(names.intern("f"), Signature::new());
1077 let entry = func.create_block();
1078 let then_block = func.create_block();
1079 let else_block = func.create_block();
1080 let join = func.create_block();
1081 let mut build = Builder::new(&mut func, entry);
1082 let cond = cond(&mut build);
1083 build.br_if(cond, then_block, &[], else_block, &[]);
1084 let mut marks = Vec::new();
1085 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1086 let mut build = Builder::new(&mut func, arm);
1087 marks.push(build.iconst(Type::int(32), mark));
1088 build.jump(join, &[]);
1089 }
1090 let mut build = Builder::new(&mut func, join);
1091 build.ret(&[]);
1092 (func, [marks[0], marks[1]])
1093 }
1094
1095 #[test]
1096 fn a_branch_on_a_true_constant_becomes_a_jump_to_the_first_arm() {
1097 let (mut func, [taken, other]) = diamond(|build| build.iconst(Type::int(1), 1));
1098 let stats = simplify(&mut func);
1099 assert!(stats.changed());
1100 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1101 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1104 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1105 assert_eq!(lives_in(&func, taken), Some(0));
1106 assert_eq!(lives_in(&func, other), None);
1107 assert_eq!(blocks(&func), [0]);
1108 }
1109
1110 #[test]
1111 fn a_branch_on_a_false_constant_becomes_a_jump_to_the_second_arm() {
1112 let (mut func, [other, taken]) = diamond(|build| build.iconst(Type::int(1), 0));
1113 assert!(simplify(&mut func).changed());
1114 assert_eq!(lives_in(&func, taken), Some(0));
1115 assert_eq!(lives_in(&func, other), None);
1116 assert_eq!(blocks(&func), [0]);
1117 }
1118
1119 #[test]
1120 fn folding_a_branch_and_merging_what_it_leaves_are_two_things_fuel_buys_apart() {
1121 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1124 let stats =
1125 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1126 assert_eq!(terminator(&func, 0), Opcode::Jump);
1127 assert_eq!(goes_to(&func, 0), [1]);
1128 assert_eq!(blocks(&func), [0, 1, 3]);
1129 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
1130 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
1133 }
1134
1135 #[test]
1136 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
1137 let cases: &[(IntPred, i128, i128, bool)] = &[
1141 (IntPred::Eq, 7, 7, true),
1142 (IntPred::Eq, 7, 8, false),
1143 (IntPred::Ne, 7, 8, true),
1144 (IntPred::Ne, 7, 7, false),
1145 (IntPred::Slt, -1, 1, true),
1146 (IntPred::Slt, 1, -1, false),
1147 (IntPred::Sle, -1, -1, true),
1148 (IntPred::Sle, 1, -1, false),
1149 (IntPred::Sgt, 1, -1, true),
1150 (IntPred::Sgt, -1, 1, false),
1151 (IntPred::Sge, -1, -1, true),
1152 (IntPred::Sge, -1, 1, false),
1153 (IntPred::Ult, 1, -1, true),
1154 (IntPred::Ult, -1, 1, false),
1155 (IntPred::Ule, -1, -1, true),
1156 (IntPred::Ule, -1, 1, false),
1157 (IntPred::Ugt, -1, 1, true),
1158 (IntPred::Ugt, 1, -1, false),
1159 (IntPred::Uge, -1, -1, true),
1160 (IntPred::Uge, 1, -1, false),
1161 ];
1162 for &(pred, lhs, rhs, taken) in cases {
1163 let (mut func, marks) = diamond(|build| {
1164 let lhs = build.iconst(Type::int(32), lhs);
1165 let rhs = build.iconst(Type::int(32), rhs);
1166 build.icmp(pred, lhs, rhs)
1167 });
1168 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
1169 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
1170 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
1171 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
1172 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
1173 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
1174 }
1175 }
1176
1177 #[test]
1178 fn a_branch_on_something_nobody_knows_is_left_alone() {
1179 let mut names = Interner::new();
1180 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
1181 let entry = func.create_block();
1182 let then_block = func.create_block();
1183 let else_block = func.create_block();
1184 let cond = func.append_param(entry, Type::int(1));
1185 let mut build = Builder::new(&mut func, entry);
1186 build.br_if(cond, then_block, &[], else_block, &[]);
1187 for arm in [then_block, else_block] {
1188 let mut build = Builder::new(&mut func, arm);
1189 build.ret(&[]);
1190 }
1191 let stats = simplify(&mut func);
1192 assert!(!stats.changed());
1193 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1194 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1195 assert_eq!(blocks(&func), [0, 1, 2]);
1196 }
1197
1198 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1201 let mut names = Interner::new();
1202 let mut func = Func::new(names.intern("f"), Signature::new());
1203 let entry = func.create_block();
1204 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1205 let mut build = Builder::new(&mut func, entry);
1206 let value = build.iconst(Type::int(32), on);
1207 let pairs: Vec<(i128, Block)> =
1208 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1209 build.switch(value, arms[0], &pairs);
1210 let mut marks = Vec::new();
1211 for (at, &arm) in arms.iter().enumerate() {
1212 let mut build = Builder::new(&mut func, arm);
1213 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1214 build.ret(&[]);
1215 }
1216 (func, marks)
1217 }
1218
1219 #[test]
1220 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1221 let (mut func, marks) = switched(5, &[4, 5]);
1222 assert!(simplify(&mut func).changed());
1223 assert_eq!(lives_in(&func, marks[2]), Some(0));
1224 assert_eq!(lives_in(&func, marks[0]), None);
1225 assert_eq!(lives_in(&func, marks[1]), None);
1226 assert_eq!(blocks(&func), [0]);
1227 }
1228
1229 #[test]
1230 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1231 let (mut func, marks) = switched(9, &[4]);
1232 assert!(simplify(&mut func).changed());
1233 assert_eq!(lives_in(&func, marks[0]), Some(0));
1234 assert_eq!(lives_in(&func, marks[1]), None);
1235 assert_eq!(blocks(&func), [0]);
1236 }
1237
1238 #[test]
1239 fn the_arguments_travel_with_the_edge_that_survives() {
1240 let mut names = Interner::new();
1246 let mut func = Func::new(names.intern("f"), Signature::new());
1247 let entry = func.create_block();
1248 let join = func.create_block();
1249 let param = func.append_param(join, Type::int(32));
1250 let mut build = Builder::new(&mut func, entry);
1251 let cond = build.iconst(Type::int(1), 0);
1252 let taken = build.iconst(Type::int(32), 11);
1253 let other = build.iconst(Type::int(32), 22);
1254 build.br_if(cond, join, &[other], join, &[taken]);
1255 let mut build = Builder::new(&mut func, join);
1256 build.ret(&[param]);
1257 assert!(simplify(&mut func).changed());
1258 assert_eq!(blocks(&func), [0]);
1262 let term = func.terminator(entry).expect("the entry has one");
1263 assert_eq!(func[func[term].args], [taken]);
1264 assert_ne!(func[func[term].args], [param]);
1265 }
1266
1267 #[test]
1268 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1269 let mut names = Interner::new();
1273 let signature = Signature::new().with_params(&[Type::int(1)]);
1274 let mut func = Func::new(names.intern("f"), signature);
1275 let entry = func.create_block();
1276 let join = func.create_block();
1277 let cond = func.append_param(entry, Type::int(1));
1278 let mut build = Builder::new(&mut func, entry);
1279 build.br_if(cond, join, &[], join, &[]);
1280 let mut build = Builder::new(&mut func, join);
1281 build.ret(&[]);
1282 let stats = simplify(&mut func);
1283 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1284 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1285 assert_eq!(blocks(&func), [0]);
1286 assert_eq!(terminator(&func, 0), Opcode::Return);
1287 }
1288
1289 #[test]
1290 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1291 let mut names = Interner::new();
1292 let signature = Signature::new().with_params(&[Type::int(32)]);
1293 let mut func = Func::new(names.intern("f"), signature);
1294 let entry = func.create_block();
1295 let join = func.create_block();
1296 let value = func.append_param(entry, Type::int(32));
1297 let mut build = Builder::new(&mut func, entry);
1298 build.switch(value, join, &[(4, join), (5, join)]);
1299 let mut build = Builder::new(&mut func, join);
1300 build.ret(&[]);
1301 let stats = simplify(&mut func);
1302 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1303 assert_eq!(blocks(&func), [0]);
1304 }
1305
1306 #[test]
1307 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1308 let mut names = Interner::new();
1311 let signature = Signature::new().with_params(&[Type::int(1)]);
1312 let mut func = Func::new(names.intern("f"), signature);
1313 let entry = func.create_block();
1314 let join = func.create_block();
1315 let cond = func.append_param(entry, Type::int(1));
1316 let param = func.append_param(join, Type::int(32));
1317 let mut build = Builder::new(&mut func, entry);
1318 let first = build.iconst(Type::int(32), 11);
1319 let second = build.iconst(Type::int(32), 22);
1320 build.br_if(cond, join, &[first], join, &[second]);
1321 let mut build = Builder::new(&mut func, join);
1322 build.ret(&[param]);
1325 let stats = simplify(&mut func);
1326 assert!(!stats.changed());
1327 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1328 assert_eq!(blocks(&func), [0, 1]);
1329 }
1330
1331 #[test]
1332 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1333 let mut names = Interner::new();
1336 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1337 let entry = func.create_block();
1338 let dead = func.create_block();
1339 let shared = func.create_block();
1340 let exit = func.create_block();
1341 let x = func.append_param(entry, Type::int(32));
1342 let mut build = Builder::new(&mut func, entry);
1343 let never = build.iconst(Type::int(1), 0);
1344 build.switch(x, exit, &[(0, dead), (1, shared)]);
1345 let mut build = Builder::new(&mut func, dead);
1349 build.iconst(Type::int(32), 1);
1350 build.br_if(never, shared, &[], exit, &[]);
1351 for arm in [shared, exit] {
1352 let mut build = Builder::new(&mut func, arm);
1353 build.ret(&[]);
1354 }
1355 let stats = simplify(&mut func);
1356 assert!(stats.changed());
1357 assert_eq!(terminator(&func, 0), Opcode::Switch);
1360 assert_eq!(goes_to(&func, 1), [3]);
1361 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1362 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1363 }
1364
1365 #[test]
1366 fn a_block_whose_address_is_taken_is_not_removed() {
1367 let mut names = Interner::new();
1371 let mut func = Func::new(names.intern("f"), Signature::new());
1372 let entry = func.create_block();
1373 let labelled = func.create_block();
1374 let arm = func.create_block();
1375 let mut build = Builder::new(&mut func, entry);
1376 let cond = build.iconst(Type::int(1), 1);
1377 let addr = build.block_addr(labelled);
1378 build.br_if(cond, arm, &[], labelled, &[]);
1379 let mut build = Builder::new(&mut func, arm);
1380 build.indirect_br(addr, &[labelled]);
1381 let mut build = Builder::new(&mut func, labelled);
1382 build.ret(&[]);
1383 assert!(simplify(&mut func).changed());
1384 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1385 assert_eq!(blocks(&func), [0, 1]);
1388 assert_eq!(goes_to(&func, 0), [1]);
1389 }
1390
1391 #[test]
1392 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1393 let mut names = Interner::new();
1396 let mut func = Func::new(names.intern("f"), Signature::new());
1397 let entry = func.create_block();
1398 let dead = func.create_block();
1399 let labelled = func.create_block();
1400 let mut build = Builder::new(&mut func, entry);
1401 let cond = build.iconst(Type::int(1), 1);
1402 build.br_if(cond, entry, &[], dead, &[]);
1403 let mut build = Builder::new(&mut func, dead);
1404 let addr = build.block_addr(labelled);
1405 build.indirect_br(addr, &[labelled]);
1406 let mut build = Builder::new(&mut func, labelled);
1407 build.ret(&[]);
1408 assert!(simplify(&mut func).changed());
1409 assert_eq!(blocks(&func), [0]);
1410 }
1411
1412 #[test]
1413 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1414 let mut func = graph(&[&[], &[]]);
1419 let stats = simplify(&mut func);
1420 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1421 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1422 assert_eq!(blocks(&func), [0]);
1423 }
1424
1425 #[test]
1426 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1427 let mut names = Interner::new();
1430 let mut func = Func::new(names.intern("f"), Signature::new());
1431 let entry = func.create_block();
1432 let middle = func.create_block();
1433 let last = func.create_block();
1434 let mut build = Builder::new(&mut func, entry);
1435 build.iconst(Type::int(32), 1);
1436 build.jump(middle, &[]);
1437 let mut build = Builder::new(&mut func, middle);
1438 build.iconst(Type::int(32), 2);
1439 build.jump(last, &[]);
1440 let mut build = Builder::new(&mut func, last);
1441 build.ret(&[]);
1442 let stats = simplify(&mut func);
1443 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1446 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1447 assert_eq!(blocks(&func), [0]);
1448 assert_eq!(terminator(&func, 0), Opcode::Return);
1449 }
1450
1451 #[test]
1452 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1453 let mut names = Interner::new();
1456 let signature = Signature::new().with_params(&[Type::int(1)]);
1457 let mut func = Func::new(names.intern("f"), signature);
1458 let entry = func.create_block();
1459 let then_block = func.create_block();
1460 let else_block = func.create_block();
1461 let join = func.create_block();
1462 let cond = func.append_param(entry, Type::int(1));
1463 let mut build = Builder::new(&mut func, entry);
1464 build.br_if(cond, then_block, &[], else_block, &[]);
1465 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1466 let mut build = Builder::new(&mut func, arm);
1469 build.iconst(Type::int(32), mark);
1470 build.jump(join, &[]);
1471 }
1472 let mut build = Builder::new(&mut func, join);
1473 build.ret(&[]);
1474 let stats = simplify(&mut func);
1475 assert!(!stats.changed());
1476 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1477 }
1478
1479 #[test]
1480 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1481 let mut names = Interner::new();
1484 let signature = Signature::new().with_params(&[Type::int(1)]);
1485 let mut func = Func::new(names.intern("f"), signature);
1486 let entry = func.create_block();
1487 let arm = func.create_block();
1488 let exit = func.create_block();
1489 let cond = func.append_param(entry, Type::int(1));
1490 let mut build = Builder::new(&mut func, entry);
1491 build.br_if(cond, arm, &[], exit, &[]);
1492 for block in [arm, exit] {
1493 let mut build = Builder::new(&mut func, block);
1494 build.ret(&[]);
1495 }
1496 let stats = simplify(&mut func);
1497 assert!(!stats.changed());
1498 assert_eq!(blocks(&func), [0, 1, 2]);
1499 }
1500
1501 #[test]
1502 fn the_entry_block_is_never_the_one_that_moves() {
1503 let mut names = Interner::new();
1507 let signature = Signature::new().with_params(&[Type::int(1)]);
1508 let mut func = Func::new(names.intern("f"), signature);
1509 let entry = func.create_block();
1510 let latch = func.create_block();
1511 let exit = func.create_block();
1512 let cond = func.append_param(entry, Type::int(1));
1513 let mut build = Builder::new(&mut func, entry);
1514 build.br_if(cond, latch, &[], exit, &[]);
1515 let mut build = Builder::new(&mut func, latch);
1517 build.iconst(Type::int(32), 1);
1518 build.jump(entry, &[]);
1519 let mut build = Builder::new(&mut func, exit);
1520 build.ret(&[]);
1521 let stats = simplify(&mut func);
1522 assert!(!stats.changed());
1523 assert_eq!(blocks(&func), [0, 1, 2]);
1524 }
1525
1526 #[test]
1527 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1528 let mut names = Interner::new();
1531 let mut func = Func::new(names.intern("f"), Signature::new());
1532 let entry = func.create_block();
1533 let middle = func.create_block();
1534 let labelled = func.create_block();
1535 let mut build = Builder::new(&mut func, entry);
1536 build.block_addr(labelled);
1537 build.jump(middle, &[]);
1538 let mut build = Builder::new(&mut func, middle);
1541 build.iconst(Type::int(32), 1);
1542 build.jump(labelled, &[]);
1543 let mut build = Builder::new(&mut func, labelled);
1544 build.ret(&[]);
1545 let stats = simplify(&mut func);
1546 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1549 assert_eq!(blocks(&func), [0, 2]);
1550 }
1551
1552 #[test]
1553 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1554 let mut names = Interner::new();
1555 let mut func = Func::new(names.intern("f"), Signature::new());
1556 let entry = func.create_block();
1557 let below = func.create_block();
1558 let param = func.append_param(below, Type::int(32));
1559 let mut build = Builder::new(&mut func, entry);
1560 let arg = build.iconst(Type::int(32), 7);
1561 build.jump(below, &[arg]);
1562 let mut build = Builder::new(&mut func, below);
1563 build.ret(&[param]);
1564 assert!(simplify(&mut func).changed());
1565 assert_eq!(blocks(&func), [0]);
1566 let term = func.terminator(entry).expect("the entry has one");
1567 assert_eq!(func[func[term].args], [arg]);
1568 }
1569
1570 #[test]
1571 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1572 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 last = func.create_block();
1580 let carried = func.append_param(middle, Type::int(32));
1581 let arrived = func.append_param(last, Type::int(32));
1582 let mut build = Builder::new(&mut func, entry);
1583 let arg = build.iconst(Type::int(32), 7);
1584 build.jump(middle, &[arg]);
1585 let mut build = Builder::new(&mut func, middle);
1586 build.jump(last, &[carried]);
1587 let mut build = Builder::new(&mut func, last);
1588 build.ret(&[arrived]);
1589 assert!(simplify(&mut func).changed());
1590 assert_eq!(blocks(&func), [0]);
1591 let term = func.terminator(entry).expect("the entry has one");
1592 assert_eq!(func[func[term].args], [arg]);
1593 }
1594
1595 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1603 let entry = func.create_block();
1604 let first = func.create_block();
1605 let second = func.create_block();
1606 let cond = func.append_param(entry, Type::int(1));
1607 let mut build = Builder::new(func, entry);
1608 let carried = build.iconst(Type::int(32), 7);
1609 build.br_if(cond, first, &[], second, &[]);
1610 for (arm, mark) in [(first, 111), (second, 222)] {
1611 let mut build = Builder::new(func, arm);
1612 build.iconst(Type::int(32), mark);
1613 }
1614 (carried, [first, second])
1615 }
1616
1617 fn taking_a_condition() -> Func {
1619 let mut names = Interner::new();
1620 let signature = Signature::new().with_params(&[Type::int(1)]);
1621 Func::new(names.intern("f"), signature)
1622 }
1623
1624 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1626 let block = Block::from_usize(block);
1627 let term = func.terminator(block).expect("every block here has one");
1628 let call = func.successors(term).nth(edge).expect("the edge is there");
1629 func[call.args].to_vec()
1630 }
1631
1632 #[test]
1633 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1634 let mut func = taking_a_condition();
1637 let (_, arms) = arms(&mut func);
1638 let forwarder = func.create_block();
1639 let exit = func.create_block();
1640 for arm in arms {
1641 Builder::new(&mut func, arm).jump(forwarder, &[]);
1642 }
1643 Builder::new(&mut func, forwarder).jump(exit, &[]);
1644 Builder::new(&mut func, exit).ret(&[]);
1645 let stats = simplify(&mut func);
1646 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1647 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1648 assert_eq!(goes_to(&func, 1), [4]);
1649 assert_eq!(goes_to(&func, 2), [4]);
1650 }
1651
1652 #[test]
1653 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1654 let mut func = taking_a_condition();
1661 let (carried, [arm, above]) = arms(&mut func);
1662 let forwarder = func.create_block();
1663 let exit = func.create_block();
1664 let other = func.append_param(exit, Type::int(32));
1665 let mut build = Builder::new(&mut func, arm);
1666 let mine = build.iconst(Type::int(32), 9);
1667 build.jump(exit, &[mine]);
1668 Builder::new(&mut func, above).jump(forwarder, &[]);
1669 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1670 Builder::new(&mut func, exit).ret(&[other]);
1671 let stats = simplify(&mut func);
1672 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1673 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1674 assert_eq!(carries(&func, 2, 0), [carried]);
1677 assert_eq!(carries(&func, 1, 0), [mine]);
1678 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1680 }
1681
1682 #[test]
1683 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1684 let mut func = taking_a_condition();
1689 let (carried, [arm, forwarder]) = arms(&mut func);
1690 let exit = func.create_block();
1691 let other = func.append_param(exit, Type::int(32));
1692 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1694 func.remove_inst(inst);
1695 }
1696 let mut build = Builder::new(&mut func, arm);
1697 let mine = build.iconst(Type::int(32), 9);
1698 build.jump(exit, &[mine]);
1699 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1700 Builder::new(&mut func, exit).ret(&[other]);
1701 let stats = simplify(&mut func);
1702 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1703 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1704 }
1705
1706 #[test]
1707 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1708 let mut func = taking_a_condition();
1711 let (_, [arm, forwarder]) = arms(&mut func);
1712 let exit = func.create_block();
1713 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1714 func.remove_inst(inst);
1715 }
1716 Builder::new(&mut func, arm).jump(exit, &[]);
1717 Builder::new(&mut func, forwarder).jump(exit, &[]);
1718 Builder::new(&mut func, exit).ret(&[]);
1719 let stats = simplify(&mut func);
1720 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1721 assert_eq!(blocks(&func), [0, 1, 3]);
1722 }
1723
1724 #[test]
1725 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1726 let mut names = Interner::new();
1729 let mut func = Func::new(names.intern("f"), Signature::new());
1730 let entry = func.create_block();
1731 let spin = func.create_block();
1732 Builder::new(&mut func, entry).jump(spin, &[]);
1733 Builder::new(&mut func, spin).jump(spin, &[]);
1734 let stats = simplify(&mut func);
1735 assert!(!stats.changed());
1736 assert_eq!(blocks(&func), [0, 1]);
1737 }
1738
1739 #[test]
1740 fn the_entry_block_is_never_the_forwarder_that_goes() {
1741 let mut names = Interner::new();
1745 let mut func = Func::new(names.intern("f"), Signature::new());
1746 let entry = func.create_block();
1747 let below = func.create_block();
1748 Builder::new(&mut func, entry).jump(below, &[]);
1749 let mut build = Builder::new(&mut func, below);
1750 build.iconst(Type::int(32), 1);
1751 build.ret(&[]);
1752 let stats = simplify(&mut func);
1753 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1754 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1755 assert_eq!(blocks(&func), [0]);
1756 }
1757
1758 #[test]
1759 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1760 let mut names = Interner::new();
1764 let mut func = Func::new(names.intern("f"), Signature::new());
1765 let entry = func.create_block();
1766 let labelled = func.create_block();
1767 let exit = func.create_block();
1768 let mut build = Builder::new(&mut func, entry);
1769 let addr = build.block_addr(labelled);
1770 build.indirect_br(addr, &[labelled]);
1771 Builder::new(&mut func, labelled).jump(exit, &[]);
1772 let mut build = Builder::new(&mut func, exit);
1773 build.iconst(Type::int(32), 1);
1774 build.ret(&[]);
1775 let stats = simplify(&mut func);
1776 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1777 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1778 }
1779
1780 #[test]
1781 fn a_run_of_forwarders_comes_out_as_one_edge() {
1782 let mut func = taking_a_condition();
1783 let (_, arms) = arms(&mut func);
1784 let first = func.create_block();
1785 let second = func.create_block();
1786 let exit = func.create_block();
1787 for arm in arms {
1788 Builder::new(&mut func, arm).jump(first, &[]);
1789 }
1790 Builder::new(&mut func, first).jump(second, &[]);
1791 Builder::new(&mut func, second).jump(exit, &[]);
1792 Builder::new(&mut func, exit).ret(&[]);
1793 let stats = simplify(&mut func);
1794 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1795 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1796 assert_eq!(goes_to(&func, 1), [5]);
1797 assert_eq!(goes_to(&func, 2), [5]);
1798 }
1799
1800 #[test]
1801 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1802 let mut func = taking_a_condition();
1805 let (carried, arms) = arms(&mut func);
1806 let join = func.create_block();
1807 let param = func.append_param(join, Type::int(32));
1808 for arm in arms {
1809 Builder::new(&mut func, arm).jump(join, &[carried]);
1810 }
1811 Builder::new(&mut func, join).ret(&[param]);
1812 let stats = simplify(&mut func);
1813 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1814 assert!(func[Block::from_usize(3)].params.is_empty());
1815 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1817 assert_eq!(func[func[term].args], [carried]);
1818 assert!(carries(&func, 1, 0).is_empty());
1821 assert!(carries(&func, 2, 0).is_empty());
1822 }
1823
1824 #[test]
1825 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1826 let mut func = taking_a_condition();
1827 let (carried, arms) = arms(&mut func);
1828 let join = func.create_block();
1829 let param = func.append_param(join, Type::int(32));
1830 let mut build = Builder::new(&mut func, arms[0]);
1831 let mine = build.iconst(Type::int(32), 9);
1832 build.jump(join, &[mine]);
1833 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1834 Builder::new(&mut func, join).ret(&[param]);
1835 let stats = simplify(&mut func);
1836 assert!(!stats.changed());
1837 assert_eq!(func[Block::from_usize(3)].params, [param]);
1838 }
1839
1840 #[test]
1841 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1842 let mut names = Interner::new();
1846 let signature = Signature::new().with_params(&[Type::int(1)]);
1847 let mut func = Func::new(names.intern("f"), signature);
1848 let entry = func.create_block();
1849 let header = func.create_block();
1850 let latch = func.create_block();
1851 let exit = func.create_block();
1852 let cond = func.append_param(entry, Type::int(1));
1853 let param = func.append_param(header, Type::int(32));
1854 let mut build = Builder::new(&mut func, entry);
1855 let init = build.iconst(Type::int(32), 7);
1856 build.jump(header, &[init]);
1857 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1858 let mut build = Builder::new(&mut func, latch);
1859 build.iconst(Type::int(32), 1);
1860 build.jump(header, &[param]);
1861 Builder::new(&mut func, exit).ret(&[param]);
1862 let stats = simplify(&mut func);
1863 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1864 assert!(func[Block::from_usize(1)].params.is_empty());
1865 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1866 assert_eq!(func[func[term].args], [init]);
1867 }
1868
1869 #[test]
1870 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1871 let mut names = Interner::new();
1875 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1876 let mut func = Func::new(names.intern("f"), signature);
1877 let entry = func.create_block();
1878 let latch = func.create_block();
1879 let exit = func.create_block();
1880 let cond = func.append_param(entry, Type::int(1));
1881 let x = func.append_param(entry, Type::int(32));
1882 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1883 let mut build = Builder::new(&mut func, latch);
1884 let one = build.iconst(Type::int(1), 1);
1885 let seven = build.iconst(Type::int(32), 7);
1886 build.jump(entry, &[one, seven]);
1887 Builder::new(&mut func, exit).ret(&[x]);
1888 let stats = simplify(&mut func);
1889 assert!(!stats.changed());
1890 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1891 }
1892
1893 #[test]
1894 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1895 let mut func = taking_a_condition();
1899 let (carried, arms) = arms(&mut func);
1900 let join = func.create_block();
1901 let inner = func.append_param(join, Type::int(32));
1902 let left = func.create_block();
1903 let right = func.create_block();
1904 let last = func.create_block();
1905 let outer = func.append_param(last, Type::int(32));
1906 for arm in arms {
1907 Builder::new(&mut func, arm).jump(join, &[carried]);
1908 }
1909 let cond = func[Block::from_usize(0)].params[0];
1910 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1911 let mut build = Builder::new(&mut func, left);
1912 build.iconst(Type::int(32), 1);
1913 build.jump(last, &[inner]);
1914 let mut build = Builder::new(&mut func, right);
1915 build.iconst(Type::int(32), 2);
1916 build.jump(last, &[carried]);
1917 Builder::new(&mut func, last).ret(&[outer]);
1918 let stats = simplify(&mut func);
1919 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1920 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1921 assert_eq!(func[func[term].args], [carried]);
1922 }
1923
1924 #[test]
1925 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1926 let mut func = taking_a_condition();
1930 let (carried, arms) = arms(&mut func);
1931 let forwarder = func.create_block();
1932 let param = func.append_param(forwarder, Type::int(32));
1933 let exit = func.create_block();
1934 let arrived = func.append_param(exit, Type::int(32));
1935 for arm in arms {
1936 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1937 }
1938 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1939 Builder::new(&mut func, exit).ret(&[arrived]);
1940 let stats = simplify(&mut func);
1941 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1942 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1945 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1946 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
1947 assert_eq!(func[func[term].args], [carried]);
1948 }
1949
1950 #[test]
1951 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
1952 let mut func = taking_a_condition();
1955 let (carried, arms) = arms(&mut func);
1956 let forwarder = func.create_block();
1957 let param = func.append_param(forwarder, Type::int(32));
1958 let exit = func.create_block();
1959 let arrived = func.append_param(exit, Type::int(32));
1962 for arm in arms {
1963 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1964 }
1965 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1966 Builder::new(&mut func, exit).ret(&[arrived]);
1967 let stats =
1968 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1969 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1970 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1971 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
1972 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
1973 }
1974
1975 fn walking_a_pointer(on_counter: bool) -> Func {
1984 let mut names = Interner::new();
1985 let signature = Signature::new().with_params(&[Type::int(64)]);
1986 let mut func = Func::new(names.intern("f"), signature);
1987 let entry = func.create_block();
1988 let head = func.create_block();
1989 let out = func.create_block();
1990 let end = func.append_param(entry, Type::int(64));
1991 let counter = func.append_param(head, Type::int(32));
1992 let pointer = func.append_param(head, Type::int(64));
1993 let mut build = Builder::new(&mut func, entry);
1994 let from_zero = build.iconst(Type::int(32), 0);
1995 let from_start = build.iconst(Type::int(64), 0);
1996 build.jump(head, &[from_zero, from_start]);
1997 let mut build = Builder::new(&mut func, head);
1998 let one = build.iconst(Type::int(32), 1);
1999 let eight = build.iconst(Type::int(64), 8);
2000 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2001 let along = build.binary(Opcode::Add, pointer, eight, Flags::NONE);
2002 let address = build.unary(Opcode::IntToPtr, pointer, Type::PTR);
2005 let info = MemInfo {
2006 size: 8,
2007 align: 8,
2008 order: MemOrder::NotAtomic,
2009 tbaa: None,
2010 owns: 0,
2011 restrict: Restrict::NONE,
2012 };
2013 build.store(eight, address, info, Flags::NONE);
2014 let going = if on_counter {
2015 let limit = build.iconst(Type::int(32), 10);
2016 build.icmp(IntPred::Ne, next, limit)
2017 } else {
2018 build.icmp(IntPred::Ne, along, end)
2019 };
2020 build.br_if(going, head, &[next, along], out, &[]);
2021 Builder::new(&mut func, out).ret(&[]);
2022 func
2023 }
2024
2025 #[test]
2026 fn a_counter_the_loop_stopped_asking_about_stops_going_round() {
2027 let mut func = walking_a_pointer(false);
2028 let stats = simplify(&mut func);
2029 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2030 assert_eq!(func[Block::from_usize(1)].params.len(), 1);
2032 assert_eq!(carries(&func, 1, 0).len(), 1);
2034 assert_eq!(carries(&func, 0, 0).len(), 1);
2035 }
2036
2037 #[test]
2038 fn a_counter_the_loop_still_asks_about_goes_round_exactly_as_before() {
2039 let mut func = walking_a_pointer(true);
2040 let stats = simplify(&mut func);
2041 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2042 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2043 }
2044
2045 fn counting_into_nothing() -> (Func, Value, Value) {
2052 let mut names = Interner::new();
2053 let signature = Signature::new().with_params(&[Type::int(32)]);
2054 let mut func = Func::new(names.intern("f"), signature);
2055 let entry = func.create_block();
2056 let head = func.create_block();
2057 let out = func.create_block();
2058 let limit = func.append_param(entry, Type::int(32));
2059 let counter = func.append_param(head, Type::int(32));
2060 let mut build = Builder::new(&mut func, entry);
2061 let zero = build.iconst(Type::int(32), 0);
2062 build.jump(head, &[zero]);
2063 let mut build = Builder::new(&mut func, head);
2064 let one = build.iconst(Type::int(32), 1);
2065 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2066 let twice = build.binary(Opcode::Add, next, next, Flags::NONE);
2067 let going = build.icmp(IntPred::Ne, limit, one);
2068 build.br_if(going, head, &[next], out, &[]);
2069 Builder::new(&mut func, out).ret(&[]);
2070 (func, next, twice)
2071 }
2072
2073 #[test]
2074 fn what_was_reading_a_parameter_nothing_reads_goes_with_it() {
2075 let (mut func, next, twice) = counting_into_nothing();
2076 let stats = simplify(&mut func);
2077 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2078 assert_eq!(lives_in(&func, next), None);
2082 assert_eq!(lives_in(&func, twice), None);
2083 }
2084
2085 #[test]
2086 fn the_functions_own_parameters_stay_whether_or_not_anything_reads_them() {
2087 let mut names = Interner::new();
2090 let signature = Signature::new().with_params(&[Type::int(32)]);
2091 let mut func = Func::new(names.intern("f"), signature);
2092 let entry = func.create_block();
2093 func.append_param(entry, Type::int(32));
2094 Builder::new(&mut func, entry).ret(&[]);
2095 let stats = simplify(&mut func);
2096 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2097 assert_eq!(func[entry].params.len(), 1);
2098 }
2099
2100 #[test]
2101 fn a_parameter_nothing_reads_costs_one_unit_of_fuel_and_stays_without_it() {
2102 let mut func = walking_a_pointer(false);
2103 let stats =
2104 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2105 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2106 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_UNREAD), 1);
2107 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2108 }
2109
2110 #[test]
2111 fn the_counter_that_went_leaves_the_verifier_nothing_to_complain_about() {
2112 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2113 let mut names = Interner::new();
2114 let mut module = Module::new(names.intern("test.c"), &target);
2115 let mut func = walking_a_pointer(false);
2116 simplify(&mut func);
2117 module.add_func(func);
2118 rucc_ir::verify(&module, &names).expect("taking a parameter out left the function whole");
2119 }
2120
2121 #[test]
2122 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
2123 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2126 let mut names = Interner::new();
2127 let mut module = Module::new(names.intern("test.c"), &target);
2128 let mut func = taking_a_condition();
2129 let (carried, arms) = arms(&mut func);
2130 let forwarder = func.create_block();
2131 let param = func.append_param(forwarder, Type::int(32));
2132 let exit = func.create_block();
2133 let arrived = func.append_param(exit, Type::int(32));
2134 let mut build = Builder::new(&mut func, arms[0]);
2135 let mine = build.iconst(Type::int(32), 9);
2136 build.jump(exit, &[mine]);
2137 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
2138 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2139 let mut build = Builder::new(&mut func, exit);
2140 build.icmp(IntPred::Eq, arrived, arrived);
2143 build.ret(&[]);
2144 simplify(&mut func);
2145 module.add_func(func);
2146 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
2147 }
2148
2149 #[test]
2150 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
2151 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
2152 let before = blocks(&func);
2153 let stats =
2154 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2155 assert!(!stats.changed());
2156 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2157 assert_eq!(terminator(&func, 0), Opcode::BrIf);
2158 assert_eq!(blocks(&func), before);
2159 }
2160
2161 #[test]
2162 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
2163 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
2167 let stats =
2168 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2169 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
2170 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2171 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
2174 }
2175
2176 #[test]
2177 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2178 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2179 let mut names = Interner::new();
2180 let mut module = Module::new(names.intern("test.c"), &target);
2181 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
2182 simplify(&mut func);
2183 module.add_func(func);
2184 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2185 }
2186
2187 #[test]
2188 fn the_pass_says_it_preserves_nothing() {
2189 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
2190 }
2191}