[hir] Refactor constant propagation of phis
This PR moves the phi evaluation to a separate function. Most importantly, it inverts the default case to _not_ constant propagate unless we have explicit validation of the phi operands.
Sathya Gunasekaran committed
Aug 16, 2023 at 15:39 UTC
b686b5dd9d72705e84bd7a18fd6f4ece3a566921
2 files changed
+54
-25
compiler/forget/packages/babel-plugin-react-forget/src/Optimization/ConstantPropagation.ts
+46
-19
@@ -6,6 +6,7 @@
6
*/
7
8
import { isValidIdentifier } from "@babel/types";
9
+import { CompilerError } from "../CompilerError";
10
import {
11
Environment,
12
GotoVariant,
@@ -14,6 +15,7 @@ import {
15
Instruction,
16
InstructionValue,
17
LoadGlobal,
18
+ Phi,
19
Place,
20
Primitive,
21
assertConsistentIdentifiers,
@@ -116,25 +118,7 @@ function applyConstantPropagation(
118
// Note that this analysis uses a single-pass only, so it will never fill in
119
// phi values for blocks that have a back-edge.
120
for (const phi of block.phis) {
119
- let value: Primitive | LoadGlobal | null = null;
120
- for (const [, operand] of phi.operands) {
121
- const operandValue = constants.get(operand.id) ?? null;
122
- if (operandValue === null) {
123
- value = null;
124
- break;
125
- }
126
- if (value === null) {
127
- value = operandValue;
128
- } else if (
129
- operandValue.kind !== value.kind ||
130
- (operandValue.kind === "Primitive" &&
131
- value.kind === "Primitive" &&
132
- operandValue.value !== value.value)
133
- ) {
134
- value = null;
135
- break;
136
- }
137
- }
121
+ let value = evaluatePhi(phi, constants);
122
if (value !== null) {
123
constants.set(phi.id.id, value);
124
}
@@ -187,6 +171,49 @@ function applyConstantPropagation(
171
return hasChanges;
172
}
173
174
+function evaluatePhi(phi: Phi, constants: Constants): Constant | null {
175
+ let value: Constant | null = null;
176
+ for (const [, operand] of phi.operands) {
177
+ const operandValue = constants.get(operand.id) ?? null;
178
+ // did not find a constant, can't constant propogate
179
+ if (operandValue === null) {
180
+ return null;
181
+ }
182
+
183
+ // first iteration of the loop, let's store the operand and continue
184
+ // looping.
185
+ if (value === null) {
186
+ value = operandValue;
187
+ continue;
188
+ }
189
+
190
+ // found different kinds of constants, can't constant propogate
191
+ if (operandValue.kind !== value.kind) {
192
+ return null;
193
+ }
194
+
195
+ switch (operandValue.kind) {
196
+ case "Primitive": {
197
+ CompilerError.invariant(value.kind === "Primitive", {
198
+ reason: "value kind expected to be Primitive",
199
+ loc: null,
200
+ suggestions: null,
201
+ });
202
+
203
+ // different constant values, can't constant propogate
204
+ if (operandValue.value !== value.value) {
205
+ return null;
206
+ }
207
+ break;
208
+ }
209
+ default:
210
+ return null;
211
+ }
212
+ }
213
+
214
+ return value;
215
+}
216
+
217
function evaluateInstruction(
218
env: Environment,
219
constants: Constants,
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/constant-propagate-global-phis.expect.md
+8
-6
@@ -16,15 +16,17 @@ function Test() {
16
```javascript
17
import { unstable_useMemoCache as useMemoCache } from "react";
18
function Test() {
19
- const $ = useMemoCache(1);
19
+ const $ = useMemoCache(2);
20
const { tab } = useFoo();
21
- tab === WAT ? WAT : BAR;
21
+ const currentTab = tab === WAT ? WAT : BAR;
22
+ const c_0 = $[0] !== currentTab;
23
let t0;
23
- if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
24
- t0 = <Foo value={WAT} />;
25
- $[0] = t0;
24
+ if (c_0) {
25
+ t0 = <Foo value={currentTab} />;
26
+ $[0] = currentTab;
27
+ $[1] = t0;
28
} else {
27
- t0 = $[0];
29
+ t0 = $[1];
30
}
31
return t0;
32
}