react_compiler_ssa/
eliminate_redundant_phi.rs1use std::collections::{HashMap, HashSet};
2
3use react_compiler_hir::environment::Environment;
4use react_compiler_hir::visitors;
5use react_compiler_hir::*;
6
7use crate::enter_ssa::placeholder_function;
8
9fn rewrite_place(place: &mut Place, rewrites: &HashMap<IdentifierId, IdentifierId>) {
14 if let Some(&rewrite) = rewrites.get(&place.identifier) {
15 place.identifier = rewrite;
16 }
17}
18
19
20pub fn eliminate_redundant_phi(func: &mut HirFunction, env: &mut Environment) {
25 let mut rewrites: HashMap<IdentifierId, IdentifierId> = HashMap::new();
26 eliminate_redundant_phi_impl(func, env, &mut rewrites);
27}
28
29fn eliminate_redundant_phi_impl(
34 func: &mut HirFunction,
35 env: &mut Environment,
36 rewrites: &mut HashMap<IdentifierId, IdentifierId>,
37) {
38 let ir = &mut func.body;
39
40 let mut has_back_edge = false;
41 let mut visited: HashSet<BlockId> = HashSet::new();
42
43 let mut size;
44 loop {
45 size = rewrites.len();
46
47 let block_ids: Vec<BlockId> = ir.blocks.keys().copied().collect();
48 for block_id in &block_ids {
49 let block_id = *block_id;
50
51 if !has_back_edge {
52 let block = ir.blocks.get(&block_id).unwrap();
53 for pred_id in &block.preds {
54 if !visited.contains(pred_id) {
55 has_back_edge = true;
56 }
57 }
58 }
59 visited.insert(block_id);
60
61 let block = ir.blocks.get_mut(&block_id).unwrap();
65 block.phis.retain_mut(|phi| {
66 for (_, operand) in phi.operands.iter_mut() {
68 rewrite_place(operand, rewrites);
69 }
70
71 let mut same: Option<IdentifierId> = None;
73 let mut is_redundant = true;
74 for (_, operand) in &phi.operands {
75 if (same.is_some() && operand.identifier == same.unwrap())
76 || operand.identifier == phi.place.identifier
77 {
78 continue;
79 } else if same.is_some() {
80 is_redundant = false;
81 break;
82 } else {
83 same = Some(operand.identifier);
84 }
85 }
86 if is_redundant {
87 let same = same.expect("Expected phis to be non-empty");
88 rewrites.insert(phi.place.identifier, same);
89 false } else {
91 true }
93 });
94
95 let instruction_ids: Vec<InstructionId> = ir
97 .blocks
98 .get(&block_id)
99 .unwrap()
100 .instructions
101 .clone();
102
103 for instr_id in &instruction_ids {
104 let instr_idx = instr_id.0 as usize;
105 let instr = &mut func.instructions[instr_idx];
106
107 rewrite_place(&mut instr.lvalue, rewrites);
109 visitors::for_each_instruction_value_lvalue_mut(&mut instr.value, &mut |place| {
110 rewrite_place(place, rewrites);
111 });
112
113 visitors::for_each_instruction_value_operand_mut(&mut func.instructions[instr_idx].value, &mut |place| {
115 rewrite_place(place, rewrites);
116 });
117
118 let instr = &func.instructions[instr_idx];
120 let func_expr_id = match &instr.value {
121 InstructionValue::FunctionExpression { lowered_func, .. }
122 | InstructionValue::ObjectMethod { lowered_func, .. } => {
123 Some(lowered_func.func)
124 }
125 _ => None,
126 };
127
128 if let Some(fid) = func_expr_id {
129 let context =
131 &mut env.functions[fid.0 as usize].context;
132 for place in context.iter_mut() {
133 rewrite_place(place, rewrites);
134 }
135
136 let mut inner_func = std::mem::replace(
138 &mut env.functions[fid.0 as usize],
139 placeholder_function(),
140 );
141
142 eliminate_redundant_phi_impl(&mut inner_func, env, rewrites);
143
144 env.functions[fid.0 as usize] = inner_func;
145 }
146 }
147
148 let terminal = &mut ir.blocks.get_mut(&block_id).unwrap().terminal;
150 visitors::for_each_terminal_operand_mut(terminal, &mut |place| {
151 rewrite_place(place, rewrites);
152 });
153 }
154
155 if !(rewrites.len() > size && has_back_edge) {
156 break;
157 }
158 }
159}