Add SSA-ify pass
The algorithm is described in detail here: https://pp.info.uni-karlsruhe.de/uploads/publikationen/braun13cc.pdf Note that the SSA form generated is not minimal. A follow on RedundantPhiElimination pass is required to prune the graph.
Sathya Gunasekaran committed
Oct 18, 2022 at 18:45 UTC
379251c65f39039ace1d2e5a80bdb5938cf3427c
40 files changed
+1193
-3
compiler/forget/src/HIR/HIR.ts
+8
@@ -94,6 +94,8 @@ export type BasicBlock = {
94
id: BlockId;
95
instructions: Array<Instruction>;
96
terminal: Terminal;
97
+ preds: Set<BasicBlock>;
98
+ phis: Set<Phi>;
99
};
100
101
/**
@@ -162,6 +164,12 @@ export enum InstructionKind {
164
*/
165
export type InstructionValue = (InstructionData & { path: NodePath }) | Place;
166
167
+export type Phi = {
168
+ kind: "Phi";
169
+ lvalue: LValue;
170
+ operands: Map<BasicBlock, Place>;
171
+};
172
+
173
export type InstructionData =
174
| { kind: "Primitive"; value: number | boolean | string | null | undefined }
175
| { kind: "JSXText"; value: string }
compiler/forget/src/HIR/HIRBuilder.ts
+73
-3
@@ -114,6 +114,8 @@ export default class HIRBuilder {
114
id: blockId,
115
instructions,
116
terminal: { kind: "return", value: null },
117
+ preds: new Set(),
118
+ phis: new Set(),
119
});
120
// First reduce indirections and prune unreachable blocks
121
let reduced = shrink({
@@ -121,7 +123,9 @@ export default class HIRBuilder {
123
entry: this.#entry,
124
});
125
// then convert to reverse postorder
124
- return reversePostorderBlocks(reduced);
126
+ const blocks = reversePostorderBlocks(reduced);
127
+ markPredecessors(blocks);
128
+ return blocks;
129
}
130
131
/**
@@ -133,6 +137,8 @@ export default class HIRBuilder {
137
id: blockId,
138
instructions,
139
terminal,
140
+ preds: new Set(),
141
+ phis: new Set(),
142
});
143
const nextId = makeBlockId(this.#nextId++);
144
this.#current = newBlock(nextId);
@@ -148,6 +154,8 @@ export default class HIRBuilder {
154
id: blockId,
155
instructions,
156
terminal,
157
+ preds: new Set(),
158
+ phis: new Set(),
159
});
160
this.#current = continuation;
161
}
@@ -166,7 +174,13 @@ export default class HIRBuilder {
174
*/
175
complete(block: WipBlock, terminal: Terminal) {
176
const { id: blockId, instructions } = block;
169
- this.#completed.set(blockId, { id: blockId, instructions, terminal });
177
+ this.#completed.set(blockId, {
178
+ id: blockId,
179
+ instructions,
180
+ terminal,
181
+ preds: new Set(),
182
+ phis: new Set(),
183
+ });
184
}
185
186
/**
@@ -181,7 +195,13 @@ export default class HIRBuilder {
195
this.#current = newBlock(nextId);
196
const terminal = fn(nextId);
197
const { id: blockId, instructions } = this.#current;
184
- this.#completed.set(blockId, { id: blockId, instructions, terminal });
198
+ this.#completed.set(blockId, {
199
+ id: blockId,
200
+ instructions,
201
+ terminal,
202
+ preds: new Set(),
203
+ phis: new Set(),
204
+ });
205
this.#current = current;
206
return nextId;
207
}
@@ -336,6 +356,8 @@ function shrink(func: HIR): HIR {
356
id: blockId,
357
instructions,
358
terminal,
359
+ preds: new Set(),
360
+ phis: new Set(),
361
});
362
}
363
@@ -438,6 +460,54 @@ function reversePostorderBlocks(func: HIR): HIR {
460
};
461
}
462
463
+function markPredecessors(func: HIR) {
464
+ const visited: Set<BlockId> = new Set();
465
+ function visit(blockId: BlockId, prevBlock?: BasicBlock) {
466
+ const block = func.blocks.get(blockId)!;
467
+ if (prevBlock) {
468
+ block.preds.add(prevBlock);
469
+ }
470
+
471
+ if (visited.has(blockId)) {
472
+ return;
473
+ }
474
+ visited.add(blockId);
475
+
476
+ const { terminal } = block;
477
+
478
+ switch (terminal.kind) {
479
+ case "return":
480
+ case "throw": {
481
+ break;
482
+ }
483
+ case "goto": {
484
+ visit(terminal.block, block);
485
+ break;
486
+ }
487
+ case "if": {
488
+ const { consequent, alternate } = terminal;
489
+ visit(alternate, block);
490
+ visit(consequent, block);
491
+ break;
492
+ }
493
+ case "switch": {
494
+ const { cases } = terminal;
495
+ for (const case_ of [...cases]) {
496
+ visit(case_.block, block);
497
+ }
498
+ break;
499
+ }
500
+ default: {
501
+ assertExhaustive(
502
+ terminal,
503
+ `Unexpected terminal kind '${(terminal as any).kind}'`
504
+ );
505
+ }
506
+ }
507
+ }
508
+ visit(func.entry);
509
+}
510
+
511
/**
512
* If the given block is a simple indirection (empty terminated with a goto),
513
* returns the block being pointed to. Otherwise returns null
compiler/forget/src/HIR/PrintHIR.ts
+25
@@ -12,6 +12,7 @@ import {
12
InstructionKind,
13
InstructionValue,
14
LValue,
15
+ Phi,
16
Place,
17
Terminal,
18
} from "./HIR";
@@ -31,6 +32,16 @@ export default function printHIR(
32
};
33
for (const [blockId, block] of ir.blocks) {
34
output.push(`bb${blockId}:`);
35
+ if (block.preds.size > 0) {
36
+ const preds = ["predecessor blocks:"];
37
+ for (const pred of block.preds) {
38
+ preds.push(`bb${pred.id}`);
39
+ }
40
+ push(preds.join(" "));
41
+ }
42
+ for (const phi of block.phis) {
43
+ push(printPhi(phi));
44
+ }
45
for (const instr of block.instructions) {
46
push(printInstruction(instr));
47
}
@@ -78,6 +89,20 @@ function printInstruction(instr: Instruction): string {
89
}
90
}
91
92
+function printPhi(phi: Phi): string {
93
+ const items = [];
94
+ items.push(printLValue(phi.lvalue));
95
+ items.push(": phi(");
96
+ const phis = [];
97
+ for (const [block, place] of phi.operands) {
98
+ phis.push(`bb${block.id}: ${printPlace(place)}`);
99
+ }
100
+
101
+ items.push(phis.join(", "));
102
+ items.push(")");
103
+ return items.join("");
104
+}
105
+
106
function printTerminal(terminal: Terminal): Array<string> | string {
107
let value;
108
switch (terminal.kind) {
compiler/forget/src/HIR/SSAify.ts
new
+333
@@ -0,0 +1,333 @@
1
+import invariant from "invariant";
2
+import { assertExhaustive } from "../Common/utils";
3
+import {
4
+ BasicBlock,
5
+ BlockId,
6
+ Effect,
7
+ HIRFunction,
8
+ IdentifierId,
9
+ Instruction,
10
+ InstructionKind,
11
+ makeIdentifierId,
12
+ Phi,
13
+ Place,
14
+} from "./HIR";
15
+
16
+type IncompletePhi = {
17
+ old: Place;
18
+ new: Place;
19
+};
20
+
21
+type State = {
22
+ defs: Map<IdentifierId, Place>;
23
+ incompletePhis: IncompletePhi[];
24
+};
25
+
26
+const unsealedPreds: Map<BasicBlock, number> = new Map();
27
+
28
+class SSABuilder {
29
+ #states: Map<BasicBlock, State> = new Map();
30
+ #current: BasicBlock | null = null;
31
+ visitedBlocks: Set<BasicBlock> = new Set();
32
+
33
+ // Hack(gsn): Start from the last stored id in HIRBuilder.
34
+ // Need to refactor makeTemporary and relevant state out of HIR Builder.
35
+ #id = 1000;
36
+
37
+ state(): State {
38
+ invariant(
39
+ this.#current !== null,
40
+ "we need to be in a block to access state!"
41
+ );
42
+ return this.#states.get(this.#current)!;
43
+ }
44
+
45
+ // This tries to reuse all existing information about the oldPlace in case there's
46
+ // useful information from previous compiler passes.
47
+ makePlace(oldPlace: Place): Place {
48
+ const identifier = {
49
+ ...oldPlace.identifier,
50
+ id: makeIdentifierId(this.#id++),
51
+ };
52
+ return {
53
+ ...oldPlace,
54
+ identifier,
55
+ };
56
+ }
57
+
58
+ makePlaceForPhi(oldPlace: Place): Place {
59
+ const identifier = {
60
+ ...oldPlace.identifier,
61
+ id: makeIdentifierId(this.#id++),
62
+ };
63
+ return {
64
+ identifier,
65
+ kind: "Identifier",
66
+ memberPath: null,
67
+ effect: Effect.Mutate,
68
+ path: null as any,
69
+ };
70
+ }
71
+
72
+ definePlace(oldPlace: Place): Place {
73
+ const newPlace = this.makePlace(oldPlace);
74
+ this.state().defs.set(oldPlace.identifier.id, newPlace);
75
+ return newPlace;
76
+ }
77
+
78
+ getPlace(oldPlace: Place): Place {
79
+ return this.getPlaceAt(oldPlace, this.#current!);
80
+ }
81
+
82
+ getPlaceAt(oldPlace: Place, block: BasicBlock): Place {
83
+ // check if Place is defined locally
84
+ const state = this.#states.get(block)!;
85
+
86
+ if (state.defs.has(oldPlace.identifier.id)) {
87
+ return state.defs.get(oldPlace.identifier.id)!;
88
+ }
89
+
90
+ if (block.preds.size == 0) {
91
+ // We're at the entry block and haven't found our defintion yet.
92
+ throw new Error(`Unable to find \$${oldPlace.identifier.id}`);
93
+ }
94
+
95
+ if (unsealedPreds.get(block)! > 0) {
96
+ // We haven't visited all our predecessors, let's place an incomplete phi
97
+ // for now.
98
+ const newPlace = this.makePlaceForPhi(oldPlace);
99
+ state.incompletePhis.push({ old: oldPlace, new: newPlace });
100
+ state.defs.set(oldPlace.identifier.id, newPlace);
101
+ return newPlace;
102
+ }
103
+
104
+ // Only one predecessor, let's check there
105
+ if (block.preds.size == 1) {
106
+ const [pred] = block.preds;
107
+ const newPlace = this.getPlaceAt(oldPlace, pred);
108
+ state.defs.set(oldPlace.identifier.id, newPlace);
109
+ return newPlace;
110
+ }
111
+
112
+ // There are multiple predecessors, we need a phi.
113
+ const newPlace = this.makePlaceForPhi(oldPlace);
114
+ // Adding a phi may loop back to our block if there is a loop in the CFG. We
115
+ // update our defs before adding the phi to terminate the recursion rather than
116
+ // looping infinitely.
117
+ state.defs.set(oldPlace.identifier.id, newPlace);
118
+ this.addPhi(block, oldPlace, newPlace);
119
+
120
+ // TODO(gsn): Can we just return `newPlace` rather than looking it up?
121
+ // `addPhi` _can_ mutate it, but _will_ it?
122
+ return state.defs.get(oldPlace.identifier.id)!;
123
+ }
124
+
125
+ addPhi(block: BasicBlock, oldPlace: Place, newPlace: Place) {
126
+ const predDefs: Map<BasicBlock, Place> = new Map();
127
+ for (const predBlock of block.preds) {
128
+ const predPlace = this.getPlaceAt(oldPlace, predBlock);
129
+ predDefs.set(predBlock, predPlace);
130
+ }
131
+
132
+ const phi: Phi = {
133
+ kind: "Phi",
134
+ lvalue: { place: newPlace, kind: InstructionKind.Const },
135
+ operands: predDefs,
136
+ };
137
+
138
+ block.phis.add(phi);
139
+ }
140
+
141
+ fixIncompletePhis(block: BasicBlock) {
142
+ const state = this.#states.get(block)!;
143
+ for (const phi of state.incompletePhis) {
144
+ this.addPhi(block, phi.old, phi.new);
145
+ }
146
+ }
147
+
148
+ startBlock(block: BasicBlock) {
149
+ this.#current = block;
150
+ this.#states.set(block, {
151
+ defs: new Map(),
152
+ incompletePhis: [],
153
+ });
154
+ }
155
+
156
+ print() {
157
+ const text = [];
158
+ for (const [block, state] of this.#states) {
159
+ text.push(`bb${block.id}:`);
160
+ for (const [id, place] of state.defs) {
161
+ text.push(` \$${id} = \$${place.identifier.id}`);
162
+ }
163
+
164
+ for (const incompletePhi of state.incompletePhis) {
165
+ text.push(
166
+ ` iphi \$${incompletePhi.new.identifier} = \$${incompletePhi.old.identifier}`
167
+ );
168
+ }
169
+ }
170
+
171
+ text.push(`current block: bb${this.#current?.id}`);
172
+ console.log(text.join("\n"));
173
+ }
174
+}
175
+
176
+export default function buildSSA(func: HIRFunction) {
177
+ const builder = new SSABuilder();
178
+
179
+ function visit(blockId: BlockId) {
180
+ const block = func.body.blocks.get(blockId)!;
181
+ if (builder.visitedBlocks.has(block)) {
182
+ return;
183
+ }
184
+ builder.visitedBlocks.add(block);
185
+
186
+ builder.startBlock(block);
187
+ for (const instr of block.instructions) {
188
+ const uses = collectUses(instr);
189
+ for (const { place, updateFn } of uses) {
190
+ updateFn(builder.getPlace(place));
191
+ }
192
+
193
+ if (instr.lvalue != null) {
194
+ const oldPlace = instr.lvalue.place;
195
+ const newPlace: Place = builder.definePlace(oldPlace);
196
+ instr.lvalue.place = newPlace;
197
+ }
198
+ }
199
+
200
+ const { uses, outputs } = collectOutputs(block);
201
+ for (const { place, updateFn } of uses) {
202
+ updateFn(builder.getPlace(place));
203
+ }
204
+
205
+ const outputBlocks = outputs.map((id) => func.body.blocks.get(id)!);
206
+ for (const output of outputBlocks) {
207
+ let count;
208
+ if (unsealedPreds.has(output)) {
209
+ count = unsealedPreds.get(output)! - 1;
210
+ } else {
211
+ count = output.preds.size - 1;
212
+ }
213
+ unsealedPreds.set(output, count);
214
+
215
+ if (count == 0 && builder.visitedBlocks.has(output)) {
216
+ builder.fixIncompletePhis(output);
217
+ }
218
+ }
219
+
220
+ for (const output of outputs) {
221
+ visit(output);
222
+ }
223
+ }
224
+
225
+ visit(func.body.entry);
226
+}
227
+
228
+function collectOutputs(block: BasicBlock): {
229
+ uses: Array<Uses>;
230
+ outputs: Array<BlockId>;
231
+} {
232
+ const outputs: Array<BlockId> = [];
233
+ const uses: Array<Uses> = [];
234
+ const { terminal } = block;
235
+ switch (terminal.kind) {
236
+ case "return":
237
+ case "throw": {
238
+ if (terminal.value) {
239
+ uses.push({
240
+ place: terminal.value,
241
+ updateFn: (newPlace) => {
242
+ terminal.value = newPlace;
243
+ },
244
+ });
245
+ }
246
+ break;
247
+ }
248
+ case "goto": {
249
+ outputs.push(terminal.block);
250
+ break;
251
+ }
252
+ case "if": {
253
+ const { test, consequent, alternate } = terminal;
254
+ uses.push({
255
+ place: test,
256
+ updateFn: (newPlace) => {
257
+ terminal.test = newPlace;
258
+ },
259
+ });
260
+ outputs.push(alternate);
261
+ outputs.push(consequent);
262
+ break;
263
+ }
264
+ case "switch": {
265
+ const { test, cases } = terminal;
266
+ uses.push({
267
+ place: test,
268
+ updateFn: (newPlace) => {
269
+ terminal.test = newPlace;
270
+ },
271
+ });
272
+ for (const case_ of [...cases].reverse()) {
273
+ if (case_.test) {
274
+ uses.push({
275
+ place: case_.test,
276
+ updateFn: (newPlace) => {
277
+ case_.test = newPlace;
278
+ },
279
+ });
280
+ }
281
+ outputs.push(case_.block);
282
+ }
283
+ break;
284
+ }
285
+ default: {
286
+ assertExhaustive(
287
+ terminal,
288
+ `Unexpected terminal kind '${(terminal as any).kind}'`
289
+ );
290
+ }
291
+ }
292
+
293
+ return { uses, outputs };
294
+}
295
+
296
+type Uses = {
297
+ place: Place;
298
+ updateFn: (place: Place) => void;
299
+};
300
+
301
+function collectUses(instr: Instruction): Array<Uses> {
302
+ const uses: Array<Uses> = [];
303
+ const instrValue = instr.value;
304
+
305
+ // TODO(gsn): Handle more kinds of Instructions
306
+ switch (instrValue.kind) {
307
+ case "BinaryExpression": {
308
+ uses.push({
309
+ place: instrValue.left,
310
+ updateFn: (newPlace) => {
311
+ instrValue.left = newPlace;
312
+ },
313
+ });
314
+ uses.push({
315
+ place: instrValue.right,
316
+ updateFn: (newPlace) => {
317
+ instrValue.right = newPlace;
318
+ },
319
+ });
320
+ break;
321
+ }
322
+ case "Identifier": {
323
+ uses.push({
324
+ place: instrValue,
325
+ updateFn: (newPlace) => {
326
+ instr.value = newPlace;
327
+ },
328
+ });
329
+ break;
330
+ }
331
+ }
332
+ return uses;
333
+}
compiler/forget/src/__tests__/fixtures/hir/component.expect.md
+7
@@ -43,20 +43,26 @@ bb0:
43
Const mutate max$7 = Call mutate Math$8.max(read $9, read maxItems$3)
44
Goto bb1
45
bb1:
46
+ predecessor blocks: bb0 bb4 bb7
47
If (read items$2) then:bb3 else:bb2
48
bb3:
49
+ predecessor blocks: bb1
50
Const mutate $11 = null
51
Const mutate $12 = Binary read item$10 == read $11
52
If (read $12) then:bb8 else:bb9
53
bb8:
54
+ predecessor blocks: bb3
55
Const mutate $13 = read $12
56
Goto bb7
57
bb9:
58
+ predecessor blocks: bb3
59
Const mutate $13 = Call mutate seen$5.has(mutate item$10)
60
Goto bb7
61
bb7:
62
+ predecessor blocks: bb9 bb8
63
If (read $13) then:bb1 else:bb4
64
bb4:
65
+ predecessor blocks: bb7
66
Call mutate seen$5.add(mutate item$10)
67
Const mutate $14 = "div"
68
Const mutate $15 = JSX <read $14>{read item$10}</read $14>
@@ -64,6 +70,7 @@ bb4:
70
Const mutate $16 = Binary read renderedItems$4.length >= read max$7
71
If (read $16) then:bb2 else:bb1
72
bb2:
73
+ predecessor blocks: bb1 bb4
74
Const mutate count$17 = read renderedItems$4.length
75
Const mutate $18 = "div"
76
Const mutate $19 = "\n "
compiler/forget/src/__tests__/fixtures/hir/conditional-break.expect.md
+10
@@ -82,9 +82,11 @@ bb0:
82
Call mutate a_DEBUG$2.push(read props$1.a)
83
If (read props$1.b) then:bb2 else:bb1
84
bb2:
85
+ predecessor blocks: bb0
86
Const mutate $3 = null
87
Return read $3
88
bb1:
89
+ predecessor blocks: bb0
90
Call mutate a_DEBUG$2.push(read props$1.d)
91
Return freeze a_DEBUG$2
92
```
@@ -111,9 +113,11 @@ bb0:
113
Call mutate a$2.push(read props$1.a)
114
If (read props$1.b) then:bb2 else:bb1
115
bb2:
116
+ predecessor blocks: bb0
117
Call mutate a$2.push(read props$1.c)
118
Goto bb1
119
bb1:
120
+ predecessor blocks: bb0 bb2
121
Call mutate a$2.push(read props$1.d)
122
Return freeze a$2
123
```
@@ -141,10 +145,12 @@ bb0:
145
Call mutate a$2.push(read props$1.a)
146
If (read props$1.b) then:bb2 else:bb1
147
bb2:
148
+ predecessor blocks: bb0
149
Call mutate a$2.push(read props$1.c)
150
Const mutate $3 = null
151
Return read $3
152
bb1:
153
+ predecessor blocks: bb0
154
Call mutate a$2.push(read props$1.d)
155
Return freeze a$2
156
```
@@ -172,9 +178,11 @@ bb0:
178
Call mutate a$2.push(read props$1.a)
179
If (read props$1.b) then:bb2 else:bb1
180
bb2:
181
+ predecessor blocks: bb0
182
Call mutate a$2.push(read props$1.c)
183
Return freeze a$2
184
bb1:
185
+ predecessor blocks: bb0
186
Call mutate a$2.push(read props$1.d)
187
Return freeze a$2
188
```
@@ -202,9 +210,11 @@ bb0:
210
Call mutate a$2.push(read props$1.a)
211
If (read props$1.b) then:bb1 else:bb2
212
bb2:
213
+ predecessor blocks: bb0
214
Call mutate a$2.push(read props$1.c)
215
Goto bb1
216
bb1:
217
+ predecessor blocks: bb2 bb0
218
Call mutate a$2.push(read props$1.d)
219
Return freeze a$2
220
```
compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md
+8
@@ -71,14 +71,18 @@ bb0:
71
Const mutate b$3 = Array []
72
If (read b$3) then:bb2 else:bb1
73
bb2:
74
+ predecessor blocks: bb0
75
Call mutate a$2.push(read props$1.p0)
76
Goto bb1
77
bb1:
78
+ predecessor blocks: bb0 bb2
79
If (read props$1.p1) then:bb4 else:bb3
80
bb4:
81
+ predecessor blocks: bb1
82
Call mutate b$3.push(read props$1.p2)
83
Goto bb3
84
bb3:
85
+ predecessor blocks: bb1 bb4
86
Const mutate $5 = JSX <read Foo$4 a={freeze a$2} b={freeze b$3} ></read Foo$4>
87
Return read $5
88
```
@@ -110,14 +114,18 @@ bb0:
114
Const mutate $5 = Call mutate mayMutate$4(mutate b$3)
115
If (read $5) then:bb2 else:bb1
116
bb2:
117
+ predecessor blocks: bb0
118
Call mutate a$2.push(read props$1.p0)
119
Goto bb1
120
bb1:
121
+ predecessor blocks: bb0 bb2
122
If (read props$1.p1) then:bb4 else:bb3
123
bb4:
124
+ predecessor blocks: bb1
125
Call mutate b$3.push(read props$1.p2)
126
Goto bb3
127
bb3:
128
+ predecessor blocks: bb1 bb4
129
Const mutate $7 = JSX <read Foo$6 a={freeze a$2} b={freeze b$3} ></read Foo$6>
130
Return read $7
131
```
compiler/forget/src/__tests__/fixtures/hir/hooks-freeze-possibly-mutable-arguments.expect.md
+3
@@ -31,12 +31,15 @@ bb0:
31
Let mutate a$4 = undefined
32
If (read cond$2) then:bb2 else:bb3
33
bb2:
34
+ predecessor blocks: bb0
35
Reassign mutate a$4 = read x$3
36
Goto bb1
37
bb3:
38
+ predecessor blocks: bb0
39
Reassign mutate a$4 = Array []
40
Goto bb1
41
bb1:
42
+ predecessor blocks: bb3 bb2
43
Call read useFreeze$5(freeze a$4)
44
Call read useFreeze$5(read a$4)
45
Call mutate call$6(read a$4)
compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md
+2
@@ -101,10 +101,12 @@ bb0:
101
Const mutate b$4 = Call mutate compute$3(read props$1.b)
102
If (read props$1.c) then:bb2 else:bb1
103
bb2:
104
+ predecessor blocks: bb0
105
Call mutate mutate$5(mutate a$2)
106
Call mutate mutate$5(mutate b$4)
107
Goto bb1
108
bb1:
109
+ predecessor blocks: bb0 bb2
110
Const mutate $7 = JSX <read Foo$6 a={freeze a$2} b={freeze b$4} ></read Foo$6>
111
Return read $7
112
```
compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md
+2
@@ -80,9 +80,11 @@ bb0:
80
Const mutate b$4 = Call mutate compute$3(read props$1.b)
81
If (read props$1.c) then:bb2 else:bb1
82
bb2:
83
+ predecessor blocks: bb0
84
Call mutate foo$5(mutate a$2, mutate b$4)
85
Goto bb1
86
bb1:
87
+ predecessor blocks: bb0 bb2
88
Const mutate $7 = JSX <read Foo$6 a={freeze a$2} b={freeze b$4} ></read Foo$6>
89
Return read $7
90
```
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-loops.expect.md
+7
@@ -79,9 +79,11 @@ bb0:
79
Let mutate d$5 = Object { }
80
Goto bb1
81
bb1:
82
+ predecessor blocks: bb0 bb3
83
Const mutate $10 = true
84
If (read $10) then:bb3 else:bb2
85
bb3:
86
+ predecessor blocks: bb1
87
Let mutate z$6 = read a$2
88
Reassign mutate a$2 = read b$3
89
Reassign mutate b$3 = read c$4
@@ -91,14 +93,19 @@ bb3:
93
Const mutate $9 = Call mutate cond$8(mutate a$2)
94
If (read $9) then:bb2 else:bb1
95
bb2:
96
+ predecessor blocks: bb1 bb3
97
If (read a$2) then:bb7 else:bb7
98
bb7:
99
+ predecessor blocks: bb2
100
If (read b$3) then:bb9 else:bb9
101
bb9:
102
+ predecessor blocks: bb7
103
If (read c$4) then:bb11 else:bb11
104
bb11:
105
+ predecessor blocks: bb9
106
If (read d$5) then:bb13 else:bb13
107
bb13:
108
+ predecessor blocks: bb11
109
Const mutate $11 = null
110
Call mutate mutate$7(mutate d$5, read $11)
111
Return
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md
+5
@@ -63,14 +63,19 @@ bb0:
63
Const mutate y$7 = Call mutate mutate$8(mutate x$6, mutate d$5)
64
If (read a$2) then:bb1 else:bb1
65
bb1:
66
+ predecessor blocks: bb0
67
If (read b$3) then:bb3 else:bb3
68
bb3:
69
+ predecessor blocks: bb1
70
If (read c$4) then:bb5 else:bb5
71
bb5:
72
+ predecessor blocks: bb3
73
If (read d$5) then:bb7 else:bb7
74
bb7:
75
+ predecessor blocks: bb5
76
If (read y$7) then:bb9 else:bb9
77
bb9:
78
+ predecessor blocks: bb7
79
Const mutate $9 = null
80
Call mutate mutate$8(mutate x$6, read $9)
81
Return
compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md
+2
@@ -28,9 +28,11 @@ bb0:
28
Let mutate y$3 = read x$2
29
If (read props$1.p1) then:bb2 else:bb1
30
bb2:
31
+ predecessor blocks: bb0
32
Reassign mutate x$2 = Array []
33
Goto bb1
34
bb1:
35
+ predecessor blocks: bb0 bb2
36
Let mutate _$4 = JSX <read Component$0 x={freeze x$2} ></read Component$0>
37
Call read y$3.push(read props$1.p2)
38
Const mutate $5 = JSX <read Component$0 x={read x$2} y={read y$3} ></read Component$0>
compiler/forget/src/__tests__/fixtures/hir/reverse-postorder.expect.md
+8
@@ -39,6 +39,7 @@ bb0:
39
Let mutate x$2 = undefined
40
If (read props$1.cond) then:bb2 else:bb10
41
bb2:
42
+ predecessor blocks: bb0
43
Const mutate $3 = 2
44
Const mutate $4 = 1
45
Const mutate $5 = 0
@@ -48,23 +49,30 @@ bb2:
49
Case read $3: bb4
50
Default: bb4
51
bb8:
52
+ predecessor blocks: bb2
53
Reassign mutate x$2 = read props$1.v0
54
Goto bb1
55
bb6:
56
+ predecessor blocks: bb2
57
Reassign mutate x$2 = read props$1.v1
58
Goto bb1
59
bb4:
60
+ predecessor blocks: bb2
61
Reassign mutate x$2 = read props$1.v2
62
Goto bb1
63
bb10:
64
+ predecessor blocks: bb0
65
If (read props$1.cond2) then:bb12 else:bb13
66
bb12:
67
+ predecessor blocks: bb10
68
Reassign mutate x$2 = read props$1.b
69
Goto bb1
70
bb13:
71
+ predecessor blocks: bb10
72
Reassign mutate x$2 = read props$1.c
73
Goto bb1
74
bb1:
75
+ predecessor blocks: bb13 bb12 bb8 bb6 bb4
76
read x$2
77
Return
78
```
compiler/forget/src/__tests__/fixtures/hir/simple.expect.md
+2
@@ -17,10 +17,12 @@ function foo(x, y) {
17
bb0:
18
If (read x$1) then:bb2 else:bb1
19
bb2:
20
+ predecessor blocks: bb0
21
Const mutate $3 = false
22
Const mutate $4 = Call read foo$0(read $3, read y$2)
23
Return freeze $4
24
bb1:
25
+ predecessor blocks: bb0
26
Const mutate $5 = 10
27
Const mutate $6 = Binary read y$2 * read $5
28
Const mutate $7 = Array [read $6]
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.expect.md
new
+70
@@ -0,0 +1,70 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ let y = 2;
8
+ if (y === 2) {
9
+ x = 3;
10
+ }
11
+
12
+ if (y === 3) {
13
+ x = 5;
14
+ }
15
+ y = x;
16
+}
17
+
18
+```
19
+
20
+## HIR
21
+
22
+```
23
+bb0:
24
+ Let mutate x$1000 = 1
25
+ Let mutate y$1001 = 2
26
+ Const mutate $1002 = 2
27
+ Const mutate $1003 = Binary mutate y$1001 === mutate $1002
28
+ If (mutate $1003) then:bb2 else:bb1
29
+bb2:
30
+ predecessor blocks: bb0
31
+ Reassign mutate x$1011 = 3
32
+ Goto bb1
33
+bb1:
34
+ predecessor blocks: bb0 bb2
35
+ Const mutate y$1005: phi(bb0: mutate y$1001, bb2: mutate y$1001)
36
+ Const mutate x$1010: phi(bb0: mutate x$1000, bb2: mutate x$1011)
37
+ Const mutate $1004 = 3
38
+ Const mutate $1006 = Binary mutate y$1005 === mutate $1004
39
+ If (mutate $1006) then:bb4 else:bb3
40
+bb4:
41
+ predecessor blocks: bb1
42
+ Reassign mutate x$1009 = 5
43
+ Goto bb3
44
+bb3:
45
+ predecessor blocks: bb1 bb4
46
+ Const mutate x$1007: phi(bb1: mutate x$1010, bb4: mutate x$1009)
47
+ Reassign mutate y$1008 = mutate x$1007
48
+ Return
49
+```
50
+
51
+## Code
52
+
53
+```javascript
54
+function foo$0() {
55
+ let x$1000 = 1;
56
+ let y$1001 = 2;
57
+ if (y$1001 === 2) {
58
+ x$1011 = 3;
59
+ ("<<TODO: handle complex control flow in codegen>>");
60
+ }
61
+ if (y$1005 === 3) {
62
+ x$1009 = 5;
63
+ ("<<TODO: handle complex control flow in codegen>>");
64
+ }
65
+ y$1008 = x$1007;
66
+ return;
67
+}
68
+
69
+```
70
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-multiple-if.js
new
+12
@@ -0,0 +1,12 @@
1
+function foo() {
2
+ let x = 1;
3
+ let y = 2;
4
+ if (y === 2) {
5
+ x = 3;
6
+ }
7
+
8
+ if (y === 3) {
9
+ x = 5;
10
+ }
11
+ y = x;
12
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.expect.md
new
+52
@@ -0,0 +1,52 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ let y = 2;
8
+ if (y === 2) {
9
+ x = 3;
10
+ }
11
+
12
+ y = x;
13
+}
14
+
15
+```
16
+
17
+## HIR
18
+
19
+```
20
+bb0:
21
+ Let mutate x$1000 = 1
22
+ Let mutate y$1001 = 2
23
+ Const mutate $1002 = 2
24
+ Const mutate $1003 = Binary mutate y$1001 === mutate $1002
25
+ If (mutate $1003) then:bb2 else:bb1
26
+bb2:
27
+ predecessor blocks: bb0
28
+ Reassign mutate x$1006 = 3
29
+ Goto bb1
30
+bb1:
31
+ predecessor blocks: bb0 bb2
32
+ Const mutate x$1004: phi(bb0: mutate x$1000, bb2: mutate x$1006)
33
+ Reassign mutate y$1005 = mutate x$1004
34
+ Return
35
+```
36
+
37
+## Code
38
+
39
+```javascript
40
+function foo$0() {
41
+ let x$1000 = 1;
42
+ let y$1001 = 2;
43
+ if (y$1001 === 2) {
44
+ x$1006 = 3;
45
+ ("<<TODO: handle complex control flow in codegen>>");
46
+ }
47
+ y$1005 = x$1004;
48
+ return;
49
+}
50
+
51
+```
52
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-complex-single-if.js
new
+9
@@ -0,0 +1,9 @@
1
+function foo() {
2
+ let x = 1;
3
+ let y = 2;
4
+ if (y === 2) {
5
+ x = 3;
6
+ }
7
+
8
+ y = x;
9
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md
new
+46
@@ -0,0 +1,46 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 0;
7
+ for (; x < 10; ) {
8
+ x = x + 1;
9
+ }
10
+ return x;
11
+}
12
+
13
+```
14
+
15
+## HIR
16
+
17
+```
18
+bb0:
19
+ Let mutate x$1000 = 0
20
+ Goto bb1
21
+bb1:
22
+ predecessor blocks: bb0 bb4
23
+ Const mutate x$1002: phi(bb0: mutate x$1000, bb4: mutate x$1005)
24
+ Const mutate $1001 = 10
25
+ Const mutate $1003 = Binary mutate x$1002 < mutate $1001
26
+ If (mutate $1003) then:bb4 else:bb2
27
+bb4:
28
+ predecessor blocks: bb1
29
+ Const mutate $1004 = 1
30
+ Reassign mutate x$1005 = Binary mutate x$1002 + mutate $1004
31
+ Goto bb1
32
+bb2:
33
+ predecessor blocks: bb1
34
+ Return mutate x$1002
35
+```
36
+
37
+## Code
38
+
39
+```javascript
40
+function foo$0() {
41
+ let x$1000 = 0;
42
+ ("<<TODO: handle complex control flow in codegen>>");
43
+}
44
+
45
+```
46
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-for.js
new
+7
@@ -0,0 +1,7 @@
1
+function foo() {
2
+ let x = 0;
3
+ for (; x < 10; ) {
4
+ x = x + 1;
5
+ }
6
+ return x;
7
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.expect.md
new
+55
@@ -0,0 +1,55 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ let y = 2;
8
+
9
+ if (y) {
10
+ let z = x + y;
11
+ } else {
12
+ let z = x;
13
+ }
14
+}
15
+
16
+```
17
+
18
+## HIR
19
+
20
+```
21
+bb0:
22
+ Let mutate x$1000 = 1
23
+ Let mutate y$1001 = 2
24
+ If (mutate y$1001) then:bb2 else:bb3
25
+bb2:
26
+ predecessor blocks: bb0
27
+ Let mutate z$1003 = Binary mutate x$1000 + mutate y$1001
28
+ Goto bb1
29
+bb3:
30
+ predecessor blocks: bb0
31
+ Let mutate z$1002 = mutate x$1000
32
+ Goto bb1
33
+bb1:
34
+ predecessor blocks: bb3 bb2
35
+ Return
36
+```
37
+
38
+## Code
39
+
40
+```javascript
41
+function foo$0() {
42
+ let x$1000 = 1;
43
+ let y$1001 = 2;
44
+ if (y$1001) {
45
+ let z$1003 = x$1000 + y$1001;
46
+ ("<<TODO: handle complex control flow in codegen>>");
47
+ } else {
48
+ let z$1002 = x$1000;
49
+ ("<<TODO: handle complex control flow in codegen>>");
50
+ }
51
+ return;
52
+}
53
+
54
+```
55
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-if-else.js
new
+10
@@ -0,0 +1,10 @@
1
+function foo() {
2
+ let x = 1;
3
+ let y = 2;
4
+
5
+ if (y) {
6
+ let z = x + y;
7
+ } else {
8
+ let z = x;
9
+ }
10
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-return.expect.md
new
+47
@@ -0,0 +1,47 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ if (x === 1) {
8
+ x = 2;
9
+ }
10
+
11
+ return x;
12
+}
13
+
14
+```
15
+
16
+## HIR
17
+
18
+```
19
+bb0:
20
+ Let mutate x$1000 = 1
21
+ Const mutate $1001 = 1
22
+ Const mutate $1002 = Binary mutate x$1000 === mutate $1001
23
+ If (mutate $1002) then:bb2 else:bb1
24
+bb2:
25
+ predecessor blocks: bb0
26
+ Reassign mutate x$1004 = 2
27
+ Goto bb1
28
+bb1:
29
+ predecessor blocks: bb0 bb2
30
+ Const mutate x$1003: phi(bb0: mutate x$1000, bb2: mutate x$1004)
31
+ Return mutate x$1003
32
+```
33
+
34
+## Code
35
+
36
+```javascript
37
+function foo$0() {
38
+ let x$1000 = 1;
39
+ if (x$1000 === 1) {
40
+ x$1004 = 2;
41
+ ("<<TODO: handle complex control flow in codegen>>");
42
+ }
43
+ return x$1003;
44
+}
45
+
46
+```
47
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-return.js
new
+8
@@ -0,0 +1,8 @@
1
+function foo() {
2
+ let x = 1;
3
+ if (x === 1) {
4
+ x = 2;
5
+ }
6
+
7
+ return x;
8
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.expect.md
new
+59
@@ -0,0 +1,59 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let y = 2;
7
+
8
+ if (y > 1) {
9
+ y = 1;
10
+ } else {
11
+ y = 2;
12
+ }
13
+
14
+ let x = y;
15
+}
16
+
17
+```
18
+
19
+## HIR
20
+
21
+```
22
+bb0:
23
+ Let mutate y$1000 = 2
24
+ Const mutate $1001 = 1
25
+ Const mutate $1002 = Binary mutate y$1000 > mutate $1001
26
+ If (mutate $1002) then:bb2 else:bb3
27
+bb2:
28
+ predecessor blocks: bb0
29
+ Reassign mutate y$1006 = 1
30
+ Goto bb1
31
+bb3:
32
+ predecessor blocks: bb0
33
+ Reassign mutate y$1003 = 2
34
+ Goto bb1
35
+bb1:
36
+ predecessor blocks: bb3 bb2
37
+ Const mutate y$1004: phi(bb3: mutate y$1003, bb2: mutate y$1006)
38
+ Let mutate x$1005 = mutate y$1004
39
+ Return
40
+```
41
+
42
+## Code
43
+
44
+```javascript
45
+function foo$0() {
46
+ let y$1000 = 2;
47
+ if (y$1000 > 1) {
48
+ y$1006 = 1;
49
+ ("<<TODO: handle complex control flow in codegen>>");
50
+ } else {
51
+ y$1003 = 2;
52
+ ("<<TODO: handle complex control flow in codegen>>");
53
+ }
54
+ let x$1005 = y$1004;
55
+ return;
56
+}
57
+
58
+```
59
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-simple-phi.js
new
+11
@@ -0,0 +1,11 @@
1
+function foo() {
2
+ let y = 2;
3
+
4
+ if (y > 1) {
5
+ y = 1;
6
+ } else {
7
+ y = 2;
8
+ }
9
+
10
+ let x = y;
11
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-simple.expect.md
new
+31
@@ -0,0 +1,31 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ let y = 2;
8
+}
9
+
10
+```
11
+
12
+## HIR
13
+
14
+```
15
+bb0:
16
+ Let mutate x$1000 = 1
17
+ Let mutate y$1001 = 2
18
+ Return
19
+```
20
+
21
+## Code
22
+
23
+```javascript
24
+function foo$0() {
25
+ let x$1000 = 1;
26
+ let y$1001 = 2;
27
+ return;
28
+}
29
+
30
+```
31
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-simple.js
new
+4
@@ -0,0 +1,4 @@
1
+function foo() {
2
+ let x = 1;
3
+ let y = 2;
4
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.expect.md
new
+46
@@ -0,0 +1,46 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ let y = 2;
8
+
9
+ if (y) {
10
+ let z = x + y;
11
+ }
12
+}
13
+
14
+```
15
+
16
+## HIR
17
+
18
+```
19
+bb0:
20
+ Let mutate x$1000 = 1
21
+ Let mutate y$1001 = 2
22
+ If (mutate y$1001) then:bb2 else:bb1
23
+bb2:
24
+ predecessor blocks: bb0
25
+ Let mutate z$1002 = Binary mutate x$1000 + mutate y$1001
26
+ Goto bb1
27
+bb1:
28
+ predecessor blocks: bb0 bb2
29
+ Return
30
+```
31
+
32
+## Code
33
+
34
+```javascript
35
+function foo$0() {
36
+ let x$1000 = 1;
37
+ let y$1001 = 2;
38
+ if (y$1001) {
39
+ let z$1002 = x$1000 + y$1001;
40
+ ("<<TODO: handle complex control flow in codegen>>");
41
+ }
42
+ return;
43
+}
44
+
45
+```
46
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-single-if.js
new
+8
@@ -0,0 +1,8 @@
1
+function foo() {
2
+ let x = 1;
3
+ let y = 2;
4
+
5
+ if (y) {
6
+ let z = x + y;
7
+ }
8
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-switch.expect.md
new
+86
@@ -0,0 +1,86 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+
8
+ switch (x) {
9
+ case x === 1: {
10
+ x = x + 1;
11
+ break;
12
+ }
13
+ case x === 2: {
14
+ x = x + 2;
15
+ break;
16
+ }
17
+ default: {
18
+ x = x + 3;
19
+ }
20
+ }
21
+
22
+ let y = x;
23
+}
24
+
25
+```
26
+
27
+## HIR
28
+
29
+```
30
+bb0:
31
+ Let mutate x$1000 = 1
32
+ Const mutate $1001 = 2
33
+ Const mutate $1002 = Binary mutate x$1000 === mutate $1001
34
+ Const mutate $1003 = 1
35
+ Const mutate $1004 = Binary mutate x$1000 === mutate $1003
36
+ Switch (mutate x$1000)
37
+ Case mutate $1004: bb5
38
+ Case mutate $1002: bb3
39
+ Default: bb2
40
+bb5:
41
+ predecessor blocks: bb0
42
+ Const mutate $1011 = 1
43
+ Reassign mutate x$1012 = Binary mutate x$1000 + mutate $1011
44
+ Goto bb1
45
+bb3:
46
+ predecessor blocks: bb0
47
+ Const mutate $1009 = 2
48
+ Reassign mutate x$1010 = Binary mutate x$1000 + mutate $1009
49
+ Goto bb1
50
+bb2:
51
+ predecessor blocks: bb0
52
+ Const mutate $1005 = 3
53
+ Reassign mutate x$1006 = Binary mutate x$1000 + mutate $1005
54
+ Goto bb1
55
+bb1:
56
+ predecessor blocks: bb5 bb3 bb2
57
+ Const mutate x$1007: phi(bb5: mutate x$1012, bb3: mutate x$1010, bb2: mutate x$1006)
58
+ Let mutate y$1008 = mutate x$1007
59
+ Return
60
+```
61
+
62
+## Code
63
+
64
+```javascript
65
+function foo$0() {
66
+ let x$1000 = 1;
67
+ switch (x$1000) {
68
+ case x$1000 === 1: {
69
+ x$1012 = x$1000 + 1;
70
+ ("<<TODO: handle complex control flow in codegen>>");
71
+ }
72
+ case x$1000 === 2: {
73
+ x$1010 = x$1000 + 2;
74
+ ("<<TODO: handle complex control flow in codegen>>");
75
+ }
76
+ default: {
77
+ x$1006 = x$1000 + 3;
78
+ ("<<TODO: handle complex control flow in codegen>>");
79
+ }
80
+ }
81
+ let y$1008 = x$1007;
82
+ return;
83
+}
84
+
85
+```
86
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-switch.js
new
+19
@@ -0,0 +1,19 @@
1
+function foo() {
2
+ let x = 1;
3
+
4
+ switch (x) {
5
+ case x === 1: {
6
+ x = x + 1;
7
+ break;
8
+ }
9
+ case x === 2: {
10
+ x = x + 2;
11
+ break;
12
+ }
13
+ default: {
14
+ x = x + 3;
15
+ }
16
+ }
17
+
18
+ let y = x;
19
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-throw.expect.md
new
+46
@@ -0,0 +1,46 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ if (x === 1) {
8
+ x = 2;
9
+ }
10
+ throw x;
11
+}
12
+
13
+```
14
+
15
+## HIR
16
+
17
+```
18
+bb0:
19
+ Let mutate x$1000 = 1
20
+ Const mutate $1001 = 1
21
+ Const mutate $1002 = Binary mutate x$1000 === mutate $1001
22
+ If (mutate $1002) then:bb2 else:bb1
23
+bb2:
24
+ predecessor blocks: bb0
25
+ Reassign mutate x$1004 = 2
26
+ Goto bb1
27
+bb1:
28
+ predecessor blocks: bb0 bb2
29
+ Const mutate x$1003: phi(bb0: mutate x$1000, bb2: mutate x$1004)
30
+ Throw mutate x$1003
31
+```
32
+
33
+## Code
34
+
35
+```javascript
36
+function foo$0() {
37
+ let x$1000 = 1;
38
+ if (x$1000 === 1) {
39
+ x$1004 = 2;
40
+ ("<<TODO: handle complex control flow in codegen>>");
41
+ }
42
+ throw x$1003;
43
+}
44
+
45
+```
46
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-throw.js
new
+7
@@ -0,0 +1,7 @@
1
+function foo() {
2
+ let x = 1;
3
+ if (x === 1) {
4
+ x = 2;
5
+ }
6
+ throw x;
7
+}
compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md
new
+47
@@ -0,0 +1,47 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function foo() {
6
+ let x = 1;
7
+ while (x < 10) {
8
+ x = x + 1;
9
+ }
10
+
11
+ return x;
12
+}
13
+
14
+```
15
+
16
+## HIR
17
+
18
+```
19
+bb0:
20
+ Let mutate x$1000 = 1
21
+ Goto bb1
22
+bb1:
23
+ predecessor blocks: bb0 bb3
24
+ Const mutate x$1002: phi(bb0: mutate x$1000, bb3: mutate x$1005)
25
+ Const mutate $1001 = 10
26
+ Const mutate $1003 = Binary mutate x$1002 < mutate $1001
27
+ If (mutate $1003) then:bb3 else:bb2
28
+bb3:
29
+ predecessor blocks: bb1
30
+ Const mutate $1004 = 1
31
+ Reassign mutate x$1005 = Binary mutate x$1002 + mutate $1004
32
+ Goto bb1
33
+bb2:
34
+ predecessor blocks: bb1
35
+ Return mutate x$1002
36
+```
37
+
38
+## Code
39
+
40
+```javascript
41
+function foo$0() {
42
+ let x$1000 = 1;
43
+ ("<<TODO: handle complex control flow in codegen>>");
44
+}
45
+
46
+```
47
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/ssa-while.js
new
+8
@@ -0,0 +1,8 @@
1
+function foo() {
2
+ let x = 1;
3
+ while (x < 10) {
4
+ x = x + 1;
5
+ }
6
+
7
+ return x;
8
+}
compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md
+3
@@ -43,13 +43,16 @@ bb0:
43
Default: bb1
44
Case read $4: bb2
45
bb6:
46
+ predecessor blocks: bb0
47
Call mutate x$2.push(read props$1.p2)
48
Reassign mutate y$3 = Array []
49
Goto bb1
50
bb2:
51
+ predecessor blocks: bb0
52
Reassign mutate y$3 = read x$2
53
Goto bb1
54
bb1:
55
+ predecessor blocks: bb0 bb6 bb2
56
Const mutate child$7 = JSX <read Component$0 data={freeze x$2} ></read Component$0>
57
Call read y$3.push(read props$1.p4)
58
Const mutate $8 = JSX <read Component$0 data={freeze y$3} >{read child$7}</read Component$0>
compiler/forget/src/__tests__/fixtures/hir/switch.expect.md
+3
@@ -37,14 +37,17 @@ bb0:
37
Case read $4: bb2
38
Default: bb1
39
bb4:
40
+ predecessor blocks: bb0
41
Call mutate x$2.push(read props$1.p2)
42
Call mutate x$2.push(read props$1.p3)
43
Reassign mutate y$3 = Array []
44
Goto bb2
45
bb2:
46
+ predecessor blocks: bb4 bb0
47
Reassign mutate y$3 = read x$2
48
Goto bb1
49
bb1:
50
+ predecessor blocks: bb2 bb0
51
Const mutate child$6 = JSX <read Component$0 data={freeze x$2} ></read Component$0>
52
Call read y$3.push(read props$1.p4)
53
Const mutate $7 = JSX <read Component$0 data={read y$3} >{read child$6}</read Component$0>
compiler/forget/src/__tests__/hir-test.ts
+4
@@ -19,6 +19,7 @@ import codegen from "../HIR/Codegen";
19
import { HIRFunction } from "../HIR/HIR";
20
import inferReferenceEffects from "../HIR/InferReferenceEffects";
21
import printHIR from "../HIR/PrintHIR";
22
+import buildSSA from "../HIR/SSAify";
23
import generateTestsFromFixtures from "./test-utils/generateTestsFromFixtures";
24
25
function wrapWithTripleBackticks(s: string, ext?: string) {
@@ -45,6 +46,9 @@ describe("React Forget (HIR version)", () => {
46
enter(nodePath) {
47
const ir: HIRFunction = lower(nodePath);
48
inferReferenceEffects(ir);
49
+ if (file.startsWith("ssa")) {
50
+ buildSSA(ir);
51
+ }
52
// const lifetimeGraph = buildDefUseGraph(ir);
53
const textHIR = printHIR(ir.body);
54
// const textLifetimeGraph = printGraph(lifetimeGraph);