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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use crate::{
context::Context, error::IrError, function::Function, instruction::Instruction,
value::ValueDatum, Block, Value,
};
use std::collections::HashMap;
fn can_eliminate_instruction(context: &Context, val: Value) -> bool {
let inst = val.get_instruction(context).unwrap();
!inst.is_terminator() && !inst.may_have_side_effect()
}
pub fn dce(context: &mut Context, function: &Function) -> Result<bool, IrError> {
let mut num_uses: HashMap<Value, (Block, u32)> = HashMap::new();
fn get_operands(inst: &Instruction) -> Vec<Value> {
match inst {
Instruction::AddrOf(v) => vec![*v],
Instruction::AsmBlock(_, args) => args.iter().filter_map(|aa| aa.initializer).collect(),
Instruction::BitCast(v, _) => vec![*v],
Instruction::BinaryOp { op: _, arg1, arg2 } => vec![*arg1, *arg2],
Instruction::Branch(_) => vec![],
Instruction::Call(_, vs) => vs.clone(),
Instruction::Cmp(_, lhs, rhs) => vec![*lhs, *rhs],
Instruction::ConditionalBranch {
cond_value,
true_block: _,
false_block: _,
} => vec![*cond_value],
Instruction::ContractCall {
return_type: _,
name: _,
params,
coins,
asset_id,
gas,
} => vec![*params, *coins, *asset_id, *gas],
Instruction::ExtractElement {
array,
ty: _,
index_val,
} => vec![*array, *index_val],
Instruction::ExtractValue {
aggregate,
ty: _,
indices: _,
} => vec![*aggregate],
Instruction::GetStorageKey => vec![],
Instruction::Gtf {
index,
tx_field_id: _,
} => vec![*index],
Instruction::GetPointer {
base_ptr: _,
ptr_ty: _,
offset: _,
} =>
{
vec![]
}
Instruction::InsertElement {
array,
ty: _,
value,
index_val,
} => vec![*array, *value, *index_val],
Instruction::InsertValue {
aggregate,
ty: _,
value,
indices: _,
} => vec![*aggregate, *value],
Instruction::IntToPtr(v, _) => vec![*v],
Instruction::Load(v) => vec![*v],
Instruction::Log {
log_val, log_id, ..
} => vec![*log_val, *log_id],
Instruction::Nop => vec![],
Instruction::Phi(ins) => ins.iter().map(|v| v.1).collect(),
Instruction::ReadRegister(_) => vec![],
Instruction::Ret(v, _) => vec![*v],
Instruction::StateLoadQuadWord { load_val, key } => vec![*load_val, *key],
Instruction::StateLoadWord(key) => vec![*key],
Instruction::StateStoreQuadWord { stored_val, key } => vec![*stored_val, *key],
Instruction::StateStoreWord { stored_val, key } => vec![*stored_val, *key],
Instruction::Store {
dst_val,
stored_val,
} => {
vec![*dst_val, *stored_val]
}
}
}
for (block, inst) in function.instruction_iter(context) {
let opds = get_operands(inst.get_instruction(context).unwrap());
for v in opds {
match context.values[v.0].value {
ValueDatum::Instruction(_) => {
num_uses
.entry(v)
.and_modify(|(_block, count)| *count += 1)
.or_insert((block, 1));
}
ValueDatum::Constant(_) | ValueDatum::Argument(_) => (),
}
}
}
let mut worklist = function
.instruction_iter(context)
.filter(|(_block, inst)| num_uses.get(inst).is_none())
.collect::<Vec<_>>();
let mut modified = false;
while !worklist.is_empty() {
let (in_block, dead) = worklist.pop().unwrap();
if !can_eliminate_instruction(context, dead) {
continue;
}
let opds = get_operands(dead.get_instruction(context).unwrap());
for v in opds {
match context.values[v.0].value {
ValueDatum::Instruction(_) => {
let (block, nu) = num_uses.get_mut(&v).unwrap();
*nu -= 1;
if *nu == 0 {
worklist.push((*block, v));
}
}
ValueDatum::Constant(_) | ValueDatum::Argument(_) => (),
}
}
if matches!(
&context.values[dead.0].value,
ValueDatum::Instruction(Instruction::Phi(_))
) {
dead.replace(context, ValueDatum::Instruction(Instruction::Phi(vec![])));
} else {
in_block.remove_instruction(context, dead);
}
modified = true;
}
Ok(modified)
}