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
83
84
use crate::{
AnalysisResults, Block, Constant, Context, Function, IrError, Pass, PassMutability, ScopedPass,
Value,
};
use rustc_hash::FxHashMap;
pub const CONSTDEMOTION_NAME: &str = "constdemotion";
pub fn create_const_demotion_pass() -> Pass {
Pass {
name: CONSTDEMOTION_NAME,
descr: "By-value constant demotion 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> {
let candidate_values = function
.instruction_iter(context)
.flat_map(|(_block, inst)| inst.get_instruction(context).unwrap().get_operands())
.filter_map(|val| {
val.get_constant(context).and_then(|c| {
super::target_fuel::is_demotable_type(context, &c.ty).then(|| (val, c.clone()))
})
})
.collect::<Vec<_>>();
if candidate_values.is_empty() {
return Ok(false);
}
let (const_init_block, orig_entry_block) =
function.get_entry_block(context).split_at(context, 0);
let replace_map =
FxHashMap::from_iter(candidate_values.into_iter().map(|(old_value, constant)| {
(
old_value,
demote(context, &function, &const_init_block, &constant),
)
}));
const_init_block
.ins(context)
.branch(orig_entry_block, Vec::new());
function.replace_values(context, &replace_map, Some(orig_entry_block));
assert_eq!(const_init_block, function.get_entry_block(context));
Ok(true)
}
fn demote(context: &mut Context, function: &Function, block: &Block, constant: &Constant) -> Value {
let var = function.new_unique_local_var(
context,
"__const".to_owned(),
constant.ty,
Some(constant.clone()),
);
let var_val = block.ins(context).get_local(var);
block.ins(context).load(var_val)
}