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
}