mirror of
https://github.com/facebook/react.git
synced 2026-02-26 04:14:59 +00:00
Autogenerated summaries of each of the compiler passes which allow agents to get the key ideas of a compiler pass, including key input/output invariants, without having to reprocess the file each time. In the subsequent diff this seemed to help. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/35595). * #35607 * #35298 * #35596 * #35573 * __->__ #35595 * #35539
3.9 KiB
3.9 KiB
deadCodeElimination
File
src/Optimization/DeadCodeElimination.ts
Purpose
Eliminates instructions whose values are unused, reducing generated code size. The pass performs mark-and-sweep analysis to identify and remove dead code while preserving side effects and program semantics.
Input Invariants
- Must run after
InferMutationAliasingEffectsbecause "dead" code may still affect effect inference - HIR is in SSA form with phi nodes
- Unreachable blocks are already pruned during HIR construction
Output Guarantees
- All instructions with unused lvalues (that are safe to prune) are removed
- Unused phi nodes are deleted
- Unused context variables are removed from
fn.context - Destructuring patterns are rewritten to remove unused bindings
StoreLocalinstructions with unused initializers are converted toDeclareLocal
Algorithm
Two-phase mark-and-sweep with fixed-point iteration for loops:
Phase 1: Mark (findReferencedIdentifiers)
- Detect if function has back-edges (loops)
- Iterate blocks in reverse postorder (successors before predecessors) to visit usages before declarations
- For each block:
- Mark all terminal operands as referenced
- Process instructions in reverse order:
- If lvalue is used OR instruction is not pruneable, mark the lvalue and all operands as referenced
- Special case for
StoreLocal: only mark initializer if the SSA lvalue is actually read
- Mark phi operands if the phi result is used
- If loops exist and new identifiers were marked, repeat until fixed point
Phase 2: Sweep
- Remove unused phi nodes from each block
- Remove instructions with unused lvalues using
retainWhere - Rewrite retained instructions:
- Array destructuring: Replace unused elements with holes, truncate trailing holes
- Object destructuring: Remove unused properties (only if rest element is unused or absent)
- StoreLocal: Convert to
DeclareLocalif initializer value is never read
- Remove unused context variables
Key Data Structures
- State class: Tracks referenced identifiers
identifiers: Set<IdentifierId>- SSA-specific usagesnamed: Set<string>- Named variable usages (any version)isIdOrNameUsed()- Checks if identifier or any version of named variable is usedisIdUsed()- Checks if specific SSA id is used
- hasBackEdge/findBlocksWithBackEdges: Detect loops requiring fixed-point iteration
Edge Cases
-
Preserved even if unused:
debuggerstatements (to not break debugging workflows)- Call expressions and method calls (may have side effects)
- Await expressions
- Store operations (ComputedStore, PropertyStore, StoreGlobal)
- Delete operations (ComputedDelete, PropertyDelete)
- Iterator operations (GetIterator, IteratorNext, NextPropertyOf)
- Context operations (LoadContext, DeclareContext, StoreContext)
- Memoization markers (StartMemoize, FinishMemoize)
-
SSR mode special case:
- In SSR mode, unused
useState,useReducer, anduseRefhooks can be removed
- In SSR mode, unused
-
Object destructuring with rest:
- Cannot remove unused properties if rest element is used (would change rest's value)
-
Block value instructions:
- Last instruction of value blocks (not 'block' kind) is never pruned as it's the block's value
TODOs
- "TODO: we could be more precise and make this conditional on whether any arguments are actually modified" (for mutating instructions)
Example
Input:
function Component(props) {
const _ = 42;
return props.value;
}
After DeadCodeElimination:
The const _ = 42 assignment is removed since _ is never used:
function Component(props) {
return props.value;
}
Array destructuring example:
Input:
function foo(props) {
const [x, unused, y] = props.a;
return x + y;
}
Output (middle element becomes a hole):
function foo(props) {
const [x, , y] = props.a;
return x + y;
}