sway_ir/optimize/
conditional_constprop.rs

1//! When a value is guaranteed to have a constant value in a region of the CFG,
2//! this optimization replaces uses of that value with the constant in that region.
3
4use 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    // In the set of blocks dominated by `key`, replace all uses of `val.0` with `val.1`.
30    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    // lets walk the dominator tree from the root.
63    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            // Preorder processing
76            if let Some(cur_replacement) = cur_replacement_opt {
77                replacements.insert(cur_replacement.0, cur_replacement.1);
78            }
79            // walk the current block.
80            block.replace_values(context, &replacements);
81        }
82
83        // walk children.
84        if let Some(child) = dom_tree.child(block, next_child) {
85            // When we arrive back at "block" next time, we should process the next child.
86            stack.last_mut().unwrap().1 = next_child + 1;
87            // Go on to process the child.
88            stack.push((child, 0));
89        } else {
90            // No children left to process. Start postorder processing.
91            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}