mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
1dfaf8a94bf5ab3f7fe2afac6f31ac75ce884fed
This PR changes BuildHIR to lower all operands to temporaries. Example: ```javascript // Input a + b; // Previous Lowering Const t0 = BinaryOperation Place(a) "+" Place(b) // New Lowering Const t0 = Place(a); Const t1 = Place(b); BinaryOperation Place(t0) "+" Place(t1) ``` This is necessary to ensure we're always referring to the correct version of a variable, even in the case of reassignment mid-expression. For example, we previously evaluated `let x=1; x + (x = 2) + x` incorrectly to 6 because we lowered the `x = 2` prior to the binary operators. We now lowers each instance of x to a temporary, ensuring they refer to the correct SSA version of the variable, and produce the correct result (5). Note that with this change, the _only_ place a variable can appear as an operator is when the InstructionValue is a raw identifier. This was already the case for globals (as of the LoadGlobal instruction). All other instruction value variants will only ever receive temporaries as arguments. This necessitated a few changes to our inference: * The logic to extend the range of phi operands (if the phi is mutated) was previously in LeaveSSA, but that was actually too late. The introduction of lowering to temporaries help discover failing cases, which I fixed earlier in the stack by moving the logic to extend the range of phi operands into the InferMutableRanges fixpoint loop. * PropagateScopeDependencies now has to track variable reassignments in addition to tracking property accesses * AnalyzeFunctions now has to track variable reassignments in addition to tracking property accesses * InferReactiveIdentifiers now needs a fixpoint iteration, because identifiers don't directly appear together in the same instruction anymore (such that we can directly propagate the reactivity between them). Instead, we'll first see that the temporaries are reactive, and have to propagate that back to the identifiers the temporaries were loaded from. Overall while this does introduce a bit more complexity, it also makes the compiler more robust. As with the phi example illustrates, there are legitimate inputs that can create similar indirections to that introduced by lowering identifiers to temporaries. Note that there’s a theme to the changes here: several analysis passes need to map an operand back to its identifier value. Ideally our HIR structure would directly support looking up the value for a temporary. For example, if operands were references to eg the index of the instruction that produced them. Because we don’t have such a representation yet (it would fall out naturally if we were writing in Rust), we have to do some bookkeeping. The key takeaway here is that this bookkeeping is incidental complexity given our current representation, not fundamental complexity of the algorithm.
Languages
JavaScript
67.1%
TypeScript
29.4%
HTML
1.5%
CSS
1.1%
C++
0.6%
Other
0.2%