Distinguish regular/value blocks
Joe Savona committed
Jan 10, 2023 at 09:37 UTC
f3168d020ff24d3530e6d4984c8ce9247bd97939
7 files changed
+73
-93
compiler/forget/src/HIR/BuildHIR.ts
+29
-21
@@ -122,7 +122,7 @@ function lowerStatement(
122
value,
123
id: makeInstructionId(0),
124
};
125
- builder.terminate(terminal);
125
+ builder.terminate("block", terminal);
126
return;
127
}
128
case "ReturnStatement": {
@@ -138,7 +138,7 @@ function lowerStatement(
138
value,
139
id: makeInstructionId(0),
140
};
141
- builder.terminateWithContinuation(terminal, fallthrough);
141
+ builder.terminateWithContinuation("block", terminal, fallthrough);
142
return;
143
}
144
case "IfStatement": {
@@ -146,7 +146,7 @@ function lowerStatement(
146
// Block for code following the if
147
const continuationBlock = builder.reserve();
148
// Block for the consequent (if the test is truthy)
149
- const consequentBlock = builder.enter((blockId) => {
149
+ const consequentBlock = builder.enter("block", (blockId) => {
150
lowerStatement(builder, stmt.get("consequent"));
151
return {
152
kind: "goto",
@@ -159,7 +159,7 @@ function lowerStatement(
159
let alternateBlock = null;
160
const alternate = stmt.get("alternate");
161
if (alternate.hasNode()) {
162
- alternateBlock = builder.enter((blockId) => {
162
+ alternateBlock = builder.enter("block", (blockId) => {
163
lowerStatement(builder, alternate);
164
return {
165
kind: "goto",
@@ -181,7 +181,7 @@ function lowerStatement(
181
fallthrough: continuationBlock.id,
182
id: makeInstructionId(0),
183
};
184
- builder.terminateWithContinuation(terminal, continuationBlock);
184
+ builder.terminateWithContinuation("block", terminal, continuationBlock);
185
return;
186
}
187
case "BlockStatement": {
@@ -192,7 +192,7 @@ function lowerStatement(
192
case "BreakStatement": {
193
const stmt = stmtPath as NodePath<t.BreakStatement>;
194
const block = builder.lookupBreak(stmt.node.label?.name ?? null);
195
- builder.terminate({
195
+ builder.terminate("block", {
196
kind: "goto",
197
block,
198
variant: GotoVariant.Break,
@@ -203,7 +203,7 @@ function lowerStatement(
203
case "ContinueStatement": {
204
const stmt = stmtPath as NodePath<t.ContinueStatement>;
205
const block = builder.lookupContinue(stmt.node.label?.name ?? null);
206
- builder.terminate({
206
+ builder.terminate("block", {
207
kind: "goto",
208
block,
209
variant: GotoVariant.Continue,
@@ -217,7 +217,7 @@ function lowerStatement(
217
const conditionalBlock = builder.reserve();
218
// Block for code following the loop
219
const continuationBlock = builder.reserve();
220
- const loopBlock = builder.enter((blockId) => {
220
+ const loopBlock = builder.enter("block", (blockId) => {
221
return builder.loop(
222
label,
223
conditionalBlock.id,
@@ -235,6 +235,7 @@ function lowerStatement(
235
});
236
// End the block leading up to the loop and jump to the conditional block
237
builder.terminateWithContinuation(
238
+ "block",
239
{
240
kind: "goto",
241
block: conditionalBlock.id,
@@ -257,7 +258,7 @@ function lowerStatement(
258
fallthrough: continuationBlock.id,
259
id: makeInstructionId(0),
260
};
260
- builder.terminateWithContinuation(terminal, continuationBlock);
261
+ builder.terminateWithContinuation("block", terminal, continuationBlock);
262
return;
263
}
264
case "ForOfStatement": {
@@ -270,7 +271,7 @@ function lowerStatement(
271
* Build the loop body, each iteration loops back to the conditional block
272
* to check whether to continue or exit
273
*/
273
- const loopBlock = builder.enter((blockId) => {
274
+ const loopBlock = builder.enter("block", (blockId) => {
275
return builder.loop(
276
label,
277
conditionalBlock.id,
@@ -288,6 +289,7 @@ function lowerStatement(
289
});
290
// End the block leading up to the loop and jump to the conditional block
291
builder.terminateWithContinuation(
292
+ "block",
293
{
294
kind: "goto",
295
block: conditionalBlock.id,
@@ -310,7 +312,7 @@ function lowerStatement(
312
fallthrough: continuationBlock.id,
313
id: makeInstructionId(0),
314
};
313
- builder.terminateWithContinuation(terminal, continuationBlock);
315
+ builder.terminateWithContinuation("block", terminal, continuationBlock);
316
return;
317
}
318
case "ForStatement": {
@@ -320,7 +322,7 @@ function lowerStatement(
322
// Block for code following the loop
323
const continuationBlock = builder.reserve();
324
323
- const initBlock = builder.enter((blockId) => {
325
+ const initBlock = builder.enter("value", (blockId) => {
326
const init = stmt.get("init") as NodePath<t.VariableDeclaration>;
327
todoInvariant(
328
t.isVariableDeclaration(init.node),
@@ -335,7 +337,7 @@ function lowerStatement(
337
};
338
});
339
338
- const updateBlock = builder.enter((blockId) => {
340
+ const updateBlock = builder.enter("value", (blockId) => {
341
const update = stmt.get("update");
342
todoInvariant(update.hasNode(), "Handle empty for updater");
343
if (update.hasNode()) {
@@ -349,7 +351,7 @@ function lowerStatement(
351
};
352
});
353
352
- const bodyBlock = builder.enter((blockId) => {
354
+ const bodyBlock = builder.enter("block", (blockId) => {
355
return builder.loop(label, updateBlock, continuationBlock.id, () => {
356
lowerStatement(builder, stmt.get("body"));
357
return {
@@ -362,6 +364,7 @@ function lowerStatement(
364
});
365
366
builder.terminateWithContinuation(
367
+ "block",
368
{
369
kind: "for",
370
init: initBlock,
@@ -377,6 +380,7 @@ function lowerStatement(
380
const test = stmt.get("test");
381
todoInvariant(test.hasNode(), "ForStatement without test");
382
builder.terminateWithContinuation(
383
+ "value",
384
{
385
kind: "if",
386
test: lowerExpressionToPlace(builder, test),
@@ -394,7 +398,7 @@ function lowerStatement(
398
// Block for code following the loop
399
const continuationBlock = builder.reserve();
400
// Loop body
397
- const loopBlock = builder.enter((loopBlock) => {
401
+ const loopBlock = builder.enter("block", (loopBlock) => {
402
return builder.loop(label, loopBlock, continuationBlock.id, () => {
403
lowerStatement(builder, stmt.get("body"));
404
/**
@@ -415,6 +419,7 @@ function lowerStatement(
419
});
420
// do-while unconditionally enters the loop
421
builder.terminateWithContinuation(
422
+ "block",
423
{
424
kind: "goto",
425
block: loopBlock,
@@ -432,7 +437,7 @@ function lowerStatement(
437
// Block for code following the loop
438
const continuationBlock = builder.reserve();
439
// Loop body
435
- const loopBlock = builder.enter((blockId) => {
440
+ const loopBlock = builder.enter("block", (blockId) => {
441
return builder.loop(
442
label,
443
conditionalBlock.id,
@@ -455,6 +460,7 @@ function lowerStatement(
460
const loc = stmt.node.loc;
461
invariant(loc, "while statement must have a location");
462
builder.terminateWithContinuation(
463
+ "block",
464
{
465
kind: "while",
466
loc,
@@ -479,7 +485,7 @@ function lowerStatement(
485
id: makeInstructionId(0),
486
};
487
// Complete the conditional and continue with code after the loop
482
- builder.terminateWithContinuation(terminal, continuationBlock);
488
+ builder.terminateWithContinuation("value", terminal, continuationBlock);
489
return;
490
}
491
case "LabeledStatement": {
@@ -505,6 +511,7 @@ function lowerStatement(
511
lowerStatement(builder, stmt.get("body"));
512
});
513
builder.terminateWithContinuation(
514
+ "block",
515
{
516
kind: "goto",
517
block: continuationBlock.id,
@@ -543,7 +550,7 @@ function lowerStatement(
550
);
551
hasDefault = true;
552
}
546
- const block = builder.enter((_blockId) => {
553
+ const block = builder.enter("block", (_blockId) => {
554
return builder.switch(label, continuationBlock.id, () => {
555
case_
556
.get("consequent")
@@ -581,6 +588,7 @@ function lowerStatement(
588
589
const test = lowerExpressionToPlace(builder, stmt.get("discriminant"));
590
builder.terminateWithContinuation(
591
+ "block",
592
{
593
kind: "switch",
594
test,
@@ -1192,7 +1200,7 @@ function lowerConditional(
1200
// Block for code following the if
1201
const continuationBlock = builder.reserve();
1202
// Block for the consequent (if the test is truthy)
1195
- const consequentBlock = builder.enter((blockId) => {
1203
+ const consequentBlock = builder.enter("value", (blockId) => {
1204
let value = consequent();
1205
builder.push({
1206
id: makeInstructionId(0),
@@ -1208,7 +1216,7 @@ function lowerConditional(
1216
};
1217
});
1218
// Block for the alternate (if the test is not truthy)
1211
- const alternateBlock = builder.enter((blockId) => {
1219
+ const alternateBlock = builder.enter("value", (blockId) => {
1220
let value = alternate();
1221
builder.push({
1222
id: makeInstructionId(0),
@@ -1231,7 +1239,7 @@ function lowerConditional(
1239
fallthrough: continuationBlock.id,
1240
id: makeInstructionId(0),
1241
};
1234
- builder.terminateWithContinuation(terminal, continuationBlock);
1242
+ builder.terminateWithContinuation("value", terminal, continuationBlock);
1243
return place;
1244
}
1245
compiler/forget/src/HIR/HIR.ts
+7
@@ -141,7 +141,9 @@ export type HIR = {
141
* an exception occurs, therefore the block model only represents explicit throw
142
* statements and not implicit exceptions which may occur.
143
*/
144
+export type BlockKind = "block" | "value";
145
export type BasicBlock = {
146
+ kind: BlockKind;
147
id: BlockId;
148
instructions: Array<Instruction>;
149
terminal: Terminal;
@@ -162,6 +164,11 @@ export type Terminal =
164
| ForTerminal
165
| WhileTerminal;
166
167
+/**
168
+ * Terminal nodes allowed for a value block
169
+ */
170
+export type ValueTerminal = IfTerminal | GotoTerminal;
171
+
172
export type ThrowTerminal = { kind: "throw"; value: Place; id: InstructionId };
173
174
export type ReturnTerminal = {
compiler/forget/src/HIR/HIRBuilder.ts
+15
-5
@@ -12,6 +12,7 @@ import { assertExhaustive } from "../Utils/utils";
12
import {
13
BasicBlock,
14
BlockId,
15
+ BlockKind,
16
GeneratedSource,
17
GotoVariant,
18
HIR,
@@ -146,6 +147,7 @@ export default class HIRBuilder {
147
build(): HIR {
148
const { id: blockId, instructions } = this.#current;
149
this.#completed.set(blockId, {
150
+ kind: "block",
151
id: blockId,
152
instructions,
153
terminal: {
@@ -177,9 +179,10 @@ export default class HIRBuilder {
179
/**
180
* Terminate the current block w the given terminal, and start a new block
181
*/
180
- terminate(terminal: Terminal) {
182
+ terminate(kind: BlockKind, terminal: Terminal) {
183
const { id: blockId, instructions } = this.#current;
184
this.#completed.set(blockId, {
185
+ kind,
186
id: blockId,
187
instructions,
188
terminal,
@@ -194,12 +197,17 @@ export default class HIRBuilder {
197
* Terminate the current block w the given terminal, and set the previously
198
* reserved block as the new current block
199
*/
197
- terminateWithContinuation(terminal: Terminal, continuation: WipBlock) {
200
+ terminateWithContinuation(
201
+ kind: BlockKind,
202
+ terminal: Terminal,
203
+ continuation: WipBlock
204
+ ) {
205
const { id: blockId, instructions } = this.#current;
206
this.#completed.set(blockId, {
207
+ kind: kind,
208
id: blockId,
209
instructions,
202
- terminal,
210
+ terminal: terminal,
211
preds: new Set(),
212
phis: new Set(),
213
});
@@ -218,9 +226,10 @@ export default class HIRBuilder {
226
/**
227
* Save a previously reserved block as completed
228
*/
221
- complete(block: WipBlock, terminal: Terminal) {
229
+ complete(kind: BlockKind, block: WipBlock, terminal: Terminal) {
230
const { id: blockId, instructions } = block;
231
this.#completed.set(blockId, {
232
+ kind,
233
id: blockId,
234
instructions,
235
terminal,
@@ -235,13 +244,14 @@ export default class HIRBuilder {
244
* The lambda must return a terminal node, which is used to terminate the
245
* newly constructed block.
246
*/
238
- enter(fn: (blockId: BlockId) => Terminal): BlockId {
247
+ enter(kind: BlockKind, fn: (blockId: BlockId) => Terminal): BlockId {
248
const current = this.#current;
249
const nextId = makeBlockId(this.#nextId++);
250
this.#current = newBlock(nextId);
251
const terminal = fn(nextId);
252
const { id: blockId, instructions } = this.#current;
253
this.#completed.set(blockId, {
254
+ kind,
255
id: blockId,
256
instructions,
257
terminal,
compiler/forget/src/HIR/MergeConsecutiveBlocks.ts
+1
-32
@@ -6,7 +6,6 @@
6
*/
7
8
import { invariant } from "../Utils/CompilerError";
9
-import { assertExhaustive } from "../Utils/utils";
9
import {
10
BlockId,
11
Effect,
@@ -30,40 +29,10 @@ import {
29
*/
30
export function mergeConsecutiveBlocks(fn: HIRFunction): void {
31
const merged = new MergedBlocks();
33
- const valueBlocks = new Set<BlockId>();
32
for (const [, block] of fn.body.blocks) {
35
- // Before evaluating the block itself, determine which other blocks
36
- // are value blocks. See above TODO which would allow us to bypass this.
37
- const terminal = block.terminal;
38
- switch (terminal.kind) {
39
- case "while": {
40
- valueBlocks.add(terminal.test);
41
- break;
42
- }
43
- case "for": {
44
- valueBlocks.add(terminal.init);
45
- valueBlocks.add(terminal.test);
46
- valueBlocks.add(terminal.update);
47
- break;
48
- }
49
- case "if":
50
- case "goto":
51
- case "return":
52
- case "throw":
53
- case "switch": {
54
- break;
55
- }
56
- default: {
57
- assertExhaustive(
58
- terminal,
59
- `Unexpected terminal kind '${(terminal as any).kind}'`
60
- );
61
- }
62
- }
63
-
33
// Can only merge blocks with a single predecessor, can't merge
34
// value blocks
66
- if (block.preds.size !== 1 || valueBlocks.has(block.id)) {
35
+ if (block.kind === "value" || block.preds.size !== 1) {
36
continue;
37
}
38
const originalPredecessorId = Array.from(block.preds)[0]!;
compiler/forget/src/HIR/PrintHIR.ts
+1
-1
@@ -47,7 +47,7 @@ export default function printHIR(
47
output.push(`${indent}${text}`);
48
};
49
for (const [blockId, block] of ir.blocks) {
50
- output.push(`bb${blockId}:`);
50
+ output.push(`bb${blockId} (${block.kind}):`);
51
if (block.preds.size > 0) {
52
const preds = ["predecessor blocks:"];
53
for (const pred of block.preds) {
compiler/forget/src/Optimization/ConstantPropagation.ts
+1
-15
@@ -6,7 +6,6 @@
6
*/
7
8
import {
9
- BlockId,
9
GotoVariant,
10
HIRFunction,
11
IdentifierId,
@@ -72,9 +71,6 @@ export function constantPropagation(fn: HIRFunction): void {
71
function applyConstantPropagation(fn: HIRFunction): boolean {
72
let hasChanges = false;
73
75
- // A set of blocks whose terminals can't (yet) be safely rewritten
76
- const valueBlocks = new Set<BlockId>();
77
-
74
const constants: Constants = new Map();
75
for (const [, block] of fn.body.blocks) {
76
// Initialize phi values if all operands have the same known constant value.
@@ -108,7 +104,7 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
104
}
105
}
106
111
- if (valueBlocks.has(block.id)) {
107
+ if (block.kind === "value") {
108
// can't rewrite terminals in value blocks yet
109
continue;
110
}
@@ -130,16 +126,6 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
126
}
127
break;
128
}
133
- case "while": {
134
- valueBlocks.add(terminal.test);
135
- break;
136
- }
137
- case "for": {
138
- valueBlocks.add(terminal.init);
139
- valueBlocks.add(terminal.test);
140
- valueBlocks.add(terminal.update);
141
- break;
142
- }
129
default: {
130
// no-op
131
}
compiler/forget/yarn.lock
+19
-19
@@ -83,12 +83,12 @@
83
"@jridgewell/gen-mapping" "^0.3.2"
84
jsesc "^2.5.1"
85
86
-"@babel/generator@^7.20.5":
87
- version "7.20.5"
88
- resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95"
89
- integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==
86
+"@babel/generator@^7.20.7":
87
+ version "7.20.7"
88
+ resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a"
89
+ integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==
90
dependencies:
91
- "@babel/types" "^7.20.5"
91
+ "@babel/types" "^7.20.7"
92
"@jridgewell/gen-mapping" "^0.3.2"
93
jsesc "^2.5.1"
94
@@ -260,10 +260,10 @@
260
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
261
integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
262
263
-"@babel/parser@^7.20.5":
264
- version "7.20.5"
265
- resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8"
266
- integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==
263
+"@babel/parser@^7.20.7":
264
+ version "7.20.7"
265
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b"
266
+ integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==
267
268
"@babel/plugin-syntax-async-generators@^7.8.4":
269
version "7.8.4"
@@ -484,18 +484,18 @@
484
lodash "^4.17.10"
485
486
"@babel/traverse@^7.19.1":
487
- version "7.20.5"
488
- resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133"
489
- integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==
487
+ version "7.20.12"
488
+ resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5"
489
+ integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==
490
dependencies:
491
"@babel/code-frame" "^7.18.6"
492
- "@babel/generator" "^7.20.5"
492
+ "@babel/generator" "^7.20.7"
493
"@babel/helper-environment-visitor" "^7.18.9"
494
"@babel/helper-function-name" "^7.19.0"
495
"@babel/helper-hoist-variables" "^7.18.6"
496
"@babel/helper-split-export-declaration" "^7.18.6"
497
- "@babel/parser" "^7.20.5"
498
- "@babel/types" "^7.20.5"
497
+ "@babel/parser" "^7.20.7"
498
+ "@babel/types" "^7.20.7"
499
debug "^4.1.0"
500
globals "^11.1.0"
501
@@ -534,10 +534,10 @@
534
"@babel/helper-validator-identifier" "^7.19.1"
535
to-fast-properties "^2.0.0"
536
537
-"@babel/types@^7.20.5":
538
- version "7.20.5"
539
- resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84"
540
- integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==
537
+"@babel/types@^7.20.7":
538
+ version "7.20.7"
539
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f"
540
+ integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==
541
dependencies:
542
"@babel/helper-string-parser" "^7.19.4"
543
"@babel/helper-validator-identifier" "^7.19.1"