1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/// Constant value demotion.
///
/// This pass demotes 'by-value' constant types to 'by-reference` pointer types, based on target
/// specific parameters.
///
/// Storage for constant values is created on the stack in variables which are initialized with the
/// original values.
use crate::{
    AnalysisResults, Block, Constant, Context, Function, InstOp, IrError, Pass, PassMutability,
    ScopedPass, Value,
};

use rustc_hash::FxHashMap;
use sway_types::FxIndexMap;

pub const CONST_DEMOTION_NAME: &str = "const-demotion";

pub fn create_const_demotion_pass() -> Pass {
    Pass {
        name: CONST_DEMOTION_NAME,
        descr: "Demotion of by-value constants to by-reference",
        deps: Vec::new(),
        runner: ScopedPass::FunctionPass(PassMutability::Transform(const_demotion)),
    }
}

pub fn const_demotion(
    context: &mut Context,
    _: &AnalysisResults,
    function: Function,
) -> Result<bool, IrError> {
    // Find all candidate constant values and their wrapped constants.
    let mut candidate_values: FxIndexMap<Block, Vec<(Value, Constant)>> = FxIndexMap::default();

    for (block, inst) in function.instruction_iter(context) {
        let operands = inst.get_instruction(context).unwrap().op.get_operands();
        for val in operands.iter() {
            if let Some(c) = val.get_constant(context) {
                if super::target_fuel::is_demotable_type(context, &c.ty) {
                    let dem = (*val, c.clone());
                    match candidate_values.entry(block) {
                        indexmap::map::Entry::Occupied(mut occ) => {
                            occ.get_mut().push(dem);
                        }
                        indexmap::map::Entry::Vacant(vac) => {
                            vac.insert(vec![dem]);
                        }
                    }
                }
            }
        }
    }

    if candidate_values.is_empty() {
        return Ok(false);
    }

    for (block, cands) in candidate_values {
        let mut replace_map: FxHashMap<Value, Value> = FxHashMap::default();
        // The new instructions we're going to insert at the start of this block.
        let mut this_block_new = Vec::new();
        for (c_val, c) in cands {
            // Create a variable for const.
            let var = function.new_unique_local_var(
                context,
                "__const".to_owned(),
                c.ty,
                Some(c.clone()),
                false,
            );
            let var_val = Value::new_instruction(context, block, InstOp::GetLocal(var));
            let load_val = Value::new_instruction(context, block, InstOp::Load(var_val));
            replace_map.insert(c_val, load_val);
            this_block_new.push(var_val);
            this_block_new.push(load_val);
        }
        block.replace_values(context, &replace_map);
        block.prepend_instructions(context, this_block_new);
    }

    Ok(true)
}