react_compiler_optimization/
prune_maybe_throws.rs1use rustc_hash::FxHashMap;
14
15use react_compiler_diagnostics::{
16 CompilerDiagnostic, CompilerDiagnosticDetail, ErrorCategory, GENERATED_SOURCE,
17};
18use react_compiler_hir::{BlockId, HirFunction, Instruction, InstructionValue, Terminal};
19use react_compiler_lowering::{
20 get_reverse_postordered_blocks, mark_instruction_ids, remove_dead_do_while_statements,
21 remove_unnecessary_try_catch, remove_unreachable_for_updates,
22};
23
24use crate::merge_consecutive_blocks::merge_consecutive_blocks;
25
26pub fn prune_maybe_throws(
28 func: &mut HirFunction,
29 functions: &mut [HirFunction],
30) -> Result<(), CompilerDiagnostic> {
31 let terminal_mapping = prune_maybe_throws_impl(func);
32 if let Some(terminal_mapping) = terminal_mapping {
33 func.body.blocks = get_reverse_postordered_blocks(&func.body, &func.instructions);
36 remove_unreachable_for_updates(&mut func.body);
37 remove_dead_do_while_statements(&mut func.body);
38 remove_unnecessary_try_catch(&mut func.body);
39 mark_instruction_ids(&mut func.body, &mut func.instructions);
40 merge_consecutive_blocks(func, functions);
41
42 for block in func.body.blocks.values_mut() {
44 let preds = &block.preds;
45 let mut phi_updates: Vec<(usize, Vec<(BlockId, BlockId)>)> = Vec::new();
46
47 for (phi_idx, phi) in block.phis.iter().enumerate() {
48 let mut updates = Vec::new();
49 for (predecessor, _) in &phi.operands {
50 if !preds.contains(predecessor) {
51 let mapped_terminal =
52 terminal_mapping.get(predecessor).copied().ok_or_else(|| {
53 CompilerDiagnostic::new(
54 ErrorCategory::Invariant,
55 "Expected non-existing phi operand's predecessor to have been mapped to a new terminal",
56 Some(format!(
57 "Could not find mapping for predecessor bb{} in block bb{}",
58 predecessor.0, block.id.0,
59 )),
60 )
61 .with_detail(CompilerDiagnosticDetail::Error {
62 loc: GENERATED_SOURCE,
63 message: None,
64 identifier_name: None,
65 })
66 })?;
67 updates.push((*predecessor, mapped_terminal));
68 }
69 }
70 if !updates.is_empty() {
71 phi_updates.push((phi_idx, updates));
72 }
73 }
74
75 for (phi_idx, updates) in phi_updates {
76 for (old_pred, new_pred) in updates {
77 let operand = block.phis[phi_idx]
78 .operands
79 .shift_remove(&old_pred)
80 .unwrap();
81 block.phis[phi_idx].operands.insert(new_pred, operand);
82 }
83 }
84 }
85 }
86 Ok(())
87}
88
89fn prune_maybe_throws_impl(func: &mut HirFunction) -> Option<FxHashMap<BlockId, BlockId>> {
90 let mut terminal_mapping: FxHashMap<BlockId, BlockId> = FxHashMap::default();
91 let instructions = &func.instructions;
92
93 for block in func.body.blocks.values_mut() {
94 let continuation = match &block.terminal {
95 Terminal::MaybeThrow { continuation, .. } => *continuation,
96 _ => continue,
97 };
98
99 let can_throw = block
100 .instructions
101 .iter()
102 .any(|instr_id| instruction_may_throw(&instructions[instr_id.0 as usize]));
103
104 if !can_throw {
105 let source = terminal_mapping.get(&block.id).copied().unwrap_or(block.id);
106 terminal_mapping.insert(continuation, source);
107 if let Terminal::MaybeThrow { handler, .. } = &mut block.terminal {
112 *handler = None;
113 }
114 }
115 }
116
117 if terminal_mapping.is_empty() {
118 None
119 } else {
120 Some(terminal_mapping)
121 }
122}
123
124fn instruction_may_throw(instr: &Instruction) -> bool {
125 match &instr.value {
126 InstructionValue::Primitive { .. }
127 | InstructionValue::ArrayExpression { .. }
128 | InstructionValue::ObjectExpression { .. } => false,
129 _ => true,
130 }
131}