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

Visit terminal ids before terminal branches

This is a follow-up to merging ranges. I realized that we need to mark terminal ids as visited _before_ processing the branches of that terminal (whereas before we were marking terminal ids only _after_ processing the branches). That exposed another bug where interleaving could fail to be detected (with an error, thankfully) if one of the branches had completed already. Our small test suite is already really good!

Joe Savona committed Nov 22, 2022 at 12:10 UTC a0054a18371a5aa1a69a1132f420d69f3c373957
10 files changed +158 -207
compiler/forget/src/HIR/Codegen.ts
+2
@@ -14,6 +14,7 @@ import {
14 Identifier,
15 IdentifierId,
16 Instruction,
17 + InstructionId,
18 InstructionKind,
19 InstructionValue,
20 LValue,
@@ -150,6 +151,7 @@ class CodegenVisitor
151 }
152 }
153 }
154 + visitTerminalId(id: InstructionId): void {}
155 visitImplicitTerminal(): t.Statement | null {
156 return null;
157 }
compiler/forget/src/HIR/HIRTreeVisitor.ts
+29 -30
@@ -67,24 +67,24 @@ class Driver<TBlock, TValue, TItem, TCase> {
67 case "return": {
68 const value =
69 terminal.value != null ? this.visitPlace(terminal.value) : null;
70 + this.visitor.visitTerminalId(terminal.id);
71 this.visitor.appendBlock(
72 blockValue,
73 this.visitor.visitTerminal({
74 kind: "return",
75 value,
75 - id: terminal.id,
76 })
77 );
78 break;
79 }
80 case "throw": {
81 const value = this.visitPlace(terminal.value);
82 + this.visitor.visitTerminalId(terminal.id);
83 this.visitor.appendBlock(
84 blockValue,
85 this.visitor.visitTerminal({
86 kind: "throw",
87 value,
87 - id: terminal.id,
88 })
89 );
90 break;
@@ -106,9 +106,10 @@ class Driver<TBlock, TValue, TItem, TCase> {
106 scheduleIds.push(scheduleId);
107 }
108
109 + this.visitor.visitTerminalId(terminal.id);
110 let consequent: TItem | null = null;
111 if (this.cx.isScheduled(terminal.consequent)) {
111 - consequent = this.visitBreak(terminal.consequent, null);
112 + consequent = this.visitBreak(terminal.consequent);
113 } else {
114 consequent = this.traverseBlock(
115 this.cx.ir.blocks.get(terminal.consequent)!
@@ -118,7 +119,7 @@ class Driver<TBlock, TValue, TItem, TCase> {
119 let alternate: TItem | null = null;
120 if (alternateId !== null) {
121 if (this.cx.isScheduled(alternateId)) {
121 - alternate = this.visitBreak(alternateId, null);
122 + alternate = this.visitBreak(alternateId);
123 } else {
124 alternate = this.traverseBlock(this.cx.ir.blocks.get(alternateId)!);
125 }
@@ -133,7 +134,6 @@ class Driver<TBlock, TValue, TItem, TCase> {
134 test,
135 consequent: consequent ?? this.emptyBlock(),
136 alternate: alternate,
136 - id: terminal.id,
137 }),
138 `bb${fallthroughId}` //
139 );
@@ -146,7 +146,6 @@ class Driver<TBlock, TValue, TItem, TCase> {
146 test,
147 consequent: consequent ?? this.emptyBlock(),
148 alternate: alternate,
149 - id: terminal.id,
149 })
150 );
151 }
@@ -164,6 +163,7 @@ class Driver<TBlock, TValue, TItem, TCase> {
163 scheduleIds.push(scheduleId);
164 }
165
166 + this.visitor.visitTerminalId(terminal.id);
167 const cases: Array<TCase> = [];
168 [...terminal.cases].reverse().forEach((case_, index) => {
169 const test = case_.test !== null ? this.visitPlace(case_.test) : null;
@@ -174,7 +174,7 @@ class Driver<TBlock, TValue, TItem, TCase> {
174 // that are already scheduled. emit as follows:
175 // - if the block is for another case branch, don't emit a break and fall-through
176 // - else, emit an explicit break.
177 - const break_ = this.visitBreak(case_.block, null);
177 + const break_ = this.visitBreak(case_.block);
178 if (
179 index === 0 &&
180 break_ === null &&
@@ -210,7 +210,6 @@ class Driver<TBlock, TValue, TItem, TCase> {
210 kind: "switch",
211 test,
212 cases,
213 - id: terminal.id,
213 }),
214 `bb${fallthroughId}`
215 );
@@ -222,7 +221,6 @@ class Driver<TBlock, TValue, TItem, TCase> {
221 kind: "switch",
222 test,
223 cases,
225 - id: terminal.id,
224 })
225 );
226 }
@@ -261,11 +259,12 @@ class Driver<TBlock, TValue, TItem, TCase> {
259 );
260 scheduleIds.push(scheduleId);
261
262 + this.visitor.visitTerminalId(terminal.id);
263 let loopBody: TItem;
264 if (loopId) {
265 loopBody = this.traverseBlock(this.cx.ir.blocks.get(loopId)!);
266 } else {
268 - const break_ = this.visitBreak(terminal.loop, terminal.id);
267 + const break_ = this.visitBreak(terminal.loop);
268 invariant(
269 break_ !== null,
270 "If loop body is already scheduled it must be a break"
@@ -284,7 +283,6 @@ class Driver<TBlock, TValue, TItem, TCase> {
283 loc: terminal.loc,
284 test: testValue,
285 loop: loopBody,
287 - id: terminal.id,
286 }),
287 `bb${fallthroughId}`
288 );
@@ -297,23 +295,23 @@ class Driver<TBlock, TValue, TItem, TCase> {
295 loc: terminal.loc,
296 test: testValue,
297 loop: loopBody,
300 - id: terminal.id,
298 })
299 );
300 }
301 break;
302 }
303 case "goto": {
304 + this.visitor.visitTerminalId(terminal.id);
305 switch (terminal.variant) {
306 case GotoVariant.Break: {
309 - const break_ = this.visitBreak(terminal.block, terminal.id);
307 + const break_ = this.visitBreak(terminal.block);
308 if (break_ !== null) {
309 this.visitor.appendBlock(blockValue, break_);
310 }
311 break;
312 }
313 case GotoVariant.Continue: {
316 - const continue_ = this.visitContinue(terminal.block, terminal.id);
314 + const continue_ = this.visitContinue(terminal.block);
315 if (continue_ !== null) {
316 this.visitor.appendBlock(blockValue, continue_);
317 }
@@ -339,7 +337,7 @@ class Driver<TBlock, TValue, TItem, TCase> {
337 return this.visitor.leaveBlock(block);
338 }
339
342 - visitBreak(block: BlockId, id: InstructionId | null): TItem | null {
340 + visitBreak(block: BlockId): TItem | null {
341 const target = this.cx.getBreakTarget(block);
342 if (target === null) {
343 // TODO: we should always have a target
@@ -347,22 +345,21 @@ class Driver<TBlock, TValue, TItem, TCase> {
345 }
346 switch (target.type) {
347 case "implicit": {
350 - return this.visitor.visitImplicitTerminal(id);
348 + return this.visitor.visitImplicitTerminal();
349 }
350 case "unlabeled": {
353 - return this.visitor.visitTerminal({ kind: "break", label: null, id });
351 + return this.visitor.visitTerminal({ kind: "break", label: null });
352 }
353 case "labeled": {
354 return this.visitor.visitTerminal({
355 kind: "break",
356 label: `bb${target.block}`,
359 - id,
357 });
358 }
359 }
360 }
361
365 - visitContinue(block: BlockId, id: InstructionId): TItem | null {
362 + visitContinue(block: BlockId): TItem | null {
363 const target = this.cx.getContinueTarget(block);
364 invariant(
365 target !== null,
@@ -373,18 +370,16 @@ class Driver<TBlock, TValue, TItem, TCase> {
370 return this.visitor.visitTerminal({
371 kind: "continue",
372 label: `bb${target.block}`,
376 - id,
373 });
374 }
375 case "unlabeled": {
376 return this.visitor.visitTerminal({
377 kind: "continue",
378 label: null,
383 - id,
379 });
380 }
381 case "implicit": {
387 - return this.visitor.visitImplicitTerminal(id);
382 + return this.visitor.visitImplicitTerminal();
383 }
384 default: {
385 assertExhaustive(
@@ -655,12 +650,18 @@ export interface Visitor<TBlock, TValue, TItem, TCase> {
650 */
651 visitInstruction(instruction: Instruction, value: TValue): TItem;
652
653 + /**
654 + * Called when a terminal is reached, before processing any of its
655 + * possible branches.
656 + */
657 + visitTerminalId(id: InstructionId): void;
658 +
659 /**
660 * Converts a break/continue that is implicit — that does not strictly
661 * have to be emitted — to the visitor's representation. The visitor
662 * can choose to return null if this does not need to be represented.
663 */
663 - visitImplicitTerminal(id: InstructionId | null): TItem | null;
664 + visitImplicitTerminal(): TItem | null;
665
666 /**
667 * Converts a terminal into the visitor's own representation of a block
@@ -691,22 +692,20 @@ export interface Visitor<TBlock, TValue, TItem, TCase> {
692 }
693
694 export type BlockTerminal<TBlock, TValue, TItem, TCase> =
694 - | { kind: "return"; value: TValue | null; id: InstructionId }
695 - | { kind: "throw"; value: TValue; id: InstructionId }
695 + | { kind: "return"; value: TValue | null }
696 + | { kind: "throw"; value: TValue }
697 | {
698 kind: "if";
699 test: TValue;
700 consequent: TItem;
701 alternate: TItem | null;
701 - id: InstructionId;
702 }
703 - | { kind: "switch"; test: TValue; cases: Array<TCase>; id: InstructionId }
703 + | { kind: "switch"; test: TValue; cases: Array<TCase> }
704 | {
705 kind: "while";
706 loc: SourceLocation;
707 test: TValue;
708 loop: TItem;
709 - id: InstructionId;
709 }
711 - | { kind: "break"; label: string | null; id: InstructionId | null }
712 - | { kind: "continue"; label: string | null; id: InstructionId };
710 + | { kind: "break"; label: string | null }
711 + | { kind: "continue"; label: string | null };
compiler/forget/src/HIR/InferReactiveScopes.ts
+38 -31
@@ -145,9 +145,14 @@ function mergeScopesWithIdenticalRanges(fn: HIRFunction) {
145
146 class BlockScope {
147 seen: Set<ScopeId> = new Set();
148 - scopes: Array<PendingReactiveScope> = [];
148 + scopes: Array<ShadowableReactiveScope> = [];
149 }
150
151 +type ShadowableReactiveScope = {
152 + scope: ReactiveScope;
153 + shadowedBy: ReactiveScope | null;
154 +};
155 +
156 // maybe we just merge cases of interleaving when at the same block scope - eh fine?
157 // then we can look at scopes that start at the same block scope and overlap
158 class MergeOverlappingReactiveScopesVisitor
@@ -167,7 +172,7 @@ class MergeOverlappingReactiveScopesVisitor
172 if (!this.seenScopes.has(scope.id)) {
173 this.seenScopes.add(scope.id);
174 currentBlock.seen.add(scope.id);
170 - currentBlock.scopes.push({ active: true, scope });
175 + currentBlock.scopes.push({ shadowedBy: null, scope });
176 return;
177 }
178 // Scope has already been seen, find it in the current block or a parent
@@ -183,8 +188,23 @@ class MergeOverlappingReactiveScopesVisitor
188 // add an entry to this.seenScopes but which are then removed when their blocks exit.
189 // this is also wrong for codegen, different versions of an identifier could be cached
190 // differently and so a reassigned version of a variable needs a separate declaration.
191 + // console.log(`scope ${scope.id} not found`);
192 +
193 + // for (let i = this.scopes.length - 1; i > index; i--) {
194 + // const s = this.scopes[i];
195 + // console.log(
196 + // JSON.stringify(
197 + // {
198 + // seen: Array.from(s.seen),
199 + // scopes: s.scopes,
200 + // },
201 + // null,
202 + // 2
203 + // )
204 + // );
205 + // }
206 currentBlock.seen.add(scope.id);
187 - currentBlock.scopes.push({ active: true, scope });
207 + currentBlock.scopes.push({ shadowedBy: null, scope });
208 return;
209 }
210 nextBlock = this.scopes[index]!;
@@ -192,26 +212,21 @@ class MergeOverlappingReactiveScopesVisitor
212
213 // Handle interleaving within a given block scope
214 let found = false;
195 - let active = null;
215 for (let i = 0; i < nextBlock.scopes.length; i++) {
216 const current = nextBlock.scopes[i]!;
198 - if (current.active && !found) {
199 - active = current;
200 - }
217 if (current.scope.id === scope.id) {
218 found = true;
203 - if (!current.active) {
204 - invariant(active !== null, "Expected an active scope");
205 - this.joinedScopes.union([active.scope, current.scope]);
219 + if (current.shadowedBy !== null) {
220 + this.joinedScopes.union([current.shadowedBy, current.scope]);
221 }
207 - } else if (found && current.active) {
222 + } else if (found && current.shadowedBy === null) {
223 // `scope` is shadowing `current`, but we don't know they are interleaved yet
209 - current.active = false;
224 + current.shadowedBy = scope;
225 }
226 }
227 if (!currentBlock.seen.has(scope.id)) {
228 currentBlock.seen.add(scope.id);
214 - currentBlock.scopes.push({ active: true, scope });
229 + currentBlock.scopes.push({ shadowedBy: null, scope });
230 }
231 }
232
@@ -219,6 +234,7 @@ class MergeOverlappingReactiveScopesVisitor
234 * Prune any scopes that are out of range
235 */
236 visitId(id: InstructionId) {
237 + // console.log(`visitId: ${id}`);
238 const currentBlock = this.scopes[this.scopes.length - 1]!;
239 retainWhere(currentBlock.scopes, (pending) => {
240 if (pending.scope.range.end > id) {
@@ -241,16 +257,11 @@ class MergeOverlappingReactiveScopesVisitor
257 }
258 this.visitId(instruction.id);
259 }
244 - visitImplicitTerminal(id: InstructionId | null): void | null {
245 - if (id !== null) {
246 - this.visitId(id);
247 - }
248 - }
249 - visitTerminal(terminal: BlockTerminal<void, void, void, void>): void {
250 - if (terminal.id !== null) {
251 - this.visitId(terminal.id);
252 - }
260 + visitTerminalId(id: InstructionId): void {
261 + this.visitId(id);
262 }
263 + visitImplicitTerminal(): void | null {}
264 + visitTerminal(terminal: BlockTerminal<void, void, void, void>): void {}
265 visitCase(test: void | null, block: void): void {}
266 appendBlock(block: void, item: void, label?: string | undefined): void {}
267 leaveBlock(block: void): void {
@@ -340,17 +351,13 @@ class AlignReactiveScopesToBlockScopeRangeVisitor
351 this.visitId(instruction.id);
352 }
353
343 - visitTerminal(terminal: BlockTerminal<void, void, void, void>): void {
344 - if (terminal.id !== null) {
345 - this.visitId(terminal.id);
346 - }
354 + visitTerminalId(id: InstructionId): void {
355 + this.visitId(id);
356 }
357
349 - visitImplicitTerminal(id: InstructionId | null): void | null {
350 - if (id !== null) {
351 - this.visitId(id);
352 - }
353 - }
358 + visitTerminal(terminal: BlockTerminal<void, void, void, void>): void {}
359 +
360 + visitImplicitTerminal(): void | null {}
361
362 // no-ops
363 visitValue(value: InstructionValue): void {}
compiler/forget/src/HIR/PrintHIRTree.ts
+7 -1
@@ -6,7 +6,12 @@
6 */
7
8 import { assertExhaustive } from "../Common/utils";
9 -import { HIRFunction, Instruction, InstructionValue } from "./HIR";
9 +import {
10 + HIRFunction,
11 + Instruction,
12 + InstructionId,
13 + InstructionValue,
14 +} from "./HIR";
15 import { BlockTerminal, Visitor, visitTree } from "./HIRTreeVisitor";
16 import { printLValue, printMixedHIR } from "./PrintHIR";
17
@@ -36,6 +41,7 @@ class PrintVisitor implements Visitor<Array<string>, string, string, string> {
41 return `[${instr.id}] ${value}`;
42 }
43 }
44 + visitTerminalId(id: InstructionId): void {}
45 visitImplicitTerminal(): string | null {
46 return null;
47 }
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md
-32
@@ -2,23 +2,6 @@
2 ## Input
3
4 ```javascript
5 -// function Component$forget(props) {
6 -// scope_a: {
7 -// const a = [];
8 -// if (b) {
9 -// a.push(props.p0);
10 -// }
11 -// }
12 -// scope_b: {
13 -// const b = [];
14 -// if (props.p1) {
15 -// b.push(props.p2);
16 -// }
17 -// }
18 -// scope_return: {
19 -// return <Foo a={a} b={b} />;
20 -// }
21 -// }
5 function Component(props) {
6 const a = [];
7 const b = [];
@@ -31,21 +14,6 @@ function Component(props) {
14 return <Foo a={a} b={b} />;
15 }
16
34 -// function Component$forget(props) {
35 -// scope_a_b: {
36 -// const a = [];
37 -// const b = [];
38 -// if (mayMutate(b)) {
39 -// a.push(props.p0);
40 -// }
41 -// if (props.p1) {
42 -// b.push(props.p2);
43 -// }
44 -// }
45 -// scope_return: {
46 -// return <Foo a={a} b={b} />;
47 -// }
48 -// }
17 function Component(props) {
18 const a = [];
19 const b = [];
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.js
-32
@@ -1,20 +1,3 @@
1 -// function Component$forget(props) {
2 -// scope_a: {
3 -// const a = [];
4 -// if (b) {
5 -// a.push(props.p0);
6 -// }
7 -// }
8 -// scope_b: {
9 -// const b = [];
10 -// if (props.p1) {
11 -// b.push(props.p2);
12 -// }
13 -// }
14 -// scope_return: {
15 -// return <Foo a={a} b={b} />;
16 -// }
17 -// }
1 function Component(props) {
2 const a = [];
3 const b = [];
@@ -27,21 +10,6 @@ function Component(props) {
10 return <Foo a={a} b={b} />;
11 }
12
30 -// function Component$forget(props) {
31 -// scope_a_b: {
32 -// const a = [];
33 -// const b = [];
34 -// if (mayMutate(b)) {
35 -// a.push(props.p0);
36 -// }
37 -// if (props.p1) {
38 -// b.push(props.p2);
39 -// }
40 -// }
41 -// scope_return: {
42 -// return <Foo a={a} b={b} />;
43 -// }
44 -// }
13 function Component(props) {
14 const a = [];
15 const b = [];
compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md
+37 -36
@@ -34,38 +34,39 @@ function foo(a, b, c) {
34
35 ```
36 bb0:
37 - [1] Let mutate x$4_@0[1:5] = Array []
38 - [2] If (read a$1) then:bb2 else:bb1
37 + [1] Let mutate x$16_@0[1:5] = Array []
38 + [2] If (read a$13) then:bb2 else:bb1
39 bb2:
40 predecessor blocks: bb0
41 - [3] Call mutate x$4_@0.push(read a$1)
41 + [3] Call mutate x$16_@0.push(read a$13)
42 [4] Goto bb1
43 bb1:
44 predecessor blocks: bb2 bb0
45 - [5] Const mutate $6_@1 = "div"
46 - [6] Let mutate y$5_@2 = JSX <read $6_@1>{freeze x$4_@0}</read $6_@1>
47 - [7] Const mutate $7_@4[7:14] = 0
48 - [8] Switch (read b$2)
49 - Case read $7_@4: bb5
45 + [5] Const mutate $17_@1 = "div"
46 + [6] Let mutate y$19_@2 = JSX <read $17_@1>{freeze x$16_@0}</read $17_@1>
47 + [7] Const mutate $20_@3 = 0
48 + [8] Switch (read b$14)
49 + Case read $20_@3: bb5
50 Default: bb4
51 bb5:
52 predecessor blocks: bb1
53 - [9] Reassign mutate x$4_@4[7:14] = Array []
54 - [10] Call mutate x$4_@4.push(read b$2)
53 + [9] Reassign mutate x$22_@4[9:14] = Array []
54 + [10] Call mutate x$22_@4.push(read b$14)
55 [11] Goto bb3
56 bb4:
57 predecessor blocks: bb1
58 - [12] Reassign mutate x$4_@4[7:14] = Array []
59 - [13] Call mutate x$4_@4.push(read c$3)
58 + [12] Reassign mutate x$23_@4[9:14] = Array []
59 + [13] Call mutate x$23_@4.push(read c$15)
60 [14] Goto bb3
61 bb3:
62 predecessor blocks: bb5 bb4
63 - [15] Const mutate $8_@5 = "div"
64 - [16] Const mutate $9_@6 = "\n "
65 - [17] Const mutate $10_@7 = "\n "
66 - [18] Const mutate $11_@8 = "\n "
67 - [19] Const mutate $12_@9 = JSX <read $8_@5>{read $9_@6}{read y$5_@2}{read $10_@7}{freeze x$4_@4}{read $11_@8}</read $8_@5>
68 - [20] Return read $12_@9
63 + x$30_@4[9:14]: phi(bb5: x$22_@4, bb4: x$23_@4)
64 + [15] Const mutate $25_@5 = "div"
65 + [16] Const mutate $26_@6 = "\n "
66 + [17] Const mutate $27_@7 = "\n "
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 ```
71
72 ### CFG
@@ -75,54 +76,54 @@ flowchart TB
76 %% Basic Blocks
77 subgraph bb0
78 bb0_instrs["
78 - [1] Let mutate x$4_@0[1:5] = Array []
79 + [1] Let mutate x$16_@0[1:5] = Array []
80 "]
80 - bb0_instrs --> bb0_terminal(["If (read a$1)"])
81 + bb0_instrs --> bb0_terminal(["If (read a$13)"])
82 end
83 subgraph bb2
84 bb2_instrs["
84 - [3] Call mutate x$4_@0.push(read a$1)
85 + [3] Call mutate x$16_@0.push(read a$13)
86 "]
87 bb2_instrs --> bb2_terminal(["Goto"])
88 end
89 subgraph bb1
90 bb1_instrs["
90 - [5] Const mutate $6_@1 = 'div'
91 - [6] Let mutate y$5_@2 = JSX <read $6_@1>{freeze x$4_@0}</read $6_@1>
92 - [7] Const mutate $7_@4[7:14] = 0
91 + [5] Const mutate $17_@1 = 'div'
92 + [6] Let mutate y$19_@2 = JSX <read $17_@1>{freeze x$16_@0}</read $17_@1>
93 + [7] Const mutate $20_@3 = 0
94 "]
94 - bb1_instrs --> bb1_terminal(["Switch (read b$2)"])
95 + bb1_instrs --> bb1_terminal(["Switch (read b$14)"])
96 end
97 subgraph bb5
98 bb5_instrs["
98 - [9] Reassign mutate x$4_@4[7:14] = Array []
99 - [10] Call mutate x$4_@4.push(read b$2)
99 + [9] Reassign mutate x$22_@4[9:14] = Array []
100 + [10] Call mutate x$22_@4.push(read b$14)
101 "]
102 bb5_instrs --> bb5_terminal(["Goto"])
103 end
104 subgraph bb4
105 bb4_instrs["
105 - [12] Reassign mutate x$4_@4[7:14] = Array []
106 - [13] Call mutate x$4_@4.push(read c$3)
106 + [12] Reassign mutate x$23_@4[9:14] = Array []
107 + [13] Call mutate x$23_@4.push(read c$15)
108 "]
109 bb4_instrs --> bb4_terminal(["Goto"])
110 end
111 subgraph bb3
112 bb3_instrs["
112 - [15] Const mutate $8_@5 = 'div'
113 - [16] Const mutate $9_@6 = '\n '
114 - [17] Const mutate $10_@7 = '\n '
115 - [18] Const mutate $11_@8 = '\n '
116 - [19] Const mutate $12_@9 = JSX <read $8_@5>{read $9_@6}{read y$5_@2}{read $10_@7}{freeze x$4_@4}{read $11_@8}</read $8_@5>
113 + [15] Const mutate $25_@5 = 'div'
114 + [16] Const mutate $26_@6 = '\n '
115 + [17] Const mutate $27_@7 = '\n '
116 + [18] Const mutate $28_@8 = '\n '
117 + [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>
118 "]
118 - bb3_instrs --> bb3_terminal(["Return read $12_@9"])
119 + bb3_instrs --> bb3_terminal(["Return read $31_@9"])
120 end
121
122 %% Jumps
123 bb0_terminal -- "then" --> bb2
124 bb0_terminal -- "else" --> bb1
125 bb2_terminal --> bb1
125 - bb1_terminal -- "read $7_@4" --> bb5
126 + bb1_terminal -- "read $20_@3" --> bb5
127 bb1_terminal -- "default" --> bb4
128 bb1_terminal -- "fallthrough" --> bb3
129 bb5_terminal --> bb3
compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md
+17 -17
@@ -36,44 +36,44 @@ function Component(props) {
36
37 ```
38 bb0:
39 - [1] Let mutate x$7_@4[1:17] = undefined
39 + [1] Let mutate x$7_@0 = undefined
40 [2] If (read props$6.cond) then:bb2 else:bb10
41 bb2:
42 predecessor blocks: bb0
43 [3] Const mutate $8_@1 = 2
44 [4] Const mutate $9_@2 = 1
45 - [5] Const mutate $10_@4[1:17] = 0
45 + [5] Const mutate $10_@3 = 0
46 [6] Switch (read props$6.test)
47 - Case read $10_@4: bb8
47 + Case read $10_@3: bb8
48 Case read $9_@2: bb6
49 Case read $8_@1: bb4
50 Default: bb4
51 bb8:
52 predecessor blocks: bb2
53 - [7] Reassign mutate x$11_@4[1:17] = read props$6.v0
53 + [7] Reassign mutate x$11_@4[7:17] = read props$6.v0
54 [8] Goto bb1
55 bb6:
56 predecessor blocks: bb2
57 - [9] Reassign mutate x$12_@4[1:17] = read props$6.v1
57 + [9] Reassign mutate x$12_@4[7:17] = read props$6.v1
58 [10] Goto bb1
59 bb4:
60 predecessor blocks: bb2
61 - [11] Reassign mutate x$13_@4[1:17] = read props$6.v2
61 + [11] Reassign mutate x$13_@4[7:17] = read props$6.v2
62 [12] Goto bb1
63 bb10:
64 predecessor blocks: bb0
65 [13] If (read props$6.cond2) then:bb12 else:bb13
66 bb12:
67 predecessor blocks: bb10
68 - [14] Reassign mutate x$14_@4[1:17] = read props$6.b
68 + [14] Reassign mutate x$14_@4[7:17] = read props$6.b
69 [15] Goto bb1
70 bb13:
71 predecessor blocks: bb10
72 - [16] Reassign mutate x$15_@4[1:17] = read props$6.c
72 + [16] Reassign mutate x$15_@4[7:17] = read props$6.c
73 [17] Goto bb1
74 bb1:
75 predecessor blocks: bb8 bb6 bb4 bb12 bb13
76 - x$16_@4[1:17]: phi(bb8: x$11_@4, bb6: x$12_@4, bb4: x$13_@4, bb12: x$14_@4, bb13: x$15_@4)
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 ```
@@ -85,7 +85,7 @@ flowchart TB
85 %% Basic Blocks
86 subgraph bb0
87 bb0_instrs["
88 - [1] Let mutate x$7_@4[1:17] = undefined
88 + [1] Let mutate x$7_@0 = undefined
89 "]
90 bb0_instrs --> bb0_terminal(["If (read props$6.cond)"])
91 end
@@ -93,25 +93,25 @@ flowchart TB
93 bb2_instrs["
94 [3] Const mutate $8_@1 = 2
95 [4] Const mutate $9_@2 = 1
96 - [5] Const mutate $10_@4[1:17] = 0
96 + [5] Const mutate $10_@3 = 0
97 "]
98 bb2_instrs --> bb2_terminal(["Switch (read props$6.test)"])
99 end
100 subgraph bb8
101 bb8_instrs["
102 - [7] Reassign mutate x$11_@4[1:17] = read props$6.v0
102 + [7] Reassign mutate x$11_@4[7:17] = read props$6.v0
103 "]
104 bb8_instrs --> bb8_terminal(["Goto"])
105 end
106 subgraph bb6
107 bb6_instrs["
108 - [9] Reassign mutate x$12_@4[1:17] = read props$6.v1
108 + [9] Reassign mutate x$12_@4[7:17] = read props$6.v1
109 "]
110 bb6_instrs --> bb6_terminal(["Goto"])
111 end
112 subgraph bb4
113 bb4_instrs["
114 - [11] Reassign mutate x$13_@4[1:17] = read props$6.v2
114 + [11] Reassign mutate x$13_@4[7:17] = read props$6.v2
115 "]
116 bb4_instrs --> bb4_terminal(["Goto"])
117 end
@@ -120,13 +120,13 @@ flowchart TB
120 end
121 subgraph bb12
122 bb12_instrs["
123 - [14] Reassign mutate x$14_@4[1:17] = read props$6.b
123 + [14] Reassign mutate x$14_@4[7:17] = read props$6.b
124 "]
125 bb12_instrs --> bb12_terminal(["Goto"])
126 end
127 subgraph bb13
128 bb13_instrs["
129 - [16] Reassign mutate x$15_@4[1:17] = read props$6.c
129 + [16] Reassign mutate x$15_@4[7:17] = read props$6.c
130 "]
131 bb13_instrs --> bb13_terminal(["Goto"])
132 end
@@ -141,7 +141,7 @@ flowchart TB
141 bb0_terminal -- "then" --> bb2
142 bb0_terminal -- "else" --> bb10
143 bb0_terminal -- "fallthrough" --> bb1
144 - bb2_terminal -- "read $10_@4" --> bb8
144 + bb2_terminal -- "read $10_@3" --> bb8
145 bb2_terminal -- "read $9_@2" --> bb6
146 bb2_terminal -- "read $8_@1" --> bb4
147 bb2_terminal -- "default" --> bb4
compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md
+11 -11
@@ -22,20 +22,20 @@ function foo() {
22 bb0:
23 [1] Let mutate y$5_@0 = 2
24 [2] Const mutate $6_@1 = 1
25 - [3] Const mutate $7_@3[3:10] = Binary read y$5_@0 > read $6_@1
26 - [4] If (read $7_@3) then:bb2 else:bb3
25 + [3] Const mutate $7_@2 = Binary read y$5_@0 > read $6_@1
26 + [4] If (read $7_@2) then:bb2 else:bb3
27 bb2:
28 predecessor blocks: bb0
29 - [5] Reassign mutate y$8_@3[3:10] = 1
29 + [5] Reassign mutate y$8_@3[5:10] = 1
30 [6] Goto bb1
31 bb3:
32 predecessor blocks: bb0
33 - [7] Reassign mutate y$9_@3[3:10] = 2
33 + [7] Reassign mutate y$9_@3[5:10] = 2
34 [8] Goto bb1
35 bb1:
36 predecessor blocks: bb2 bb3
37 - y$10_@3[3:10]: phi(bb2: y$8_@3, bb3: y$9_@3)
38 - [9] Let mutate x$11_@3[3:10] = read y$10_@3
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 ```
41
@@ -48,25 +48,25 @@ flowchart TB
48 bb0_instrs["
49 [1] Let mutate y$5_@0 = 2
50 [2] Const mutate $6_@1 = 1
51 - [3] Const mutate $7_@3[3:10] = Binary read y$5_@0 > read $6_@1
51 + [3] Const mutate $7_@2 = Binary read y$5_@0 > read $6_@1
52 "]
53 - bb0_instrs --> bb0_terminal(["If (read $7_@3)"])
53 + bb0_instrs --> bb0_terminal(["If (read $7_@2)"])
54 end
55 subgraph bb2
56 bb2_instrs["
57 - [5] Reassign mutate y$8_@3[3:10] = 1
57 + [5] Reassign mutate y$8_@3[5:10] = 1
58 "]
59 bb2_instrs --> bb2_terminal(["Goto"])
60 end
61 subgraph bb3
62 bb3_instrs["
63 - [7] Reassign mutate y$9_@3[3:10] = 2
63 + [7] Reassign mutate y$9_@3[5:10] = 2
64 "]
65 bb3_instrs --> bb3_terminal(["Goto"])
66 end
67 subgraph bb1
68 bb1_instrs["
69 - [9] Let mutate x$11_@3[3:10] = read y$10_@3
69 + [9] Let mutate x$11_@3[5:10] = read y$10_@3
70 "]
71 bb1_instrs --> bb1_terminal(["Return"])
72 end
compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md
+17 -17
@@ -32,30 +32,30 @@ bb0:
32 [2] Const mutate $11_@1 = 2
33 [3] Const mutate $12_@2 = Binary read x$10_@0 === read $11_@1
34 [4] Const mutate $13_@3 = 1
35 - [5] Const mutate $14_@6[5:17] = Binary read x$10_@0 === read $13_@3
35 + [5] Const mutate $14_@4 = Binary read x$10_@0 === read $13_@3
36 [6] Switch (read x$10_@0)
37 - Case read $14_@6: bb5
37 + Case read $14_@4: bb5
38 Case read $12_@2: bb3
39 Default: bb2
40 bb5:
41 predecessor blocks: bb0
42 - [7] Const mutate $15_@6[5:17] = 1
43 - [8] Reassign mutate x$16_@6[5:17] = Binary read x$10_@0 + read $15_@6
42 + [7] Const mutate $15_@6[7:17] = 1
43 + [8] Reassign mutate x$16_@6[7:17] = Binary read x$10_@0 + read $15_@6
44 [9] Goto bb1
45 bb3:
46 predecessor blocks: bb0
47 - [10] Const mutate $17_@6[5:17] = 2
48 - [11] Reassign mutate x$18_@6[5:17] = Binary read x$10_@0 + read $17_@6
47 + [10] Const mutate $17_@6[7:17] = 2
48 + [11] Reassign mutate x$18_@6[7:17] = Binary read x$10_@0 + read $17_@6
49 [12] Goto bb1
50 bb2:
51 predecessor blocks: bb0
52 [13] Const mutate $19_@8 = 3
53 - [14] Reassign mutate x$20_@6[5:17] = Binary read x$10_@0 + read $19_@8
53 + [14] Reassign mutate x$20_@6[7:17] = Binary read x$10_@0 + read $19_@8
54 [15] Goto bb1
55 bb1:
56 predecessor blocks: bb5 bb3 bb2
57 - x$21_@6[5:17]: phi(bb5: x$16_@6, bb3: x$18_@6, bb2: x$20_@6)
58 - [16] Let mutate y$22_@6[5:17] = read x$21_@6
57 + x$21_@6[7:17]: phi(bb5: x$16_@6, bb3: x$18_@6, bb2: x$20_@6)
58 + [16] Let mutate y$22_@6[7:17] = read x$21_@6
59 [17] Return
60 ```
61
@@ -70,40 +70,40 @@ flowchart TB
70 [2] Const mutate $11_@1 = 2
71 [3] Const mutate $12_@2 = Binary read x$10_@0 === read $11_@1
72 [4] Const mutate $13_@3 = 1
73 - [5] Const mutate $14_@6[5:17] = Binary read x$10_@0 === read $13_@3
73 + [5] Const mutate $14_@4 = Binary read x$10_@0 === read $13_@3
74 "]
75 bb0_instrs --> bb0_terminal(["Switch (read x$10_@0)"])
76 end
77 subgraph bb5
78 bb5_instrs["
79 - [7] Const mutate $15_@6[5:17] = 1
80 - [8] Reassign mutate x$16_@6[5:17] = Binary read x$10_@0 + read $15_@6
79 + [7] Const mutate $15_@6[7:17] = 1
80 + [8] Reassign mutate x$16_@6[7:17] = Binary read x$10_@0 + read $15_@6
81 "]
82 bb5_instrs --> bb5_terminal(["Goto"])
83 end
84 subgraph bb3
85 bb3_instrs["
86 - [10] Const mutate $17_@6[5:17] = 2
87 - [11] Reassign mutate x$18_@6[5:17] = Binary read x$10_@0 + read $17_@6
86 + [10] Const mutate $17_@6[7:17] = 2
87 + [11] Reassign mutate x$18_@6[7:17] = Binary read x$10_@0 + read $17_@6
88 "]
89 bb3_instrs --> bb3_terminal(["Goto"])
90 end
91 subgraph bb2
92 bb2_instrs["
93 [13] Const mutate $19_@8 = 3
94 - [14] Reassign mutate x$20_@6[5:17] = Binary read x$10_@0 + read $19_@8
94 + [14] Reassign mutate x$20_@6[7:17] = Binary read x$10_@0 + read $19_@8
95 "]
96 bb2_instrs --> bb2_terminal(["Goto"])
97 end
98 subgraph bb1
99 bb1_instrs["
100 - [16] Let mutate y$22_@6[5:17] = read x$21_@6
100 + [16] Let mutate y$22_@6[7:17] = read x$21_@6
101 "]
102 bb1_instrs --> bb1_terminal(["Return"])
103 end
104
105 %% Jumps
106 - bb0_terminal -- "read $14_@6" --> bb5
106 + bb0_terminal -- "read $14_@4" --> bb5
107 bb0_terminal -- "read $12_@2" --> bb3
108 bb0_terminal -- "default" --> bb2
109 bb0_terminal -- "fallthrough" --> bb1