@samitouri / QOS-React-2 / commits / 86e2edfa87

Prune memoize instructions in codegen

The previous PR introduced `memoize` instructions whose lvalues aren't used, but which can't be pruned by DCE due to pipeline ordering. Here we change to make memoize an instruction intended for its side effects only, and prune during codegen.

Joe Savona committed Dec 15, 2023 at 13:47 UTC 86e2edfa879f4cbd06a1f6bbaf0c8a6ab08cf59b
8 files changed +26 -32
compiler/packages/babel-plugin-react-forget/src/HIR/HIR.ts
+4
@@ -849,6 +849,10 @@ export type InstructionValue =
849 * Represents semantic information from useMemo/useCallback that the developer
850 * has indicated a particular value should be memoized. This value is ignored
851 * unless the TODO flag is enabled.
852 + *
853 + * NOTE: the Memoize instruction is intended for side-effects only, and is pruned
854 + * during codegen. It can't be pruned during DCE because we need to preserve the
855 + * instruction so it can be visible in InferReferenceEffects.
856 */
857 | { kind: "Memoize"; value: Place; loc: SourceLocation }
858 /*
compiler/packages/babel-plugin-react-forget/src/Inference/DropManualMemoization.ts
+5 -7
@@ -137,9 +137,6 @@ export function dropManualMemoization(func: HIRFunction): void {
137 * the original lvalue for the result of the Memoize instruction, so that
138 * we don't have to rewrite subsequent instructions.
139 */
140 - const lvalue = instr.lvalue;
141 - const temp = createTemporaryPlace(func.env);
142 - instr.lvalue = { ...temp };
140 nextInstructions =
141 nextInstructions ?? block.instructions.slice(0, i);
142
@@ -148,10 +145,10 @@ export function dropManualMemoization(func: HIRFunction): void {
145 for (const operand of eachInstructionValueOperand(
146 functionExpression
147 )) {
151 - const operandLValue = createTemporaryPlace(func.env);
148 + const temp = createTemporaryPlace(func.env);
149 nextInstructions.push({
150 id: makeInstructionId(0),
154 - lvalue: operandLValue,
151 + lvalue: temp,
152 value: {
153 kind: "Memoize",
154 value: { ...operand },
@@ -164,12 +161,13 @@ export function dropManualMemoization(func: HIRFunction): void {
161
162 nextInstructions.push(instr);
163
164 + const temp = createTemporaryPlace(func.env);
165 nextInstructions.push({
166 id: makeInstructionId(0),
169 - lvalue,
167 + lvalue: temp,
168 value: {
169 kind: "Memoize",
172 - value: temp,
170 + value: { ...instr.lvalue },
171 loc: instr.loc,
172 },
173 loc: instr.loc,
compiler/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+5 -5
@@ -1163,14 +1163,14 @@ function inferBlock(
1163 continue;
1164 }
1165 case "Memoize": {
1166 - state.initialize(instrValue, {
1167 - kind: ValueKind.Frozen,
1168 - reason: new Set([ValueReason.Other]),
1169 - });
1166 state.reference(instrValue.value, Effect.Freeze, ValueReason.Other);
1167 const lvalue = instr.lvalue;
1168 lvalue.effect = Effect.ConditionallyMutate;
1173 - state.alias(lvalue, instrValue.value);
1169 + state.initialize(instrValue, {
1170 + kind: ValueKind.Immutable,
1171 + reason: new Set([ValueReason.Other]),
1172 + });
1173 + state.define(lvalue, instrValue);
1174 continue;
1175 }
1176 case "LoadLocal": {
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/CodegenReactiveFunction.ts
+3 -4
@@ -835,6 +835,8 @@ function codegenInstructionNullable(
835 assertExhaustive(kind, `Unexpected instruction kind '${kind}'`);
836 }
837 }
838 + } else if (instr.value.kind === "Memoize") {
839 + return null;
840 } else if (instr.value.kind === "Debugger") {
841 return t.debuggerStatement();
842 } else if (instr.value.kind === "ObjectMethod") {
@@ -1628,10 +1630,7 @@ function codegenInstructionValue(
1630 );
1631 break;
1632 }
1631 - case "Memoize": {
1632 - value = codegenPlaceToExpression(cx, instrValue.value);
1633 - break;
1634 - }
1633 + case "Memoize":
1634 case "Debugger":
1635 case "DeclareLocal":
1636 case "DeclareContext":
compiler/packages/babel-plugin-react-forget/src/ReactiveScopes/PruneNonEscapingScopes.ts
+1 -1
@@ -453,6 +453,7 @@ function computeMemoizationInputs(
453 };
454 }
455 case "NextPropertyOf":
456 + case "Memoize":
457 case "Debugger":
458 case "ComputedDelete":
459 case "PropertyDelete":
@@ -471,7 +472,6 @@ function computeMemoizationInputs(
472 rvalues: [],
473 };
474 }
474 - case "Memoize":
475 case "Await":
476 case "TypeCastExpression":
477 case "NextIterableOf": {
compiler/packages/babel-plugin-react-forget/src/TypeInference/InferTypes.ts
+2 -5
@@ -280,11 +280,6 @@ function* generateInstructionTypes(
280 break;
281 }
282
283 - case "Memoize": {
284 - yield equation(left, value.value.identifier.type);
285 - break;
286 - }
287 -
283 case "PropertyDelete":
284 case "ComputedDelete": {
285 yield equation(left, { kind: "Primitive" });
@@ -319,7 +314,9 @@ function* generateInstructionTypes(
314 case "NextIterableOf":
315 case "UnsupportedNode":
316 case "Debugger":
317 + case "Memoize": {
318 break;
319 + }
320 default:
321 assertExhaustive(value, `Unhandled instruction value kind: ${value}`);
322 }
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useMemo-mabye-modified-free-variable-preserve-memoization-guarantees.expect.md
+3 -7
@@ -64,11 +64,7 @@ function Component(props) {
64 const free2 = t1;
65 const part = free2.part;
66 useHook();
67 -
68 - props.value;
69 - free;
70 - part;
71 - let t44;
67 + let t39;
68 let x;
69 if ($[2] !== props.value) {
70 x = makeObject_Primitives();
@@ -79,8 +75,8 @@ function Component(props) {
75 } else {
76 x = $[3];
77 }
82 - t44 = x;
83 - const object = t44;
78 + t39 = x;
79 + const object = t39;
80 return object;
81 }
82
compiler/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/useMemo-maybe-modified-later-preserve-memoization-guarantees.expect.md
+3 -3
@@ -28,7 +28,7 @@ import { identity, makeObject_Primitives, mutate } from "shared-runtime";
28
29 function Component(props) {
30 const $ = useMemoCache(1);
31 - let t15;
31 + let t7;
32 let t0;
33 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
34 t0 = makeObject_Primitives();
@@ -36,8 +36,8 @@ function Component(props) {
36 } else {
37 t0 = $[0];
38 }
39 - t15 = t0;
40 - const object = t15;
39 + t7 = t0;
40 + const object = t7;
41 identity(object);
42 return object;
43 }