sway_ir/optimize/
conditional_constprop.rs1use rustc_hash::FxHashMap;
5
6use crate::{
7 AnalysisResults, Context, DomTree, Function, InstOp, Instruction, IrError, Pass,
8 PassMutability, Predicate, ScopedPass, DOMINATORS_NAME,
9};
10
11pub const CCP_NAME: &str = "ccp";
12
13pub fn create_ccp_pass() -> Pass {
14 Pass {
15 name: CCP_NAME,
16 descr: "Conditional constant proparagion",
17 deps: vec![DOMINATORS_NAME],
18 runner: ScopedPass::FunctionPass(PassMutability::Transform(ccp)),
19 }
20}
21
22pub fn ccp(
23 context: &mut Context,
24 analyses: &AnalysisResults,
25 function: Function,
26) -> Result<bool, IrError> {
27 let dom_tree: &DomTree = analyses.get_analysis_result(function);
28
29 let mut dom_region_replacements = FxHashMap::default();
31
32 for block in function.block_iter(context) {
33 let term = block
34 .get_terminator(context)
35 .expect("Malformed block: no terminator");
36 if let InstOp::ConditionalBranch {
37 cond_value,
38 true_block,
39 false_block,
40 } = &term.op
41 {
42 if let Some(Instruction {
43 parent: _,
44 op: InstOp::Cmp(pred, v1, v2),
45 }) = cond_value.get_instruction(context)
46 {
47 if true_block.block != false_block.block
48 && matches!(pred, Predicate::Equal)
49 && (v1.is_constant(context) ^ v2.is_constant(context)
50 && true_block.block.num_predecessors(context) == 1)
51 {
52 if v1.is_constant(context) {
53 dom_region_replacements.insert(true_block.block, (*v2, *v1));
54 } else {
55 dom_region_replacements.insert(true_block.block, (*v1, *v2));
56 }
57 }
58 }
59 }
60 }
61
62 let root_block = function.get_entry_block(context);
64
65 if dom_region_replacements.is_empty() {
66 return Ok(false);
67 }
68
69 let mut stack = vec![(root_block, 0)];
70 let mut replacements = FxHashMap::default();
71 while let Some((block, next_child)) = stack.last().cloned() {
72 let cur_replacement_opt = dom_region_replacements.get(&block);
73
74 if next_child == 0 {
75 if let Some(cur_replacement) = cur_replacement_opt {
77 replacements.insert(cur_replacement.0, cur_replacement.1);
78 }
79 block.replace_values(context, &replacements);
81 }
82
83 if let Some(child) = dom_tree.child(block, next_child) {
85 stack.last_mut().unwrap().1 = next_child + 1;
87 stack.push((child, 0));
89 } else {
90 if let Some(cur_replacement) = cur_replacement_opt {
92 replacements.remove(&cur_replacement.0);
93 }
94 stack.pop();
95 }
96 }
97
98 Ok(true)
99}