@samitouri / QOS-React-1 / commits / b686b5dd9d

[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 }