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

Infer reactive scope dependencies

This builds upon @josephsavona's prior PR #817 to add support for inferring reactive scope dependencies for all instructions and terminals. Still TODO (probably in follow up PRs): - [ ] fix duplicate/different identity scope issue (see this [test fixture](https://github.com/facebook/react-forget/blob/5f3b260aaa3e936fb013716df3ef4e7676b7e9ea/forget/src/__tests__/fixtures/hir/overlapping-scopes-while.expect.md)) - [ ] also collect outputs of each scope - [ ] add new tree visitor that only visits, consider renaming the current `visitTree` to `mapTree` or similar Co-authored-by: Joe Savona <joesavonafb.com>

Lauren Tan committed Nov 29, 2022 at 17:07 UTC b3a26f1de27e38b285a16bd013d285857344390a
81 files changed +872 -25
compiler/forget/packages/playground/components/Editor/Output.tsx
+11
@@ -26,6 +26,7 @@ const {
26 eliminateRedundantPhi,
27 inferReferenceEffects,
28 inferMutableRanges,
29 + inferReactiveScopeDependencies,
30 leaveSSA,
31 lower,
32 printHIR,
@@ -48,6 +49,7 @@ type CompilerOutput = {
49 eliminateRedundantPhiOutput: string;
50 inferReferenceEffectsOutput: string;
51 inferMutableRangesOutput: string;
52 + inferReactiveScopeDependenciesOutput: string;
53 leaveSSAOutput: string;
54 codegenOutput: string;
55 sourceMapUrl: string | null;
@@ -81,6 +83,9 @@ function compile(source: string): CompilerOutput | CompilerError {
83 inferMutableRanges(ir);
84 const inferMutableRangesOutput = printHIR(ir.body);
85
86 + inferReactiveScopeDependencies(ir);
87 + const inferReactiveScopeDependenciesOutput = printHIR(ir.body);
88 +
89 leaveSSA(ir);
90 const leaveSSAOutput = printHIR(ir.body);
91
@@ -110,6 +115,7 @@ function compile(source: string): CompilerOutput | CompilerError {
115 eliminateRedundantPhiOutput,
116 inferReferenceEffectsOutput,
117 inferMutableRangesOutput,
118 + inferReactiveScopeDependenciesOutput,
119 leaveSSAOutput,
120 codegenOutput,
121 sourceMapUrl,
@@ -165,6 +171,11 @@ function Output({ store, setTabsOpen, tabsOpen }: Props) {
171 output={compilerOutput.inferMutableRangesOutput}
172 ></TextTabContent>
173 ),
174 + InferReactiveScopeDependencies: (
175 + <TextTabContent
176 + output={compilerOutput.inferReactiveScopeDependenciesOutput}
177 + ></TextTabContent>
178 + ),
179 LeaveSSA: (
180 <TextTabContent
181 output={compilerOutput.leaveSSAOutput}
compiler/forget/packages/playground/components/TabbedWindow.tsx
+2 -2
@@ -5,8 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 -import clsx from "clsx";
9 -import React, { useCallback, useState } from "react";
8 +import React, { useCallback } from "react";
9
10 export type TabTypes =
11 | "HIR"
@@ -14,6 +13,7 @@ export type TabTypes =
13 | "EliminateRedundantPhi"
14 | "InferReferenceEffects"
15 | "InferMutableRanges"
16 + | "InferReactiveScopeDependencies"
17 | "LeaveSSA"
18 | "JS"
19 | "SourceMap"
compiler/forget/src/Common/utils.ts
+17
@@ -64,3 +64,20 @@ export function hasOwnProperty<T>(obj: T, key: PropertyKey): key is keyof T {
64 export function assertExhaustive(_: never, errorMsg: string): never {
65 throw new Error(errorMsg);
66 }
67 +
68 +/**
69 + * Modifies @param array in place, retaining only the items where the predicate returns true.
70 + */
71 +export function retainWhere<T>(
72 + array: Array<T>,
73 + predicate: (item: T) => boolean
74 +) {
75 + let writeIndex = 0;
76 + for (let readIndex = 0; readIndex < array.length; readIndex++) {
77 + const item = array[readIndex];
78 + if (predicate(item) === true) {
79 + array[writeIndex++] = item;
80 + }
81 + }
82 + array.length = writeIndex;
83 +}
compiler/forget/src/HIR/HIR.ts
+2
@@ -63,6 +63,7 @@ export type HIRFunction = {
63 body: HIR;
64 generator: boolean;
65 async: boolean;
66 + extra?: string;
67 };
68
69 /**
@@ -327,6 +328,7 @@ export enum Effect {
328 export type ReactiveScope = {
329 id: ScopeId;
330 range: MutableRange;
331 + dependencies: Set<Place>;
332 };
333
334 /**
compiler/forget/src/HIR/InferReactiveScopeDependencies.ts new
+195
@@ -0,0 +1,195 @@
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 { assertExhaustive, retainWhere } from "../Common/utils";
9 +import {
10 + HIRFunction,
11 + Identifier,
12 + Instruction,
13 + InstructionId,
14 + InstructionValue,
15 + makeInstructionId,
16 + Place,
17 + ReactiveScope,
18 +} from "./HIR";
19 +import { BlockTerminal, Visitor, visitTree } from "./HIRTreeVisitor";
20 +import { printPlace } from "./PrintHIR";
21 +import { eachInstructionValueOperand } from "./visitors";
22 +
23 +export function inferReactiveScopeDependencies(fn: HIRFunction) {
24 + // TODO: visitTree is more of a transformer visitor, consider adding a new tree visitor
25 + // that only visits and replacing this usage
26 + const visitor = new ScopeDependenciesVisitor(fn);
27 + visitTree(fn, visitor);
28 +
29 + const output = [];
30 + for (const scope of visitor.allScopes) {
31 + if (scope.dependencies.size > 0) {
32 + output.push(
33 + `scope${scope.id} [${scope.range.start}:${
34 + scope.range.end
35 + }]:\n${Array.from(scope.dependencies)
36 + .map((p) => " - " + printPlace(p))
37 + .join("\n")}`
38 + );
39 + }
40 + }
41 + fn.extra = output.join("\n");
42 +}
43 +
44 +function instructionInScope(instrId: InstructionId, scope: ReactiveScope) {
45 + return instrId >= scope.range.start && instrId < scope.range.end;
46 +}
47 +
48 +class ScopeDependenciesVisitor
49 + implements Visitor<void, InstructionValue, void, void>
50 +{
51 + #identifiers: Map<Identifier, InstructionId> = new Map();
52 + // Scopes that are currently active at this point in the traversal
53 + #activeScopes: Set<ReactiveScope> = new Set();
54 + // All scopes encountered during the traversal
55 + allScopes: Set<ReactiveScope> = new Set();
56 +
57 + get #lastActiveScope(): ReactiveScope | null {
58 + const scopes = [...this.#activeScopes];
59 + return scopes[scopes.length - 1];
60 + }
61 +
62 + constructor(fn: HIRFunction) {
63 + if (fn.id !== null) {
64 + this.#identifiers.set(fn.id, makeInstructionId(0));
65 + }
66 + for (const param of fn.params) {
67 + this.#identifiers.set(param.identifier, makeInstructionId(0));
68 + }
69 + }
70 +
71 + #recordActiveScope(scope: ReactiveScope) {
72 + this.#activeScopes.add(scope);
73 + this.allScopes.add(scope);
74 + }
75 +
76 + /**
77 + * Prune any scopes that are out of range
78 + */
79 + #visitId(id: InstructionId): void {
80 + const scopes = [...this.#activeScopes];
81 + retainWhere(scopes, (pending) => pending.range.end > id);
82 + this.#activeScopes = new Set(scopes);
83 + }
84 +
85 + /**
86 + * Adds a dependency on the terminal operand to the last active scope
87 + */
88 + #addTerminalDependency(operand: Place): void {
89 + const activeScope = this.#lastActiveScope;
90 + if (activeScope != null) {
91 + const identId = this.#identifiers.get(operand.identifier);
92 + if (identId !== undefined && identId < activeScope.range.start) {
93 + activeScope.dependencies.add(operand);
94 + }
95 + }
96 + }
97 +
98 + visitTerminal(
99 + terminal: BlockTerminal<void, InstructionValue, void, void>
100 + ): void {
101 + switch (terminal.kind) {
102 + case "if":
103 + case "switch":
104 + case "while": {
105 + for (const operand of eachInstructionValueOperand(terminal.test)) {
106 + this.#addTerminalDependency(operand);
107 + }
108 + break;
109 + }
110 + case "return":
111 + case "throw": {
112 + if (terminal.value != null) {
113 + for (const operand of eachInstructionValueOperand(terminal.value)) {
114 + this.#addTerminalDependency(operand);
115 + }
116 + }
117 + break;
118 + }
119 + case "continue":
120 + case "break": {
121 + break;
122 + }
123 + default:
124 + assertExhaustive(terminal, `unhandled terminal ${terminal}`);
125 + }
126 + }
127 +
128 + visitTerminalId(id: InstructionId): void {
129 + this.#visitId(id);
130 + }
131 +
132 + /**
133 + * We don't need to map to a different representation, so just return the value directly
134 + */
135 + visitValue(value: InstructionValue, id: InstructionId): InstructionValue {
136 + return value;
137 + }
138 +
139 + visitInstruction(instr: Instruction, _value: InstructionValue): void {
140 + const { lvalue, value } = instr;
141 + if (lvalue !== null && lvalue.place.memberPath === null) {
142 + if (!this.#identifiers.has(lvalue.place.identifier)) {
143 + this.#identifiers.set(lvalue.place.identifier, instr.id);
144 + }
145 + }
146 +
147 + const activeScopes: Set<ReactiveScope> = new Set();
148 + const dependencies: Array<Place> = [];
149 + if (lvalue != null) {
150 + if (
151 + lvalue.place.identifier.scope !== null &&
152 + lvalue.place.memberPath === null &&
153 + instructionInScope(instr.id, lvalue.place.identifier.scope)
154 + ) {
155 + activeScopes.add(lvalue.place.identifier.scope);
156 + } else {
157 + dependencies.push(lvalue.place);
158 + }
159 + }
160 + for (const operand of eachInstructionValueOperand(value)) {
161 + if (
162 + operand.identifier.scope !== null &&
163 + instructionInScope(instr.id, operand.identifier.scope)
164 + ) {
165 + activeScopes.add(operand.identifier.scope);
166 + } else {
167 + dependencies.push(operand);
168 + }
169 + }
170 +
171 + for (const scope of activeScopes) {
172 + this.#recordActiveScope(scope);
173 + }
174 +
175 + if (dependencies.length > 0) {
176 + const scope = this.#lastActiveScope;
177 + if (scope != null) {
178 + for (const dep of dependencies) {
179 + const identId = this.#identifiers.get(dep.identifier);
180 + if (identId !== undefined && identId < scope.range.start) {
181 + scope.dependencies.add(dep);
182 + }
183 + }
184 + }
185 + }
186 +
187 + this.#visitId(instr.id);
188 + }
189 +
190 + enterBlock(): void {}
191 + visitImplicitTerminal(): void | null {}
192 + visitCase(test: InstructionValue, block: void): void {}
193 + appendBlock(block: void, item: void, label?: string | undefined): void {}
194 + leaveBlock(block: void): void {}
195 +}
compiler/forget/src/HIR/InferReactiveScopeVariables.ts
+1
@@ -112,6 +112,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction) {
112 scope = {
113 id: makeScopeId(scopes.size),
114 range: identifier.mutableRange,
115 + dependencies: new Set(),
116 };
117 scopes.set(groupIdentifier, scope);
118 } else {
compiler/forget/src/HIR/InferReactiveScopes.ts
+1 -14
@@ -6,6 +6,7 @@
6 */
7
8 import invariant from "invariant";
9 +import { retainWhere } from "../Common/utils";
10 import DisjointSet from "./DisjointSet";
11 import {
12 HIRFunction,
@@ -395,17 +396,3 @@ function getInstructionScope(instr: Instruction): ReactiveScope | null {
396 }
397 return null;
398 }
398 -
399 -/**
400 - * Modifies @param array, retaining only the items where the predicate returns true.
401 - */
402 -function retainWhere<T>(array: Array<T>, predicate: (item: T) => boolean) {
403 - let writeIndex = 0;
404 - for (let readIndex = 0; readIndex < array.length; readIndex++) {
405 - const item = array[readIndex];
406 - if (predicate(item) === true) {
407 - array[writeIndex++] = item;
408 - }
409 - }
410 - array.length = writeIndex;
411 -}
compiler/forget/src/HIR/Pipeline.ts
+5
@@ -15,6 +15,7 @@ import inferReferenceEffects from "../HIR/InferReferenceEffects";
15 import leaveSSA from "../HIR/LeaveSSA";
16 import codegen from "./Codegen";
17 import { HIRFunction } from "./HIR";
18 +import { inferReactiveScopeDependencies } from "./InferReactiveScopeDependencies";
19 import { inferReactiveScopes } from "./InferReactiveScopes";
20 import { inferReactiveScopeVariables } from "./InferReactiveScopeVariables";
21
@@ -24,6 +25,7 @@ export type CompilerFlags = {
25 inferMutableRanges: boolean;
26 inferReactiveScopeVariables: boolean;
27 inferReactiveScopes: boolean;
28 + inferReactiveScopeDependencies: boolean;
29 leaveSSA: boolean;
30 codegen: boolean;
31 };
@@ -55,6 +57,9 @@ export default function (
57 if (flags.inferReactiveScopes) {
58 inferReactiveScopes(ir);
59 }
60 + if (flags.inferReactiveScopeDependencies) {
61 + inferReactiveScopeDependencies(ir);
62 + }
63 if (flags.leaveSSA) {
64 leaveSSA(ir);
65 }
compiler/forget/src/HIR/PrintHIR.ts
+11 -1
@@ -10,6 +10,7 @@ import { assertExhaustive } from "../Common/utils";
10 import {
11 GotoVariant,
12 HIR,
13 + HIRFunction,
14 Identifier,
15 Instruction,
16 InstructionKind,
@@ -26,6 +27,15 @@ export type Options = {
27 indent: number;
28 };
29
30 +export function printFunction(fn: HIRFunction): string {
31 + const output = [];
32 + output.push(printHIR(fn.body));
33 + if (fn.extra !== undefined) {
34 + output.push(fn.extra);
35 + }
36 + return output.join("\n");
37 +}
38 +
39 export default function printHIR(
40 ir: HIR,
41 options: Options | null = null
@@ -111,7 +121,7 @@ function printPhi(phi: Phi): string {
121 return items.join("");
122 }
123
114 -function printTerminal(terminal: Terminal): Array<string> | string {
124 +export function printTerminal(terminal: Terminal): Array<string> | string {
125 let value;
126 switch (terminal.kind) {
127 case "if": {
compiler/forget/src/__tests__/fixtures/hir/alias-while.expect.md
+2
@@ -51,6 +51,7 @@ bb2:
51 [13] read b$14_@0
52 [14] read c$16_@0
53 [15] Return freeze a$12_@0
54 +
55 ```
56
57 ### CFG
@@ -125,6 +126,7 @@ function foo$0(cond$1) {
126 ```
127 bb0:
128 [1] Return
129 +
130 ```
131
132 ### CFG
compiler/forget/src/__tests__/fixtures/hir/assignment-variations.expect.md
+11
@@ -28,6 +28,15 @@ bb0:
28 [6] Const mutate $10_@5 = 1
29 [7] Reassign mutate x$11_@6 = Binary read x$9_@4 >>> read $10_@5
30 [8] Return
31 +scope2 [3:4]:
32 + - read x$5_@0
33 + - read $6_@1
34 +scope4 [5:6]:
35 + - read x$7_@2
36 + - read $8_@3
37 +scope6 [7:8]:
38 + - read x$9_@4
39 + - read $10_@5
40 ```
41
42 ### CFG
@@ -72,6 +81,8 @@ bb0:
81 [3] Const mutate $6_@2 = 2
82 [4] Reassign mutate a$4_@1.b.c[0:5] = Binary read a$4_@1.b.c * read $6_@2
83 [5] Return
84 +scope2 [3:4]:
85 + - mutate a$4_@1.b.c
86 ```
87
88 ### CFG
compiler/forget/src/__tests__/fixtures/hir/call.expect.md
+7
@@ -20,6 +20,7 @@ function Component(props) {
20 ```
21 bb0:
22 [1] Return
23 +
24 ```
25
26 ### CFG
@@ -54,6 +55,12 @@ bb0:
55 [7] Const mutate $14_@3 = "div"
56 [8] Const mutate $15_@4 = JSX <read $14_@3 a={read a$10_@0} b={freeze b$11_@0} ></read $14_@3>
57 [9] Return read $15_@4
58 +scope2 [5:6]:
59 + - read $12_@1
60 +scope4 [8:9]:
61 + - read $14_@3
62 + - read a$10_@0
63 + - freeze b$11_@0
64 ```
65
66 ### CFG
compiler/forget/src/__tests__/fixtures/hir/complex-while.expect.md
+1
@@ -34,6 +34,7 @@ bb7:
34 bb1:
35 predecessor blocks: bb6 bb4 bb0
36 [6] Return
37 +
38 ```
39
40 ### CFG
compiler/forget/src/__tests__/fixtures/hir/component.expect.md
+4
@@ -88,6 +88,10 @@ bb2:
88 [32] Const mutate $59_@17 = "\n "
89 [33] Const mutate $60_@18 = JSX <read $53_@11>{read $54_@12}{read $57_@15}{read $58_@16}{freeze renderedItems$29_@2}{read $59_@17}</read $53_@11>
90 [34] Return read $60_@18
91 +scope0 [1:2]:
92 + - read props$26.items
93 +scope1 [2:3]:
94 + - read props$26.maxItems
95 ```
96
97 ### CFG
compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md
+23
@@ -89,6 +89,10 @@ bb1:
89 predecessor blocks: bb0
90 [6] Call mutate a_DEBUG$5_@0.push(read props$4.d)
91 [7] Return freeze a_DEBUG$5_@0
92 +scope0 [1:7]:
93 + - read props$4.a
94 + - read props$4.b
95 + - read props$4.d
96 ```
97
98 ### CFG
@@ -152,6 +156,11 @@ bb1:
156 predecessor blocks: bb2 bb0
157 [6] Call mutate a$4_@0.push(read props$3.d)
158 [7] Return freeze a$4_@0
159 +scope0 [1:7]:
160 + - read props$3.a
161 + - read props$3.c
162 + - read props$3.b
163 + - read props$3.d
164 ```
165
166 ### CFG
@@ -217,6 +226,11 @@ bb1:
226 predecessor blocks: bb0
227 [7] Call mutate a$5_@0.push(read props$4.d)
228 [8] Return freeze a$5_@0
229 +scope0 [1:8]:
230 + - read props$4.a
231 + - read props$4.c
232 + - read props$4.b
233 + - read props$4.d
234 ```
235
236 ### CFG
@@ -282,6 +296,11 @@ bb1:
296 predecessor blocks: bb0
297 [6] Call mutate a$4_@0.push(read props$3.d)
298 [7] Return freeze a$4_@0
299 +scope0 [1:7]:
300 + - read props$3.a
301 + - read props$3.c
302 + - read props$3.b
303 + - read props$3.d
304 ```
305
306 ### CFG
@@ -346,6 +365,10 @@ bb1:
365 predecessor blocks: bb0 bb2
366 [6] Call mutate a$4_@0.push(read props$3.d)
367 [7] Return freeze a$4_@0
368 +scope0 [1:7]:
369 + - read props$3.a
370 + - read props$3.d
371 + - read props$3.c
372 ```
373
374 ### CFG
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md
+12
@@ -53,6 +53,13 @@ bb3:
53 predecessor blocks: bb4 bb1
54 [9] Const mutate $16_@2 = JSX <read Foo$4 a={freeze a$7_@0} b={freeze b$8_@0} ></read Foo$4>
55 [10] Return read $16_@2
56 +scope0 [1:9]:
57 + - read props$6.p0
58 + - read props$6.p2
59 + - read props$6.p1
60 +scope2 [9:10]:
61 + - freeze a$7_@0
62 + - freeze b$8_@0
63 ```
64
65 ### CFG
@@ -140,6 +147,9 @@ bb3:
147 predecessor blocks: bb4 bb1
148 [10] Const mutate $19_@2 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
149 [11] Return read $19_@2
150 +scope2 [10:11]:
151 + - freeze a$9_@0
152 + - freeze b$10_@0
153 ```
154
155 ### CFG
@@ -210,6 +220,7 @@ function Component$0(props$1) {
220 ```
221 bb0:
222 [1] Return
223 +
224 ```
225
226 ### CFG
@@ -236,6 +247,7 @@ function Foo$0() {}
247 ```
248 bb0:
249 [1] Return
250 +
251 ```
252
253 ### CFG
compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md
+7
@@ -20,6 +20,7 @@ function Component(props) {
20 ```
21 bb0:
22 [1] Return
23 +
24 ```
25
26 ### CFG
@@ -54,6 +55,12 @@ bb0:
55 [7] Const mutate $14_@3 = "div"
56 [8] Const mutate $15_@4 = JSX <read $14_@3 a={read a$10_@0} b={freeze b$11_@0} ></read $14_@3>
57 [9] Return read $15_@4
58 +scope2 [5:6]:
59 + - read $12_@1
60 +scope4 [8:9]:
61 + - read $14_@3
62 + - read a$10_@0
63 + - freeze b$11_@0
64 ```
65
66 ### CFG
compiler/forget/src/__tests__/fixtures/hir/dependencies-outputs.expect.md new
+118
@@ -0,0 +1,118 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(a, b) {
6 + const x = [];
7 + x.push(a);
8 + <div>{x}</div>;
9 +
10 + const y = [];
11 + if (x.length) {
12 + y.push(x);
13 + }
14 + if (b) {
15 + y.push(b);
16 + }
17 +}
18 +
19 +```
20 +
21 +## HIR
22 +
23 +```
24 +bb0:
25 + [1] Const mutate x$8_@0[1:3] = Array []
26 + [2] Call mutate x$8_@0.push(read a$6)
27 + [3] Const mutate $9_@1 = "div"
28 + [4] JSX <read $9_@1>{freeze x$8_@0}</read $9_@1>
29 + [5] Const mutate y$10_@2[5:12] = Array []
30 + [6] If (read x$8_@0.length) then:bb2 else:bb1
31 +bb2:
32 + predecessor blocks: bb0
33 + [7] Call mutate y$10_@2.push(read x$8_@0)
34 + [8] Goto bb1
35 +bb1:
36 + predecessor blocks: bb2 bb0
37 + [9] If (read b$7) then:bb4 else:bb3
38 +bb4:
39 + predecessor blocks: bb1
40 + [10] Call mutate y$10_@2.push(read b$7)
41 + [11] Goto bb3
42 +bb3:
43 + predecessor blocks: bb4 bb1
44 + [12] Return
45 +scope0 [1:3]:
46 + - read a$6
47 +scope1 [3:4]:
48 + - freeze x$8_@0
49 +scope2 [5:12]:
50 + - read x$8_@0
51 + - read x$8_@0.length
52 + - read b$7
53 + - read b$7
54 +```
55 +
56 +### CFG
57 +
58 +```mermaid
59 +flowchart TB
60 + %% Basic Blocks
61 + subgraph bb0
62 + bb0_instrs["
63 + [1] Const mutate x$8_@0[1:3] = Array []
64 + [2] Call mutate x$8_@0.push(read a$6)
65 + [3] Const mutate $9_@1 = 'div'
66 + [4] JSX <read $9_@1>{freeze x$8_@0}</read $9_@1>
67 + [5] Const mutate y$10_@2[5:12] = Array []
68 + "]
69 + bb0_instrs --> bb0_terminal(["If (read x$8_@0.length)"])
70 + end
71 + subgraph bb2
72 + bb2_instrs["
73 + [7] Call mutate y$10_@2.push(read x$8_@0)
74 + "]
75 + bb2_instrs --> bb2_terminal(["Goto"])
76 + end
77 + subgraph bb1
78 + bb1_terminal(["If (read b$7)"])
79 + end
80 + subgraph bb4
81 + bb4_instrs["
82 + [10] Call mutate y$10_@2.push(read b$7)
83 + "]
84 + bb4_instrs --> bb4_terminal(["Goto"])
85 + end
86 + subgraph bb3
87 + bb3_terminal(["Return"])
88 + end
89 +
90 + %% Jumps
91 + bb0_terminal -- "then" --> bb2
92 + bb0_terminal -- "else" --> bb1
93 + bb2_terminal --> bb1
94 + bb1_terminal -- "then" --> bb4
95 + bb1_terminal -- "else" --> bb3
96 + bb4_terminal --> bb3
97 +
98 +```
99 +
100 +## Code
101 +
102 +```javascript
103 +function foo$0(a$1, b$2) {
104 + const x$3 = [];
105 + x$3.push(a$1);
106 + <div>{x$3}</div>;
107 + const y$5 = [];
108 + bb1: if (x$3.length) {
109 + y$5.push(x$3);
110 + }
111 +
112 + bb3: if (b$2) {
113 + y$5.push(b$2);
114 + }
115 +}
116 +
117 +```
118 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/dependencies-outputs.js new
+13
@@ -0,0 +1,13 @@
1 +function foo(a, b) {
2 + const x = [];
3 + x.push(a);
4 + <div>{x}</div>;
5 +
6 + const y = [];
7 + if (x.length) {
8 + y.push(x);
9 + }
10 + if (b) {
11 + y.push(b);
12 + }
13 +}
compiler/forget/src/__tests__/fixtures/hir/dependencies.expect.md new
+116
@@ -0,0 +1,116 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo(x, y, z) {
6 + const items = [z];
7 + items.push(x);
8 +
9 + const items2 = [];
10 + if (x) {
11 + items2.push(y);
12 + }
13 +
14 + if (y) {
15 + items.push(x);
16 + }
17 +
18 + return items2;
19 +}
20 +
21 +```
22 +
23 +## HIR
24 +
25 +```
26 +bb0:
27 + [1] Const mutate items$9_@0[1:10] = Array [read z$8]
28 + [2] Call mutate items$9_@0.push(read x$6)
29 + [3] Const mutate items2$10_@1[3:7] = Array []
30 + [4] If (read x$6) then:bb2 else:bb1
31 +bb2:
32 + predecessor blocks: bb0
33 + [5] Call mutate items2$10_@1.push(read y$7)
34 + [6] Goto bb1
35 +bb1:
36 + predecessor blocks: bb2 bb0
37 + [7] If (read y$7) then:bb4 else:bb3
38 +bb4:
39 + predecessor blocks: bb1
40 + [8] Call mutate items$9_@0.push(read x$6)
41 + [9] Goto bb3
42 +bb3:
43 + predecessor blocks: bb4 bb1
44 + [10] Return freeze items2$10_@1
45 +scope0 [1:10]:
46 + - read z$8
47 + - read x$6
48 + - read x$6
49 + - read y$7
50 +scope1 [3:7]:
51 + - read y$7
52 + - read x$6
53 +```
54 +
55 +### CFG
56 +
57 +```mermaid
58 +flowchart TB
59 + %% Basic Blocks
60 + subgraph bb0
61 + bb0_instrs["
62 + [1] Const mutate items$9_@0[1:10] = Array [read z$8]
63 + [2] Call mutate items$9_@0.push(read x$6)
64 + [3] Const mutate items2$10_@1[3:7] = Array []
65 + "]
66 + bb0_instrs --> bb0_terminal(["If (read x$6)"])
67 + end
68 + subgraph bb2
69 + bb2_instrs["
70 + [5] Call mutate items2$10_@1.push(read y$7)
71 + "]
72 + bb2_instrs --> bb2_terminal(["Goto"])
73 + end
74 + subgraph bb1
75 + bb1_terminal(["If (read y$7)"])
76 + end
77 + subgraph bb4
78 + bb4_instrs["
79 + [8] Call mutate items$9_@0.push(read x$6)
80 + "]
81 + bb4_instrs --> bb4_terminal(["Goto"])
82 + end
83 + subgraph bb3
84 + bb3_terminal(["Return freeze items2$10_@1"])
85 + end
86 +
87 + %% Jumps
88 + bb0_terminal -- "then" --> bb2
89 + bb0_terminal -- "else" --> bb1
90 + bb2_terminal --> bb1
91 + bb1_terminal -- "then" --> bb4
92 + bb1_terminal -- "else" --> bb3
93 + bb4_terminal --> bb3
94 +
95 +```
96 +
97 +## Code
98 +
99 +```javascript
100 +function foo$0(x$1, y$2, z$3) {
101 + const items$4 = [z$3];
102 + items$4.push(x$1);
103 + const items2$5 = [];
104 + bb1: if (x$1) {
105 + items2$5.push(y$2);
106 + }
107 +
108 + bb3: if (y$2) {
109 + items$4.push(x$1);
110 + }
111 +
112 + return items2$5;
113 +}
114 +
115 +```
116 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/dependencies.js new
+15
@@ -0,0 +1,15 @@
1 +function foo(x, y, z) {
2 + const items = [z];
3 + items.push(x);
4 +
5 + const items2 = [];
6 + if (x) {
7 + items2.push(y);
8 + }
9 +
10 + if (y) {
11 + items.push(x);
12 + }
13 +
14 + return items2;
15 +}
compiler/forget/src/__tests__/fixtures/hir/extend-scopes-if.expect.md
+4
@@ -46,6 +46,10 @@ bb7:
46 predecessor blocks: bb1
47 [10] Const mutate $13_@2 = null
48 [11] Return read $13_@2
49 +scope0 [1:8]:
50 + - read c$9
51 + - read b$8
52 + - read a$7
53 ```
54
55 ### CFG
compiler/forget/src/__tests__/fixtures/hir/frozen-after-alias.expect.md
+3
@@ -23,6 +23,7 @@ bb0:
23 [3] Call read useFreeze$3(freeze a$5_@0)
24 [4] Call mutate foo$4_@1(read b$6_@0)
25 [5] Return
26 +
27 ```
28
29 ### CFG
@@ -60,6 +61,7 @@ function Component$0() {
61 ```
62 bb0:
63 [1] Return
64 +
65 ```
66
67 ### CFG
@@ -86,6 +88,7 @@ function useFreeze$0() {}
88 ```
89 bb0:
90 [1] Return
91 +
92 ```
93
94 ### CFG
compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md
+11
@@ -24,6 +24,7 @@ function Component(props) {
24 ```
25 bb0:
26 [1] Return
27 +
28 ```
29
30 ### CFG
@@ -50,6 +51,7 @@ function useFreeze$0() {}
51 ```
52 bb0:
53 [1] Return
54 +
55 ```
56
57 ### CFG
@@ -83,6 +85,15 @@ bb0:
85 [6] Const mutate $15_@5 = "\n "
86 [7] Const mutate $16_@6 = JSX <read Component$0>{read $13_@3}{read x$11_@0}{read $14_@4}{read y$12_@1}{read $15_@5}</read Component$0>
87 [8] Return read $16_@6
88 +scope1 [2:3]:
89 + - freeze x$11_@0
90 +scope6 [7:8]:
91 + - read Component$0
92 + - read $13_@3
93 + - read x$11_@0
94 + - read $14_@4
95 + - read y$12_@1
96 + - read $15_@5
97 ```
98
99 ### CFG
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-arguments.expect.md
+3
@@ -24,6 +24,7 @@ bb0:
24 [3] Call read useFreeze$2(read a$4_@0)
25 [4] Call mutate call$3_@1(read a$4_@0)
26 [5] Return read a$4_@0
27 +
28 ```
29
30 ### CFG
@@ -62,6 +63,7 @@ function Component$0() {
63 ```
64 bb0:
65 [1] Return
66 +
67 ```
68
69 ### CFG
@@ -88,6 +90,7 @@ function useFreeze$0(x$1) {}
90 ```
91 bb0:
92 [1] Return
93 +
94 ```
95
96 ### CFG
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md
+7
@@ -45,6 +45,11 @@ bb1:
45 [10] Call read useFreeze$5(read a$14_@1)
46 [11] Call mutate call$6_@3(read a$14_@1)
47 [12] Return read a$14_@1
48 +scope0 [1:2]:
49 + - read props$7.cond
50 +scope1 [2:9]:
51 + - read props$7.x
52 + - read cond$8_@0
53 ```
54
55 ### CFG
@@ -115,6 +120,7 @@ function Component$0(props$1) {
120 ```
121 bb0:
122 [1] Return
123 +
124 ```
125
126 ### CFG
@@ -141,6 +147,7 @@ function useFreeze$0(x$1) {}
147 ```
148 bb0:
149 [1] Return
150 +
151 ```
152
153 ### CFG
compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md
+7
@@ -38,6 +38,7 @@ function Component(props) {
38 ```
39 bb0:
40 [1] Return
41 +
42 ```
43
44 ### CFG
@@ -64,6 +65,7 @@ function compute$0() {}
65 ```
66 bb0:
67 [1] Return
68 +
69 ```
70
71 ### CFG
@@ -90,6 +92,7 @@ function mutate$0() {}
92 ```
93 bb0:
94 [1] Return
95 +
96 ```
97
98 ### CFG
@@ -116,6 +119,7 @@ function foo$0() {}
119 ```
120 bb0:
121 [1] Return
122 +
123 ```
124
125 ### CFG
@@ -153,6 +157,9 @@ bb1:
157 predecessor blocks: bb2 bb0
158 [7] Const mutate $14_@1 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
159 [8] Return read $14_@1
160 +scope1 [7:8]:
161 + - freeze a$9_@0
162 + - freeze b$10_@0
163 ```
164
165 ### CFG
compiler/forget/src/__tests__/fixtures/hir/independent.expect.md
+6
@@ -32,6 +32,9 @@ bb0:
32 [2] Const mutate b$9_@0[0:3] = Call mutate compute$3_@0(read props$7.b)
33 [3] Const mutate $10_@1 = JSX <read Foo$5 a={freeze a$8_@0} b={freeze b$9_@0} ></read Foo$5>
34 [4] Return read $10_@1
35 +scope1 [3:4]:
36 + - freeze a$8_@0
37 + - freeze b$9_@0
38 ```
39
40 ### CFG
@@ -67,6 +70,7 @@ function Component$0(props$1) {
70 ```
71 bb0:
72 [1] Return
73 +
74 ```
75
76 ### CFG
@@ -93,6 +97,7 @@ function compute$0() {}
97 ```
98 bb0:
99 [1] Return
100 +
101 ```
102
103 ### CFG
@@ -119,6 +124,7 @@ function foo$0() {}
124 ```
125 bb0:
126 [1] Return
127 +
128 ```
129
130 ### CFG
compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md
+6
@@ -32,6 +32,7 @@ function Component(props) {
32 ```
33 bb0:
34 [1] Return
35 +
36 ```
37
38 ### CFG
@@ -58,6 +59,7 @@ function compute$0() {}
59 ```
60 bb0:
61 [1] Return
62 +
63 ```
64
65 ### CFG
@@ -84,6 +86,7 @@ function foo$0() {}
86 ```
87 bb0:
88 [1] Return
89 +
90 ```
91
92 ### CFG
@@ -120,6 +123,9 @@ bb1:
123 predecessor blocks: bb2 bb0
124 [6] Const mutate $14_@1 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
125 [7] Return read $14_@1
126 +scope1 [6:7]:
127 + - freeze a$9_@0
128 + - freeze b$10_@0
129 ```
130
131 ### CFG
compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md
+6
@@ -33,6 +33,9 @@ bb0:
33 [3] Call mutate foo$5_@0(mutate a$9_@0, mutate b$10_@0)
34 [4] Const mutate $11_@1 = JSX <read Foo$6 a={freeze a$9_@0} b={freeze b$10_@0} ></read Foo$6>
35 [5] Return read $11_@1
36 +scope1 [4:5]:
37 + - freeze a$9_@0
38 + - freeze b$10_@0
39 ```
40
41 ### CFG
@@ -70,6 +73,7 @@ function Component$0(props$1) {
73 ```
74 bb0:
75 [1] Return
76 +
77 ```
78
79 ### CFG
@@ -96,6 +100,7 @@ function compute$0() {}
100 ```
101 bb0:
102 [1] Return
103 +
104 ```
105
106 ### CFG
@@ -122,6 +127,7 @@ function foo$0() {}
127 ```
128 bb0:
129 [1] Return
130 +
131 ```
132
133 ### CFG
compiler/forget/src/__tests__/fixtures/hir/inverted-if.expect.md
+4
@@ -30,6 +30,10 @@ bb5:
30 bb1:
31 predecessor blocks: bb5 bb3 bb0
32 [6] Return
33 +scope0 [1:6]:
34 + - read c$7
35 + - read b$6
36 + - read a$5
37 ```
38
39 ### CFG
compiler/forget/src/__tests__/fixtures/hir/jsx-fragment.expect.md
+14
@@ -31,6 +31,20 @@ bb0:
31 [10] Const mutate $23_@9 = "\n "
32 [11] Const mutate $24_@10 = JsxFragment [read $14_@0, read props$13.greeting, read $15_@1, read $16_@2, read $22_@8, read $23_@9]
33 [12] Return read $24_@10
34 +scope6 [7:8]:
35 + - read $19_@5
36 +scope8 [9:10]:
37 + - read $17_@3
38 + - read $18_@4
39 + - read $20_@6
40 + - read $21_@7
41 +scope10 [11:12]:
42 + - read $14_@0
43 + - read props$13.greeting
44 + - read $15_@1
45 + - read $16_@2
46 + - read $22_@8
47 + - read $23_@9
48 ```
49
50 ### CFG
compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md
+5
@@ -37,6 +37,7 @@ bb1:
37 predecessor blocks: bb2 bb3
38 $8_@0[0:7]: phi(bb2: $6_@0, bb3: $7_@0)
39 [7] Return freeze $8_@0
40 +
41 ```
42
43 ### CFG
@@ -104,6 +105,7 @@ bb1:
105 predecessor blocks: bb2 bb3
106 $8_@0[0:7]: phi(bb2: $6_@0, bb3: $7_@0)
107 [7] Return freeze $8_@0
108 +
109 ```
110
111 ### CFG
@@ -173,6 +175,7 @@ bb1:
175 predecessor blocks: bb2 bb3
176 $14_@0[0:9]: phi(bb2: $12_@0, bb3: $13_@0)
177 [9] Return freeze $14_@0
178 +
179 ```
180
181 ### CFG
@@ -229,6 +232,7 @@ function QuestionQuestion$0(props$1) {
232 ```
233 bb0:
234 [1] Return
235 +
236 ```
237
238 ### CFG
@@ -255,6 +259,7 @@ function f$0() {}
259 ```
260 bb0:
261 [1] Return
262 +
263 ```
264
265 ### CFG
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md
+3
@@ -44,6 +44,7 @@ function Component(props) {
44 ```
45 bb0:
46 [1] Return
47 +
48 ```
49
50 ### CFG
@@ -70,6 +71,7 @@ function mutate$0(x$1, y$2) {}
71 ```
72 bb0:
73 [1] Return
74 +
75 ```
76
77 ### CFG
@@ -142,6 +144,7 @@ bb13:
144 [21] Const mutate $34_@2 = null
145 [22] Call mutate mutate$7_@0(mutate d$33_@0, read $34_@2)
146 [23] Return
147 +
148 ```
149
150 ### CFG
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md
+3
@@ -40,6 +40,7 @@ function Component(props) {
40 ```
41 bb0:
42 [1] Return
43 +
44 ```
45
46 ### CFG
@@ -90,6 +91,8 @@ bb9:
91 [13] Const mutate $17_@4 = null
92 [14] Call mutate mutate$8_@3(mutate x$15_@3, read $17_@4)
93 [15] Return
94 +scope1 [2:3]:
95 + - read a$11_@0
96 ```
97
98 ### CFG
compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md
+3
@@ -39,6 +39,7 @@ function Component(props) {
39 ```
40 bb0:
41 [1] Return
42 +
43 ```
44
45 ### CFG
@@ -65,6 +66,7 @@ function mutate$0() {}
66 ```
67 bb0:
68 [1] Return
69 +
70 ```
71
72 ### CFG
@@ -124,6 +126,7 @@ bb13:
126 [16] Const mutate $28_@3 = null
127 [17] Call mutate mutate$6_@0(mutate d$15_@0, read $28_@3)
128 [18] Return
129 +
130 ```
131
132 ### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved-by-terminal.expect.md
+3
@@ -27,6 +27,9 @@ bb1:
27 [4] Call mutate y$10_@0.push(read a$6)
28 [5] Call mutate x$9_@0.push(read b$7)
29 [6] Return
30 +scope0 [1:6]:
31 + - read a$6
32 + - read b$7
33 ```
34
35 ### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-interleaved.expect.md
+3
@@ -20,6 +20,9 @@ bb0:
20 [3] Call mutate x$7_@0.push(read a$5)
21 [4] Call mutate y$8_@0.push(read b$6)
22 [5] Return
23 +scope0 [1:5]:
24 + - read a$5
25 + - read b$6
26 ```
27
28 ### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowed.expect.md
+3
@@ -20,6 +20,9 @@ bb0:
20 [3] Call mutate y$8_@1.push(read b$6)
21 [4] Call mutate x$7_@0.push(read a$5)
22 [5] Return
23 +scope1 [2:4]:
24 + - read b$6
25 + - read a$5
26 ```
27
28 ### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md
+8
@@ -40,6 +40,14 @@ bb3:
40 bb1:
41 predecessor blocks: bb3 bb0
42 [11] Return freeze x$11_@0
43 +scope0 [1:11]:
44 + - read a$8
45 +scope1 [3:7]:
46 + - read c$10
47 + - read b$9
48 +scope3 [8:9]:
49 + - read $13_@2
50 + - freeze y$12_@1
51 ```
52
53 ### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-while.expect.md
+4
@@ -31,6 +31,10 @@ bb3:
31 bb2:
32 predecessor blocks: bb1
33 [8] Return
34 +scope0 [1:8]:
35 + - read b$7
36 + - read a$6
37 + - read c$8
38 ```
39
40 ### CFG
compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-within-block.expect.md
+4
@@ -38,6 +38,10 @@ bb3:
38 bb1:
39 predecessor blocks: bb3 bb0
40 [9] Return freeze x$9_@0
41 +scope0 [1:9]:
42 + - read c$8
43 + - read b$7
44 + - read a$6
45 ```
46
47 ### CFG
compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md
+10
@@ -24,6 +24,16 @@ bb0:
24 [5] Call mutate x$7_@0.y.push(read props$6.p0)
25 [6] Const mutate $10_@3 = JSX <read Component$0 data={freeze x$7_@0} >{read child$9_@2}</read Component$0>
26 [7] Return read $10_@3
27 +scope1 [2:3]:
28 + - mutate x$7_@0.y
29 +scope2 [4:5]:
30 + - read Component$0
31 + - freeze y$8_@1
32 + - read props$6.p0
33 +scope3 [6:7]:
34 + - read Component$0
35 + - freeze x$7_@0
36 + - read child$9_@2
37 ```
38
39 ### CFG
compiler/forget/src/__tests__/fixtures/hir/reactive-scope-grouping.expect.md
+2
@@ -24,6 +24,8 @@ bb0:
24 [4] Call mutate y$5_@1.push(mutate z$6_@1)
25 [5] Reassign mutate x$4_@0.y[1:6] = read y$5_@1
26 [6] Return freeze x$4_@0
27 +scope1 [2:5]:
28 + - mutate x$4_@0.y
29 ```
30
31 ### CFG
compiler/forget/src/__tests__/fixtures/hir/reactive-scopes.expect.md
+9
@@ -35,6 +35,15 @@ bb1:
35 [8] Const mutate $13_@3 = "div"
36 [9] Const mutate $15_@4 = JSX <read $13_@3>{freeze x$10_@0}</read $13_@3>
37 [10] Return read $15_@4
38 +scope0 [1:8]:
39 + - read b$9
40 + - read b$9
41 +scope2 [3:4]:
42 + - read a$8.length
43 + - read $11_@1
44 +scope4 [9:10]:
45 + - read $13_@3
46 + - freeze x$10_@0
47 ```
48
49 ### CFG
compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md
+10
@@ -38,6 +38,16 @@ bb1:
38 [8] Call read y$8_@0.push(read props$6.p2)
39 [9] Const mutate $15_@2 = JSX <read Component$0 x={read x$11_@0} y={read y$8_@0} ></read Component$0>
40 [10] Return read $15_@2
41 +scope0 [1:7]:
42 + - read props$6.p0
43 + - read props$6.p1
44 +scope1 [7:8]:
45 + - read Component$0
46 + - read y$8_@0.push
47 + - read props$6.p2
48 +scope2 [9:10]:
49 + - read Component$0
50 + - read y$8_@0
51 ```
52
53 ### CFG
compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md
+16
@@ -67,6 +67,22 @@ bb3:
67 [18] Const mutate $28_@8 = "\n "
68 [19] Const mutate $31_@9 = JSX <read $25_@5>{read $26_@6}{read y$19_@2}{read $27_@7}{freeze x$30_@4}{read $28_@8}</read $25_@5>
69 [20] Return read $31_@9
70 +scope0 [1:5]:
71 + - read a$13
72 + - read a$13
73 +scope2 [6:7]:
74 + - read $17_@1
75 + - freeze x$16_@0
76 +scope4 [9:14]:
77 + - read c$15
78 + - read b$14
79 + - read b$14
80 +scope9 [19:20]:
81 + - read $25_@5
82 + - read $26_@6
83 + - read y$19_@2
84 + - read $27_@7
85 + - read $28_@8
86 ```
87
88 ### CFG
compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md
+10
@@ -29,6 +29,16 @@ bb0:
29 [6] Call mutate y$8_@0.push(read props$6.p1)
30 [7] Const mutate $11_@3 = JSX <read Component$0 x={read x$9_@1} y={freeze y$8_@0} ></read Component$0>
31 [8] Return read $11_@3
32 +scope0 [1:7]:
33 + - read props$6.p0
34 +scope2 [5:6]:
35 + - read Component$0
36 + - freeze x$9_@1
37 + - read props$6.p1
38 +scope3 [7:8]:
39 + - read Component$0
40 + - read x$9_@1
41 + - freeze y$8_@0
42 ```
43
44 ### CFG
compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md
+7
@@ -76,6 +76,13 @@ bb1:
76 x$16_@4[7:17]: phi(bb8: x$11_@4, bb6: x$12_@4, bb4: x$13_@4, bb12: x$14_@4, bb13: x$15_@4)
77 [18] read x$16_@4
78 [19] Return
79 +scope4 [7:17]:
80 + - read props$6.v2
81 + - read props$6.v1
82 + - read props$6.v0
83 + - read props$6.test
84 + - read props$6.b
85 + - read props$6.c
86 ```
87
88 ### CFG
compiler/forget/src/__tests__/fixtures/hir/simple-alias.expect.md
+2
@@ -21,6 +21,7 @@ function foo() {
21 ```
22 bb0:
23 [1] Return
24 +
25 ```
26
27 ### CFG
@@ -54,6 +55,7 @@ bb0:
55 [6] Reassign mutate c$10_@1[0:8] = read a$8_@1
56 [7] Call mutate mutate$4_@1(mutate a$8_@1, mutate b$9_@1)
57 [8] Return freeze c$10_@1
58 +
59 ```
60
61 ### CFG
compiler/forget/src/__tests__/fixtures/hir/simple.expect.md
+9
@@ -27,6 +27,15 @@ bb1:
27 [6] Const mutate $13_@3 = Binary read y$9 * read $12_@2
28 [7] Const mutate $14_@4 = Array [read $13_@3]
29 [8] Return freeze $14_@4
30 +scope1 [3:4]:
31 + - read foo$0
32 + - read $10_@0
33 + - read y$9
34 +scope3 [6:7]:
35 + - read y$9
36 + - read $12_@2
37 +scope4 [7:8]:
38 + - read $13_@3
39 ```
40
41 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-arrayexpression.expect.md
+3
@@ -19,6 +19,9 @@ bb0:
19 [2] Const mutate b$7_@1 = 2
20 [3] Const mutate x$8_@2 = Array [read a$6_@0, read b$7_@1]
21 [4] Return freeze x$8_@2
22 +scope2 [3:4]:
23 + - read a$6_@0
24 + - read b$7_@1
25 ```
26
27 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md
+7
@@ -23,6 +23,7 @@ function Component(props) {
23 ```
24 bb0:
25 [1] Return
26 +
27 ```
28
29 ### CFG
@@ -64,6 +65,12 @@ bb1:
65 [10] Const mutate $19_@3 = "div"
66 [11] Const mutate $20_@4 = JSX <read $19_@3 a={freeze a$11_@0} b={freeze b$12_@0} ></read $19_@3>
67 [12] Return read $20_@4
68 +scope2 [7:8]:
69 + - read $14_@1
70 +scope4 [11:12]:
71 + - read $19_@3
72 + - freeze a$11_@0
73 + - freeze b$12_@0
74 ```
75
76 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md
+7
@@ -20,6 +20,7 @@ function Component(props) {
20 ```
21 bb0:
22 [1] Return
23 +
24 ```
25
26 ### CFG
@@ -54,6 +55,12 @@ bb0:
55 [7] Const mutate $14_@3 = "div"
56 [8] Const mutate $15_@4 = JSX <read $14_@3 a={read a$10_@0} b={freeze b$11_@0} ></read $14_@3>
57 [9] Return read $15_@4
58 +scope2 [5:6]:
59 + - read $12_@1
60 +scope4 [8:9]:
61 + - read $14_@3
62 + - read a$10_@0
63 + - freeze b$11_@0
64 ```
65
66 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md
+6
@@ -45,6 +45,12 @@ bb3:
45 x$16_@0[1:14]: phi(bb4: x$15_@0, bb1: x$17_@0)
46 [13] Reassign mutate y$18_@0[1:14] = read x$16_@0
47 [14] Return
48 +scope3 [4:5]:
49 + - read y$8_@1
50 + - read $9_@2
51 +scope5 [9:10]:
52 + - read y$8_@1
53 + - read $12_@4
54 ```
55
56 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md
+3
@@ -32,6 +32,9 @@ bb1:
32 x$10_@0[1:9]: phi(bb2: x$9_@0, bb0: x$5_@0)
33 [8] Reassign mutate y$11_@0[1:9] = read x$10_@0
34 [9] Return
35 +scope3 [4:5]:
36 + - read y$6_@1
37 + - read $7_@2
38 ```
39
40 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-for-of.expect.md
+1
@@ -38,6 +38,7 @@ bb4:
38 bb2:
39 predecessor blocks: bb1
40 [9] Return freeze items$5_@0
41 +
42 ```
43
44 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md
+1
@@ -32,6 +32,7 @@ bb4:
32 bb2:
33 predecessor blocks: bb1
34 [9] Return read x$7_@0
35 +
36 ```
37
38 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md
+1
@@ -33,6 +33,7 @@ bb3:
33 bb1:
34 predecessor blocks: bb2 bb3
35 [8] Return
36 +
37 ```
38
39 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-nested-loops-no-reassign.expect.md
+2
@@ -52,6 +52,8 @@ bb5:
52 bb2:
53 predecessor blocks: bb1
54 [13] Return read x$9_@0
55 +scope1 [8:9]:
56 + - read x$9_@0
57 ```
58
59 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-newexpression.expect.md
+2
@@ -18,6 +18,7 @@ function Component(props) {
18 ```
19 bb0:
20 [1] Return
21 +
22 ```
23
24 ### CFG
@@ -47,6 +48,7 @@ bb0:
48 [2] Const mutate b$8_@0[0:4] = Object { }
49 [3] Let mutate c$9_@0[0:4] = New mutate Foo$5_@0(mutate a$7_@0, mutate b$8_@0)
50 [4] Return freeze c$9_@0
51 +
52 ```
53
54 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-objectexpression-phi.expect.md
+2
@@ -41,6 +41,8 @@ bb1:
41 y$13_@0[1:10]: phi(bb2: y$7_@0, bb3: y$11_@0)
42 [10] Let mutate t$14_@4 = Object { x: read x$12_@0, y: read y$13_@0 }
43 [11] Return freeze t$14_@4
44 +scope3 [4:5]:
45 + - read $8_@2
46 ```
47
48 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-objectexpression.expect.md
+3
@@ -19,6 +19,9 @@ bb0:
19 [2] Const mutate b$7_@1 = 2
20 [3] Const mutate x$8_@2 = Object { a: read a$6_@0, b: read b$7_@1 }
21 [4] Return freeze x$8_@2
22 +scope2 [3:4]:
23 + - read a$6_@0
24 + - read b$7_@1
25 ```
26
27 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-property-call.expect.md
+2
@@ -20,6 +20,8 @@ bb0:
20 [3] Const mutate $6_@1[2:5] = Array []
21 [4] Call mutate y$5_@1.x.push(mutate $6_@1)
22 [5] Return freeze y$5_@1
23 +scope1 [2:5]:
24 + - read x$4_@0
25 ```
26
27 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-property.expect.md
+2
@@ -19,6 +19,8 @@ bb0:
19 [2] Const mutate y$4_@1[2:4] = Object { }
20 [3] Reassign mutate y$4_@1.x[2:4] = read x$3_@0
21 [4] Return freeze y$4_@1
22 +scope1 [2:4]:
23 + - read x$3_@0
24 ```
25
26 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md
+2
@@ -29,6 +29,8 @@ bb1:
29 predecessor blocks: bb2 bb0
30 x$8_@0[1:7]: phi(bb2: x$7_@0, bb0: x$4_@0)
31 [7] Return read x$8_@0
32 +scope2 [3:4]:
33 + - read $5_@1
34 ```
35
36 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-shadowing.expect.md
+2
@@ -22,6 +22,7 @@ function Foo(cond) {
22 ```
23 bb0:
24 [1] Return
25 +
26 ```
27
28 ### CFG
@@ -63,6 +64,7 @@ bb1:
64 str$10_@0[1:8]: phi(bb2: str$6_@0, bb3: str$8_@0)
65 [8] Call mutate log$4_@2(read str$10_@0)
66 [9] Return
67 +
68 ```
69
70 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md
+5
@@ -37,6 +37,11 @@ bb1:
37 y$10_@3[5:10]: phi(bb2: y$8_@3, bb3: y$9_@3)
38 [9] Let mutate x$11_@3[5:10] = read y$10_@3
39 [10] Return
40 +scope2 [3:4]:
41 + - read y$5_@0
42 + - read $6_@1
43 +scope3 [5:10]:
44 + - read $7_@2
45 ```
46
47 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-simple.expect.md
+1
@@ -16,6 +16,7 @@ bb0:
16 [1] Let mutate x$3_@0 = 1
17 [2] Let mutate y$4_@1 = 2
18 [3] Return
19 +
20 ```
21
22 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md
+3
@@ -27,6 +27,9 @@ bb2:
27 bb1:
28 predecessor blocks: bb2 bb0
29 [6] Return
30 +scope2 [4:5]:
31 + - read x$4_@0
32 + - read y$5_@1
33 ```
34
35 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md
+13
@@ -57,6 +57,19 @@ bb1:
57 x$21_@6[8:17]: phi(bb5: x$16_@6, bb3: x$18_@6, bb2: x$20_@6)
58 [16] Let mutate y$22_@6[8:17] = read x$21_@6
59 [17] Return
60 +scope2 [3:4]:
61 + - read x$10_@0
62 + - read $11_@1
63 +scope4 [5:6]:
64 + - read x$10_@0
65 + - read $13_@3
66 +scope6 [8:17]:
67 + - read x$10_@0
68 + - read x$10_@0
69 +scope7 [10:11]:
70 + - read x$10_@0
71 +scope5 [7:8]:
72 + - read x$10_@0
73 ```
74
75 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md
+2
@@ -28,6 +28,8 @@ bb1:
28 predecessor blocks: bb2 bb0
29 x$8_@0[1:7]: phi(bb2: x$7_@0, bb0: x$4_@0)
30 [7] Throw read x$8_@0
31 +scope2 [3:4]:
32 + - read $5_@1
33 ```
34
35 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-while-no-reassign.expect.md
+5
@@ -32,6 +32,11 @@ bb3:
32 bb2:
33 predecessor blocks: bb1
34 [9] Return read x$5_@0
35 +scope2 [4:9]:
36 + - read x$5_@0
37 + - read $6_@1
38 +scope3 [6:7]:
39 + - read x$5_@0
40 ```
41
42 ### CFG
compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md
+2
@@ -33,6 +33,8 @@ bb3:
33 bb2:
34 predecessor blocks: bb1
35 [9] Return read x$7_@0
36 +scope2 [4:9]:
37 + - read $6_@1
38 ```
39
40 ### CFG
compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md
+10
@@ -58,6 +58,16 @@ bb1:
58 [13] Call read y$20_@0.push(read props$9.p4)
59 [14] Const mutate $22_@5 = JSX <read Component$0 data={freeze y$20_@0} >{read child$19_@4}</read Component$0>
60 [15] Return read $22_@5
61 +scope0 [1:12]:
62 + - read props$9.p2
63 + - read props$9.p0
64 +scope4 [12:13]:
65 + - read Component$0
66 + - freeze x$10_@0
67 + - read props$9.p4
68 +scope5 [14:15]:
69 + - read Component$0
70 + - read child$19_@4
71 ```
72
73 ### CFG
compiler/forget/src/__tests__/fixtures/hir/switch-with-fallthrough.expect.md
+1
@@ -78,6 +78,7 @@ bb2:
78 bb1:
79 predecessor blocks: bb9 bb0 bb5 bb2
80 [21] Return
81 +
82 ```
83
84 ### CFG
compiler/forget/src/__tests__/fixtures/hir/switch.expect.md
+11
@@ -53,6 +53,17 @@ bb1:
53 [13] Call read y$20_@0.push(read props$8.p4)
54 [14] Const mutate $23_@5 = JSX <read Component$0 data={read y$20_@0} >{read child$19_@4}</read Component$0>
55 [15] Return read $23_@5
56 +scope0 [1:12]:
57 + - read props$8.p2
58 + - read props$8.p3
59 + - read props$8.p0
60 +scope4 [12:13]:
61 + - read Component$0
62 + - freeze x$9_@0
63 + - read props$8.p4
64 +scope5 [14:15]:
65 + - read Component$0
66 + - read child$19_@4
67 ```
68
69 ### CFG
compiler/forget/src/__tests__/fixtures/hir/while-break.expect.md
+1
@@ -22,6 +22,7 @@ bb1:
22 bb2:
23 predecessor blocks: bb1
24 [3] Return read b$4
25 +
26 ```
27
28 ### CFG
compiler/forget/src/__tests__/fixtures/hir/while-conditional-continue.expect.md
+1
@@ -37,6 +37,7 @@ bb2:
37 predecessor blocks: bb1
38 [7] Call read d$8()
39 [8] Return
40 +
41 ```
42
43 ### CFG
compiler/forget/src/__tests__/hir-test.ts
+4 -2
@@ -15,7 +15,7 @@ import invariant from "invariant";
15 import path from "path";
16 import prettier from "prettier";
17 import run from "../HIR/Pipeline";
18 -import printHIR from "../HIR/PrintHIR";
18 +import { printFunction } from "../HIR/PrintHIR";
19 import visualizeHIRMermaid from "../HIR/VisualizeHIRMermaid";
20 import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures";
21
@@ -62,12 +62,13 @@ describe("React Forget (HIR version)", () => {
62 inferMutableRanges: true,
63 inferReactiveScopeVariables: true,
64 inferReactiveScopes: true,
65 + inferReactiveScopeDependencies: true,
66 leaveSSA: false,
67 codegen: false,
68 });
69
70 // Print the HIR before leaving SSA.
70 - const textHIR = printHIR(ir.body);
71 + const textHIR = printFunction(ir);
72 const visualization = visualizeHIRMermaid(ir);
73
74 const { ast } = run(nodePath, {
@@ -76,6 +77,7 @@ describe("React Forget (HIR version)", () => {
77 inferMutableRanges: true,
78 inferReactiveScopeVariables: true,
79 inferReactiveScopes: true,
80 + inferReactiveScopeDependencies: true,
81 leaveSSA: true,
82 codegen: true,
83 });
compiler/forget/src/index.ts
+8 -6
@@ -21,18 +21,19 @@ export * from "./Diagnostic";
21 export * from "./Logger";
22 export { NoUseBeforeDefineRule } from "./Validation";
23
24 +import { parse } from "@babel/parser";
25 +import traverse, { NodePath } from "@babel/traverse";
26 +import * as t from "@babel/types";
27 import { lower } from "./HIR/BuildHIR";
28 +import codegen from "./HIR/Codegen";
29 import { eliminateRedundantPhi } from "./HIR/EliminateRedundantPhi";
30 import enterSSA from "./HIR/EnterSSA";
31 +import { Environment } from "./HIR/HIRBuilder";
32 import { inferMutableRanges } from "./HIR/InferMutableLifetimes";
33 +import { inferReactiveScopeDependencies } from "./HIR/InferReactiveScopeDependencies";
34 import inferReferenceEffects from "./HIR/InferReferenceEffects";
29 -import printHIR from "./HIR/PrintHIR";
30 -import { Environment } from "./HIR/HIRBuilder";
35 import leaveSSA from "./HIR/LeaveSSA";
32 -import traverse, { NodePath } from "@babel/traverse";
33 -import * as t from "@babel/types";
34 -import { parse } from "@babel/parser";
35 -import codegen from "./HIR/Codegen";
36 +import printHIR from "./HIR/PrintHIR";
37
38 function parseFunctions(
39 source: string
@@ -62,6 +63,7 @@ export const HIR = {
63 enterSSA,
64 inferMutableRanges,
65 inferReferenceEffects,
66 + inferReactiveScopeDependencies,
67 printHIR,
68 Environment,
69 leaveSSA,