@samitouri / QOS-React / commits / 8b01a2e0bf

[compiler] Option to always take the non-memo branch

Summary: This adds a debugging mode to the compiler that simply adds a `|| true` to the guard on all memoization blocks, which results in the generated code never using memoized values and always recomputing them. This is designed as a validation tool for the compiler's correctness--every program *should* behave exactly the same with this option enabled as it would with it disabled, and so any difference in behavior should be investigated as either a compiler bug or a pipeline issue. (We add `|| true` rather than dropping the conditional block entirely because we still want to exercise the guard tests, in case the guards themselves are the source of an error, like reading a property from undefined in a guard.) ghstack-source-id: 955a47ec1689842da82552225a19a1008c57fe2c Pull Request resolved: https://github.com/facebook/react/pull/29655

Mike Vitousek committed May 31, 2024 at 14:04 UTC 8b01a2e0bf17adc6bb7b81d1d0063c7efe9ea8b1
5 files changed +100 -1
compiler/packages/babel-plugin-react-compiler/src/Entrypoint/Pipeline.ts
+4 -1
@@ -147,7 +147,10 @@ function* runWithEnvironment(
147 validateContextVariableLValues(hir);
148 validateUseMemo(hir);
149
150 - if (!env.config.enablePreserveExistingManualUseMemo) {
150 + if (
151 + !env.config.enablePreserveExistingManualUseMemo &&
152 + !env.config.disableMemoizationForDebugging
153 + ) {
154 dropManualMemoization(hir);
155 yield log({ kind: "hir", name: "DropManualMemoization", value: hir });
156 }
compiler/packages/babel-plugin-react-compiler/src/HIR/Environment.ts
+9
@@ -350,6 +350,15 @@ const EnvironmentConfigSchema = z.object({
350 */
351 enableTreatFunctionDepsAsConditional: z.boolean().default(false),
352
353 + /**
354 + * When true, always act as though the dependencies of a memoized value
355 + * have changed. This makes the compiler not actually perform any optimizations,
356 + * but is useful for debugging. Implicitly also sets
357 + * @enablePreserveExistingManualUseMemo, because otherwise memoization in the
358 + * original source will be disabled as well.
359 + */
360 + disableMemoizationForDebugging: z.boolean().default(false),
361 +
362 /**
363 * The react native re-animated library uses custom Babel transforms that
364 * requires the calls to library API remain unmodified.
compiler/packages/babel-plugin-react-compiler/src/ReactiveScopes/CodegenReactiveFunction.ts
+8
@@ -616,6 +616,14 @@ function codegenReactiveScope(
616 );
617 }
618
619 + if (cx.env.config.disableMemoizationForDebugging) {
620 + testCondition = t.logicalExpression(
621 + "||",
622 + testCondition,
623 + t.booleanLiteral(true)
624 + );
625 + }
626 +
627 let computationBlock = codegenBlock(cx, block);
628 computationBlock.body.push(...cacheStoreStatements);
629 const memoBlock = t.blockStatement(cacheLoadStatements);
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved-nomemo.expect.md new
+66
@@ -0,0 +1,66 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +// @disableMemoizationForDebugging
6 +import { useMemo } from "react";
7 +
8 +function Component({ a }) {
9 + let x = useMemo(() => [a], []);
10 + return <div>{x}</div>;
11 +}
12 +
13 +export const FIXTURE_ENTRYPOINT = {
14 + fn: Component,
15 + params: [{ a: 42 }],
16 + isComponent: true,
17 +};
18 +
19 +```
20 +
21 +## Code
22 +
23 +```javascript
24 +import { c as _c } from "react/compiler-runtime"; // @disableMemoizationForDebugging
25 +import { useMemo } from "react";
26 +
27 +function Component(t0) {
28 + const $ = _c(5);
29 + const { a } = t0;
30 + let t1;
31 + if ($[0] !== a || true) {
32 + t1 = () => [a];
33 + $[0] = a;
34 + $[1] = t1;
35 + } else {
36 + t1 = $[1];
37 + }
38 + let t2;
39 + if ($[2] === Symbol.for("react.memo_cache_sentinel") || true) {
40 + t2 = [];
41 + $[2] = t2;
42 + } else {
43 + t2 = $[2];
44 + }
45 + const x = useMemo(t1, t2);
46 + let t3;
47 + if ($[3] !== x || true) {
48 + t3 = <div>{x}</div>;
49 + $[3] = x;
50 + $[4] = t3;
51 + } else {
52 + t3 = $[4];
53 + }
54 + return t3;
55 +}
56 +
57 +export const FIXTURE_ENTRYPOINT = {
58 + fn: Component,
59 + params: [{ a: 42 }],
60 + isComponent: true,
61 +};
62 +
63 +```
64 +
65 +### Eval output
66 +(kind: ok) <div>42</div>
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/useMemo-simple-preserved-nomemo.js new
+13
@@ -0,0 +1,13 @@
1 +// @disableMemoizationForDebugging
2 +import { useMemo } from "react";
3 +
4 +function Component({ a }) {
5 + let x = useMemo(() => [a], []);
6 + return <div>{x}</div>;
7 +}
8 +
9 +export const FIXTURE_ENTRYPOINT = {
10 + fn: Component,
11 + params: [{ a: 42 }],
12 + isComponent: true,
13 +};