[valueblocks] Specify block type at creation
We need to know the kind of each block (regular or value). Rather than specify the kind when closing the block — when we've lost context about why the block was created — it's simpler and more accurate to specify the kind when creating/reserving the block.
Joe Savona committed
Jan 25, 2023 at 17:05 UTC
1014727d41162e267ba5b9efdf64270fc96cf056
2 files changed
+60
-63
compiler/forget/src/HIR/BuildHIR.ts
+40
-43
@@ -82,14 +82,14 @@ export function lower(
82
83
const body = func.get("body");
84
if (body.isExpression()) {
85
- const fallthrough = builder.reserve();
85
+ const fallthrough = builder.reserve("block");
86
const terminal: ReturnTerminal = {
87
kind: "return",
88
loc: GeneratedSource,
89
value: lowerExpressionToPlace(builder, body),
90
id: makeInstructionId(0),
91
};
92
- builder.terminateWithContinuation("block", terminal, fallthrough);
92
+ builder.terminateWithContinuation(terminal, fallthrough);
93
} else if (body.isBlockStatement()) {
94
lowerStatement(builder, body);
95
} else {
@@ -133,7 +133,7 @@ function lowerStatement(
133
value,
134
id: makeInstructionId(0),
135
};
136
- builder.terminate("block", terminal);
136
+ builder.terminate(terminal, "block");
137
return;
138
}
139
case "ReturnStatement": {
@@ -143,20 +143,19 @@ function lowerStatement(
143
argument.node != null
144
? lowerExpressionToPlace(builder, argument as NodePath<t.Expression>)
145
: null;
146
- const fallthrough = builder.reserve();
146
const terminal: ReturnTerminal = {
147
kind: "return",
148
loc: stmt.node.loc ?? GeneratedSource,
149
value,
150
id: makeInstructionId(0),
151
};
153
- builder.terminateWithContinuation("block", terminal, fallthrough);
152
+ builder.terminate(terminal, "block");
153
return;
154
}
155
case "IfStatement": {
156
const stmt = stmtPath as NodePath<t.IfStatement>;
157
// Block for code following the if
159
- const continuationBlock = builder.reserve();
158
+ const continuationBlock = builder.reserve("block");
159
// Block for the consequent (if the test is truthy)
160
const consequentBlock = builder.enter("block", (blockId) => {
161
lowerStatement(builder, stmt.get("consequent"));
@@ -193,7 +192,7 @@ function lowerStatement(
192
fallthrough: continuationBlock.id,
193
id: makeInstructionId(0),
194
};
196
- builder.terminateWithContinuation("block", terminal, continuationBlock);
195
+ builder.terminateWithContinuation(terminal, continuationBlock);
196
return;
197
}
198
case "BlockStatement": {
@@ -204,31 +203,37 @@ function lowerStatement(
203
case "BreakStatement": {
204
const stmt = stmtPath as NodePath<t.BreakStatement>;
205
const block = builder.lookupBreak(stmt.node.label?.name ?? null);
207
- builder.terminate("block", {
208
- kind: "goto",
209
- block,
210
- variant: GotoVariant.Break,
211
- id: makeInstructionId(0),
212
- });
206
+ builder.terminate(
207
+ {
208
+ kind: "goto",
209
+ block,
210
+ variant: GotoVariant.Break,
211
+ id: makeInstructionId(0),
212
+ },
213
+ "block"
214
+ );
215
return;
216
}
217
case "ContinueStatement": {
218
const stmt = stmtPath as NodePath<t.ContinueStatement>;
219
const block = builder.lookupContinue(stmt.node.label?.name ?? null);
218
- builder.terminate("block", {
219
- kind: "goto",
220
- block,
221
- variant: GotoVariant.Continue,
222
- id: makeInstructionId(0),
223
- });
220
+ builder.terminate(
221
+ {
222
+ kind: "goto",
223
+ block,
224
+ variant: GotoVariant.Continue,
225
+ id: makeInstructionId(0),
226
+ },
227
+ "block"
228
+ );
229
return;
230
}
231
case "ForInStatement": {
232
const stmt = stmtPath as NodePath<t.ForInStatement>;
233
// Block used to evaluate whether to (re)enter or exit the loop
229
- const conditionalBlock = builder.reserve();
234
+ const conditionalBlock = builder.reserve("block");
235
// Block for code following the loop
231
- const continuationBlock = builder.reserve();
236
+ const continuationBlock = builder.reserve("block");
237
const loopBlock = builder.enter("block", (blockId) => {
238
return builder.loop(
239
label,
@@ -247,7 +252,6 @@ function lowerStatement(
252
});
253
// End the block leading up to the loop and jump to the conditional block
254
builder.terminateWithContinuation(
250
- "block",
255
{
256
kind: "goto",
257
block: conditionalBlock.id,
@@ -270,15 +274,15 @@ function lowerStatement(
274
fallthrough: continuationBlock.id,
275
id: makeInstructionId(0),
276
};
273
- builder.terminateWithContinuation("block", terminal, continuationBlock);
277
+ builder.terminateWithContinuation(terminal, continuationBlock);
278
return;
279
}
280
case "ForOfStatement": {
281
const stmt = stmtPath as NodePath<t.ForOfStatement>;
282
// Block used to evaluate whether to (re)enter or exit the loop
279
- const conditionalBlock = builder.reserve();
283
+ const conditionalBlock = builder.reserve("block");
284
// Block for code following the loop
281
- const continuationBlock = builder.reserve();
285
+ const continuationBlock = builder.reserve("block");
286
/**
287
* Build the loop body, each iteration loops back to the conditional block
288
* to check whether to continue or exit
@@ -301,7 +305,6 @@ function lowerStatement(
305
});
306
// End the block leading up to the loop and jump to the conditional block
307
builder.terminateWithContinuation(
304
- "block",
308
{
309
kind: "goto",
310
block: conditionalBlock.id,
@@ -324,15 +327,15 @@ function lowerStatement(
327
fallthrough: continuationBlock.id,
328
id: makeInstructionId(0),
329
};
327
- builder.terminateWithContinuation("block", terminal, continuationBlock);
330
+ builder.terminateWithContinuation(terminal, continuationBlock);
331
return;
332
}
333
case "ForStatement": {
334
const stmt = stmtPath as NodePath<t.ForStatement>;
335
333
- const testBlock = builder.reserve();
336
+ const testBlock = builder.reserve("value");
337
// Block for code following the loop
335
- const continuationBlock = builder.reserve();
338
+ const continuationBlock = builder.reserve("block");
339
340
const initBlock = builder.enter("value", (blockId) => {
341
const init = stmt.get("init");
@@ -386,7 +389,6 @@ function lowerStatement(
389
});
390
391
builder.terminateWithContinuation(
389
- "block",
392
{
393
kind: "for",
394
init: initBlock,
@@ -408,7 +410,6 @@ function lowerStatement(
410
});
411
} else {
412
builder.terminateWithContinuation(
411
- "value",
413
{
414
kind: "if",
415
test: lowerExpressionToPlace(
@@ -428,7 +429,7 @@ function lowerStatement(
429
case "DoWhileStatement": {
430
const stmt = stmtPath as NodePath<t.DoWhileStatement>;
431
// Block for code following the loop
431
- const continuationBlock = builder.reserve();
432
+ const continuationBlock = builder.reserve("block");
433
// Loop body
434
const loopBlock = builder.enter("block", (loopBlock) => {
435
return builder.loop(label, loopBlock, continuationBlock.id, () => {
@@ -451,7 +452,6 @@ function lowerStatement(
452
});
453
// do-while unconditionally enters the loop
454
builder.terminateWithContinuation(
454
- "block",
455
{
456
kind: "goto",
457
block: loopBlock,
@@ -465,9 +465,9 @@ function lowerStatement(
465
case "WhileStatement": {
466
const stmt = stmtPath as NodePath<t.WhileStatement>;
467
// Block used to evaluate whether to (re)enter or exit the loop
468
- const conditionalBlock = builder.reserve();
468
+ const conditionalBlock = builder.reserve("value");
469
// Block for code following the loop
470
- const continuationBlock = builder.reserve();
470
+ const continuationBlock = builder.reserve("block");
471
// Loop body
472
const loopBlock = builder.enter("block", (blockId) => {
473
return builder.loop(
@@ -498,7 +498,6 @@ function lowerStatement(
498
});
499
} else {
500
builder.terminateWithContinuation(
501
- "block",
501
{
502
kind: "while",
503
loc,
@@ -524,7 +523,7 @@ function lowerStatement(
523
id: makeInstructionId(0),
524
};
525
// Complete the conditional and continue with code after the loop
527
- builder.terminateWithContinuation("value", terminal, continuationBlock);
526
+ builder.terminateWithContinuation(terminal, continuationBlock);
527
return;
528
}
529
case "LabeledStatement": {
@@ -545,12 +544,11 @@ function lowerStatement(
544
default: {
545
// All other statements create a continuation block to allow `break`,
546
// explicitly *don't* pass the label down
548
- const continuationBlock = builder.reserve();
547
+ const continuationBlock = builder.reserve("block");
548
builder.label(label, continuationBlock.id, () => {
549
lowerStatement(builder, stmt.get("body"));
550
});
551
builder.terminateWithContinuation(
553
- "block",
552
{
553
kind: "goto",
554
block: continuationBlock.id,
@@ -566,7 +564,7 @@ function lowerStatement(
564
case "SwitchStatement": {
565
const stmt = stmtPath as NodePath<t.SwitchStatement>;
566
// Block following the switch
569
- const continuationBlock = builder.reserve();
567
+ const continuationBlock = builder.reserve("block");
568
/**
569
* The goto target for any cases that fallthrough, which initially starts
570
* as the continuation block and is then updated as we iterate through cases
@@ -635,7 +633,6 @@ function lowerStatement(
633
634
const test = lowerExpressionToPlace(builder, stmt.get("discriminant"));
635
builder.terminateWithContinuation(
638
- "block",
636
{
637
kind: "switch",
638
test,
@@ -1501,7 +1498,7 @@ function lowerConditional(
1498
): Place {
1499
const place: Place = buildTemporaryPlace(builder, loc);
1500
// Block for code following the if
1504
- const continuationBlock = builder.reserve();
1501
+ const continuationBlock = builder.reserve("block");
1502
// Block for the consequent (if the test is truthy)
1503
const consequentBlock = builder.enter("value", (blockId) => {
1504
let value = consequent();
@@ -1542,7 +1539,7 @@ function lowerConditional(
1539
fallthrough: continuationBlock.id,
1540
id: makeInstructionId(0),
1541
};
1545
- builder.terminateWithContinuation("value", terminal, continuationBlock);
1542
+ builder.terminateWithContinuation(terminal, continuationBlock);
1543
return place;
1544
}
1545
compiler/forget/src/HIR/HIRBuilder.ts
+20
-20
@@ -40,7 +40,11 @@ import { eachTerminalSuccessor, mapTerminalSuccessors } from "./visitors";
40
/**
41
* A work-in-progress block that does not yet have a terminator
42
*/
43
-export type WipBlock = { id: BlockId; instructions: Array<Instruction> };
43
+export type WipBlock = {
44
+ id: BlockId;
45
+ instructions: Array<Instruction>;
46
+ kind: BlockKind;
47
+};
48
49
type Scope = LoopScope | LabelScope | SwitchScope;
50
@@ -63,8 +67,8 @@ type LabelScope = {
67
breakBlock: BlockId;
68
};
69
66
-function newBlock(id: BlockId): WipBlock {
67
- return { id, instructions: [] };
70
+function newBlock(id: BlockId, kind: BlockKind): WipBlock {
71
+ return { id, kind, instructions: [] };
72
}
73
74
export class Environment {
@@ -81,7 +85,7 @@ export class Environment {
85
export default class HIRBuilder {
86
#completed: Map<BlockId, BasicBlock> = new Map();
87
#nextId: BlockId = makeBlockId(1);
84
- #current: WipBlock = newBlock(makeBlockId(0));
88
+ #current: WipBlock = newBlock(makeBlockId(0), "block");
89
#entry: BlockId = makeBlockId(0);
90
#scopes: Array<Scope> = [];
91
#bindings: Map<string, { node: t.Identifier; identifier: Identifier }> =
@@ -228,8 +232,8 @@ export default class HIRBuilder {
232
/**
233
* Terminate the current block w the given terminal, and start a new block
234
*/
231
- terminate(kind: BlockKind, terminal: Terminal) {
232
- const { id: blockId, instructions } = this.#current;
235
+ terminate(terminal: Terminal, nextBlockKind: BlockKind) {
236
+ const { id: blockId, kind, instructions } = this.#current;
237
this.#completed.set(blockId, {
238
kind,
239
id: blockId,
@@ -239,19 +243,15 @@ export default class HIRBuilder {
243
phis: new Set(),
244
});
245
const nextId = makeBlockId(this.#nextId++);
242
- this.#current = newBlock(nextId);
246
+ this.#current = newBlock(nextId, nextBlockKind);
247
}
248
249
/**
250
* Terminate the current block w the given terminal, and set the previously
251
* reserved block as the new current block
252
*/
249
- terminateWithContinuation(
250
- kind: BlockKind,
251
- terminal: Terminal,
252
- continuation: WipBlock
253
- ) {
254
- const { id: blockId, instructions } = this.#current;
253
+ terminateWithContinuation(terminal: Terminal, continuation: WipBlock) {
254
+ const { id: blockId, kind, instructions } = this.#current;
255
this.#completed.set(blockId, {
256
kind: kind,
257
id: blockId,
@@ -268,15 +268,15 @@ export default class HIRBuilder {
268
* Make this the current block with `terminateWithContinuation()` or
269
* call `complete()` to save it without setting it as the current block.
270
*/
271
- reserve(): WipBlock {
272
- return newBlock(makeBlockId(this.#nextId++));
271
+ reserve(kind: BlockKind): WipBlock {
272
+ return newBlock(makeBlockId(this.#nextId++), kind);
273
}
274
275
/**
276
* Save a previously reserved block as completed
277
*/
278
- complete(kind: BlockKind, block: WipBlock, terminal: Terminal) {
279
- const { id: blockId, instructions } = block;
278
+ complete(block: WipBlock, terminal: Terminal) {
279
+ const { id: blockId, kind, instructions } = block;
280
this.#completed.set(blockId, {
281
kind,
282
id: blockId,
@@ -293,12 +293,12 @@ export default class HIRBuilder {
293
* The lambda must return a terminal node, which is used to terminate the
294
* newly constructed block.
295
*/
296
- enter(kind: BlockKind, fn: (blockId: BlockId) => Terminal): BlockId {
296
+ enter(nextBlockKind: BlockKind, fn: (blockId: BlockId) => Terminal): BlockId {
297
const current = this.#current;
298
const nextId = makeBlockId(this.#nextId++);
299
- this.#current = newBlock(nextId);
299
+ this.#current = newBlock(nextId, nextBlockKind);
300
const terminal = fn(nextId);
301
- const { id: blockId, instructions } = this.#current;
301
+ const { id: blockId, kind, instructions } = this.#current;
302
this.#completed.set(blockId, {
303
kind,
304
id: blockId,