@samitouri / QOS-React-2 / commits / 43c17d13ef

[compiler] HIR-based FlattenReactiveLoops

Pre the title, this implements an HIR-based version of FlattenReactiveLoops. Another step on the way to HIR-everywhere. ghstack-source-id: e1d166352df6b0725e4c4915a19445437916251f Pull Request resolved: https://github.com/facebook/react/pull/29838

Joe Savona committed Jun 10, 2024 at 13:22 UTC 43c17d13efc0c356a89a9036f173680ba1c115d2
2 files changed +87 -8
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+16 -8
@@ -70,7 +70,9 @@ import {
70 } from "../ReactiveScopes";
71 import { alignMethodCallScopes } from "../ReactiveScopes/AlignMethodCallScopes";
72 import { alignReactiveScopesToBlockScopesHIR } from "../ReactiveScopes/AlignReactiveScopesToBlockScopesHIR";
73 +import { flattenReactiveLoopsHIR } from "../ReactiveScopes/FlattenReactiveLoopsHIR";
74 import { pruneAlwaysInvalidatingScopes } from "../ReactiveScopes/PruneAlwaysInvalidatingScopes";
75 +import pruneInitializationDependencies from "../ReactiveScopes/PruneInitializationDependencies";
76 import { stabilizeBlockIds } from "../ReactiveScopes/StabilizeBlockIds";
77 import { eliminateRedundantPhi, enterSSA, leaveSSA } from "../SSA";
78 import { inferTypes } from "../TypeInference";
@@ -91,7 +93,6 @@ import {
93 validatePreservedManualMemoization,
94 validateUseMemo,
95 } from "../Validation";
94 -import pruneInitializationDependencies from "../ReactiveScopes/PruneInitializationDependencies";
96
97 export type CompilerPipelineValue =
98 | { kind: "ast"; name: string; value: CodegenFunction }
@@ -281,6 +282,13 @@ function* runWithEnvironment(
282 });
283
284 assertValidBlockNesting(hir);
285 +
286 + flattenReactiveLoopsHIR(hir);
287 + yield log({
288 + kind: "hir",
289 + name: "FlattenReactiveLoopsHIR",
290 + value: hir,
291 + });
292 }
293
294 const reactiveFunction = buildReactiveFunction(hir);
@@ -320,14 +328,14 @@ function* runWithEnvironment(
328 name: "BuildReactiveBlocks",
329 value: reactiveFunction,
330 });
323 - }
331
325 - flattenReactiveLoops(reactiveFunction);
326 - yield log({
327 - kind: "reactive",
328 - name: "FlattenReactiveLoops",
329 - value: reactiveFunction,
330 - });
332 + flattenReactiveLoops(reactiveFunction);
333 + yield log({
334 + kind: "reactive",
335 + name: "FlattenReactiveLoops",
336 + value: reactiveFunction,
337 + });
338 + }
339
340 assertScopeInstructionsWithinScopes(reactiveFunction);
341
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/FlattenReactiveLoopsHIR.ts new
+71
@@ -0,0 +1,71 @@
1 +/**
2 + * Copyright (c) Meta Platforms, Inc. and affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import { BlockId, HIRFunction, PrunedScopeTerminal } from "../HIR";
9 +import { assertExhaustive, retainWhere } from "../Utils/utils";
10 +
11 +/**
12 + * Prunes any reactive scopes that are within a loop (for, while, etc). We don't yet
13 + * support memoization within loops because this would require an extra layer of reconciliation
14 + * (plus a way to identify values across runs, similar to how we use `key` in JSX for lists).
15 + * Eventually we may integrate more deeply into the runtime so that we can do a single level
16 + * of reconciliation, but for now we've found it's sufficient to memoize *around* the loop.
17 + */
18 +export function flattenReactiveLoopsHIR(fn: HIRFunction): void {
19 + const activeLoops = Array<BlockId>();
20 + for (const [, block] of fn.body.blocks) {
21 + retainWhere(activeLoops, (id) => id !== block.id);
22 + const { terminal } = block;
23 + switch (terminal.kind) {
24 + case "do-while":
25 + case "for":
26 + case "for-in":
27 + case "for-of":
28 + case "while": {
29 + activeLoops.push(terminal.fallthrough);
30 + break;
31 + }
32 + case "scope": {
33 + if (activeLoops.length !== 0) {
34 + block.terminal = {
35 + kind: "pruned-scope",
36 + block: terminal.block,
37 + fallthrough: terminal.fallthrough,
38 + id: terminal.id,
39 + loc: terminal.loc,
40 + scope: terminal.scope,
41 + } as PrunedScopeTerminal;
42 + }
43 + break;
44 + }
45 + case "branch":
46 + case "goto":
47 + case "if":
48 + case "label":
49 + case "logical":
50 + case "maybe-throw":
51 + case "optional":
52 + case "pruned-scope":
53 + case "return":
54 + case "sequence":
55 + case "switch":
56 + case "ternary":
57 + case "throw":
58 + case "try":
59 + case "unreachable":
60 + case "unsupported": {
61 + break;
62 + }
63 + default: {
64 + assertExhaustive(
65 + terminal,
66 + `Unexpected terminal kind \`${(terminal as any).kind}\``
67 + );
68 + }
69 + }
70 + }
71 +}