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 args = func.push_values(&args);
719 func.set_block_call(at, BlockCall { block: into, args });
720 }
721 edges.entry(into).or_default().extend(ins.iter().copied());
722 func.remove_block(block);
723 gone.insert(block);
724 stats.optimized(FORWARDED);
725 changed = true;
726 requeue(into, &mut work, &mut queued);
727 for &(from, _) in &ins {
728 requeue(from, &mut work, &mut queued);
729 }
730 }
731 changed
732}
733
734fn requeue(block: Block, work: &mut VecDeque<Block>, queued: &mut HashSet<Block>) {
736 if queued.insert(block) {
737 work.push_back(block);
738 }
739}
740
741fn redundant(
757 func: &Func,
758 block: Block,
759 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
760 forward: &HashMap<Value, Value>,
761) -> Vec<(usize, Value)> {
762 let Some(ins) = ins.filter(|ins| !ins.is_empty()) else { return Vec::new() };
763 let mut found = Vec::new();
764 for (index, ¶m) in func[block].params.iter().enumerate() {
765 let mut only = None;
766 let mut agree = true;
767 for &(_, at) in ins {
768 let list = func[at].args;
769 let Some(&arg) = func[list].get(index) else {
770 agree = false;
773 break;
774 };
775 let arg = uses::chase(forward, arg);
776 if arg == param {
777 continue;
778 }
779 match only {
780 None => only = Some(arg),
781 Some(seen) if seen == arg => {}
782 Some(_) => {
783 agree = false;
784 break;
785 }
786 }
787 }
788 if !agree {
789 continue;
790 }
791 if let Some(value) = only {
792 found.push((index, value));
793 }
794 }
795 found
796}
797
798fn take_params(
803 func: &mut Func,
804 block: Block,
805 taking: &[usize],
806 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
807) {
808 for &(_, at) in ins.into_iter().flatten() {
809 let call = func[at];
810 let kept: Vec<Value> = func[call.args]
811 .iter()
812 .enumerate()
813 .filter(|(index, _)| !taking.contains(index))
814 .map(|(_, &value)| value)
815 .collect();
816 let args = func.push_values(&kept);
817 func.set_block_call(at, BlockCall { block: call.block, args });
818 }
819 let mut index = 0;
820 func.retain_params(block, |_| {
821 let keep = !taking.contains(&index);
822 index += 1;
823 keep
824 });
825}
826
827fn forwards(
833 func: &Func,
834 block: Block,
835 entry: Block,
836 addressed: &HashSet<Block>,
837 edges: &Edges,
838) -> Option<(Inst, Block, Vec<Value>)> {
839 if block == entry || addressed.contains(&block) || !func[block].params.is_empty() {
840 return None;
841 }
842 let term = func.terminator(block)?;
843 if func[term].opcode != Opcode::Jump {
844 return None;
845 }
846 if func.insts(block).count() != 1 {
849 return None;
850 }
851 let call = func.successors(term).next()?;
852 if call.block == block {
853 return None;
854 }
855 if carrying(func, block, call.block, func[call.args].len(), edges) {
856 return None;
857 }
858 Some((term, call.block, func[call.args].to_vec()))
859}
860
861fn carrying(func: &Func, block: Block, into: Block, args: usize, edges: &Edges) -> bool {
875 if args == 0 {
876 return false;
877 }
878 let ins = edges.get(&block).map_or(0, Vec::len);
879 let after = edges.get(&into).map_or(0, Vec::len) - 1 + ins;
880 if after < 2 {
881 return false;
882 }
883 edges.get(&block).into_iter().flatten().any(|&(from, _)| {
884 let Some(term) = func.terminator(from) else { return false };
885 func.target_list(term).iter().count() >= 2
886 })
887}
888
889fn chains(func: &Func, an: &mut Analyses) -> Vec<Vec<Block>> {
910 let cfg = an.cfg(func);
911 let Some(entry) = cfg.entry() else { return Vec::new() };
912 let addressed = addressed(func);
913 let mut below = HashMap::new();
914 let mut is_below = HashSet::new();
915 for block in func.blocks() {
916 let Some(term) = func.terminator(block) else { continue };
917 if func[term].opcode != Opcode::Jump {
918 continue;
919 }
920 let Some(call) = func.successors(term).next() else { continue };
921 let into = call.block;
922 let preds = cfg.predecessors(into);
923 if into == entry || into == block || addressed.contains(&into) {
924 continue;
925 }
926 if preds.len() != 1 || preds[0] != block {
927 continue;
928 }
929 below.insert(block, into);
930 is_below.insert(into);
931 }
932 let heads = func.blocks().filter(|it| below.contains_key(it) && !is_below.contains(it));
933 heads
934 .map(|head| {
935 let mut chain = vec![head];
936 let mut at = head;
937 while let Some(&next) = below.get(&at) {
938 chain.push(next);
939 at = next;
940 }
941 chain
942 })
943 .collect()
944}
945
946fn addressed(func: &Func) -> HashSet<Block> {
948 let mut taken = HashSet::new();
949 for block in func.blocks() {
950 for inst in func.insts(block) {
951 if func[inst].opcode != Opcode::BlockAddr {
952 continue;
953 }
954 for call in func.successors(inst) {
955 taken.insert(call.block);
956 }
957 }
958 }
959 taken
960}
961
962fn merge(func: &mut Func, head: Block, block: Block, forward: &mut HashMap<Value, Value>) {
969 let term = func.terminator(head).expect("the head of a chain ends in a jump");
970 let call = func.successors(term).next().expect("a jump goes somewhere");
971 let args = func[call.args].to_vec();
972 let params = func[block].params.clone();
973 for (param, arg) in params.into_iter().zip(args) {
974 let arg = uses::chase(forward, arg);
978 forward.insert(param, arg);
979 }
980 func.remove_inst(term);
981 for inst in func.insts(block).collect::<Vec<Inst>>() {
982 func.remove_inst(inst);
983 func.append_inst(head, inst);
984 }
985 func.remove_block(block);
986}
987
988fn known(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
990 let value = resolve(subst, value);
991 if let Some((imm, _)) = constant(func, value) {
992 return Some(imm.unsigned() != 0);
993 }
994 compared(func, value, subst)
995}
996
997fn compared(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
1008 let Def::Result { inst, .. } = func[value].def else { return None };
1009 let data = &func[inst];
1010 if data.opcode != Opcode::ICmp {
1011 return None;
1012 }
1013 let Extra::IntPred(pred) = data.extra else { return None };
1014 let args = &func[data.args];
1015 let (lhs, ty) = constant(func, resolve(subst, *args.first()?))?;
1016 let (rhs, _) = constant(func, resolve(subst, *args.get(1)?))?;
1017 Some(crate::fold::compare(pred, lhs, rhs, ty))
1018}
1019
1020#[cfg(test)]
1021mod tests {
1022 use rucc_base::Interner;
1023 use rucc_ir::{
1024 Block, Builder, Def, Flags, Func, Inst, IntPred, MemInfo, MemOrder, Module, Opcode,
1025 Restrict, Signature, Type, Value,
1026 };
1027 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
1028
1029 use super::SimplifyCfg;
1030 use crate::stats::Kind;
1031 use crate::testing::graph;
1032 use crate::{Fuel, Pass, Preserved, Stats};
1033
1034 fn simplify(func: &mut Func) -> Stats {
1036 SimplifyCfg.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
1037 }
1038
1039 fn blocks(func: &Func) -> Vec<usize> {
1041 func.blocks().map(Block::index).collect()
1042 }
1043
1044 fn terminator(func: &Func, block: usize) -> Opcode {
1046 let block = Block::from_usize(block);
1047 func[func.terminator(block).expect("every block here has one")].opcode
1048 }
1049
1050 fn goes_to(func: &Func, block: usize) -> Vec<usize> {
1052 let block = Block::from_usize(block);
1053 let term = func.terminator(block).expect("every block here has one");
1054 func.successors(term).map(|call| call.block.index()).collect()
1055 }
1056
1057 fn lives_in(func: &Func, value: Value) -> Option<usize> {
1063 let Def::Result { inst, .. } = func[value].def else { return None };
1064 func.block_of(inst).map(Block::index)
1065 }
1066
1067 fn diamond(cond: impl FnOnce(&mut Builder<'_>) -> Value) -> (Func, [Value; 2]) {
1074 let mut names = Interner::new();
1075 let mut func = Func::new(names.intern("f"), Signature::new());
1076 let entry = func.create_block();
1077 let then_block = func.create_block();
1078 let else_block = func.create_block();
1079 let join = func.create_block();
1080 let mut build = Builder::new(&mut func, entry);
1081 let cond = cond(&mut build);
1082 build.br_if(cond, then_block, &[], else_block, &[]);
1083 let mut marks = Vec::new();
1084 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1085 let mut build = Builder::new(&mut func, arm);
1086 marks.push(build.iconst(Type::int(32), mark));
1087 build.jump(join, &[]);
1088 }
1089 let mut build = Builder::new(&mut func, join);
1090 build.ret(&[]);
1091 (func, [marks[0], marks[1]])
1092 }
1093
1094 #[test]
1095 fn a_branch_on_a_true_constant_becomes_a_jump_to_the_first_arm() {
1096 let (mut func, [taken, other]) = diamond(|build| build.iconst(Type::int(1), 1));
1097 let stats = simplify(&mut func);
1098 assert!(stats.changed());
1099 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1100 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1103 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1104 assert_eq!(lives_in(&func, taken), Some(0));
1105 assert_eq!(lives_in(&func, other), None);
1106 assert_eq!(blocks(&func), [0]);
1107 }
1108
1109 #[test]
1110 fn a_branch_on_a_false_constant_becomes_a_jump_to_the_second_arm() {
1111 let (mut func, [other, taken]) = diamond(|build| build.iconst(Type::int(1), 0));
1112 assert!(simplify(&mut func).changed());
1113 assert_eq!(lives_in(&func, taken), Some(0));
1114 assert_eq!(lives_in(&func, other), None);
1115 assert_eq!(blocks(&func), [0]);
1116 }
1117
1118 #[test]
1119 fn folding_a_branch_and_merging_what_it_leaves_are_two_things_fuel_buys_apart() {
1120 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1123 let stats =
1124 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1125 assert_eq!(terminator(&func, 0), Opcode::Jump);
1126 assert_eq!(goes_to(&func, 0), [1]);
1127 assert_eq!(blocks(&func), [0, 1, 3]);
1128 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
1129 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
1132 }
1133
1134 #[test]
1135 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
1136 let cases: &[(IntPred, i128, i128, bool)] = &[
1140 (IntPred::Eq, 7, 7, true),
1141 (IntPred::Eq, 7, 8, false),
1142 (IntPred::Ne, 7, 8, true),
1143 (IntPred::Ne, 7, 7, false),
1144 (IntPred::Slt, -1, 1, true),
1145 (IntPred::Slt, 1, -1, false),
1146 (IntPred::Sle, -1, -1, true),
1147 (IntPred::Sle, 1, -1, false),
1148 (IntPred::Sgt, 1, -1, true),
1149 (IntPred::Sgt, -1, 1, false),
1150 (IntPred::Sge, -1, -1, true),
1151 (IntPred::Sge, -1, 1, false),
1152 (IntPred::Ult, 1, -1, true),
1153 (IntPred::Ult, -1, 1, false),
1154 (IntPred::Ule, -1, -1, true),
1155 (IntPred::Ule, -1, 1, false),
1156 (IntPred::Ugt, -1, 1, true),
1157 (IntPred::Ugt, 1, -1, false),
1158 (IntPred::Uge, -1, -1, true),
1159 (IntPred::Uge, 1, -1, false),
1160 ];
1161 for &(pred, lhs, rhs, taken) in cases {
1162 let (mut func, marks) = diamond(|build| {
1163 let lhs = build.iconst(Type::int(32), lhs);
1164 let rhs = build.iconst(Type::int(32), rhs);
1165 build.icmp(pred, lhs, rhs)
1166 });
1167 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
1168 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
1169 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
1170 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
1171 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
1172 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
1173 }
1174 }
1175
1176 #[test]
1177 fn a_branch_on_something_nobody_knows_is_left_alone() {
1178 let mut names = Interner::new();
1179 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
1180 let entry = func.create_block();
1181 let then_block = func.create_block();
1182 let else_block = func.create_block();
1183 let cond = func.append_param(entry, Type::int(1));
1184 let mut build = Builder::new(&mut func, entry);
1185 build.br_if(cond, then_block, &[], else_block, &[]);
1186 for arm in [then_block, else_block] {
1187 let mut build = Builder::new(&mut func, arm);
1188 build.ret(&[]);
1189 }
1190 let stats = simplify(&mut func);
1191 assert!(!stats.changed());
1192 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1193 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1194 assert_eq!(blocks(&func), [0, 1, 2]);
1195 }
1196
1197 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1200 let mut names = Interner::new();
1201 let mut func = Func::new(names.intern("f"), Signature::new());
1202 let entry = func.create_block();
1203 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1204 let mut build = Builder::new(&mut func, entry);
1205 let value = build.iconst(Type::int(32), on);
1206 let pairs: Vec<(i128, Block)> =
1207 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1208 build.switch(value, arms[0], &pairs);
1209 let mut marks = Vec::new();
1210 for (at, &arm) in arms.iter().enumerate() {
1211 let mut build = Builder::new(&mut func, arm);
1212 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1213 build.ret(&[]);
1214 }
1215 (func, marks)
1216 }
1217
1218 #[test]
1219 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1220 let (mut func, marks) = switched(5, &[4, 5]);
1221 assert!(simplify(&mut func).changed());
1222 assert_eq!(lives_in(&func, marks[2]), Some(0));
1223 assert_eq!(lives_in(&func, marks[0]), None);
1224 assert_eq!(lives_in(&func, marks[1]), None);
1225 assert_eq!(blocks(&func), [0]);
1226 }
1227
1228 #[test]
1229 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1230 let (mut func, marks) = switched(9, &[4]);
1231 assert!(simplify(&mut func).changed());
1232 assert_eq!(lives_in(&func, marks[0]), Some(0));
1233 assert_eq!(lives_in(&func, marks[1]), None);
1234 assert_eq!(blocks(&func), [0]);
1235 }
1236
1237 #[test]
1238 fn the_arguments_travel_with_the_edge_that_survives() {
1239 let mut names = Interner::new();
1245 let mut func = Func::new(names.intern("f"), Signature::new());
1246 let entry = func.create_block();
1247 let join = func.create_block();
1248 let param = func.append_param(join, Type::int(32));
1249 let mut build = Builder::new(&mut func, entry);
1250 let cond = build.iconst(Type::int(1), 0);
1251 let taken = build.iconst(Type::int(32), 11);
1252 let other = build.iconst(Type::int(32), 22);
1253 build.br_if(cond, join, &[other], join, &[taken]);
1254 let mut build = Builder::new(&mut func, join);
1255 build.ret(&[param]);
1256 assert!(simplify(&mut func).changed());
1257 assert_eq!(blocks(&func), [0]);
1261 let term = func.terminator(entry).expect("the entry has one");
1262 assert_eq!(func[func[term].args], [taken]);
1263 assert_ne!(func[func[term].args], [param]);
1264 }
1265
1266 #[test]
1267 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1268 let mut names = Interner::new();
1272 let signature = Signature::new().with_params(&[Type::int(1)]);
1273 let mut func = Func::new(names.intern("f"), signature);
1274 let entry = func.create_block();
1275 let join = func.create_block();
1276 let cond = func.append_param(entry, Type::int(1));
1277 let mut build = Builder::new(&mut func, entry);
1278 build.br_if(cond, join, &[], join, &[]);
1279 let mut build = Builder::new(&mut func, join);
1280 build.ret(&[]);
1281 let stats = simplify(&mut func);
1282 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1283 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1284 assert_eq!(blocks(&func), [0]);
1285 assert_eq!(terminator(&func, 0), Opcode::Return);
1286 }
1287
1288 #[test]
1289 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1290 let mut names = Interner::new();
1291 let signature = Signature::new().with_params(&[Type::int(32)]);
1292 let mut func = Func::new(names.intern("f"), signature);
1293 let entry = func.create_block();
1294 let join = func.create_block();
1295 let value = func.append_param(entry, Type::int(32));
1296 let mut build = Builder::new(&mut func, entry);
1297 build.switch(value, join, &[(4, join), (5, join)]);
1298 let mut build = Builder::new(&mut func, join);
1299 build.ret(&[]);
1300 let stats = simplify(&mut func);
1301 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1302 assert_eq!(blocks(&func), [0]);
1303 }
1304
1305 #[test]
1306 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1307 let mut names = Interner::new();
1310 let signature = Signature::new().with_params(&[Type::int(1)]);
1311 let mut func = Func::new(names.intern("f"), signature);
1312 let entry = func.create_block();
1313 let join = func.create_block();
1314 let cond = func.append_param(entry, Type::int(1));
1315 let param = func.append_param(join, Type::int(32));
1316 let mut build = Builder::new(&mut func, entry);
1317 let first = build.iconst(Type::int(32), 11);
1318 let second = build.iconst(Type::int(32), 22);
1319 build.br_if(cond, join, &[first], join, &[second]);
1320 let mut build = Builder::new(&mut func, join);
1321 build.ret(&[param]);
1324 let stats = simplify(&mut func);
1325 assert!(!stats.changed());
1326 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1327 assert_eq!(blocks(&func), [0, 1]);
1328 }
1329
1330 #[test]
1331 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1332 let mut names = Interner::new();
1335 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1336 let entry = func.create_block();
1337 let dead = func.create_block();
1338 let shared = func.create_block();
1339 let exit = func.create_block();
1340 let x = func.append_param(entry, Type::int(32));
1341 let mut build = Builder::new(&mut func, entry);
1342 let never = build.iconst(Type::int(1), 0);
1343 build.switch(x, exit, &[(0, dead), (1, shared)]);
1344 let mut build = Builder::new(&mut func, dead);
1348 build.iconst(Type::int(32), 1);
1349 build.br_if(never, shared, &[], exit, &[]);
1350 for arm in [shared, exit] {
1351 let mut build = Builder::new(&mut func, arm);
1352 build.ret(&[]);
1353 }
1354 let stats = simplify(&mut func);
1355 assert!(stats.changed());
1356 assert_eq!(terminator(&func, 0), Opcode::Switch);
1359 assert_eq!(goes_to(&func, 1), [3]);
1360 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1361 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1362 }
1363
1364 #[test]
1365 fn a_block_whose_address_is_taken_is_not_removed() {
1366 let mut names = Interner::new();
1370 let mut func = Func::new(names.intern("f"), Signature::new());
1371 let entry = func.create_block();
1372 let labelled = func.create_block();
1373 let arm = func.create_block();
1374 let mut build = Builder::new(&mut func, entry);
1375 let cond = build.iconst(Type::int(1), 1);
1376 let addr = build.block_addr(labelled);
1377 build.br_if(cond, arm, &[], labelled, &[]);
1378 let mut build = Builder::new(&mut func, arm);
1379 build.indirect_br(addr, &[labelled]);
1380 let mut build = Builder::new(&mut func, labelled);
1381 build.ret(&[]);
1382 assert!(simplify(&mut func).changed());
1383 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1384 assert_eq!(blocks(&func), [0, 1]);
1387 assert_eq!(goes_to(&func, 0), [1]);
1388 }
1389
1390 #[test]
1391 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1392 let mut names = Interner::new();
1395 let mut func = Func::new(names.intern("f"), Signature::new());
1396 let entry = func.create_block();
1397 let dead = func.create_block();
1398 let labelled = func.create_block();
1399 let mut build = Builder::new(&mut func, entry);
1400 let cond = build.iconst(Type::int(1), 1);
1401 build.br_if(cond, entry, &[], dead, &[]);
1402 let mut build = Builder::new(&mut func, dead);
1403 let addr = build.block_addr(labelled);
1404 build.indirect_br(addr, &[labelled]);
1405 let mut build = Builder::new(&mut func, labelled);
1406 build.ret(&[]);
1407 assert!(simplify(&mut func).changed());
1408 assert_eq!(blocks(&func), [0]);
1409 }
1410
1411 #[test]
1412 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1413 let mut func = graph(&[&[], &[]]);
1418 let stats = simplify(&mut func);
1419 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1420 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1421 assert_eq!(blocks(&func), [0]);
1422 }
1423
1424 #[test]
1425 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1426 let mut names = Interner::new();
1429 let mut func = Func::new(names.intern("f"), Signature::new());
1430 let entry = func.create_block();
1431 let middle = func.create_block();
1432 let last = func.create_block();
1433 let mut build = Builder::new(&mut func, entry);
1434 build.iconst(Type::int(32), 1);
1435 build.jump(middle, &[]);
1436 let mut build = Builder::new(&mut func, middle);
1437 build.iconst(Type::int(32), 2);
1438 build.jump(last, &[]);
1439 let mut build = Builder::new(&mut func, last);
1440 build.ret(&[]);
1441 let stats = simplify(&mut func);
1442 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1445 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1446 assert_eq!(blocks(&func), [0]);
1447 assert_eq!(terminator(&func, 0), Opcode::Return);
1448 }
1449
1450 #[test]
1451 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1452 let mut names = Interner::new();
1455 let signature = Signature::new().with_params(&[Type::int(1)]);
1456 let mut func = Func::new(names.intern("f"), signature);
1457 let entry = func.create_block();
1458 let then_block = func.create_block();
1459 let else_block = func.create_block();
1460 let join = func.create_block();
1461 let cond = func.append_param(entry, Type::int(1));
1462 let mut build = Builder::new(&mut func, entry);
1463 build.br_if(cond, then_block, &[], else_block, &[]);
1464 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1465 let mut build = Builder::new(&mut func, arm);
1468 build.iconst(Type::int(32), mark);
1469 build.jump(join, &[]);
1470 }
1471 let mut build = Builder::new(&mut func, join);
1472 build.ret(&[]);
1473 let stats = simplify(&mut func);
1474 assert!(!stats.changed());
1475 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1476 }
1477
1478 #[test]
1479 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1480 let mut names = Interner::new();
1483 let signature = Signature::new().with_params(&[Type::int(1)]);
1484 let mut func = Func::new(names.intern("f"), signature);
1485 let entry = func.create_block();
1486 let arm = func.create_block();
1487 let exit = func.create_block();
1488 let cond = func.append_param(entry, Type::int(1));
1489 let mut build = Builder::new(&mut func, entry);
1490 build.br_if(cond, arm, &[], exit, &[]);
1491 for block in [arm, exit] {
1492 let mut build = Builder::new(&mut func, block);
1493 build.ret(&[]);
1494 }
1495 let stats = simplify(&mut func);
1496 assert!(!stats.changed());
1497 assert_eq!(blocks(&func), [0, 1, 2]);
1498 }
1499
1500 #[test]
1501 fn the_entry_block_is_never_the_one_that_moves() {
1502 let mut names = Interner::new();
1506 let signature = Signature::new().with_params(&[Type::int(1)]);
1507 let mut func = Func::new(names.intern("f"), signature);
1508 let entry = func.create_block();
1509 let latch = func.create_block();
1510 let exit = func.create_block();
1511 let cond = func.append_param(entry, Type::int(1));
1512 let mut build = Builder::new(&mut func, entry);
1513 build.br_if(cond, latch, &[], exit, &[]);
1514 let mut build = Builder::new(&mut func, latch);
1516 build.iconst(Type::int(32), 1);
1517 build.jump(entry, &[]);
1518 let mut build = Builder::new(&mut func, exit);
1519 build.ret(&[]);
1520 let stats = simplify(&mut func);
1521 assert!(!stats.changed());
1522 assert_eq!(blocks(&func), [0, 1, 2]);
1523 }
1524
1525 #[test]
1526 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1527 let mut names = Interner::new();
1530 let mut func = Func::new(names.intern("f"), Signature::new());
1531 let entry = func.create_block();
1532 let middle = func.create_block();
1533 let labelled = func.create_block();
1534 let mut build = Builder::new(&mut func, entry);
1535 build.block_addr(labelled);
1536 build.jump(middle, &[]);
1537 let mut build = Builder::new(&mut func, middle);
1540 build.iconst(Type::int(32), 1);
1541 build.jump(labelled, &[]);
1542 let mut build = Builder::new(&mut func, labelled);
1543 build.ret(&[]);
1544 let stats = simplify(&mut func);
1545 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1548 assert_eq!(blocks(&func), [0, 2]);
1549 }
1550
1551 #[test]
1552 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1553 let mut names = Interner::new();
1554 let mut func = Func::new(names.intern("f"), Signature::new());
1555 let entry = func.create_block();
1556 let below = func.create_block();
1557 let param = func.append_param(below, Type::int(32));
1558 let mut build = Builder::new(&mut func, entry);
1559 let arg = build.iconst(Type::int(32), 7);
1560 build.jump(below, &[arg]);
1561 let mut build = Builder::new(&mut func, below);
1562 build.ret(&[param]);
1563 assert!(simplify(&mut func).changed());
1564 assert_eq!(blocks(&func), [0]);
1565 let term = func.terminator(entry).expect("the entry has one");
1566 assert_eq!(func[func[term].args], [arg]);
1567 }
1568
1569 #[test]
1570 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1571 let mut names = Interner::new();
1575 let mut func = Func::new(names.intern("f"), Signature::new());
1576 let entry = func.create_block();
1577 let middle = func.create_block();
1578 let last = func.create_block();
1579 let carried = func.append_param(middle, Type::int(32));
1580 let arrived = func.append_param(last, Type::int(32));
1581 let mut build = Builder::new(&mut func, entry);
1582 let arg = build.iconst(Type::int(32), 7);
1583 build.jump(middle, &[arg]);
1584 let mut build = Builder::new(&mut func, middle);
1585 build.jump(last, &[carried]);
1586 let mut build = Builder::new(&mut func, last);
1587 build.ret(&[arrived]);
1588 assert!(simplify(&mut func).changed());
1589 assert_eq!(blocks(&func), [0]);
1590 let term = func.terminator(entry).expect("the entry has one");
1591 assert_eq!(func[func[term].args], [arg]);
1592 }
1593
1594 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1602 let entry = func.create_block();
1603 let first = func.create_block();
1604 let second = func.create_block();
1605 let cond = func.append_param(entry, Type::int(1));
1606 let mut build = Builder::new(func, entry);
1607 let carried = build.iconst(Type::int(32), 7);
1608 build.br_if(cond, first, &[], second, &[]);
1609 for (arm, mark) in [(first, 111), (second, 222)] {
1610 let mut build = Builder::new(func, arm);
1611 build.iconst(Type::int(32), mark);
1612 }
1613 (carried, [first, second])
1614 }
1615
1616 fn taking_a_condition() -> Func {
1618 let mut names = Interner::new();
1619 let signature = Signature::new().with_params(&[Type::int(1)]);
1620 Func::new(names.intern("f"), signature)
1621 }
1622
1623 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1625 let block = Block::from_usize(block);
1626 let term = func.terminator(block).expect("every block here has one");
1627 let call = func.successors(term).nth(edge).expect("the edge is there");
1628 func[call.args].to_vec()
1629 }
1630
1631 #[test]
1632 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1633 let mut func = taking_a_condition();
1636 let (_, arms) = arms(&mut func);
1637 let forwarder = func.create_block();
1638 let exit = func.create_block();
1639 for arm in arms {
1640 Builder::new(&mut func, arm).jump(forwarder, &[]);
1641 }
1642 Builder::new(&mut func, forwarder).jump(exit, &[]);
1643 Builder::new(&mut func, exit).ret(&[]);
1644 let stats = simplify(&mut func);
1645 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1646 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1647 assert_eq!(goes_to(&func, 1), [4]);
1648 assert_eq!(goes_to(&func, 2), [4]);
1649 }
1650
1651 #[test]
1652 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1653 let mut func = taking_a_condition();
1660 let (carried, [arm, above]) = arms(&mut func);
1661 let forwarder = func.create_block();
1662 let exit = func.create_block();
1663 let other = func.append_param(exit, Type::int(32));
1664 let mut build = Builder::new(&mut func, arm);
1665 let mine = build.iconst(Type::int(32), 9);
1666 build.jump(exit, &[mine]);
1667 Builder::new(&mut func, above).jump(forwarder, &[]);
1668 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1669 Builder::new(&mut func, exit).ret(&[other]);
1670 let stats = simplify(&mut func);
1671 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1672 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1673 assert_eq!(carries(&func, 2, 0), [carried]);
1676 assert_eq!(carries(&func, 1, 0), [mine]);
1677 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1679 }
1680
1681 #[test]
1682 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1683 let mut func = taking_a_condition();
1688 let (carried, [arm, forwarder]) = arms(&mut func);
1689 let exit = func.create_block();
1690 let other = func.append_param(exit, Type::int(32));
1691 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1693 func.remove_inst(inst);
1694 }
1695 let mut build = Builder::new(&mut func, arm);
1696 let mine = build.iconst(Type::int(32), 9);
1697 build.jump(exit, &[mine]);
1698 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1699 Builder::new(&mut func, exit).ret(&[other]);
1700 let stats = simplify(&mut func);
1701 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1702 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1703 }
1704
1705 #[test]
1706 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1707 let mut func = taking_a_condition();
1710 let (_, [arm, forwarder]) = arms(&mut func);
1711 let exit = func.create_block();
1712 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1713 func.remove_inst(inst);
1714 }
1715 Builder::new(&mut func, arm).jump(exit, &[]);
1716 Builder::new(&mut func, forwarder).jump(exit, &[]);
1717 Builder::new(&mut func, exit).ret(&[]);
1718 let stats = simplify(&mut func);
1719 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1720 assert_eq!(blocks(&func), [0, 1, 3]);
1721 }
1722
1723 #[test]
1724 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1725 let mut names = Interner::new();
1728 let mut func = Func::new(names.intern("f"), Signature::new());
1729 let entry = func.create_block();
1730 let spin = func.create_block();
1731 Builder::new(&mut func, entry).jump(spin, &[]);
1732 Builder::new(&mut func, spin).jump(spin, &[]);
1733 let stats = simplify(&mut func);
1734 assert!(!stats.changed());
1735 assert_eq!(blocks(&func), [0, 1]);
1736 }
1737
1738 #[test]
1739 fn the_entry_block_is_never_the_forwarder_that_goes() {
1740 let mut names = Interner::new();
1744 let mut func = Func::new(names.intern("f"), Signature::new());
1745 let entry = func.create_block();
1746 let below = func.create_block();
1747 Builder::new(&mut func, entry).jump(below, &[]);
1748 let mut build = Builder::new(&mut func, below);
1749 build.iconst(Type::int(32), 1);
1750 build.ret(&[]);
1751 let stats = simplify(&mut func);
1752 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1753 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1754 assert_eq!(blocks(&func), [0]);
1755 }
1756
1757 #[test]
1758 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1759 let mut names = Interner::new();
1763 let mut func = Func::new(names.intern("f"), Signature::new());
1764 let entry = func.create_block();
1765 let labelled = func.create_block();
1766 let exit = func.create_block();
1767 let mut build = Builder::new(&mut func, entry);
1768 let addr = build.block_addr(labelled);
1769 build.indirect_br(addr, &[labelled]);
1770 Builder::new(&mut func, labelled).jump(exit, &[]);
1771 let mut build = Builder::new(&mut func, exit);
1772 build.iconst(Type::int(32), 1);
1773 build.ret(&[]);
1774 let stats = simplify(&mut func);
1775 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1776 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1777 }
1778
1779 #[test]
1780 fn a_run_of_forwarders_comes_out_as_one_edge() {
1781 let mut func = taking_a_condition();
1782 let (_, arms) = arms(&mut func);
1783 let first = func.create_block();
1784 let second = func.create_block();
1785 let exit = func.create_block();
1786 for arm in arms {
1787 Builder::new(&mut func, arm).jump(first, &[]);
1788 }
1789 Builder::new(&mut func, first).jump(second, &[]);
1790 Builder::new(&mut func, second).jump(exit, &[]);
1791 Builder::new(&mut func, exit).ret(&[]);
1792 let stats = simplify(&mut func);
1793 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1794 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1795 assert_eq!(goes_to(&func, 1), [5]);
1796 assert_eq!(goes_to(&func, 2), [5]);
1797 }
1798
1799 #[test]
1800 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1801 let mut func = taking_a_condition();
1804 let (carried, arms) = arms(&mut func);
1805 let join = func.create_block();
1806 let param = func.append_param(join, Type::int(32));
1807 for arm in arms {
1808 Builder::new(&mut func, arm).jump(join, &[carried]);
1809 }
1810 Builder::new(&mut func, join).ret(&[param]);
1811 let stats = simplify(&mut func);
1812 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1813 assert!(func[Block::from_usize(3)].params.is_empty());
1814 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1816 assert_eq!(func[func[term].args], [carried]);
1817 assert!(carries(&func, 1, 0).is_empty());
1820 assert!(carries(&func, 2, 0).is_empty());
1821 }
1822
1823 #[test]
1824 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1825 let mut func = taking_a_condition();
1826 let (carried, arms) = arms(&mut func);
1827 let join = func.create_block();
1828 let param = func.append_param(join, Type::int(32));
1829 let mut build = Builder::new(&mut func, arms[0]);
1830 let mine = build.iconst(Type::int(32), 9);
1831 build.jump(join, &[mine]);
1832 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1833 Builder::new(&mut func, join).ret(&[param]);
1834 let stats = simplify(&mut func);
1835 assert!(!stats.changed());
1836 assert_eq!(func[Block::from_usize(3)].params, [param]);
1837 }
1838
1839 #[test]
1840 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1841 let mut names = Interner::new();
1845 let signature = Signature::new().with_params(&[Type::int(1)]);
1846 let mut func = Func::new(names.intern("f"), signature);
1847 let entry = func.create_block();
1848 let header = func.create_block();
1849 let latch = func.create_block();
1850 let exit = func.create_block();
1851 let cond = func.append_param(entry, Type::int(1));
1852 let param = func.append_param(header, Type::int(32));
1853 let mut build = Builder::new(&mut func, entry);
1854 let init = build.iconst(Type::int(32), 7);
1855 build.jump(header, &[init]);
1856 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1857 let mut build = Builder::new(&mut func, latch);
1858 build.iconst(Type::int(32), 1);
1859 build.jump(header, &[param]);
1860 Builder::new(&mut func, exit).ret(&[param]);
1861 let stats = simplify(&mut func);
1862 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1863 assert!(func[Block::from_usize(1)].params.is_empty());
1864 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1865 assert_eq!(func[func[term].args], [init]);
1866 }
1867
1868 #[test]
1869 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1870 let mut names = Interner::new();
1874 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1875 let mut func = Func::new(names.intern("f"), signature);
1876 let entry = func.create_block();
1877 let latch = func.create_block();
1878 let exit = func.create_block();
1879 let cond = func.append_param(entry, Type::int(1));
1880 let x = func.append_param(entry, Type::int(32));
1881 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1882 let mut build = Builder::new(&mut func, latch);
1883 let one = build.iconst(Type::int(1), 1);
1884 let seven = build.iconst(Type::int(32), 7);
1885 build.jump(entry, &[one, seven]);
1886 Builder::new(&mut func, exit).ret(&[x]);
1887 let stats = simplify(&mut func);
1888 assert!(!stats.changed());
1889 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1890 }
1891
1892 #[test]
1893 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1894 let mut func = taking_a_condition();
1898 let (carried, arms) = arms(&mut func);
1899 let join = func.create_block();
1900 let inner = func.append_param(join, Type::int(32));
1901 let left = func.create_block();
1902 let right = func.create_block();
1903 let last = func.create_block();
1904 let outer = func.append_param(last, Type::int(32));
1905 for arm in arms {
1906 Builder::new(&mut func, arm).jump(join, &[carried]);
1907 }
1908 let cond = func[Block::from_usize(0)].params[0];
1909 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1910 let mut build = Builder::new(&mut func, left);
1911 build.iconst(Type::int(32), 1);
1912 build.jump(last, &[inner]);
1913 let mut build = Builder::new(&mut func, right);
1914 build.iconst(Type::int(32), 2);
1915 build.jump(last, &[carried]);
1916 Builder::new(&mut func, last).ret(&[outer]);
1917 let stats = simplify(&mut func);
1918 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1919 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1920 assert_eq!(func[func[term].args], [carried]);
1921 }
1922
1923 #[test]
1924 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1925 let mut func = taking_a_condition();
1929 let (carried, arms) = arms(&mut func);
1930 let forwarder = func.create_block();
1931 let param = func.append_param(forwarder, Type::int(32));
1932 let exit = func.create_block();
1933 let arrived = func.append_param(exit, Type::int(32));
1934 for arm in arms {
1935 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1936 }
1937 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1938 Builder::new(&mut func, exit).ret(&[arrived]);
1939 let stats = simplify(&mut func);
1940 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1941 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1944 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1945 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
1946 assert_eq!(func[func[term].args], [carried]);
1947 }
1948
1949 #[test]
1950 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
1951 let mut func = taking_a_condition();
1954 let (carried, arms) = arms(&mut func);
1955 let forwarder = func.create_block();
1956 let param = func.append_param(forwarder, Type::int(32));
1957 let exit = func.create_block();
1958 let arrived = func.append_param(exit, Type::int(32));
1961 for arm in arms {
1962 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1963 }
1964 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1965 Builder::new(&mut func, exit).ret(&[arrived]);
1966 let stats =
1967 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1968 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1969 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1970 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
1971 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
1972 }
1973
1974 fn walking_a_pointer(on_counter: bool) -> Func {
1983 let mut names = Interner::new();
1984 let signature = Signature::new().with_params(&[Type::int(64)]);
1985 let mut func = Func::new(names.intern("f"), signature);
1986 let entry = func.create_block();
1987 let head = func.create_block();
1988 let out = func.create_block();
1989 let end = func.append_param(entry, Type::int(64));
1990 let counter = func.append_param(head, Type::int(32));
1991 let pointer = func.append_param(head, Type::int(64));
1992 let mut build = Builder::new(&mut func, entry);
1993 let from_zero = build.iconst(Type::int(32), 0);
1994 let from_start = build.iconst(Type::int(64), 0);
1995 build.jump(head, &[from_zero, from_start]);
1996 let mut build = Builder::new(&mut func, head);
1997 let one = build.iconst(Type::int(32), 1);
1998 let eight = build.iconst(Type::int(64), 8);
1999 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2000 let along = build.binary(Opcode::Add, pointer, eight, Flags::NONE);
2001 let address = build.unary(Opcode::IntToPtr, pointer, Type::PTR);
2004 let info = MemInfo {
2005 size: 8,
2006 align: 8,
2007 order: MemOrder::NotAtomic,
2008 tbaa: None,
2009 owns: 0,
2010 restrict: Restrict::NONE,
2011 };
2012 build.store(eight, address, info, Flags::NONE);
2013 let going = if on_counter {
2014 let limit = build.iconst(Type::int(32), 10);
2015 build.icmp(IntPred::Ne, next, limit)
2016 } else {
2017 build.icmp(IntPred::Ne, along, end)
2018 };
2019 build.br_if(going, head, &[next, along], out, &[]);
2020 Builder::new(&mut func, out).ret(&[]);
2021 func
2022 }
2023
2024 #[test]
2025 fn a_counter_the_loop_stopped_asking_about_stops_going_round() {
2026 let mut func = walking_a_pointer(false);
2027 let stats = simplify(&mut func);
2028 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2029 assert_eq!(func[Block::from_usize(1)].params.len(), 1);
2031 assert_eq!(carries(&func, 1, 0).len(), 1);
2033 assert_eq!(carries(&func, 0, 0).len(), 1);
2034 }
2035
2036 #[test]
2037 fn a_counter_the_loop_still_asks_about_goes_round_exactly_as_before() {
2038 let mut func = walking_a_pointer(true);
2039 let stats = simplify(&mut func);
2040 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2041 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2042 }
2043
2044 fn counting_into_nothing() -> (Func, Value, Value) {
2051 let mut names = Interner::new();
2052 let signature = Signature::new().with_params(&[Type::int(32)]);
2053 let mut func = Func::new(names.intern("f"), signature);
2054 let entry = func.create_block();
2055 let head = func.create_block();
2056 let out = func.create_block();
2057 let limit = func.append_param(entry, Type::int(32));
2058 let counter = func.append_param(head, Type::int(32));
2059 let mut build = Builder::new(&mut func, entry);
2060 let zero = build.iconst(Type::int(32), 0);
2061 build.jump(head, &[zero]);
2062 let mut build = Builder::new(&mut func, head);
2063 let one = build.iconst(Type::int(32), 1);
2064 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
2065 let twice = build.binary(Opcode::Add, next, next, Flags::NONE);
2066 let going = build.icmp(IntPred::Ne, limit, one);
2067 build.br_if(going, head, &[next], out, &[]);
2068 Builder::new(&mut func, out).ret(&[]);
2069 (func, next, twice)
2070 }
2071
2072 #[test]
2073 fn what_was_reading_a_parameter_nothing_reads_goes_with_it() {
2074 let (mut func, next, twice) = counting_into_nothing();
2075 let stats = simplify(&mut func);
2076 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
2077 assert_eq!(lives_in(&func, next), None);
2081 assert_eq!(lives_in(&func, twice), None);
2082 }
2083
2084 #[test]
2085 fn the_functions_own_parameters_stay_whether_or_not_anything_reads_them() {
2086 let mut names = Interner::new();
2089 let signature = Signature::new().with_params(&[Type::int(32)]);
2090 let mut func = Func::new(names.intern("f"), signature);
2091 let entry = func.create_block();
2092 func.append_param(entry, Type::int(32));
2093 Builder::new(&mut func, entry).ret(&[]);
2094 let stats = simplify(&mut func);
2095 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2096 assert_eq!(func[entry].params.len(), 1);
2097 }
2098
2099 #[test]
2100 fn a_parameter_nothing_reads_costs_one_unit_of_fuel_and_stays_without_it() {
2101 let mut func = walking_a_pointer(false);
2102 let stats =
2103 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2104 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2105 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_UNREAD), 1);
2106 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2107 }
2108
2109 #[test]
2110 fn the_counter_that_went_leaves_the_verifier_nothing_to_complain_about() {
2111 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2112 let mut names = Interner::new();
2113 let mut module = Module::new(names.intern("test.c"), &target);
2114 let mut func = walking_a_pointer(false);
2115 simplify(&mut func);
2116 module.add_func(func);
2117 rucc_ir::verify(&module, &names).expect("taking a parameter out left the function whole");
2118 }
2119
2120 #[test]
2121 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
2122 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2125 let mut names = Interner::new();
2126 let mut module = Module::new(names.intern("test.c"), &target);
2127 let mut func = taking_a_condition();
2128 let (carried, arms) = arms(&mut func);
2129 let forwarder = func.create_block();
2130 let param = func.append_param(forwarder, Type::int(32));
2131 let exit = func.create_block();
2132 let arrived = func.append_param(exit, Type::int(32));
2133 let mut build = Builder::new(&mut func, arms[0]);
2134 let mine = build.iconst(Type::int(32), 9);
2135 build.jump(exit, &[mine]);
2136 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
2137 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2138 let mut build = Builder::new(&mut func, exit);
2139 build.icmp(IntPred::Eq, arrived, arrived);
2142 build.ret(&[]);
2143 simplify(&mut func);
2144 module.add_func(func);
2145 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
2146 }
2147
2148 #[test]
2149 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
2150 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
2151 let before = blocks(&func);
2152 let stats =
2153 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2154 assert!(!stats.changed());
2155 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2156 assert_eq!(terminator(&func, 0), Opcode::BrIf);
2157 assert_eq!(blocks(&func), before);
2158 }
2159
2160 #[test]
2161 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
2162 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
2166 let stats =
2167 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2168 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
2169 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2170 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
2173 }
2174
2175 #[test]
2176 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2177 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2178 let mut names = Interner::new();
2179 let mut module = Module::new(names.intern("test.c"), &target);
2180 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
2181 simplify(&mut func);
2182 module.add_func(func);
2183 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2184 }
2185
2186 #[test]
2187 fn the_pass_says_it_preserves_nothing() {
2188 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
2189 }
2190}