1use std::collections::{HashMap, HashSet, VecDeque};
145
146use rucc_base::Idx;
147use rucc_ir::{Block, BlockCall, Def, Extra, Func, Inst, Opcode, Value};
148
149use crate::fold::constant;
150use crate::{Analyses, Fuel, Pass, Preserved, Stats, uses};
151
152const FOLDED: &str = "branch on a condition that is always the same way replaced by a jump";
154
155pub(crate) const REMOVED: &str = "block nothing reaches removed";
157
158const MERGED: &str = "block with one way into it merged into the block above it";
160
161const FORWARDED: &str = "block that only jumped somewhere else removed and its edges pointed past";
163
164const SAME_EVERY_WAY: &str = "block parameter that arrives as the same value every way in removed";
166
167const NO_FUEL: &str = "branch on a known condition left alone, the pass ran out of fuel";
169
170const NO_FUEL_MERGE: &str = "block with one way into it left alone, the pass ran out of fuel";
172
173const NO_FUEL_FORWARD: &str =
175 "block that only jumped somewhere else kept, the pass ran out of fuel";
176
177const NO_FUEL_PARAM: &str = "block parameter that is one value kept, the pass ran out of fuel";
179
180const NOTHING_READS_IT: &str = "block parameter nothing reads removed, and the argument on every \
182 edge that was feeding it";
183
184const NO_FUEL_UNREAD: &str = "block parameter nothing reads kept, the pass ran out of fuel";
186
187#[derive(Debug, Clone, Copy, PartialEq, Eq)]
189pub struct SimplifyCfg;
190
191impl Pass for SimplifyCfg {
192 fn name(&self) -> &'static str {
193 "simplify-cfg"
194 }
195
196 fn describe(&self) -> &'static str {
197 "unreachable blocks go, a branch that only goes one way becomes a jump, a block that only \
198 jumps stops being in the way, and a block with one way in is merged into the one above it"
199 }
200
201 fn preserves(&self) -> Preserved {
202 Preserved::NONE
205 }
206
207 fn run(&self, func: &mut Func, an: &mut Analyses, fuel: &mut Fuel) -> Stats {
208 let mut stats = Stats::new();
209 sweep(func, an, &mut stats);
213 let mut folded = false;
214 let unbound = Bindings::new();
218 for block in func.blocks().collect::<Vec<Block>>() {
219 let Some(term) = func.terminator(block) else { continue };
220 let Some(taken) = taken(func, term, &unbound) else { continue };
221 if !fuel.take() {
222 stats.missed(NO_FUEL);
225 continue;
226 }
227 jump_to(func, term, taken);
228 stats.optimized(FOLDED);
229 folded = true;
230 }
231 if folded {
232 an.clear();
236 sweep(func, an, &mut stats);
237 }
238 let mut forward = HashMap::new();
239 let dropped = drop_unread(func, fuel, &mut stats);
245 if straighten(func, fuel, &mut stats, &mut forward) || dropped {
246 an.clear();
247 }
248 for chain in chains(func, an) {
252 for (at, &block) in chain.iter().enumerate().skip(1) {
253 if !fuel.take() {
254 for _ in at..chain.len() {
258 stats.missed(NO_FUEL_MERGE);
259 }
260 break;
261 }
262 merge(func, chain[0], block, &mut forward);
263 stats.optimized(MERGED);
264 }
265 }
266 if !forward.is_empty() {
267 uses::substitute(func, &forward);
270 }
271 stats
272 }
273}
274
275pub(crate) type Bindings = HashMap<Value, Value>;
282
283fn resolve(subst: &Bindings, value: Value) -> Value {
285 subst.get(&value).copied().unwrap_or(value)
286}
287
288pub(crate) fn taken(func: &Func, term: Inst, subst: &Bindings) -> Option<BlockCall> {
297 let data = &func[term];
298 let arg = *func[data.args].first()?;
299 match data.opcode {
300 Opcode::BrIf => {
301 let Extra::Targets(targets) = data.extra else { return None };
302 if let Some(call) = one_place(func, &func[targets]) {
303 return Some(call);
304 }
305 let arm = usize::from(!known(func, arg, subst)?);
308 func[targets].get(arm).copied()
309 }
310 Opcode::Switch => {
311 let Extra::Switch(at) = data.extra else { return None };
312 let info = func[at];
313 if let Some(call) = one_place(func, &func[info.targets]) {
314 return Some(call);
315 }
316 let (value, _) = constant(func, resolve(subst, arg))?;
317 let case = func[info.cases].iter().position(|it| *it == value);
320 func[info.targets].get(case.map_or(0, |case| case + 1)).copied()
321 }
322 _ => None,
323 }
324}
325
326fn one_place(func: &Func, calls: &[BlockCall]) -> Option<BlockCall> {
338 let &first = calls.first()?;
339 let same = |call: &BlockCall| call.block == first.block && func[call.args] == func[first.args];
340 calls[1..].iter().all(same).then_some(first)
341}
342
343pub(crate) fn jump_to(func: &mut Func, term: Inst, call: BlockCall) {
348 let targets = func.push_block_calls(&[call]);
349 let args = func.push_values(&[]);
350 let data = &mut func[term];
351 data.opcode = Opcode::Jump;
352 data.args = args;
353 data.extra = Extra::Targets(targets);
354}
355
356pub(crate) fn sweep(func: &mut Func, an: &mut Analyses, stats: &mut Stats) {
365 let gone = stranded(func, an);
366 if gone.is_empty() {
367 return;
368 }
369 for block in gone {
370 func.remove_block(block);
371 stats.optimized(REMOVED);
372 }
373 an.clear();
374}
375
376fn stranded(func: &Func, an: &mut Analyses) -> Vec<Block> {
385 let cfg = an.cfg(func);
386 let Some(entry) = cfg.entry() else { return Vec::new() };
387 let mut seen = vec![false; cfg.capacity()];
388 seen[entry.index()] = true;
389 let mut stack = vec![entry];
390 let mut reached = Vec::new();
391 while let Some(block) = stack.pop() {
392 for &succ in cfg.successors(block) {
393 if !seen[succ.index()] {
394 seen[succ.index()] = true;
395 stack.push(succ);
396 }
397 }
398 reached.push(block);
399 }
400 let mut next = reached;
403 while !next.is_empty() {
404 let mut found = Vec::new();
405 for block in next {
406 for inst in func.insts(block) {
407 if func[inst].opcode != Opcode::BlockAddr {
408 continue;
409 }
410 for call in func.successors(inst) {
411 if !seen[call.block.index()] {
412 seen[call.block.index()] = true;
413 found.push(call.block);
414 }
415 }
416 }
417 }
418 let mut stack = found.clone();
421 while let Some(block) = stack.pop() {
422 for &succ in cfg.successors(block) {
423 if !seen[succ.index()] {
424 seen[succ.index()] = true;
425 stack.push(succ);
426 found.push(succ);
427 }
428 }
429 }
430 next = found;
431 }
432 func.blocks().filter(|block| !seen[block.index()]).collect()
433}
434
435pub(crate) type Edges = HashMap<Block, Vec<(Block, Idx<BlockCall>)>>;
442
443pub(crate) fn incoming(func: &Func) -> Edges {
451 let mut edges: Edges = HashMap::new();
452 for block in func.blocks() {
453 let Some(term) = func.terminator(block) else { continue };
454 for at in func.target_list(term).iter() {
455 edges.entry(func[at].block).or_default().push((block, at));
456 }
457 }
458 edges
459}
460
461fn drop_unread(func: &mut Func, fuel: &mut Fuel, stats: &mut Stats) -> bool {
474 let Some(entry) = func.entry() else { return false };
475 let live = live(func, entry, &addressed(func));
476 let edges = incoming(func);
477 let mut changed = false;
478 for block in func.blocks().collect::<Vec<Block>>() {
479 let mut taking = Vec::new();
480 for (index, ¶m) in func[block].params.iter().enumerate() {
481 if live.contains(¶m) {
482 continue;
483 }
484 if !fuel.take() {
485 stats.missed(NO_FUEL_UNREAD);
486 continue;
487 }
488 taking.push(index);
489 }
490 if taking.is_empty() {
491 continue;
492 }
493 for _ in &taking {
494 stats.optimized(NOTHING_READS_IT);
495 }
496 take_params(func, block, &taking, edges.get(&block));
497 changed = true;
498 }
499 changed
500}
501
502fn live(func: &Func, entry: Block, addressed: &HashSet<Block>) -> HashSet<Value> {
517 let mut where_from: HashMap<Value, (Block, usize)> = HashMap::new();
518 let mut live: HashSet<Value> = HashSet::new();
519 let mut work: Vec<Value> = Vec::new();
520 let seed = |value: Value, live: &mut HashSet<Value>, work: &mut Vec<Value>| {
521 if live.insert(value) {
522 work.push(value);
523 }
524 };
525 for block in func.blocks() {
526 let held = block == entry || addressed.contains(&block);
527 for (index, ¶m) in func[block].params.iter().enumerate() {
528 where_from.insert(param, (block, index));
529 if held {
530 seed(param, &mut live, &mut work);
531 }
532 }
533 for inst in func.insts(block) {
534 if !func.is_terminator(inst) && !func[inst].opcode.has_effects() {
535 continue;
536 }
537 for &value in &func[func[inst].args] {
538 seed(value, &mut live, &mut work);
539 }
540 }
541 }
542
543 let edges = incoming(func);
544 while let Some(value) = work.pop() {
545 match func[value].def {
546 Def::Result { inst, .. } => {
547 for &operand in &func[func[inst].args] {
548 seed(operand, &mut live, &mut work);
549 }
550 }
551 Def::Param { .. } => {
552 let Some(&(block, index)) = where_from.get(&value) else { continue };
553 for &(_, at) in edges.get(&block).into_iter().flatten() {
554 let Some(&arg) = func[func[at].args].get(index) else { continue };
555 seed(arg, &mut live, &mut work);
556 }
557 }
558 }
559 }
560 live
561}
562
563fn straighten(
589 func: &mut Func,
590 fuel: &mut Fuel,
591 stats: &mut Stats,
592 forward: &mut HashMap<Value, Value>,
593) -> bool {
594 let Some(entry) = func.entry() else { return false };
595 let addressed = addressed(func);
596 let mut edges = incoming(func);
597 let mut work: VecDeque<Block> = func.blocks().collect();
598 let mut queued: HashSet<Block> = work.iter().copied().collect();
599 let mut gone: HashSet<Block> = HashSet::new();
600 let mut changed = false;
601 while let Some(block) = work.pop_front() {
602 queued.remove(&block);
603 if gone.contains(&block) {
604 continue;
605 }
606 let mut starved = false;
607 if block != entry {
608 let drop = redundant(func, block, edges.get(&block), forward);
609 let mut taking = Vec::new();
610 for (index, value) in drop {
611 if !fuel.take() {
612 stats.missed(NO_FUEL_PARAM);
613 starved = true;
614 break;
615 }
616 let value = uses::chase(forward, value);
619 forward.insert(func[block].params[index], value);
620 taking.push(index);
621 stats.optimized(SAME_EVERY_WAY);
622 }
623 if !taking.is_empty() {
624 take_params(func, block, &taking, edges.get(&block));
625 requeue(block, &mut work, &mut queued);
628 if let Some(term) = func.terminator(block) {
631 for call in func.successors(term).collect::<Vec<BlockCall>>() {
632 requeue(call.block, &mut work, &mut queued);
633 }
634 }
635 changed = true;
636 }
637 }
638 if starved {
641 break;
642 }
643 let Some((term, into, args)) = forwards(func, block, entry, &addressed, &edges) else {
644 continue;
645 };
646 if !fuel.take() {
647 stats.missed(NO_FUEL_FORWARD);
648 break;
649 }
650 let out = func.target_list(term).iter().next().expect("a jump has a target");
654 if let Some(list) = edges.get_mut(&into) {
655 list.retain(|&(_, at)| at != out);
656 }
657 let ins = edges.remove(&block).unwrap_or_default();
658 for &(_, at) in &ins {
659 let args = func.push_values(&args);
663 func.set_block_call(at, BlockCall { block: into, args });
664 }
665 edges.entry(into).or_default().extend(ins.iter().copied());
666 func.remove_block(block);
667 gone.insert(block);
668 stats.optimized(FORWARDED);
669 changed = true;
670 requeue(into, &mut work, &mut queued);
671 for &(from, _) in &ins {
672 requeue(from, &mut work, &mut queued);
673 }
674 }
675 changed
676}
677
678fn requeue(block: Block, work: &mut VecDeque<Block>, queued: &mut HashSet<Block>) {
680 if queued.insert(block) {
681 work.push_back(block);
682 }
683}
684
685fn redundant(
701 func: &Func,
702 block: Block,
703 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
704 forward: &HashMap<Value, Value>,
705) -> Vec<(usize, Value)> {
706 let Some(ins) = ins.filter(|ins| !ins.is_empty()) else { return Vec::new() };
707 let mut found = Vec::new();
708 for (index, ¶m) in func[block].params.iter().enumerate() {
709 let mut only = None;
710 let mut agree = true;
711 for &(_, at) in ins {
712 let list = func[at].args;
713 let Some(&arg) = func[list].get(index) else {
714 agree = false;
717 break;
718 };
719 let arg = uses::chase(forward, arg);
720 if arg == param {
721 continue;
722 }
723 match only {
724 None => only = Some(arg),
725 Some(seen) if seen == arg => {}
726 Some(_) => {
727 agree = false;
728 break;
729 }
730 }
731 }
732 if !agree {
733 continue;
734 }
735 if let Some(value) = only {
736 found.push((index, value));
737 }
738 }
739 found
740}
741
742fn take_params(
747 func: &mut Func,
748 block: Block,
749 taking: &[usize],
750 ins: Option<&Vec<(Block, Idx<BlockCall>)>>,
751) {
752 for &(_, at) in ins.into_iter().flatten() {
753 let call = func[at];
754 let kept: Vec<Value> = func[call.args]
755 .iter()
756 .enumerate()
757 .filter(|(index, _)| !taking.contains(index))
758 .map(|(_, &value)| value)
759 .collect();
760 let args = func.push_values(&kept);
761 func.set_block_call(at, BlockCall { block: call.block, args });
762 }
763 let mut index = 0;
764 func.retain_params(block, |_| {
765 let keep = !taking.contains(&index);
766 index += 1;
767 keep
768 });
769}
770
771fn forwards(
777 func: &Func,
778 block: Block,
779 entry: Block,
780 addressed: &HashSet<Block>,
781 edges: &Edges,
782) -> Option<(Inst, Block, Vec<Value>)> {
783 if block == entry || addressed.contains(&block) || !func[block].params.is_empty() {
784 return None;
785 }
786 let term = func.terminator(block)?;
787 if func[term].opcode != Opcode::Jump {
788 return None;
789 }
790 if func.insts(block).count() != 1 {
793 return None;
794 }
795 let call = func.successors(term).next()?;
796 if call.block == block {
797 return None;
798 }
799 if carrying(func, block, call.block, func[call.args].len(), edges) {
800 return None;
801 }
802 Some((term, call.block, func[call.args].to_vec()))
803}
804
805fn carrying(func: &Func, block: Block, into: Block, args: usize, edges: &Edges) -> bool {
819 if args == 0 {
820 return false;
821 }
822 let ins = edges.get(&block).map_or(0, Vec::len);
823 let after = edges.get(&into).map_or(0, Vec::len) - 1 + ins;
824 if after < 2 {
825 return false;
826 }
827 edges.get(&block).into_iter().flatten().any(|&(from, _)| {
828 let Some(term) = func.terminator(from) else { return false };
829 func.target_list(term).iter().count() >= 2
830 })
831}
832
833fn chains(func: &Func, an: &mut Analyses) -> Vec<Vec<Block>> {
854 let cfg = an.cfg(func);
855 let Some(entry) = cfg.entry() else { return Vec::new() };
856 let addressed = addressed(func);
857 let mut below = HashMap::new();
858 let mut is_below = HashSet::new();
859 for block in func.blocks() {
860 let Some(term) = func.terminator(block) else { continue };
861 if func[term].opcode != Opcode::Jump {
862 continue;
863 }
864 let Some(call) = func.successors(term).next() else { continue };
865 let into = call.block;
866 let preds = cfg.predecessors(into);
867 if into == entry || into == block || addressed.contains(&into) {
868 continue;
869 }
870 if preds.len() != 1 || preds[0] != block {
871 continue;
872 }
873 below.insert(block, into);
874 is_below.insert(into);
875 }
876 let heads = func.blocks().filter(|it| below.contains_key(it) && !is_below.contains(it));
877 heads
878 .map(|head| {
879 let mut chain = vec![head];
880 let mut at = head;
881 while let Some(&next) = below.get(&at) {
882 chain.push(next);
883 at = next;
884 }
885 chain
886 })
887 .collect()
888}
889
890fn addressed(func: &Func) -> HashSet<Block> {
892 let mut taken = HashSet::new();
893 for block in func.blocks() {
894 for inst in func.insts(block) {
895 if func[inst].opcode != Opcode::BlockAddr {
896 continue;
897 }
898 for call in func.successors(inst) {
899 taken.insert(call.block);
900 }
901 }
902 }
903 taken
904}
905
906fn merge(func: &mut Func, head: Block, block: Block, forward: &mut HashMap<Value, Value>) {
913 let term = func.terminator(head).expect("the head of a chain ends in a jump");
914 let call = func.successors(term).next().expect("a jump goes somewhere");
915 let args = func[call.args].to_vec();
916 let params = func[block].params.clone();
917 for (param, arg) in params.into_iter().zip(args) {
918 let arg = uses::chase(forward, arg);
922 forward.insert(param, arg);
923 }
924 func.remove_inst(term);
925 for inst in func.insts(block).collect::<Vec<Inst>>() {
926 func.remove_inst(inst);
927 func.append_inst(head, inst);
928 }
929 func.remove_block(block);
930}
931
932fn known(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
934 let value = resolve(subst, value);
935 if let Some((imm, _)) = constant(func, value) {
936 return Some(imm.unsigned() != 0);
937 }
938 compared(func, value, subst)
939}
940
941fn compared(func: &Func, value: Value, subst: &Bindings) -> Option<bool> {
952 let Def::Result { inst, .. } = func[value].def else { return None };
953 let data = &func[inst];
954 if data.opcode != Opcode::ICmp {
955 return None;
956 }
957 let Extra::IntPred(pred) = data.extra else { return None };
958 let args = &func[data.args];
959 let (lhs, ty) = constant(func, resolve(subst, *args.first()?))?;
960 let (rhs, _) = constant(func, resolve(subst, *args.get(1)?))?;
961 Some(crate::fold::compare(pred, lhs, rhs, ty))
962}
963
964#[cfg(test)]
965mod tests {
966 use rucc_base::Interner;
967 use rucc_ir::{
968 Block, Builder, Def, Flags, Func, Inst, IntPred, MemInfo, MemOrder, Module, Opcode,
969 Restrict, Signature, Type, Value,
970 };
971 use rucc_target::{Arch, Env, Os, TargetInfo, Triple};
972
973 use super::SimplifyCfg;
974 use crate::stats::Kind;
975 use crate::testing::graph;
976 use crate::{Fuel, Pass, Preserved, Stats};
977
978 fn simplify(func: &mut Func) -> Stats {
980 SimplifyCfg.run(func, &mut crate::machine::fixtures::analyses(), &mut Fuel::unlimited())
981 }
982
983 fn blocks(func: &Func) -> Vec<usize> {
985 func.blocks().map(Block::index).collect()
986 }
987
988 fn terminator(func: &Func, block: usize) -> Opcode {
990 let block = Block::from_usize(block);
991 func[func.terminator(block).expect("every block here has one")].opcode
992 }
993
994 fn goes_to(func: &Func, block: usize) -> Vec<usize> {
996 let block = Block::from_usize(block);
997 let term = func.terminator(block).expect("every block here has one");
998 func.successors(term).map(|call| call.block.index()).collect()
999 }
1000
1001 fn lives_in(func: &Func, value: Value) -> Option<usize> {
1007 let Def::Result { inst, .. } = func[value].def else { return None };
1008 func.block_of(inst).map(Block::index)
1009 }
1010
1011 fn diamond(cond: impl FnOnce(&mut Builder<'_>) -> Value) -> (Func, [Value; 2]) {
1018 let mut names = Interner::new();
1019 let mut func = Func::new(names.intern("f"), Signature::new());
1020 let entry = func.create_block();
1021 let then_block = func.create_block();
1022 let else_block = func.create_block();
1023 let join = func.create_block();
1024 let mut build = Builder::new(&mut func, entry);
1025 let cond = cond(&mut build);
1026 build.br_if(cond, then_block, &[], else_block, &[]);
1027 let mut marks = Vec::new();
1028 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1029 let mut build = Builder::new(&mut func, arm);
1030 marks.push(build.iconst(Type::int(32), mark));
1031 build.jump(join, &[]);
1032 }
1033 let mut build = Builder::new(&mut func, join);
1034 build.ret(&[]);
1035 (func, [marks[0], marks[1]])
1036 }
1037
1038 #[test]
1039 fn a_branch_on_a_true_constant_becomes_a_jump_to_the_first_arm() {
1040 let (mut func, [taken, other]) = diamond(|build| build.iconst(Type::int(1), 1));
1041 let stats = simplify(&mut func);
1042 assert!(stats.changed());
1043 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1044 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1047 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1048 assert_eq!(lives_in(&func, taken), Some(0));
1049 assert_eq!(lives_in(&func, other), None);
1050 assert_eq!(blocks(&func), [0]);
1051 }
1052
1053 #[test]
1054 fn a_branch_on_a_false_constant_becomes_a_jump_to_the_second_arm() {
1055 let (mut func, [other, taken]) = diamond(|build| build.iconst(Type::int(1), 0));
1056 assert!(simplify(&mut func).changed());
1057 assert_eq!(lives_in(&func, taken), Some(0));
1058 assert_eq!(lives_in(&func, other), None);
1059 assert_eq!(blocks(&func), [0]);
1060 }
1061
1062 #[test]
1063 fn folding_a_branch_and_merging_what_it_leaves_are_two_things_fuel_buys_apart() {
1064 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
1067 let stats =
1068 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1069 assert_eq!(terminator(&func, 0), Opcode::Jump);
1070 assert_eq!(goes_to(&func, 0), [1]);
1071 assert_eq!(blocks(&func), [0, 1, 3]);
1072 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 0);
1073 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_MERGE), 2);
1076 }
1077
1078 #[test]
1079 fn a_branch_on_a_comparison_of_two_constants_is_read_without_folding_it() {
1080 let cases: &[(IntPred, i128, i128, bool)] = &[
1084 (IntPred::Eq, 7, 7, true),
1085 (IntPred::Eq, 7, 8, false),
1086 (IntPred::Ne, 7, 8, true),
1087 (IntPred::Ne, 7, 7, false),
1088 (IntPred::Slt, -1, 1, true),
1089 (IntPred::Slt, 1, -1, false),
1090 (IntPred::Sle, -1, -1, true),
1091 (IntPred::Sle, 1, -1, false),
1092 (IntPred::Sgt, 1, -1, true),
1093 (IntPred::Sgt, -1, 1, false),
1094 (IntPred::Sge, -1, -1, true),
1095 (IntPred::Sge, -1, 1, false),
1096 (IntPred::Ult, 1, -1, true),
1097 (IntPred::Ult, -1, 1, false),
1098 (IntPred::Ule, -1, -1, true),
1099 (IntPred::Ule, -1, 1, false),
1100 (IntPred::Ugt, -1, 1, true),
1101 (IntPred::Ugt, 1, -1, false),
1102 (IntPred::Uge, -1, -1, true),
1103 (IntPred::Uge, 1, -1, false),
1104 ];
1105 for &(pred, lhs, rhs, taken) in cases {
1106 let (mut func, marks) = diamond(|build| {
1107 let lhs = build.iconst(Type::int(32), lhs);
1108 let rhs = build.iconst(Type::int(32), rhs);
1109 build.icmp(pred, lhs, rhs)
1110 });
1111 assert!(simplify(&mut func).changed(), "{pred:?} {lhs} {rhs}");
1112 let [went, gone] = if taken { [marks[0], marks[1]] } else { [marks[1], marks[0]] };
1113 assert_eq!(lives_in(&func, went), Some(0), "{pred:?} {lhs} {rhs}");
1114 assert_eq!(lives_in(&func, gone), None, "{pred:?} {lhs} {rhs}");
1115 let kept = func.insts(Block::from_usize(0)).any(|it| func[it].opcode == Opcode::ICmp);
1116 assert!(kept, "the comparison was folded away and issue 352 says it must not be");
1117 }
1118 }
1119
1120 #[test]
1121 fn a_branch_on_something_nobody_knows_is_left_alone() {
1122 let mut names = Interner::new();
1123 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(1)]));
1124 let entry = func.create_block();
1125 let then_block = func.create_block();
1126 let else_block = func.create_block();
1127 let cond = func.append_param(entry, Type::int(1));
1128 let mut build = Builder::new(&mut func, entry);
1129 build.br_if(cond, then_block, &[], else_block, &[]);
1130 for arm in [then_block, else_block] {
1131 let mut build = Builder::new(&mut func, arm);
1132 build.ret(&[]);
1133 }
1134 let stats = simplify(&mut func);
1135 assert!(!stats.changed());
1136 assert!(stats.is_empty(), "a pass with nothing to say should say nothing");
1137 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1138 assert_eq!(blocks(&func), [0, 1, 2]);
1139 }
1140
1141 fn switched(on: i128, cases: &[i128]) -> (Func, Vec<Value>) {
1144 let mut names = Interner::new();
1145 let mut func = Func::new(names.intern("f"), Signature::new());
1146 let entry = func.create_block();
1147 let arms: Vec<Block> = (0..=cases.len()).map(|_| func.create_block()).collect();
1148 let mut build = Builder::new(&mut func, entry);
1149 let value = build.iconst(Type::int(32), on);
1150 let pairs: Vec<(i128, Block)> =
1151 cases.iter().enumerate().map(|(at, &case)| (case, arms[at + 1])).collect();
1152 build.switch(value, arms[0], &pairs);
1153 let mut marks = Vec::new();
1154 for (at, &arm) in arms.iter().enumerate() {
1155 let mut build = Builder::new(&mut func, arm);
1156 marks.push(build.iconst(Type::int(32), 100 + at as i128));
1157 build.ret(&[]);
1158 }
1159 (func, marks)
1160 }
1161
1162 #[test]
1163 fn a_switch_on_a_constant_takes_the_case_that_matches() {
1164 let (mut func, marks) = switched(5, &[4, 5]);
1165 assert!(simplify(&mut func).changed());
1166 assert_eq!(lives_in(&func, marks[2]), Some(0));
1167 assert_eq!(lives_in(&func, marks[0]), None);
1168 assert_eq!(lives_in(&func, marks[1]), None);
1169 assert_eq!(blocks(&func), [0]);
1170 }
1171
1172 #[test]
1173 fn a_switch_on_a_constant_no_case_names_takes_the_default() {
1174 let (mut func, marks) = switched(9, &[4]);
1175 assert!(simplify(&mut func).changed());
1176 assert_eq!(lives_in(&func, marks[0]), Some(0));
1177 assert_eq!(lives_in(&func, marks[1]), None);
1178 assert_eq!(blocks(&func), [0]);
1179 }
1180
1181 #[test]
1182 fn the_arguments_travel_with_the_edge_that_survives() {
1183 let mut names = Interner::new();
1189 let mut func = Func::new(names.intern("f"), Signature::new());
1190 let entry = func.create_block();
1191 let join = func.create_block();
1192 let param = func.append_param(join, Type::int(32));
1193 let mut build = Builder::new(&mut func, entry);
1194 let cond = build.iconst(Type::int(1), 0);
1195 let taken = build.iconst(Type::int(32), 11);
1196 let other = build.iconst(Type::int(32), 22);
1197 build.br_if(cond, join, &[other], join, &[taken]);
1198 let mut build = Builder::new(&mut func, join);
1199 build.ret(&[param]);
1200 assert!(simplify(&mut func).changed());
1201 assert_eq!(blocks(&func), [0]);
1205 let term = func.terminator(entry).expect("the entry has one");
1206 assert_eq!(func[func[term].args], [taken]);
1207 assert_ne!(func[func[term].args], [param]);
1208 }
1209
1210 #[test]
1211 fn a_branch_whose_arms_are_the_same_edge_becomes_a_jump() {
1212 let mut names = Interner::new();
1216 let signature = Signature::new().with_params(&[Type::int(1)]);
1217 let mut func = Func::new(names.intern("f"), signature);
1218 let entry = func.create_block();
1219 let join = func.create_block();
1220 let cond = func.append_param(entry, Type::int(1));
1221 let mut build = Builder::new(&mut func, entry);
1222 build.br_if(cond, join, &[], join, &[]);
1223 let mut build = Builder::new(&mut func, join);
1224 build.ret(&[]);
1225 let stats = simplify(&mut func);
1226 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1227 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1228 assert_eq!(blocks(&func), [0]);
1229 assert_eq!(terminator(&func, 0), Opcode::Return);
1230 }
1231
1232 #[test]
1233 fn a_switch_whose_cases_all_go_to_one_place_becomes_a_jump() {
1234 let mut names = Interner::new();
1235 let signature = Signature::new().with_params(&[Type::int(32)]);
1236 let mut func = Func::new(names.intern("f"), signature);
1237 let entry = func.create_block();
1238 let join = func.create_block();
1239 let value = func.append_param(entry, Type::int(32));
1240 let mut build = Builder::new(&mut func, entry);
1241 build.switch(value, join, &[(4, join), (5, join)]);
1242 let mut build = Builder::new(&mut func, join);
1243 build.ret(&[]);
1244 let stats = simplify(&mut func);
1245 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
1246 assert_eq!(blocks(&func), [0]);
1247 }
1248
1249 #[test]
1250 fn a_branch_to_one_block_by_two_edges_that_differ_is_left_alone() {
1251 let mut names = Interner::new();
1254 let signature = Signature::new().with_params(&[Type::int(1)]);
1255 let mut func = Func::new(names.intern("f"), signature);
1256 let entry = func.create_block();
1257 let join = func.create_block();
1258 let cond = func.append_param(entry, Type::int(1));
1259 let param = func.append_param(join, Type::int(32));
1260 let mut build = Builder::new(&mut func, entry);
1261 let first = build.iconst(Type::int(32), 11);
1262 let second = build.iconst(Type::int(32), 22);
1263 build.br_if(cond, join, &[first], join, &[second]);
1264 let mut build = Builder::new(&mut func, join);
1265 build.ret(&[param]);
1268 let stats = simplify(&mut func);
1269 assert!(!stats.changed());
1270 assert_eq!(terminator(&func, 0), Opcode::BrIf);
1271 assert_eq!(blocks(&func), [0, 1]);
1272 }
1273
1274 #[test]
1275 fn a_block_the_dead_arm_shared_with_a_live_one_stays() {
1276 let mut names = Interner::new();
1279 let mut func = Func::new(names.intern("f"), Signature::new().with_params(&[Type::int(32)]));
1280 let entry = func.create_block();
1281 let dead = func.create_block();
1282 let shared = func.create_block();
1283 let exit = func.create_block();
1284 let x = func.append_param(entry, Type::int(32));
1285 let mut build = Builder::new(&mut func, entry);
1286 let never = build.iconst(Type::int(1), 0);
1287 build.switch(x, exit, &[(0, dead), (1, shared)]);
1288 let mut build = Builder::new(&mut func, dead);
1292 build.iconst(Type::int(32), 1);
1293 build.br_if(never, shared, &[], exit, &[]);
1294 for arm in [shared, exit] {
1295 let mut build = Builder::new(&mut func, arm);
1296 build.ret(&[]);
1297 }
1298 let stats = simplify(&mut func);
1299 assert!(stats.changed());
1300 assert_eq!(terminator(&func, 0), Opcode::Switch);
1303 assert_eq!(goes_to(&func, 1), [3]);
1304 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1305 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 0);
1306 }
1307
1308 #[test]
1309 fn a_block_whose_address_is_taken_is_not_removed() {
1310 let mut names = Interner::new();
1314 let mut func = Func::new(names.intern("f"), Signature::new());
1315 let entry = func.create_block();
1316 let labelled = func.create_block();
1317 let arm = func.create_block();
1318 let mut build = Builder::new(&mut func, entry);
1319 let cond = build.iconst(Type::int(1), 1);
1320 let addr = build.block_addr(labelled);
1321 build.br_if(cond, arm, &[], labelled, &[]);
1322 let mut build = Builder::new(&mut func, arm);
1323 build.indirect_br(addr, &[labelled]);
1324 let mut build = Builder::new(&mut func, labelled);
1325 build.ret(&[]);
1326 assert!(simplify(&mut func).changed());
1327 assert!(blocks(&func).contains(&1), "the labelled block went with the arm");
1328 assert_eq!(blocks(&func), [0, 1]);
1331 assert_eq!(goes_to(&func, 0), [1]);
1332 }
1333
1334 #[test]
1335 fn a_block_only_an_unreachable_block_takes_the_address_of_goes_too() {
1336 let mut names = Interner::new();
1339 let mut func = Func::new(names.intern("f"), Signature::new());
1340 let entry = func.create_block();
1341 let dead = func.create_block();
1342 let labelled = func.create_block();
1343 let mut build = Builder::new(&mut func, entry);
1344 let cond = build.iconst(Type::int(1), 1);
1345 build.br_if(cond, entry, &[], dead, &[]);
1346 let mut build = Builder::new(&mut func, dead);
1347 let addr = build.block_addr(labelled);
1348 build.indirect_br(addr, &[labelled]);
1349 let mut build = Builder::new(&mut func, labelled);
1350 build.ret(&[]);
1351 assert!(simplify(&mut func).changed());
1352 assert_eq!(blocks(&func), [0]);
1353 }
1354
1355 #[test]
1356 fn a_block_nothing_reaches_goes_even_when_no_branch_folded() {
1357 let mut func = graph(&[&[], &[]]);
1362 let stats = simplify(&mut func);
1363 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 0);
1364 assert_eq!(stats.count(Kind::Optimized, super::REMOVED), 1);
1365 assert_eq!(blocks(&func), [0]);
1366 }
1367
1368 #[test]
1369 fn a_block_with_one_way_into_it_goes_into_the_block_above_it() {
1370 let mut names = Interner::new();
1373 let mut func = Func::new(names.intern("f"), Signature::new());
1374 let entry = func.create_block();
1375 let middle = func.create_block();
1376 let last = func.create_block();
1377 let mut build = Builder::new(&mut func, entry);
1378 build.iconst(Type::int(32), 1);
1379 build.jump(middle, &[]);
1380 let mut build = Builder::new(&mut func, middle);
1381 build.iconst(Type::int(32), 2);
1382 build.jump(last, &[]);
1383 let mut build = Builder::new(&mut func, last);
1384 build.ret(&[]);
1385 let stats = simplify(&mut func);
1386 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 2);
1389 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1390 assert_eq!(blocks(&func), [0]);
1391 assert_eq!(terminator(&func, 0), Opcode::Return);
1392 }
1393
1394 #[test]
1395 fn a_block_with_two_ways_into_it_stays_where_it_is() {
1396 let mut names = Interner::new();
1399 let signature = Signature::new().with_params(&[Type::int(1)]);
1400 let mut func = Func::new(names.intern("f"), signature);
1401 let entry = func.create_block();
1402 let then_block = func.create_block();
1403 let else_block = func.create_block();
1404 let join = func.create_block();
1405 let cond = func.append_param(entry, Type::int(1));
1406 let mut build = Builder::new(&mut func, entry);
1407 build.br_if(cond, then_block, &[], else_block, &[]);
1408 for (arm, mark) in [(then_block, 111), (else_block, 222)] {
1409 let mut build = Builder::new(&mut func, arm);
1412 build.iconst(Type::int(32), mark);
1413 build.jump(join, &[]);
1414 }
1415 let mut build = Builder::new(&mut func, join);
1416 build.ret(&[]);
1417 let stats = simplify(&mut func);
1418 assert!(!stats.changed());
1419 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1420 }
1421
1422 #[test]
1423 fn a_block_above_one_that_does_not_end_in_a_jump_keeps_it() {
1424 let mut names = Interner::new();
1427 let signature = Signature::new().with_params(&[Type::int(1)]);
1428 let mut func = Func::new(names.intern("f"), signature);
1429 let entry = func.create_block();
1430 let arm = func.create_block();
1431 let exit = func.create_block();
1432 let cond = func.append_param(entry, Type::int(1));
1433 let mut build = Builder::new(&mut func, entry);
1434 build.br_if(cond, arm, &[], exit, &[]);
1435 for block in [arm, exit] {
1436 let mut build = Builder::new(&mut func, block);
1437 build.ret(&[]);
1438 }
1439 let stats = simplify(&mut func);
1440 assert!(!stats.changed());
1441 assert_eq!(blocks(&func), [0, 1, 2]);
1442 }
1443
1444 #[test]
1445 fn the_entry_block_is_never_the_one_that_moves() {
1446 let mut names = Interner::new();
1450 let signature = Signature::new().with_params(&[Type::int(1)]);
1451 let mut func = Func::new(names.intern("f"), signature);
1452 let entry = func.create_block();
1453 let latch = func.create_block();
1454 let exit = func.create_block();
1455 let cond = func.append_param(entry, Type::int(1));
1456 let mut build = Builder::new(&mut func, entry);
1457 build.br_if(cond, latch, &[], exit, &[]);
1458 let mut build = Builder::new(&mut func, latch);
1460 build.iconst(Type::int(32), 1);
1461 build.jump(entry, &[]);
1462 let mut build = Builder::new(&mut func, exit);
1463 build.ret(&[]);
1464 let stats = simplify(&mut func);
1465 assert!(!stats.changed());
1466 assert_eq!(blocks(&func), [0, 1, 2]);
1467 }
1468
1469 #[test]
1470 fn a_block_whose_address_is_taken_is_not_merged_away_either() {
1471 let mut names = Interner::new();
1474 let mut func = Func::new(names.intern("f"), Signature::new());
1475 let entry = func.create_block();
1476 let middle = func.create_block();
1477 let labelled = func.create_block();
1478 let mut build = Builder::new(&mut func, entry);
1479 build.block_addr(labelled);
1480 build.jump(middle, &[]);
1481 let mut build = Builder::new(&mut func, middle);
1484 build.iconst(Type::int(32), 1);
1485 build.jump(labelled, &[]);
1486 let mut build = Builder::new(&mut func, labelled);
1487 build.ret(&[]);
1488 let stats = simplify(&mut func);
1489 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1492 assert_eq!(blocks(&func), [0, 2]);
1493 }
1494
1495 #[test]
1496 fn merging_binds_a_block_parameter_to_the_argument_the_jump_carried() {
1497 let mut names = Interner::new();
1498 let mut func = Func::new(names.intern("f"), Signature::new());
1499 let entry = func.create_block();
1500 let below = func.create_block();
1501 let param = func.append_param(below, Type::int(32));
1502 let mut build = Builder::new(&mut func, entry);
1503 let arg = build.iconst(Type::int(32), 7);
1504 build.jump(below, &[arg]);
1505 let mut build = Builder::new(&mut func, below);
1506 build.ret(&[param]);
1507 assert!(simplify(&mut func).changed());
1508 assert_eq!(blocks(&func), [0]);
1509 let term = func.terminator(entry).expect("the entry has one");
1510 assert_eq!(func[func[term].args], [arg]);
1511 }
1512
1513 #[test]
1514 fn a_chain_of_merges_follows_a_parameter_bound_to_a_parameter() {
1515 let mut names = Interner::new();
1519 let mut func = Func::new(names.intern("f"), Signature::new());
1520 let entry = func.create_block();
1521 let middle = func.create_block();
1522 let last = func.create_block();
1523 let carried = func.append_param(middle, Type::int(32));
1524 let arrived = func.append_param(last, Type::int(32));
1525 let mut build = Builder::new(&mut func, entry);
1526 let arg = build.iconst(Type::int(32), 7);
1527 build.jump(middle, &[arg]);
1528 let mut build = Builder::new(&mut func, middle);
1529 build.jump(last, &[carried]);
1530 let mut build = Builder::new(&mut func, last);
1531 build.ret(&[arrived]);
1532 assert!(simplify(&mut func).changed());
1533 assert_eq!(blocks(&func), [0]);
1534 let term = func.terminator(entry).expect("the entry has one");
1535 assert_eq!(func[func[term].args], [arg]);
1536 }
1537
1538 fn arms(func: &mut Func) -> (Value, [Block; 2]) {
1546 let entry = func.create_block();
1547 let first = func.create_block();
1548 let second = func.create_block();
1549 let cond = func.append_param(entry, Type::int(1));
1550 let mut build = Builder::new(func, entry);
1551 let carried = build.iconst(Type::int(32), 7);
1552 build.br_if(cond, first, &[], second, &[]);
1553 for (arm, mark) in [(first, 111), (second, 222)] {
1554 let mut build = Builder::new(func, arm);
1555 build.iconst(Type::int(32), mark);
1556 }
1557 (carried, [first, second])
1558 }
1559
1560 fn taking_a_condition() -> Func {
1562 let mut names = Interner::new();
1563 let signature = Signature::new().with_params(&[Type::int(1)]);
1564 Func::new(names.intern("f"), signature)
1565 }
1566
1567 fn carries(func: &Func, block: usize, edge: usize) -> Vec<Value> {
1569 let block = Block::from_usize(block);
1570 let term = func.terminator(block).expect("every block here has one");
1571 let call = func.successors(term).nth(edge).expect("the edge is there");
1572 func[call.args].to_vec()
1573 }
1574
1575 #[test]
1576 fn a_block_that_does_nothing_but_jump_stops_being_in_the_way() {
1577 let mut func = taking_a_condition();
1580 let (_, arms) = arms(&mut func);
1581 let forwarder = func.create_block();
1582 let exit = func.create_block();
1583 for arm in arms {
1584 Builder::new(&mut func, arm).jump(forwarder, &[]);
1585 }
1586 Builder::new(&mut func, forwarder).jump(exit, &[]);
1587 Builder::new(&mut func, exit).ret(&[]);
1588 let stats = simplify(&mut func);
1589 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1590 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1591 assert_eq!(goes_to(&func, 1), [4]);
1592 assert_eq!(goes_to(&func, 2), [4]);
1593 }
1594
1595 #[test]
1596 fn a_forwarder_hands_its_predecessors_the_arguments_it_was_passing() {
1597 let mut func = taking_a_condition();
1604 let (carried, [arm, above]) = arms(&mut func);
1605 let forwarder = func.create_block();
1606 let exit = func.create_block();
1607 let other = func.append_param(exit, Type::int(32));
1608 let mut build = Builder::new(&mut func, arm);
1609 let mine = build.iconst(Type::int(32), 9);
1610 build.jump(exit, &[mine]);
1611 Builder::new(&mut func, above).jump(forwarder, &[]);
1612 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1613 Builder::new(&mut func, exit).ret(&[other]);
1614 let stats = simplify(&mut func);
1615 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1616 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1617 assert_eq!(carries(&func, 2, 0), [carried]);
1620 assert_eq!(carries(&func, 1, 0), [mine]);
1621 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 0);
1623 }
1624
1625 #[test]
1626 fn a_forwarder_carrying_something_on_an_edge_out_of_a_branch_stays() {
1627 let mut func = taking_a_condition();
1632 let (carried, [arm, forwarder]) = arms(&mut func);
1633 let exit = func.create_block();
1634 let other = func.append_param(exit, Type::int(32));
1635 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1637 func.remove_inst(inst);
1638 }
1639 let mut build = Builder::new(&mut func, arm);
1640 let mine = build.iconst(Type::int(32), 9);
1641 build.jump(exit, &[mine]);
1642 Builder::new(&mut func, forwarder).jump(exit, &[carried]);
1643 Builder::new(&mut func, exit).ret(&[other]);
1644 let stats = simplify(&mut func);
1645 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1646 assert_eq!(blocks(&func), [0, 1, 2, 3]);
1647 }
1648
1649 #[test]
1650 fn a_forwarder_carrying_nothing_out_of_a_branch_goes_anyway() {
1651 let mut func = taking_a_condition();
1654 let (_, [arm, forwarder]) = arms(&mut func);
1655 let exit = func.create_block();
1656 for inst in func.insts(forwarder).collect::<Vec<Inst>>() {
1657 func.remove_inst(inst);
1658 }
1659 Builder::new(&mut func, arm).jump(exit, &[]);
1660 Builder::new(&mut func, forwarder).jump(exit, &[]);
1661 Builder::new(&mut func, exit).ret(&[]);
1662 let stats = simplify(&mut func);
1663 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1664 assert_eq!(blocks(&func), [0, 1, 3]);
1665 }
1666
1667 #[test]
1668 fn a_block_that_jumps_to_itself_is_not_a_forwarder() {
1669 let mut names = Interner::new();
1672 let mut func = Func::new(names.intern("f"), Signature::new());
1673 let entry = func.create_block();
1674 let spin = func.create_block();
1675 Builder::new(&mut func, entry).jump(spin, &[]);
1676 Builder::new(&mut func, spin).jump(spin, &[]);
1677 let stats = simplify(&mut func);
1678 assert!(!stats.changed());
1679 assert_eq!(blocks(&func), [0, 1]);
1680 }
1681
1682 #[test]
1683 fn the_entry_block_is_never_the_forwarder_that_goes() {
1684 let mut names = Interner::new();
1688 let mut func = Func::new(names.intern("f"), Signature::new());
1689 let entry = func.create_block();
1690 let below = func.create_block();
1691 Builder::new(&mut func, entry).jump(below, &[]);
1692 let mut build = Builder::new(&mut func, below);
1693 build.iconst(Type::int(32), 1);
1694 build.ret(&[]);
1695 let stats = simplify(&mut func);
1696 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1697 assert_eq!(stats.count(Kind::Optimized, super::MERGED), 1);
1698 assert_eq!(blocks(&func), [0]);
1699 }
1700
1701 #[test]
1702 fn a_block_whose_address_is_taken_is_not_forwarded_past_either() {
1703 let mut names = Interner::new();
1707 let mut func = Func::new(names.intern("f"), Signature::new());
1708 let entry = func.create_block();
1709 let labelled = func.create_block();
1710 let exit = func.create_block();
1711 let mut build = Builder::new(&mut func, entry);
1712 let addr = build.block_addr(labelled);
1713 build.indirect_br(addr, &[labelled]);
1714 Builder::new(&mut func, labelled).jump(exit, &[]);
1715 let mut build = Builder::new(&mut func, exit);
1716 build.iconst(Type::int(32), 1);
1717 build.ret(&[]);
1718 let stats = simplify(&mut func);
1719 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1720 assert!(blocks(&func).contains(&1), "the labelled block was forwarded past");
1721 }
1722
1723 #[test]
1724 fn a_run_of_forwarders_comes_out_as_one_edge() {
1725 let mut func = taking_a_condition();
1726 let (_, arms) = arms(&mut func);
1727 let first = func.create_block();
1728 let second = func.create_block();
1729 let exit = func.create_block();
1730 for arm in arms {
1731 Builder::new(&mut func, arm).jump(first, &[]);
1732 }
1733 Builder::new(&mut func, first).jump(second, &[]);
1734 Builder::new(&mut func, second).jump(exit, &[]);
1735 Builder::new(&mut func, exit).ret(&[]);
1736 let stats = simplify(&mut func);
1737 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 2);
1738 assert_eq!(blocks(&func), [0, 1, 2, 5]);
1739 assert_eq!(goes_to(&func, 1), [5]);
1740 assert_eq!(goes_to(&func, 2), [5]);
1741 }
1742
1743 #[test]
1744 fn a_block_parameter_that_arrives_as_one_value_every_way_in_goes() {
1745 let mut func = taking_a_condition();
1748 let (carried, arms) = arms(&mut func);
1749 let join = func.create_block();
1750 let param = func.append_param(join, Type::int(32));
1751 for arm in arms {
1752 Builder::new(&mut func, arm).jump(join, &[carried]);
1753 }
1754 Builder::new(&mut func, join).ret(&[param]);
1755 let stats = simplify(&mut func);
1756 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1757 assert!(func[Block::from_usize(3)].params.is_empty());
1758 let term = func.terminator(Block::from_usize(3)).expect("the join has one");
1760 assert_eq!(func[func[term].args], [carried]);
1761 assert!(carries(&func, 1, 0).is_empty());
1764 assert!(carries(&func, 2, 0).is_empty());
1765 }
1766
1767 #[test]
1768 fn a_block_parameter_that_differs_on_one_way_in_stays() {
1769 let mut func = taking_a_condition();
1770 let (carried, arms) = arms(&mut func);
1771 let join = func.create_block();
1772 let param = func.append_param(join, Type::int(32));
1773 let mut build = Builder::new(&mut func, arms[0]);
1774 let mine = build.iconst(Type::int(32), 9);
1775 build.jump(join, &[mine]);
1776 Builder::new(&mut func, arms[1]).jump(join, &[carried]);
1777 Builder::new(&mut func, join).ret(&[param]);
1778 let stats = simplify(&mut func);
1779 assert!(!stats.changed());
1780 assert_eq!(func[Block::from_usize(3)].params, [param]);
1781 }
1782
1783 #[test]
1784 fn a_loop_header_parameter_whose_other_argument_is_itself_is_what_it_started_as() {
1785 let mut names = Interner::new();
1789 let signature = Signature::new().with_params(&[Type::int(1)]);
1790 let mut func = Func::new(names.intern("f"), signature);
1791 let entry = func.create_block();
1792 let header = func.create_block();
1793 let latch = func.create_block();
1794 let exit = func.create_block();
1795 let cond = func.append_param(entry, Type::int(1));
1796 let param = func.append_param(header, Type::int(32));
1797 let mut build = Builder::new(&mut func, entry);
1798 let init = build.iconst(Type::int(32), 7);
1799 build.jump(header, &[init]);
1800 Builder::new(&mut func, header).br_if(cond, latch, &[], exit, &[]);
1801 let mut build = Builder::new(&mut func, latch);
1802 build.iconst(Type::int(32), 1);
1803 build.jump(header, &[param]);
1804 Builder::new(&mut func, exit).ret(&[param]);
1805 let stats = simplify(&mut func);
1806 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1807 assert!(func[Block::from_usize(1)].params.is_empty());
1808 let term = func.terminator(Block::from_usize(3)).expect("the exit has one");
1809 assert_eq!(func[func[term].args], [init]);
1810 }
1811
1812 #[test]
1813 fn the_entry_blocks_parameters_are_the_functions_and_stay() {
1814 let mut names = Interner::new();
1818 let signature = Signature::new().with_params(&[Type::int(1), Type::int(32)]);
1819 let mut func = Func::new(names.intern("f"), signature);
1820 let entry = func.create_block();
1821 let latch = func.create_block();
1822 let exit = func.create_block();
1823 let cond = func.append_param(entry, Type::int(1));
1824 let x = func.append_param(entry, Type::int(32));
1825 Builder::new(&mut func, entry).br_if(cond, latch, &[], exit, &[]);
1826 let mut build = Builder::new(&mut func, latch);
1827 let one = build.iconst(Type::int(1), 1);
1828 let seven = build.iconst(Type::int(32), 7);
1829 build.jump(entry, &[one, seven]);
1830 Builder::new(&mut func, exit).ret(&[x]);
1831 let stats = simplify(&mut func);
1832 assert!(!stats.changed());
1833 assert_eq!(func[Block::from_usize(0)].params, [cond, x]);
1834 }
1835
1836 #[test]
1837 fn taking_one_parameter_away_is_what_makes_the_next_one_redundant() {
1838 let mut func = taking_a_condition();
1842 let (carried, arms) = arms(&mut func);
1843 let join = func.create_block();
1844 let inner = func.append_param(join, Type::int(32));
1845 let left = func.create_block();
1846 let right = func.create_block();
1847 let last = func.create_block();
1848 let outer = func.append_param(last, Type::int(32));
1849 for arm in arms {
1850 Builder::new(&mut func, arm).jump(join, &[carried]);
1851 }
1852 let cond = func[Block::from_usize(0)].params[0];
1853 Builder::new(&mut func, join).br_if(cond, left, &[], right, &[]);
1854 let mut build = Builder::new(&mut func, left);
1855 build.iconst(Type::int(32), 1);
1856 build.jump(last, &[inner]);
1857 let mut build = Builder::new(&mut func, right);
1858 build.iconst(Type::int(32), 2);
1859 build.jump(last, &[carried]);
1860 Builder::new(&mut func, last).ret(&[outer]);
1861 let stats = simplify(&mut func);
1862 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1863 let term = func.terminator(Block::from_usize(6)).expect("the last block has one");
1864 assert_eq!(func[func[term].args], [carried]);
1865 }
1866
1867 #[test]
1868 fn a_forwarder_with_a_parameter_goes_once_the_parameter_does() {
1869 let mut func = taking_a_condition();
1873 let (carried, arms) = arms(&mut func);
1874 let forwarder = func.create_block();
1875 let param = func.append_param(forwarder, Type::int(32));
1876 let exit = func.create_block();
1877 let arrived = func.append_param(exit, Type::int(32));
1878 for arm in arms {
1879 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1880 }
1881 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1882 Builder::new(&mut func, exit).ret(&[arrived]);
1883 let stats = simplify(&mut func);
1884 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 1);
1885 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 2);
1888 assert_eq!(blocks(&func), [0, 1, 2, 4]);
1889 let term = func.terminator(Block::from_usize(4)).expect("the exit has one");
1890 assert_eq!(func[func[term].args], [carried]);
1891 }
1892
1893 #[test]
1894 fn fuel_stops_step_three_the_same_way_it_stops_the_rest() {
1895 let mut func = taking_a_condition();
1898 let (carried, arms) = arms(&mut func);
1899 let forwarder = func.create_block();
1900 let param = func.append_param(forwarder, Type::int(32));
1901 let exit = func.create_block();
1902 let arrived = func.append_param(exit, Type::int(32));
1905 for arm in arms {
1906 Builder::new(&mut func, arm).jump(forwarder, &[carried]);
1907 }
1908 Builder::new(&mut func, forwarder).jump(exit, &[param]);
1909 Builder::new(&mut func, exit).ret(&[arrived]);
1910 let stats =
1911 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
1912 assert_eq!(stats.count(Kind::Optimized, super::SAME_EVERY_WAY), 1);
1913 assert_eq!(stats.count(Kind::Optimized, super::FORWARDED), 0);
1914 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_FORWARD), 1);
1915 assert_eq!(blocks(&func), [0, 1, 2, 3, 4]);
1916 }
1917
1918 fn walking_a_pointer(on_counter: bool) -> Func {
1927 let mut names = Interner::new();
1928 let signature = Signature::new().with_params(&[Type::int(64)]);
1929 let mut func = Func::new(names.intern("f"), signature);
1930 let entry = func.create_block();
1931 let head = func.create_block();
1932 let out = func.create_block();
1933 let end = func.append_param(entry, Type::int(64));
1934 let counter = func.append_param(head, Type::int(32));
1935 let pointer = func.append_param(head, Type::int(64));
1936 let mut build = Builder::new(&mut func, entry);
1937 let from_zero = build.iconst(Type::int(32), 0);
1938 let from_start = build.iconst(Type::int(64), 0);
1939 build.jump(head, &[from_zero, from_start]);
1940 let mut build = Builder::new(&mut func, head);
1941 let one = build.iconst(Type::int(32), 1);
1942 let eight = build.iconst(Type::int(64), 8);
1943 let next = build.binary(Opcode::Add, counter, one, Flags::NONE);
1944 let along = build.binary(Opcode::Add, pointer, eight, Flags::NONE);
1945 let address = build.unary(Opcode::IntToPtr, pointer, Type::PTR);
1948 let info = MemInfo {
1949 size: 8,
1950 align: 8,
1951 order: MemOrder::NotAtomic,
1952 tbaa: None,
1953 restrict: Restrict::NONE,
1954 };
1955 build.store(eight, address, info, Flags::NONE);
1956 let going = if on_counter {
1957 let limit = build.iconst(Type::int(32), 10);
1958 build.icmp(IntPred::Ne, next, limit)
1959 } else {
1960 build.icmp(IntPred::Ne, along, end)
1961 };
1962 build.br_if(going, head, &[next, along], out, &[]);
1963 Builder::new(&mut func, out).ret(&[]);
1964 func
1965 }
1966
1967 #[test]
1968 fn a_counter_the_loop_stopped_asking_about_stops_going_round() {
1969 let mut func = walking_a_pointer(false);
1970 let stats = simplify(&mut func);
1971 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 1);
1972 assert_eq!(func[Block::from_usize(1)].params.len(), 1);
1974 assert_eq!(carries(&func, 1, 0).len(), 1);
1976 assert_eq!(carries(&func, 0, 0).len(), 1);
1977 }
1978
1979 #[test]
1980 fn a_counter_the_loop_still_asks_about_goes_round_exactly_as_before() {
1981 let mut func = walking_a_pointer(true);
1982 let stats = simplify(&mut func);
1983 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
1984 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
1985 }
1986
1987 #[test]
1988 fn the_functions_own_parameters_stay_whether_or_not_anything_reads_them() {
1989 let mut names = Interner::new();
1992 let signature = Signature::new().with_params(&[Type::int(32)]);
1993 let mut func = Func::new(names.intern("f"), signature);
1994 let entry = func.create_block();
1995 func.append_param(entry, Type::int(32));
1996 Builder::new(&mut func, entry).ret(&[]);
1997 let stats = simplify(&mut func);
1998 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
1999 assert_eq!(func[entry].params.len(), 1);
2000 }
2001
2002 #[test]
2003 fn a_parameter_nothing_reads_costs_one_unit_of_fuel_and_stays_without_it() {
2004 let mut func = walking_a_pointer(false);
2005 let stats =
2006 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2007 assert_eq!(stats.count(Kind::Optimized, super::NOTHING_READS_IT), 0);
2008 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL_UNREAD), 1);
2009 assert_eq!(func[Block::from_usize(1)].params.len(), 2);
2010 }
2011
2012 #[test]
2013 fn the_counter_that_went_leaves_the_verifier_nothing_to_complain_about() {
2014 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2015 let mut names = Interner::new();
2016 let mut module = Module::new(names.intern("test.c"), &target);
2017 let mut func = walking_a_pointer(false);
2018 simplify(&mut func);
2019 module.add_func(func);
2020 rucc_ir::verify(&module, &names).expect("taking a parameter out left the function whole");
2021 }
2022
2023 #[test]
2024 fn step_three_leaves_the_verifier_nothing_to_complain_about() {
2025 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2028 let mut names = Interner::new();
2029 let mut module = Module::new(names.intern("test.c"), &target);
2030 let mut func = taking_a_condition();
2031 let (carried, arms) = arms(&mut func);
2032 let forwarder = func.create_block();
2033 let param = func.append_param(forwarder, Type::int(32));
2034 let exit = func.create_block();
2035 let arrived = func.append_param(exit, Type::int(32));
2036 let mut build = Builder::new(&mut func, arms[0]);
2037 let mine = build.iconst(Type::int(32), 9);
2038 build.jump(exit, &[mine]);
2039 Builder::new(&mut func, arms[1]).jump(forwarder, &[carried]);
2040 Builder::new(&mut func, forwarder).jump(exit, &[param]);
2041 let mut build = Builder::new(&mut func, exit);
2042 build.icmp(IntPred::Eq, arrived, arrived);
2045 build.ret(&[]);
2046 simplify(&mut func);
2047 module.add_func(func);
2048 rucc_ir::verify(&module, &names).expect("step three left the function verifiable");
2049 }
2050
2051 #[test]
2052 fn out_of_fuel_leaves_the_function_exactly_as_it_was() {
2053 let (mut func, _) = diamond(|build| build.iconst(Type::int(1), 1));
2054 let before = blocks(&func);
2055 let stats =
2056 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(0));
2057 assert!(!stats.changed());
2058 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2059 assert_eq!(terminator(&func, 0), Opcode::BrIf);
2060 assert_eq!(blocks(&func), before);
2061 }
2062
2063 #[test]
2064 fn what_fuel_buys_is_one_whole_change_and_never_half_of_one() {
2065 let mut func = graph(&[&[1, 2], &[3, 4], &[5], &[5], &[5], &[]]);
2069 let stats =
2070 SimplifyCfg.run(&mut func, &mut crate::machine::fixtures::analyses(), &mut Fuel::of(1));
2071 assert_eq!(stats.count(Kind::Optimized, super::FOLDED), 1);
2072 assert_eq!(stats.count(Kind::Missed, super::NO_FUEL), 1);
2073 assert_eq!(blocks(&func), [0, 1, 3, 4, 5]);
2076 }
2077
2078 #[test]
2079 fn the_pass_leaves_the_verifier_nothing_to_complain_about() {
2080 let target = TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu));
2081 let mut names = Interner::new();
2082 let mut module = Module::new(names.intern("test.c"), &target);
2083 let mut func = graph(&[&[1, 2], &[3], &[3], &[4, 1], &[]]);
2084 simplify(&mut func);
2085 module.add_func(func);
2086 rucc_ir::verify(&module, &names).expect("the pass left the function verifiable");
2087 }
2088
2089 #[test]
2090 fn the_pass_says_it_preserves_nothing() {
2091 assert_eq!(SimplifyCfg.preserves(), Preserved::NONE);
2092 }
2093}