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