main
rs 156 lines 6.01 KB
Raw
1 use rustc_hash::{FxHashMap, FxHashSet};
2
3 use react_compiler_hir::environment::Environment;
4 use react_compiler_hir::visitors;
5 use react_compiler_hir::*;
6
7 use crate::enter_ssa::placeholder_function;
8
9 // =============================================================================
10 // Helper: rewrite_place
11 // =============================================================================
12
13 fn rewrite_place(place: &mut Place, rewrites: &FxHashMap<IdentifierId, IdentifierId>) {
14 if let Some(&rewrite) = rewrites.get(&place.identifier) {
15 place.identifier = rewrite;
16 }
17 }
18
19 // =============================================================================
20 // Public entry point
21 // =============================================================================
22
23 pub fn eliminate_redundant_phi(func: &mut HirFunction, env: &mut Environment) {
24 let mut rewrites: FxHashMap<IdentifierId, IdentifierId> = FxHashMap::default();
25 eliminate_redundant_phi_impl(func, env, &mut rewrites);
26 }
27
28 // =============================================================================
29 // Inner implementation
30 // =============================================================================
31
32 fn eliminate_redundant_phi_impl(
33 func: &mut HirFunction,
34 env: &mut Environment,
35 rewrites: &mut FxHashMap<IdentifierId, IdentifierId>,
36 ) {
37 let ir = &mut func.body;
38
39 let mut has_back_edge = false;
40 let mut visited: FxHashSet<BlockId> = FxHashSet::default();
41
42 let mut size;
43 loop {
44 size = rewrites.len();
45
46 let block_ids: Vec<BlockId> = ir.blocks.keys().copied().collect();
47 for block_id in &block_ids {
48 let block_id = *block_id;
49
50 if !has_back_edge {
51 let block = ir.blocks.get(&block_id).unwrap();
52 for pred_id in &block.preds {
53 if !visited.contains(pred_id) {
54 has_back_edge = true;
55 }
56 }
57 }
58 visited.insert(block_id);
59
60 // Find any redundant phis: rewrite operands, identify redundant phis, remove them.
61 // Matches TS behavior: each phi's operands are rewritten before checking redundancy,
62 // so that rewrites from earlier phis in the same block are visible to later phis.
63 let block = ir.blocks.get_mut(&block_id).unwrap();
64 block.phis.retain_mut(|phi| {
65 // Remap phis in case operands are from eliminated phis
66 for (_, operand) in phi.operands.iter_mut() {
67 rewrite_place(operand, rewrites);
68 }
69
70 // Find if the phi can be eliminated
71 let mut same: Option<IdentifierId> = None;
72 let mut is_redundant = true;
73 for (_, operand) in &phi.operands {
74 if (same.is_some() && operand.identifier == same.unwrap())
75 || operand.identifier == phi.place.identifier
76 {
77 continue;
78 } else if same.is_some() {
79 is_redundant = false;
80 break;
81 } else {
82 same = Some(operand.identifier);
83 }
84 }
85 if is_redundant {
86 let same = same.expect("Expected phis to be non-empty");
87 rewrites.insert(phi.place.identifier, same);
88 false // remove this phi
89 } else {
90 true // keep this phi
91 }
92 });
93
94 // Rewrite instructions
95 let instruction_ids: Vec<InstructionId> =
96 ir.blocks.get(&block_id).unwrap().instructions.clone();
97
98 for instr_id in &instruction_ids {
99 let instr_idx = instr_id.0 as usize;
100 let instr = &mut func.instructions[instr_idx];
101
102 // Rewrite all lvalues (matches TS eachInstructionLValue)
103 rewrite_place(&mut instr.lvalue, rewrites);
104 visitors::for_each_instruction_value_lvalue_mut(&mut instr.value, &mut |place| {
105 rewrite_place(place, rewrites);
106 });
107
108 // Rewrite operands using canonical visitor
109 visitors::for_each_instruction_value_operand_mut(
110 &mut func.instructions[instr_idx].value,
111 &mut |place| {
112 rewrite_place(place, rewrites);
113 },
114 );
115
116 // Handle FunctionExpression/ObjectMethod context and recursion
117 let instr = &func.instructions[instr_idx];
118 let func_expr_id = match &instr.value {
119 InstructionValue::FunctionExpression { lowered_func, .. }
120 | InstructionValue::ObjectMethod { lowered_func, .. } => {
121 Some(lowered_func.func)
122 }
123 _ => None,
124 };
125
126 if let Some(fid) = func_expr_id {
127 // Rewrite context places
128 let context = &mut env.functions[fid.0 as usize].context;
129 for place in context.iter_mut() {
130 rewrite_place(place, rewrites);
131 }
132
133 // Take inner function out, process it, put it back
134 let mut inner_func = std::mem::replace(
135 &mut env.functions[fid.0 as usize],
136 placeholder_function(),
137 );
138
139 eliminate_redundant_phi_impl(&mut inner_func, env, rewrites);
140
141 env.functions[fid.0 as usize] = inner_func;
142 }
143 }
144
145 // Rewrite terminal operands using canonical visitor
146 let terminal = &mut ir.blocks.get_mut(&block_id).unwrap().terminal;
147 visitors::for_each_terminal_operand_mut(terminal, &mut |place| {
148 rewrite_place(place, rewrites);
149 });
150 }
151
152 if !(rewrites.len() > size && has_back_edge) {
153 break;
154 }
155 }
156 }