[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.
This commit is contained in:
Joe Savona
2023-01-25 17:05:22 -05:00
parent 840bb6fec2
commit 1014727d41
2 changed files with 60 additions and 63 deletions
+40 -43
View File
@@ -82,14 +82,14 @@ export function lower(
const body = func.get("body");
if (body.isExpression()) {
const fallthrough = builder.reserve();
const fallthrough = builder.reserve("block");
const terminal: ReturnTerminal = {
kind: "return",
loc: GeneratedSource,
value: lowerExpressionToPlace(builder, body),
id: makeInstructionId(0),
};
builder.terminateWithContinuation("block", terminal, fallthrough);
builder.terminateWithContinuation(terminal, fallthrough);
} else if (body.isBlockStatement()) {
lowerStatement(builder, body);
} else {
@@ -133,7 +133,7 @@ function lowerStatement(
value,
id: makeInstructionId(0),
};
builder.terminate("block", terminal);
builder.terminate(terminal, "block");
return;
}
case "ReturnStatement": {
@@ -143,20 +143,19 @@ function lowerStatement(
argument.node != null
? lowerExpressionToPlace(builder, argument as NodePath<t.Expression>)
: null;
const fallthrough = builder.reserve();
const terminal: ReturnTerminal = {
kind: "return",
loc: stmt.node.loc ?? GeneratedSource,
value,
id: makeInstructionId(0),
};
builder.terminateWithContinuation("block", terminal, fallthrough);
builder.terminate(terminal, "block");
return;
}
case "IfStatement": {
const stmt = stmtPath as NodePath<t.IfStatement>;
// Block for code following the if
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
// Block for the consequent (if the test is truthy)
const consequentBlock = builder.enter("block", (blockId) => {
lowerStatement(builder, stmt.get("consequent"));
@@ -193,7 +192,7 @@ function lowerStatement(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation("block", terminal, continuationBlock);
builder.terminateWithContinuation(terminal, continuationBlock);
return;
}
case "BlockStatement": {
@@ -204,31 +203,37 @@ function lowerStatement(
case "BreakStatement": {
const stmt = stmtPath as NodePath<t.BreakStatement>;
const block = builder.lookupBreak(stmt.node.label?.name ?? null);
builder.terminate("block", {
kind: "goto",
block,
variant: GotoVariant.Break,
id: makeInstructionId(0),
});
builder.terminate(
{
kind: "goto",
block,
variant: GotoVariant.Break,
id: makeInstructionId(0),
},
"block"
);
return;
}
case "ContinueStatement": {
const stmt = stmtPath as NodePath<t.ContinueStatement>;
const block = builder.lookupContinue(stmt.node.label?.name ?? null);
builder.terminate("block", {
kind: "goto",
block,
variant: GotoVariant.Continue,
id: makeInstructionId(0),
});
builder.terminate(
{
kind: "goto",
block,
variant: GotoVariant.Continue,
id: makeInstructionId(0),
},
"block"
);
return;
}
case "ForInStatement": {
const stmt = stmtPath as NodePath<t.ForInStatement>;
// Block used to evaluate whether to (re)enter or exit the loop
const conditionalBlock = builder.reserve();
const conditionalBlock = builder.reserve("block");
// Block for code following the loop
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
const loopBlock = builder.enter("block", (blockId) => {
return builder.loop(
label,
@@ -247,7 +252,6 @@ function lowerStatement(
});
// End the block leading up to the loop and jump to the conditional block
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: conditionalBlock.id,
@@ -270,15 +274,15 @@ function lowerStatement(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation("block", terminal, continuationBlock);
builder.terminateWithContinuation(terminal, continuationBlock);
return;
}
case "ForOfStatement": {
const stmt = stmtPath as NodePath<t.ForOfStatement>;
// Block used to evaluate whether to (re)enter or exit the loop
const conditionalBlock = builder.reserve();
const conditionalBlock = builder.reserve("block");
// Block for code following the loop
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
/**
* Build the loop body, each iteration loops back to the conditional block
* to check whether to continue or exit
@@ -301,7 +305,6 @@ function lowerStatement(
});
// End the block leading up to the loop and jump to the conditional block
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: conditionalBlock.id,
@@ -324,15 +327,15 @@ function lowerStatement(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation("block", terminal, continuationBlock);
builder.terminateWithContinuation(terminal, continuationBlock);
return;
}
case "ForStatement": {
const stmt = stmtPath as NodePath<t.ForStatement>;
const testBlock = builder.reserve();
const testBlock = builder.reserve("value");
// Block for code following the loop
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
const initBlock = builder.enter("value", (blockId) => {
const init = stmt.get("init");
@@ -386,7 +389,6 @@ function lowerStatement(
});
builder.terminateWithContinuation(
"block",
{
kind: "for",
init: initBlock,
@@ -408,7 +410,6 @@ function lowerStatement(
});
} else {
builder.terminateWithContinuation(
"value",
{
kind: "if",
test: lowerExpressionToPlace(
@@ -428,7 +429,7 @@ function lowerStatement(
case "DoWhileStatement": {
const stmt = stmtPath as NodePath<t.DoWhileStatement>;
// Block for code following the loop
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
// Loop body
const loopBlock = builder.enter("block", (loopBlock) => {
return builder.loop(label, loopBlock, continuationBlock.id, () => {
@@ -451,7 +452,6 @@ function lowerStatement(
});
// do-while unconditionally enters the loop
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: loopBlock,
@@ -465,9 +465,9 @@ function lowerStatement(
case "WhileStatement": {
const stmt = stmtPath as NodePath<t.WhileStatement>;
// Block used to evaluate whether to (re)enter or exit the loop
const conditionalBlock = builder.reserve();
const conditionalBlock = builder.reserve("value");
// Block for code following the loop
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
// Loop body
const loopBlock = builder.enter("block", (blockId) => {
return builder.loop(
@@ -498,7 +498,6 @@ function lowerStatement(
});
} else {
builder.terminateWithContinuation(
"block",
{
kind: "while",
loc,
@@ -524,7 +523,7 @@ function lowerStatement(
id: makeInstructionId(0),
};
// Complete the conditional and continue with code after the loop
builder.terminateWithContinuation("value", terminal, continuationBlock);
builder.terminateWithContinuation(terminal, continuationBlock);
return;
}
case "LabeledStatement": {
@@ -545,12 +544,11 @@ function lowerStatement(
default: {
// All other statements create a continuation block to allow `break`,
// explicitly *don't* pass the label down
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
builder.label(label, continuationBlock.id, () => {
lowerStatement(builder, stmt.get("body"));
});
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: continuationBlock.id,
@@ -566,7 +564,7 @@ function lowerStatement(
case "SwitchStatement": {
const stmt = stmtPath as NodePath<t.SwitchStatement>;
// Block following the switch
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
/**
* The goto target for any cases that fallthrough, which initially starts
* as the continuation block and is then updated as we iterate through cases
@@ -635,7 +633,6 @@ function lowerStatement(
const test = lowerExpressionToPlace(builder, stmt.get("discriminant"));
builder.terminateWithContinuation(
"block",
{
kind: "switch",
test,
@@ -1501,7 +1498,7 @@ function lowerConditional(
): Place {
const place: Place = buildTemporaryPlace(builder, loc);
// Block for code following the if
const continuationBlock = builder.reserve();
const continuationBlock = builder.reserve("block");
// Block for the consequent (if the test is truthy)
const consequentBlock = builder.enter("value", (blockId) => {
let value = consequent();
@@ -1542,7 +1539,7 @@ function lowerConditional(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation("value", terminal, continuationBlock);
builder.terminateWithContinuation(terminal, continuationBlock);
return place;
}
+20 -20
View File
@@ -40,7 +40,11 @@ import { eachTerminalSuccessor, mapTerminalSuccessors } from "./visitors";
/**
* A work-in-progress block that does not yet have a terminator
*/
export type WipBlock = { id: BlockId; instructions: Array<Instruction> };
export type WipBlock = {
id: BlockId;
instructions: Array<Instruction>;
kind: BlockKind;
};
type Scope = LoopScope | LabelScope | SwitchScope;
@@ -63,8 +67,8 @@ type LabelScope = {
breakBlock: BlockId;
};
function newBlock(id: BlockId): WipBlock {
return { id, instructions: [] };
function newBlock(id: BlockId, kind: BlockKind): WipBlock {
return { id, kind, instructions: [] };
}
export class Environment {
@@ -81,7 +85,7 @@ export class Environment {
export default class HIRBuilder {
#completed: Map<BlockId, BasicBlock> = new Map();
#nextId: BlockId = makeBlockId(1);
#current: WipBlock = newBlock(makeBlockId(0));
#current: WipBlock = newBlock(makeBlockId(0), "block");
#entry: BlockId = makeBlockId(0);
#scopes: Array<Scope> = [];
#bindings: Map<string, { node: t.Identifier; identifier: Identifier }> =
@@ -228,8 +232,8 @@ export default class HIRBuilder {
/**
* Terminate the current block w the given terminal, and start a new block
*/
terminate(kind: BlockKind, terminal: Terminal) {
const { id: blockId, instructions } = this.#current;
terminate(terminal: Terminal, nextBlockKind: BlockKind) {
const { id: blockId, kind, instructions } = this.#current;
this.#completed.set(blockId, {
kind,
id: blockId,
@@ -239,19 +243,15 @@ export default class HIRBuilder {
phis: new Set(),
});
const nextId = makeBlockId(this.#nextId++);
this.#current = newBlock(nextId);
this.#current = newBlock(nextId, nextBlockKind);
}
/**
* Terminate the current block w the given terminal, and set the previously
* reserved block as the new current block
*/
terminateWithContinuation(
kind: BlockKind,
terminal: Terminal,
continuation: WipBlock
) {
const { id: blockId, instructions } = this.#current;
terminateWithContinuation(terminal: Terminal, continuation: WipBlock) {
const { id: blockId, kind, instructions } = this.#current;
this.#completed.set(blockId, {
kind: kind,
id: blockId,
@@ -268,15 +268,15 @@ export default class HIRBuilder {
* Make this the current block with `terminateWithContinuation()` or
* call `complete()` to save it without setting it as the current block.
*/
reserve(): WipBlock {
return newBlock(makeBlockId(this.#nextId++));
reserve(kind: BlockKind): WipBlock {
return newBlock(makeBlockId(this.#nextId++), kind);
}
/**
* Save a previously reserved block as completed
*/
complete(kind: BlockKind, block: WipBlock, terminal: Terminal) {
const { id: blockId, instructions } = block;
complete(block: WipBlock, terminal: Terminal) {
const { id: blockId, kind, instructions } = block;
this.#completed.set(blockId, {
kind,
id: blockId,
@@ -293,12 +293,12 @@ export default class HIRBuilder {
* The lambda must return a terminal node, which is used to terminate the
* newly constructed block.
*/
enter(kind: BlockKind, fn: (blockId: BlockId) => Terminal): BlockId {
enter(nextBlockKind: BlockKind, fn: (blockId: BlockId) => Terminal): BlockId {
const current = this.#current;
const nextId = makeBlockId(this.#nextId++);
this.#current = newBlock(nextId);
this.#current = newBlock(nextId, nextBlockKind);
const terminal = fn(nextId);
const { id: blockId, instructions } = this.#current;
const { id: blockId, kind, instructions } = this.#current;
this.#completed.set(blockId, {
kind,
id: blockId,