sway_ir/optimize/
const_demotion.rs

1/// Constant value demotion.
2///
3/// This pass demotes 'by-value' constant types to 'by-reference` pointer types, based on target
4/// specific parameters.
5///
6/// Storage for constant values is created on the stack in variables which are initialized with the
7/// original values.
8use crate::{
9    AnalysisResults, Block, Constant, Context, Function, InstOp, IrError, Pass, PassMutability,
10    ScopedPass, Value,
11};
12
13use rustc_hash::FxHashMap;
14use sway_types::FxIndexMap;
15
16pub const CONST_DEMOTION_NAME: &str = "const-demotion";
17
18pub fn create_const_demotion_pass() -> Pass {
19    Pass {
20        name: CONST_DEMOTION_NAME,
21        descr: "Demotion of by-value constants to by-reference",
22        deps: Vec::new(),
23        runner: ScopedPass::FunctionPass(PassMutability::Transform(const_demotion)),
24    }
25}
26
27pub fn const_demotion(
28    context: &mut Context,
29    _: &AnalysisResults,
30    function: Function,
31) -> Result<bool, IrError> {
32    // Find all candidate constant values and their wrapped constants.
33    let mut candidate_values: FxIndexMap<Block, Vec<(Value, Constant)>> = FxIndexMap::default();
34
35    for (block, inst) in function.instruction_iter(context) {
36        let operands = inst.get_instruction(context).unwrap().op.get_operands();
37        for val in operands.iter() {
38            if let Some(c) = val.get_constant(context) {
39                if super::target_fuel::is_demotable_type(context, &c.get_content(context).ty) {
40                    let dem = (*val, *c);
41                    match candidate_values.entry(block) {
42                        indexmap::map::Entry::Occupied(mut occ) => {
43                            occ.get_mut().push(dem);
44                        }
45                        indexmap::map::Entry::Vacant(vac) => {
46                            vac.insert(vec![dem]);
47                        }
48                    }
49                }
50            }
51        }
52    }
53
54    if candidate_values.is_empty() {
55        return Ok(false);
56    }
57
58    for (block, cands) in candidate_values {
59        let mut replace_map: FxHashMap<Value, Value> = FxHashMap::default();
60        // The new instructions we're going to insert at the start of this block.
61        let mut this_block_new = Vec::new();
62        for (c_val, c) in cands {
63            // Create a variable for const.
64            let var = function.new_unique_local_var(
65                context,
66                "__const".to_owned(),
67                c.get_content(context).ty,
68                Some(c),
69                false,
70            );
71            let var_val = Value::new_instruction(context, block, InstOp::GetLocal(var));
72            let load_val = Value::new_instruction(context, block, InstOp::Load(var_val));
73            replace_map.insert(c_val, load_val);
74            this_block_new.push(var_val);
75            this_block_new.push(load_val);
76        }
77        block.replace_values(context, &replace_map);
78        block.prepend_instructions(context, this_block_new);
79    }
80
81    Ok(true)
82}