@samitouri / QOS-React-2 / commits / d35fef9e21

[compiler] Fix for consecutive DCE'd branches with phis (#33725)

This is an optimized version of @asmjmp0's fix in https://github.com/facebook/react/pull/31940. When we merge consecutive blocks we need to take care to rewrite later phis whose operands will now be different blocks due to merging. Rather than iterate all the blocks on each merge as in #31940, we can do a single iteration over all the phis at the end to fix them up. Note: this is a redo of #31959 --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33725). * #33726 * __->__ #33725

Joseph Savona committed Jul 8, 2025 at 19:36 UTC d35fef9e21100463b0bfecb975e9d9eca45c963f
3 files changed +83
compiler/packages/babel-plugin-react-compiler/src/HIR/MergeConsecutiveBlocks.ts
+11
@@ -107,6 +107,17 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
107 merged.merge(block.id, predecessorId);
108 fn.body.blocks.delete(block.id);
109 }
110 + for (const [, block] of fn.body.blocks) {
111 + for (const phi of block.phis) {
112 + for (const [predecessorId, operand] of phi.operands) {
113 + const mapped = merged.get(predecessorId);
114 + if (mapped !== predecessorId) {
115 + phi.operands.delete(predecessorId);
116 + phi.operands.set(mapped, operand);
117 + }
118 + }
119 + }
120 + }
121 markPredecessors(fn.body);
122 for (const [, {terminal}] of fn.body.blocks) {
123 if (terminalHasFallthrough(terminal)) {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-phi-after-dce-merge-scopes.expect.md new
+52
@@ -0,0 +1,52 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component() {
6 + let v3, v4, acc;
7 + v3 = false;
8 + v4 = v3;
9 + acc = v3;
10 + if (acc) {
11 + acc = true;
12 + v3 = acc;
13 + }
14 + if (acc) {
15 + v3 = v4;
16 + }
17 + v4 = v3;
18 + return [acc, v3, v4];
19 +}
20 +
21 +export const FIXTURE_ENTRYPOINT = {
22 + fn: Component,
23 + params: [],
24 +};
25 +
26 +```
27 +
28 +## Code
29 +
30 +```javascript
31 +import { c as _c } from "react/compiler-runtime";
32 +function Component() {
33 + const $ = _c(1);
34 + let t0;
35 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
36 + t0 = [false, false, false];
37 + $[0] = t0;
38 + } else {
39 + t0 = $[0];
40 + }
41 + return t0;
42 +}
43 +
44 +export const FIXTURE_ENTRYPOINT = {
45 + fn: Component,
46 + params: [],
47 +};
48 +
49 +```
50 +
51 +### Eval output
52 +(kind: ok) [false,false,false]
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/repro-missing-phi-after-dce-merge-scopes.js new
+20
@@ -0,0 +1,20 @@
1 +function Component() {
2 + let v3, v4, acc;
3 + v3 = false;
4 + v4 = v3;
5 + acc = v3;
6 + if (acc) {
7 + acc = true;
8 + v3 = acc;
9 + }
10 + if (acc) {
11 + v3 = v4;
12 + }
13 + v4 = v3;
14 + return [acc, v3, v4];
15 +}
16 +
17 +export const FIXTURE_ENTRYPOINT = {
18 + fn: Component,
19 + params: [],
20 +};