@samitouri / QOS-React-2 / commits / dce5371763

Constant propagation/folding

Implements constant propagation/constant folding for a conservative subset of the language. The approach is described in detail in the comments in the file itself, a key note here is that this pass currently emits what looks like garbage: ``` // input const x = 1; const y = x + 1; // output const x = 1; 2; // <---- you'll see a bunch of lines like this const y = 2; ``` These useless lines occur where previously there was a temporary getting calculated that was used later (so we saved it until it was used), but now it isn't used later so we just emit it in-place. Dead code elimination (DCE) can eliminate these and other useless statements later. Note that a key motivation for implementing this pass is to reduce memoization blocks to what is strictly required for dynamic computations. Why memoize at runtime when we compute at build time?

Joe Savona committed Jan 9, 2023 at 15:30 UTC dce5371763114ba352dfd47b317a10ea923c7585
30 files changed +570 -243
compiler/forget/src/CompilerPipeline.ts
+4
@@ -14,6 +14,7 @@ import {
14 ReactiveFunction,
15 } from "./HIR";
16 import { inferMutableRanges, inferReferenceEffects } from "./Inference";
17 +import { constantPropagation } from "./Optimization";
18 import {
19 buildReactiveFunction,
20 codegenReactiveFunction,
@@ -53,6 +54,9 @@ export default function (
54 eliminateRedundantPhi(ir);
55 logHIRFunction("eliminateRedundantPhi", ir);
56
57 + constantPropagation(ir);
58 + logHIRFunction("constantPropagation", ir);
59 +
60 inferTypes(ir);
61 logHIRFunction("inferTypes", ir);
62
compiler/forget/src/HIR/HIRBuilder.ts
+5 -5
@@ -360,7 +360,7 @@ export default class HIRBuilder {
360 /**
361 * Helper to shrink a CFG eliminate jump-only blocks.
362 */
363 -function shrink(func: HIR): void {
363 +export function shrink(func: HIR): void {
364 const gotos = new Map();
365 /**
366 * Given a target block for some terminator, resolves the ideal block that should be
@@ -408,7 +408,7 @@ function shrink(func: HIR): void {
408 }
409 }
410
411 -function removeUnreachableFallthroughs(func: HIR): void {
411 +export function removeUnreachableFallthroughs(func: HIR): void {
412 const visited: Set<BlockId> = new Set();
413 for (const [_, block] of func.blocks) {
414 visited.add(block.id);
@@ -434,7 +434,7 @@ function removeUnreachableFallthroughs(func: HIR): void {
434 * Converts the graph to reverse-postorder, with predecessor blocks appearing
435 * before successors except in the case of back links (ie loops).
436 */
437 -function reversePostorderBlocks(func: HIR): void {
437 +export function reversePostorderBlocks(func: HIR): void {
438 const visited: Set<BlockId> = new Set();
439 const postorder: Array<BlockId> = [];
440 function visit(blockId: BlockId) {
@@ -521,7 +521,7 @@ function reversePostorderBlocks(func: HIR): void {
521 func.blocks = blocks;
522 }
523
524 -function markInstructionIds(func: HIR) {
524 +export function markInstructionIds(func: HIR) {
525 let id = 0;
526 const visited = new Set<Instruction>();
527 for (const [_, block] of func.blocks) {
@@ -537,7 +537,7 @@ function markInstructionIds(func: HIR) {
537 }
538 }
539
540 -function markPredecessors(func: HIR) {
540 +export function markPredecessors(func: HIR) {
541 for (const [, block] of func.blocks) {
542 block.preds.clear();
543 }
compiler/forget/src/HIR/index.ts
+9 -2
@@ -6,7 +6,14 @@
6 */
7
8 export { lower } from "./BuildHIR";
9 -export { HIRFunction, ReactiveFunction } from "./HIR";
10 -export { Environment } from "./HIRBuilder";
9 +export * from "./HIR";
10 +export {
11 + Environment,
12 + markInstructionIds,
13 + markPredecessors,
14 + removeUnreachableFallthroughs,
15 + reversePostorderBlocks,
16 + shrink,
17 +} from "./HIRBuilder";
18 export { mergeConsecutiveBlocks } from "./MergeConsecutiveBlocks";
19 export { printFunction } from "./PrintHIR";
compiler/forget/src/Optimization/ConstantPropagation.ts new
+270
@@ -0,0 +1,270 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +import {
9 + BlockId,
10 + GotoVariant,
11 + HIRFunction,
12 + IdentifierId,
13 + InstructionValue,
14 + markInstructionIds,
15 + markPredecessors,
16 + mergeConsecutiveBlocks,
17 + Place,
18 + Primitive,
19 + removeUnreachableFallthroughs,
20 + reversePostorderBlocks,
21 + shrink,
22 +} from "../HIR";
23 +import { eliminateRedundantPhi } from "../SSA";
24 +
25 +/**
26 + * Applies constant propagation and constant folding to the given function.
27 + * Note that because HIR operands are always a Place, constants cannot be directly
28 + * propagated into the HIR itself (the closest option would be to copy constants to
29 + * new temporaries just before each use, and update usage sites to reference those
30 + * new temporaries).
31 + *
32 + * Instead this pass implements constant folding, in which constant values are
33 + * propagated internally to the pass and subsequent operations are removed/folded where
34 + * possible.
35 + *
36 + * Note that this pass may prune control flow blocks that are unreachable, for example
37 + * a consequent or alternate branch if an `if` test is provably truthy or falsey.
38 + * If (and only if) terminals change, the pass re-runs various stages to ensure the
39 + * CFG is in minimal form. This means instruction ids *may* change as a result of this
40 + * pass.
41 + */
42 +export function constantPropagation(fn: HIRFunction): void {
43 + const haveTerminalsChanged = applyConstantPropagation(fn);
44 + if (haveTerminalsChanged) {
45 + // If terminals have changed then blocks may have become newly unreachable.
46 + // Re-run minification of the graph (incl reordering instruction ids)
47 + shrink(fn.body);
48 + reversePostorderBlocks(fn.body);
49 + removeUnreachableFallthroughs(fn.body);
50 + markInstructionIds(fn.body);
51 + markPredecessors(fn.body);
52 +
53 + // Now that predecessors are updated, prune phi operands that can never be reached
54 + for (const [, block] of fn.body.blocks) {
55 + for (const phi of block.phis) {
56 + for (const [predecessor] of phi.operands) {
57 + if (!block.preds.has(predecessor)) {
58 + phi.operands.delete(predecessor);
59 + }
60 + }
61 + }
62 + }
63 + // By removing some phi operands, there may be phis that were not previously
64 + // redundant but now are
65 + eliminateRedundantPhi(fn);
66 + // Finally, merge together any blocks that are now guaranteed to execute
67 + // consecutively
68 + mergeConsecutiveBlocks(fn);
69 + }
70 +}
71 +
72 +function applyConstantPropagation(fn: HIRFunction): boolean {
73 + let hasChanges = false;
74 +
75 + // A set of blocks whose terminals can't (yet) be safely rewritten
76 + const valueBlocks = new Set<BlockId>();
77 +
78 + const constants: Constants = new Map();
79 + for (const [, block] of fn.body.blocks) {
80 + // Initialize phi values if all operands have the same known constant value.
81 + // Note that this analysis uses a single-pass only, so it will never fill in
82 + // phi values for blocks that have a back-edge.
83 + for (const phi of block.phis) {
84 + let value: Primitive | null = null;
85 + for (const [, operand] of phi.operands) {
86 + const operandValue = constants.get(operand.id) ?? null;
87 + if (operandValue === null) {
88 + value = null;
89 + break;
90 + }
91 + if (value === null) {
92 + value = operandValue;
93 + } else if (operandValue.value !== value.value) {
94 + value = null;
95 + break;
96 + }
97 + }
98 + if (value !== null) {
99 + constants.set(phi.id.id, value);
100 + }
101 + }
102 +
103 + for (const instr of block.instructions) {
104 + const value = evaluateInstruction(constants, instr.value);
105 + if (value !== null) {
106 + instr.value = value;
107 + constants.set(instr.lvalue.place.identifier.id, value);
108 + }
109 + }
110 +
111 + if (valueBlocks.has(block.id)) {
112 + // can't rewrite terminals in value blocks yet
113 + continue;
114 + }
115 + const terminal = block.terminal;
116 + switch (terminal.kind) {
117 + case "if": {
118 + const testValue = read(constants, terminal.test);
119 + if (testValue !== null && testValue.kind === "Primitive") {
120 + hasChanges = true;
121 + const targetBlockId = Boolean(testValue.value)
122 + ? terminal.consequent
123 + : terminal.alternate;
124 + block.terminal = {
125 + kind: "goto",
126 + variant: GotoVariant.Break,
127 + block: targetBlockId,
128 + id: terminal.id,
129 + };
130 + }
131 + break;
132 + }
133 + case "while": {
134 + valueBlocks.add(terminal.test);
135 + break;
136 + }
137 + case "for": {
138 + valueBlocks.add(terminal.init);
139 + valueBlocks.add(terminal.test);
140 + valueBlocks.add(terminal.update);
141 + break;
142 + }
143 + default: {
144 + // no-op
145 + }
146 + }
147 + }
148 +
149 + return hasChanges;
150 +}
151 +
152 +function evaluateInstruction(
153 + constants: Constants,
154 + instr: InstructionValue
155 +): Constant | null {
156 + switch (instr.kind) {
157 + case "Primitive": {
158 + return instr;
159 + }
160 + case "BinaryExpression": {
161 + const lhsValue = read(constants, instr.left);
162 + const rhsValue = read(constants, instr.right);
163 + if (lhsValue !== null && rhsValue !== null) {
164 + const lhs = lhsValue.value;
165 + const rhs = rhsValue.value;
166 + switch (instr.operator) {
167 + case "+": {
168 + if (typeof lhs === "number" && typeof rhs === "number") {
169 + return { kind: "Primitive", value: lhs + rhs, loc: instr.loc };
170 + }
171 + return null;
172 + }
173 + case "-": {
174 + if (typeof lhs === "number" && typeof rhs === "number") {
175 + return { kind: "Primitive", value: lhs - rhs, loc: instr.loc };
176 + }
177 + return null;
178 + }
179 + case "*": {
180 + if (typeof lhs === "number" && typeof rhs === "number") {
181 + return { kind: "Primitive", value: lhs * rhs, loc: instr.loc };
182 + }
183 + return null;
184 + }
185 + case "/": {
186 + if (typeof lhs === "number" && typeof rhs === "number") {
187 + return { kind: "Primitive", value: lhs / rhs, loc: instr.loc };
188 + }
189 + return null;
190 + }
191 + case "<": {
192 + if (typeof lhs === "number" && typeof rhs === "number") {
193 + return { kind: "Primitive", value: lhs < rhs, loc: instr.loc };
194 + }
195 + return null;
196 + }
197 + case "<=": {
198 + if (typeof lhs === "number" && typeof rhs === "number") {
199 + return { kind: "Primitive", value: lhs <= rhs, loc: instr.loc };
200 + }
201 + return null;
202 + }
203 + case ">": {
204 + if (typeof lhs === "number" && typeof rhs === "number") {
205 + return { kind: "Primitive", value: lhs > rhs, loc: instr.loc };
206 + }
207 + return null;
208 + }
209 + case ">=": {
210 + if (typeof lhs === "number" && typeof rhs === "number") {
211 + return { kind: "Primitive", value: lhs >= rhs, loc: instr.loc };
212 + }
213 + return null;
214 + }
215 + case "==": {
216 + return { kind: "Primitive", value: lhs == rhs, loc: instr.loc };
217 + }
218 + case "===": {
219 + return { kind: "Primitive", value: lhs === rhs, loc: instr.loc };
220 + }
221 + case "!=": {
222 + return { kind: "Primitive", value: lhs != rhs, loc: instr.loc };
223 + }
224 + case "!==": {
225 + return { kind: "Primitive", value: lhs !== rhs, loc: instr.loc };
226 + }
227 + default: {
228 + // TODO: handle more cases
229 + return null;
230 + }
231 + }
232 + }
233 + return null;
234 + }
235 + case "PropertyLoad": {
236 + const objectValue = read(constants, instr.object);
237 + if (objectValue !== null) {
238 + if (
239 + typeof objectValue.value === "string" &&
240 + instr.property === "length"
241 + ) {
242 + return {
243 + kind: "Primitive",
244 + value: objectValue.value.length,
245 + loc: instr.loc,
246 + };
247 + }
248 + }
249 + return null;
250 + }
251 + case "Identifier": {
252 + return read(constants, instr);
253 + }
254 + default: {
255 + // TODO: handle more cases
256 + return null;
257 + }
258 + }
259 +}
260 +
261 +/**
262 + * Recursively read the value of a place: if it is a constant place, attempt to read
263 + * from that place until reaching a primitive or finding a value that is unset.
264 + */
265 +function read(constants: Constants, place: Place): Constant | null {
266 + return constants.get(place.identifier.id) ?? null;
267 +}
268 +
269 +type Constant = Primitive;
270 +type Constants = Map<IdentifierId, Constant>;
compiler/forget/src/Optimization/index.ts new
+8
@@ -0,0 +1,8 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its affiliates.
3 + *
4 + * This source code is licensed under the MIT license found in the
5 + * LICENSE file in the root directory of this source tree.
6 + */
7 +
8 +export { constantPropagation } from "./ConstantPropagation";
compiler/forget/src/__tests__/fixtures/hir/_bug_expression-with-assignment.expect.md
+2 -1
@@ -17,7 +17,8 @@ function f() {
17 function f() {
18 const x = 1;
19 const x$0 = 2;
20 - return x$0 + x$0 + x$0;
20 + 4;
21 + return 6;
22 }
23
24 ```
compiler/forget/src/__tests__/fixtures/hir/assignment-variations.expect.md
+4 -2
@@ -16,8 +16,10 @@ function f() {
16 ```javascript
17 function f() {
18 const x = 1;
19 - const x$0 = x + 1;
20 - const x$1 = x$0 + 1;
19 + 1;
20 + const x$0 = 2;
21 + 1;
22 + const x$1 = 3;
23 const x$2 = x$1 >>> 1;
24 }
25
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-for.expect.md new
+37
@@ -0,0 +1,37 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + let y = 0;
7 + for (const x = 100; x < 10; x) {
8 + y = y + 1;
9 + }
10 + return y;
11 +}
12 +
13 +```
14 +
15 +## Code
16 +
17 +```javascript
18 +function foo() {
19 + const $ = React.useMemoCache();
20 + let y;
21 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 + y = 0;
23 +
24 + for (const x = 100; 10, false; 100) {
25 + y = y + 1;
26 + }
27 +
28 + $[0] = y;
29 + } else {
30 + y = $[0];
31 + }
32 +
33 + return y;
34 +}
35 +
36 +```
37 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-for.js new
+7
@@ -0,0 +1,7 @@
1 +function foo() {
2 + let y = 0;
3 + for (const x = 100; x < 10; x) {
4 + y = y + 1;
5 + }
6 + return y;
7 +}
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-phi.expect.md new
+45
@@ -0,0 +1,45 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a, b, c) {
6 + let x;
7 + if (a) {
8 + x = 2 - 1;
9 + } else {
10 + x = 0 + 1;
11 + }
12 + if (x === 1) {
13 + return b;
14 + } else {
15 + return c;
16 + }
17 +}
18 +
19 +```
20 +
21 +## Code
22 +
23 +```javascript
24 +function foo(a, b, c) {
25 + const x = undefined;
26 + let x$0 = undefined;
27 + if (a) {
28 + 2;
29 + 1;
30 + const x$1 = 1;
31 + x$0 = x$1;
32 + } else {
33 + 0;
34 + 1;
35 + const x$2 = 1;
36 + x$0 = x$2;
37 + }
38 +
39 + 1;
40 + true;
41 + return b;
42 +}
43 +
44 +```
45 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-phi.js new
+13
@@ -0,0 +1,13 @@
1 +function foo(a, b, c) {
2 + let x;
3 + if (a) {
4 + x = 2 - 1;
5 + } else {
6 + x = 0 + 1;
7 + }
8 + if (x === 1) {
9 + return b;
10 + } else {
11 + return c;
12 + }
13 +}
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-while.expect.md new
+39
@@ -0,0 +1,39 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + let x = 100;
7 + let y = 0;
8 + while (x < 10) {
9 + y += 1;
10 + }
11 + return y;
12 +}
13 +
14 +```
15 +
16 +## Code
17 +
18 +```javascript
19 +function foo() {
20 + const $ = React.useMemoCache();
21 + const x = 100;
22 + let y;
23 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
24 + y = 0;
25 +
26 + while ((10, false)) {
27 + y = y + 1;
28 + }
29 +
30 + $[0] = y;
31 + } else {
32 + y = $[0];
33 + }
34 +
35 + return y;
36 +}
37 +
38 +```
39 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/constant-propagation-while.js new
+8
@@ -0,0 +1,8 @@
1 +function foo() {
2 + let x = 100;
3 + let y = 0;
4 + while (x < 10) {
5 + y += 1;
6 + }
7 + return y;
8 +}
compiler/forget/src/__tests__/fixtures/hir/constant-propagation.expect.md new
+45
@@ -0,0 +1,45 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + const a = 1;
7 + const b = 2;
8 + const c = 3;
9 + const d = a + b;
10 + const e = d * c;
11 + const f = e / d;
12 + const g = f - e;
13 +
14 + if (g) {
15 + console.log("foo");
16 + }
17 +
18 + const h = g;
19 + const i = h;
20 + const j = i;
21 + return j;
22 +}
23 +
24 +```
25 +
26 +## Code
27 +
28 +```javascript
29 +function foo() {
30 + const a = 1;
31 + const b = 2;
32 + const c = 3;
33 + const d = 3;
34 + const e = 9;
35 + const f = 3;
36 + const g = -6;
37 + console.log("foo");
38 + const h = -6;
39 + const i = -6;
40 + const j = -6;
41 + return j;
42 +}
43 +
44 +```
45 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/constant-propagation.js new
+18
@@ -0,0 +1,18 @@
1 +function foo() {
2 + const a = 1;
3 + const b = 2;
4 + const c = 3;
5 + const d = a + b;
6 + const e = d * c;
7 + const f = e / d;
8 + const g = f - e;
9 +
10 + if (g) {
11 + console.log("foo");
12 + }
13 +
14 + const h = g;
15 + const i = h;
16 + const j = i;
17 + return j;
18 +}
compiler/forget/src/__tests__/fixtures/hir/object-computed-access-assignment.expect.md
+3 -1
@@ -14,7 +14,9 @@ function foo(a, b, c) {
14 ```javascript
15 function foo(a, b, c) {
16 a[b] = c[b];
17 - a[1 + 2] = c[b * 4];
17 + 1;
18 + 2;
19 + a[3] = c[b * 4];
20 }
21
22 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md
+6 -31
@@ -21,39 +21,14 @@ function foo() {
21
22 ```javascript
23 function foo() {
24 - const $ = React.useMemoCache();
24 const x = 1;
25 const y = 2;
27 - let x$0;
28 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
29 - x$0 = x;
30 -
31 - if (y === 2) {
32 - const x$1 = 3;
33 - x$0 = x$1;
34 - }
35 -
36 - $[0] = x$0;
37 - } else {
38 - x$0 = $[0];
39 - }
40 -
41 - let x$2;
42 -
43 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
44 - x$2 = x$0;
45 -
46 - if (y === 3) {
47 - const x$3 = 5;
48 - x$2 = x$3;
49 - }
50 -
51 - $[1] = x$2;
52 - } else {
53 - x$2 = $[1];
54 - }
55 -
56 - const y$4 = x$2;
26 + 2;
27 + true;
28 + const x$0 = 3;
29 + 3;
30 + false;
31 + const y$1 = x$0;
32 }
33
34 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md
+4 -16
@@ -18,24 +18,12 @@ function foo() {
18
19 ```javascript
20 function foo() {
21 - const $ = React.useMemoCache();
21 const x = 1;
22 const y = 2;
24 - let x$0;
25 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
26 - x$0 = x;
27 -
28 - if (y === 2) {
29 - const x$1 = 3;
30 - x$0 = x$1;
31 - }
32 -
33 - $[0] = x$0;
34 - } else {
35 - x$0 = $[0];
36 - }
37 -
38 - const y$2 = x$0;
23 + 2;
24 + true;
25 + const x$0 = 3;
26 + const y$1 = x$0;
27 }
28
29 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-for-trivial-update.expect.md
+1 -1
@@ -21,7 +21,7 @@ function foo() {
21 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 x = 1;
23
24 - for (const i = 0; i < 10; i) {
24 + for (const i = 0; 10, true; 0) {
25 x = x + 1;
26 }
27
compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md
+1 -5
@@ -21,11 +21,7 @@ function foo() {
21 function foo() {
22 const x = 1;
23 const y = 2;
24 - if (y) {
25 - const z = x + y;
26 - } else {
27 - const z = x;
28 - }
24 + const z = 3;
25 }
26
27 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-multiple-phis.expect.md
+4 -42
@@ -28,49 +28,11 @@ function foo(a, b, c, d) {
28
29 ```javascript
30 function foo(a, b, c, d) {
31 - const $ = React.useMemoCache();
31 const x = 0;
33 - const c_0 = $[0] !== a;
34 - const c_1 = $[1] !== b;
35 - const c_2 = $[2] !== c;
36 - const c_3 = $[3] !== d;
37 - let x$0;
38 - if (c_0 || c_1 || c_2 || c_3) {
39 - x$0 = undefined;
40 -
41 - if (true) {
42 - if (true) {
43 - const x$1 = a;
44 - x$0 = x$1;
45 - } else {
46 - const x$2 = b;
47 - x$0 = x$2;
48 - }
49 -
50 - x$3;
51 - x$0 = x$3;
52 - } else {
53 - if (true) {
54 - const x$4 = c;
55 - x$0 = x$4;
56 - } else {
57 - const x$5 = d;
58 - x$0 = x$5;
59 - }
60 -
61 - x$6;
62 - x$0 = x$6;
63 - }
64 -
65 - $[0] = a;
66 - $[1] = b;
67 - $[2] = c;
68 - $[3] = d;
69 - $[4] = x$0;
70 - } else {
71 - x$0 = $[4];
72 - }
73 -
32 + true;
33 + true;
34 + const x$0 = a;
35 + x$0;
36 return x$0;
37 }
38
compiler/forget/src/__tests__/fixtures/hir/ssa-nested-loops-no-reassign.expect.md
+2 -1
@@ -25,7 +25,8 @@ function foo(a, b, c) {
25 while (a) {
26 while (b) {
27 while (c) {
28 - x + 1;
28 + 1;
29 + 1;
30 }
31 }
32 }
compiler/forget/src/__tests__/fixtures/hir/ssa-objectexpression-phi.expect.md
+8 -28
@@ -25,38 +25,18 @@ function foo() {
25 const $ = React.useMemoCache();
26 const x = 1;
27 const y = 2;
28 - let x$0;
29 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 - x$0 = x;
31 - let y$1 = y;
32 -
33 - if (x > 1) {
34 - const x$2 = 2;
35 - x$0 = x$2;
36 - } else {
37 - const y$3 = 3;
38 - y$1 = y$3;
39 - }
40 -
41 - $[0] = x$0;
42 - } else {
43 - x$0 = $[0];
44 - }
45 -
46 - const c_1 = $[1] !== x$0;
47 - const c_2 = $[2] !== y$1;
28 + 1;
29 + false;
30 + const y$0 = 3;
31 let t;
49 -
50 - if (c_1 || c_2) {
32 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
33 t = {
52 - x: x$0,
53 - y: y$1,
34 + x: x,
35 + y: y$0,
36 };
55 - $[1] = x$0;
56 - $[2] = y$1;
57 - $[3] = t;
37 + $[0] = t;
38 } else {
59 - t = $[3];
39 + t = $[0];
40 }
41
42 return t;
compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md
+3 -15
@@ -17,22 +17,10 @@ function foo() {
17
18 ```javascript
19 function foo() {
20 - const $ = React.useMemoCache();
20 const x = 1;
22 - let x$0;
23 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
24 - x$0 = x;
25 -
26 - if (x === 1) {
27 - const x$1 = 2;
28 - x$0 = x$1;
29 - }
30 -
31 - $[0] = x$0;
32 - } else {
33 - x$0 = $[0];
34 - }
35 -
21 + 1;
22 + true;
23 + const x$0 = 2;
24 return x$0;
25 }
26
compiler/forget/src/__tests__/fixtures/hir/ssa-sibling-phis.expect.md
+4 -50
@@ -28,57 +28,11 @@ function foo(a, b, c, d) {
28
29 ```javascript
30 function foo(a, b, c, d) {
31 - const $ = React.useMemoCache();
31 const x = 0;
33 - if (true) {
34 - const c_0 = $[0] !== a;
35 - const c_1 = $[1] !== b;
36 - let x$0;
37 -
38 - if (c_0 || c_1) {
39 - x$0 = undefined;
40 -
41 - if (true) {
42 - const x$1 = a;
43 - x$0 = x$1;
44 - } else {
45 - const x$2 = b;
46 - x$0 = x$2;
47 - }
48 -
49 - $[0] = a;
50 - $[1] = b;
51 - $[2] = x$0;
52 - } else {
53 - x$0 = $[2];
54 - }
55 -
56 - x$0;
57 - } else {
58 - const c_3 = $[3] !== c;
59 - const c_4 = $[4] !== d;
60 - let x$3;
61 -
62 - if (c_3 || c_4) {
63 - x$3 = undefined;
64 -
65 - if (true) {
66 - const x$4 = c;
67 - x$3 = x$4;
68 - } else {
69 - const x$5 = d;
70 - x$3 = x$5;
71 - }
72 -
73 - $[3] = c;
74 - $[4] = d;
75 - $[5] = x$3;
76 - } else {
77 - x$3 = $[5];
78 - }
79 -
80 - x$3;
81 - }
32 + true;
33 + true;
34 + const x$0 = a;
35 + x$0;
36 }
37
38 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md
+3 -18
@@ -20,25 +20,10 @@ function foo() {
20
21 ```javascript
22 function foo() {
23 - const $ = React.useMemoCache();
23 const y = 2;
25 - let y$0;
26 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
27 - y$0 = undefined;
28 -
29 - if (y > 1) {
30 - const y$1 = 1;
31 - y$0 = y$1;
32 - } else {
33 - const y$2 = 2;
34 - y$0 = y$2;
35 - }
36 -
37 - $[0] = y$0;
38 - } else {
39 - y$0 = $[0];
40 - }
41 -
24 + 1;
25 + true;
26 + const y$0 = 1;
27 const x = y$0;
28 }
29
compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md
+1 -3
@@ -19,9 +19,7 @@ function foo() {
19 function foo() {
20 const x = 1;
21 const y = 2;
22 - if (y) {
23 - const z = x + y;
24 - }
22 + const z = 3;
23 }
24
25 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md
+10 -5
@@ -30,25 +30,30 @@ function foo() {
30 function foo() {
31 const $ = React.useMemoCache();
32 const x = 1;
33 + 2;
34 + 1;
35 let x$0;
36 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
37 x$0 = undefined;
38
39 bb1: switch (x) {
38 - case x === 1: {
39 - const x$1 = x + 1;
40 + case true: {
41 + 1;
42 + const x$1 = 2;
43 x$0 = x$1;
44 break bb1;
45 }
46
44 - case x === 2: {
45 - const x$2 = x + 2;
47 + case false: {
48 + 2;
49 + const x$2 = 3;
50 x$0 = x$2;
51 break bb1;
52 }
53
54 default: {
51 - const x$3 = x + 3;
55 + 3;
56 + const x$3 = 4;
57 x$0 = x$3;
58 }
59 }
compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md
+3 -15
@@ -16,22 +16,10 @@ function foo() {
16
17 ```javascript
18 function foo() {
19 - const $ = React.useMemoCache();
19 const x = 1;
21 - let x$0;
22 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23 - x$0 = x;
24 -
25 - if (x === 1) {
26 - const x$1 = 2;
27 - x$0 = x$1;
28 - }
29 -
30 - $[0] = x$0;
31 - } else {
32 - x$0 = $[0];
33 - }
34 -
20 + 1;
21 + true;
22 + const x$0 = 2;
23 throw x$0;
24 }
25
compiler/forget/src/__tests__/fixtures/hir/ssa-while-no-reassign.expect.md
+3 -2
@@ -18,8 +18,9 @@ function foo() {
18 ```javascript
19 function foo() {
20 const x = 1;
21 - while (x < 10) {
22 - x + 1;
21 + while ((10, true)) {
22 + 1;
23 + 2;
24 }
25
26 return x;