Distinguish regular/value blocks

This commit is contained in:
Joe Savona
2023-01-10 09:37:33 -08:00
parent dce5371763
commit f3168d020f
7 changed files with 73 additions and 93 deletions
+29 -21
View File
@@ -122,7 +122,7 @@ function lowerStatement(
value,
id: makeInstructionId(0),
};
builder.terminate(terminal);
builder.terminate("block", terminal);
return;
}
case "ReturnStatement": {
@@ -138,7 +138,7 @@ function lowerStatement(
value,
id: makeInstructionId(0),
};
builder.terminateWithContinuation(terminal, fallthrough);
builder.terminateWithContinuation("block", terminal, fallthrough);
return;
}
case "IfStatement": {
@@ -146,7 +146,7 @@ function lowerStatement(
// Block for code following the if
const continuationBlock = builder.reserve();
// Block for the consequent (if the test is truthy)
const consequentBlock = builder.enter((blockId) => {
const consequentBlock = builder.enter("block", (blockId) => {
lowerStatement(builder, stmt.get("consequent"));
return {
kind: "goto",
@@ -159,7 +159,7 @@ function lowerStatement(
let alternateBlock = null;
const alternate = stmt.get("alternate");
if (alternate.hasNode()) {
alternateBlock = builder.enter((blockId) => {
alternateBlock = builder.enter("block", (blockId) => {
lowerStatement(builder, alternate);
return {
kind: "goto",
@@ -181,7 +181,7 @@ function lowerStatement(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation(terminal, continuationBlock);
builder.terminateWithContinuation("block", terminal, continuationBlock);
return;
}
case "BlockStatement": {
@@ -192,7 +192,7 @@ function lowerStatement(
case "BreakStatement": {
const stmt = stmtPath as NodePath<t.BreakStatement>;
const block = builder.lookupBreak(stmt.node.label?.name ?? null);
builder.terminate({
builder.terminate("block", {
kind: "goto",
block,
variant: GotoVariant.Break,
@@ -203,7 +203,7 @@ function lowerStatement(
case "ContinueStatement": {
const stmt = stmtPath as NodePath<t.ContinueStatement>;
const block = builder.lookupContinue(stmt.node.label?.name ?? null);
builder.terminate({
builder.terminate("block", {
kind: "goto",
block,
variant: GotoVariant.Continue,
@@ -217,7 +217,7 @@ function lowerStatement(
const conditionalBlock = builder.reserve();
// Block for code following the loop
const continuationBlock = builder.reserve();
const loopBlock = builder.enter((blockId) => {
const loopBlock = builder.enter("block", (blockId) => {
return builder.loop(
label,
conditionalBlock.id,
@@ -235,6 +235,7 @@ function lowerStatement(
});
// End the block leading up to the loop and jump to the conditional block
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: conditionalBlock.id,
@@ -257,7 +258,7 @@ function lowerStatement(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation(terminal, continuationBlock);
builder.terminateWithContinuation("block", terminal, continuationBlock);
return;
}
case "ForOfStatement": {
@@ -270,7 +271,7 @@ function lowerStatement(
* Build the loop body, each iteration loops back to the conditional block
* to check whether to continue or exit
*/
const loopBlock = builder.enter((blockId) => {
const loopBlock = builder.enter("block", (blockId) => {
return builder.loop(
label,
conditionalBlock.id,
@@ -288,6 +289,7 @@ function lowerStatement(
});
// End the block leading up to the loop and jump to the conditional block
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: conditionalBlock.id,
@@ -310,7 +312,7 @@ function lowerStatement(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation(terminal, continuationBlock);
builder.terminateWithContinuation("block", terminal, continuationBlock);
return;
}
case "ForStatement": {
@@ -320,7 +322,7 @@ function lowerStatement(
// Block for code following the loop
const continuationBlock = builder.reserve();
const initBlock = builder.enter((blockId) => {
const initBlock = builder.enter("value", (blockId) => {
const init = stmt.get("init") as NodePath<t.VariableDeclaration>;
todoInvariant(
t.isVariableDeclaration(init.node),
@@ -335,7 +337,7 @@ function lowerStatement(
};
});
const updateBlock = builder.enter((blockId) => {
const updateBlock = builder.enter("value", (blockId) => {
const update = stmt.get("update");
todoInvariant(update.hasNode(), "Handle empty for updater");
if (update.hasNode()) {
@@ -349,7 +351,7 @@ function lowerStatement(
};
});
const bodyBlock = builder.enter((blockId) => {
const bodyBlock = builder.enter("block", (blockId) => {
return builder.loop(label, updateBlock, continuationBlock.id, () => {
lowerStatement(builder, stmt.get("body"));
return {
@@ -362,6 +364,7 @@ function lowerStatement(
});
builder.terminateWithContinuation(
"block",
{
kind: "for",
init: initBlock,
@@ -377,6 +380,7 @@ function lowerStatement(
const test = stmt.get("test");
todoInvariant(test.hasNode(), "ForStatement without test");
builder.terminateWithContinuation(
"value",
{
kind: "if",
test: lowerExpressionToPlace(builder, test),
@@ -394,7 +398,7 @@ function lowerStatement(
// Block for code following the loop
const continuationBlock = builder.reserve();
// Loop body
const loopBlock = builder.enter((loopBlock) => {
const loopBlock = builder.enter("block", (loopBlock) => {
return builder.loop(label, loopBlock, continuationBlock.id, () => {
lowerStatement(builder, stmt.get("body"));
/**
@@ -415,6 +419,7 @@ function lowerStatement(
});
// do-while unconditionally enters the loop
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: loopBlock,
@@ -432,7 +437,7 @@ function lowerStatement(
// Block for code following the loop
const continuationBlock = builder.reserve();
// Loop body
const loopBlock = builder.enter((blockId) => {
const loopBlock = builder.enter("block", (blockId) => {
return builder.loop(
label,
conditionalBlock.id,
@@ -455,6 +460,7 @@ function lowerStatement(
const loc = stmt.node.loc;
invariant(loc, "while statement must have a location");
builder.terminateWithContinuation(
"block",
{
kind: "while",
loc,
@@ -479,7 +485,7 @@ function lowerStatement(
id: makeInstructionId(0),
};
// Complete the conditional and continue with code after the loop
builder.terminateWithContinuation(terminal, continuationBlock);
builder.terminateWithContinuation("value", terminal, continuationBlock);
return;
}
case "LabeledStatement": {
@@ -505,6 +511,7 @@ function lowerStatement(
lowerStatement(builder, stmt.get("body"));
});
builder.terminateWithContinuation(
"block",
{
kind: "goto",
block: continuationBlock.id,
@@ -543,7 +550,7 @@ function lowerStatement(
);
hasDefault = true;
}
const block = builder.enter((_blockId) => {
const block = builder.enter("block", (_blockId) => {
return builder.switch(label, continuationBlock.id, () => {
case_
.get("consequent")
@@ -581,6 +588,7 @@ function lowerStatement(
const test = lowerExpressionToPlace(builder, stmt.get("discriminant"));
builder.terminateWithContinuation(
"block",
{
kind: "switch",
test,
@@ -1192,7 +1200,7 @@ function lowerConditional(
// Block for code following the if
const continuationBlock = builder.reserve();
// Block for the consequent (if the test is truthy)
const consequentBlock = builder.enter((blockId) => {
const consequentBlock = builder.enter("value", (blockId) => {
let value = consequent();
builder.push({
id: makeInstructionId(0),
@@ -1208,7 +1216,7 @@ function lowerConditional(
};
});
// Block for the alternate (if the test is not truthy)
const alternateBlock = builder.enter((blockId) => {
const alternateBlock = builder.enter("value", (blockId) => {
let value = alternate();
builder.push({
id: makeInstructionId(0),
@@ -1231,7 +1239,7 @@ function lowerConditional(
fallthrough: continuationBlock.id,
id: makeInstructionId(0),
};
builder.terminateWithContinuation(terminal, continuationBlock);
builder.terminateWithContinuation("value", terminal, continuationBlock);
return place;
}
+7
View File
@@ -141,7 +141,9 @@ export type HIR = {
* an exception occurs, therefore the block model only represents explicit throw
* statements and not implicit exceptions which may occur.
*/
export type BlockKind = "block" | "value";
export type BasicBlock = {
kind: BlockKind;
id: BlockId;
instructions: Array<Instruction>;
terminal: Terminal;
@@ -162,6 +164,11 @@ export type Terminal =
| ForTerminal
| WhileTerminal;
/**
* Terminal nodes allowed for a value block
*/
export type ValueTerminal = IfTerminal | GotoTerminal;
export type ThrowTerminal = { kind: "throw"; value: Place; id: InstructionId };
export type ReturnTerminal = {
+15 -5
View File
@@ -12,6 +12,7 @@ import { assertExhaustive } from "../Utils/utils";
import {
BasicBlock,
BlockId,
BlockKind,
GeneratedSource,
GotoVariant,
HIR,
@@ -146,6 +147,7 @@ export default class HIRBuilder {
build(): HIR {
const { id: blockId, instructions } = this.#current;
this.#completed.set(blockId, {
kind: "block",
id: blockId,
instructions,
terminal: {
@@ -177,9 +179,10 @@ export default class HIRBuilder {
/**
* Terminate the current block w the given terminal, and start a new block
*/
terminate(terminal: Terminal) {
terminate(kind: BlockKind, terminal: Terminal) {
const { id: blockId, instructions } = this.#current;
this.#completed.set(blockId, {
kind,
id: blockId,
instructions,
terminal,
@@ -194,12 +197,17 @@ export default class HIRBuilder {
* Terminate the current block w the given terminal, and set the previously
* reserved block as the new current block
*/
terminateWithContinuation(terminal: Terminal, continuation: WipBlock) {
terminateWithContinuation(
kind: BlockKind,
terminal: Terminal,
continuation: WipBlock
) {
const { id: blockId, instructions } = this.#current;
this.#completed.set(blockId, {
kind: kind,
id: blockId,
instructions,
terminal,
terminal: terminal,
preds: new Set(),
phis: new Set(),
});
@@ -218,9 +226,10 @@ export default class HIRBuilder {
/**
* Save a previously reserved block as completed
*/
complete(block: WipBlock, terminal: Terminal) {
complete(kind: BlockKind, block: WipBlock, terminal: Terminal) {
const { id: blockId, instructions } = block;
this.#completed.set(blockId, {
kind,
id: blockId,
instructions,
terminal,
@@ -235,13 +244,14 @@ export default class HIRBuilder {
* The lambda must return a terminal node, which is used to terminate the
* newly constructed block.
*/
enter(fn: (blockId: BlockId) => Terminal): BlockId {
enter(kind: BlockKind, fn: (blockId: BlockId) => Terminal): BlockId {
const current = this.#current;
const nextId = makeBlockId(this.#nextId++);
this.#current = newBlock(nextId);
const terminal = fn(nextId);
const { id: blockId, instructions } = this.#current;
this.#completed.set(blockId, {
kind,
id: blockId,
instructions,
terminal,
@@ -6,7 +6,6 @@
*/
import { invariant } from "../Utils/CompilerError";
import { assertExhaustive } from "../Utils/utils";
import {
BlockId,
Effect,
@@ -30,40 +29,10 @@ import {
*/
export function mergeConsecutiveBlocks(fn: HIRFunction): void {
const merged = new MergedBlocks();
const valueBlocks = new Set<BlockId>();
for (const [, block] of fn.body.blocks) {
// Before evaluating the block itself, determine which other blocks
// are value blocks. See above TODO which would allow us to bypass this.
const terminal = block.terminal;
switch (terminal.kind) {
case "while": {
valueBlocks.add(terminal.test);
break;
}
case "for": {
valueBlocks.add(terminal.init);
valueBlocks.add(terminal.test);
valueBlocks.add(terminal.update);
break;
}
case "if":
case "goto":
case "return":
case "throw":
case "switch": {
break;
}
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind '${(terminal as any).kind}'`
);
}
}
// Can only merge blocks with a single predecessor, can't merge
// value blocks
if (block.preds.size !== 1 || valueBlocks.has(block.id)) {
if (block.kind === "value" || block.preds.size !== 1) {
continue;
}
const originalPredecessorId = Array.from(block.preds)[0]!;
+1 -1
View File
@@ -47,7 +47,7 @@ export default function printHIR(
output.push(`${indent}${text}`);
};
for (const [blockId, block] of ir.blocks) {
output.push(`bb${blockId}:`);
output.push(`bb${blockId} (${block.kind}):`);
if (block.preds.size > 0) {
const preds = ["predecessor blocks:"];
for (const pred of block.preds) {
@@ -6,7 +6,6 @@
*/
import {
BlockId,
GotoVariant,
HIRFunction,
IdentifierId,
@@ -72,9 +71,6 @@ export function constantPropagation(fn: HIRFunction): void {
function applyConstantPropagation(fn: HIRFunction): boolean {
let hasChanges = false;
// A set of blocks whose terminals can't (yet) be safely rewritten
const valueBlocks = new Set<BlockId>();
const constants: Constants = new Map();
for (const [, block] of fn.body.blocks) {
// Initialize phi values if all operands have the same known constant value.
@@ -108,7 +104,7 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
}
}
if (valueBlocks.has(block.id)) {
if (block.kind === "value") {
// can't rewrite terminals in value blocks yet
continue;
}
@@ -130,16 +126,6 @@ function applyConstantPropagation(fn: HIRFunction): boolean {
}
break;
}
case "while": {
valueBlocks.add(terminal.test);
break;
}
case "for": {
valueBlocks.add(terminal.init);
valueBlocks.add(terminal.test);
valueBlocks.add(terminal.update);
break;
}
default: {
// no-op
}
+19 -19
View File
@@ -83,12 +83,12 @@
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
"@babel/generator@^7.20.5":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.5.tgz#cb25abee3178adf58d6814b68517c62bdbfdda95"
integrity sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==
"@babel/generator@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.20.7.tgz#f8ef57c8242665c5929fe2e8d82ba75460187b4a"
integrity sha512-7wqMOJq8doJMZmP4ApXTzLxSr7+oO2jroJURrVEp6XShrQUObV8Tq/D0NCcoYg2uHqUrjzO0zwBjoYzelxK+sw==
dependencies:
"@babel/types" "^7.20.5"
"@babel/types" "^7.20.7"
"@jridgewell/gen-mapping" "^0.3.2"
jsesc "^2.5.1"
@@ -260,10 +260,10 @@
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.19.3.tgz#8dd36d17c53ff347f9e55c328710321b49479a9a"
integrity sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==
"@babel/parser@^7.20.5":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.5.tgz#7f3c7335fe417665d929f34ae5dceae4c04015e8"
integrity sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==
"@babel/parser@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b"
integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg==
"@babel/plugin-syntax-async-generators@^7.8.4":
version "7.8.4"
@@ -484,18 +484,18 @@
lodash "^4.17.10"
"@babel/traverse@^7.19.1":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.5.tgz#78eb244bea8270fdda1ef9af22a5d5e5b7e57133"
integrity sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==
version "7.20.12"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.20.12.tgz#7f0f787b3a67ca4475adef1f56cb94f6abd4a4b5"
integrity sha512-MsIbFN0u+raeja38qboyF8TIT7K0BFzz/Yd/77ta4MsUsmP2RAnidIlwq7d5HFQrH/OZJecGV6B71C4zAgpoSQ==
dependencies:
"@babel/code-frame" "^7.18.6"
"@babel/generator" "^7.20.5"
"@babel/generator" "^7.20.7"
"@babel/helper-environment-visitor" "^7.18.9"
"@babel/helper-function-name" "^7.19.0"
"@babel/helper-hoist-variables" "^7.18.6"
"@babel/helper-split-export-declaration" "^7.18.6"
"@babel/parser" "^7.20.5"
"@babel/types" "^7.20.5"
"@babel/parser" "^7.20.7"
"@babel/types" "^7.20.7"
debug "^4.1.0"
globals "^11.1.0"
@@ -534,10 +534,10 @@
"@babel/helper-validator-identifier" "^7.19.1"
to-fast-properties "^2.0.0"
"@babel/types@^7.20.5":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.5.tgz#e206ae370b5393d94dfd1d04cd687cace53efa84"
integrity sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==
"@babel/types@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.20.7.tgz#54ec75e252318423fc07fb644dc6a58a64c09b7f"
integrity sha512-69OnhBxSSgK0OzTJai4kyPDiKTIe3j+ctaHdIGVbRahTLAT7L3R9oeXHC2aVSuGYt3cVnoAMDmOCgJ2yaiLMvg==
dependencies:
"@babel/helper-string-parser" "^7.19.4"
"@babel/helper-validator-identifier" "^7.19.1"