diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts index 5bee25f325..ff633bc2fa 100644 --- a/compiler/forget/src/HIR/BuildHIR.ts +++ b/compiler/forget/src/HIR/BuildHIR.ts @@ -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; 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; 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; 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; } diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index 075669a31e..2fc713b755 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -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; 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 = { diff --git a/compiler/forget/src/HIR/HIRBuilder.ts b/compiler/forget/src/HIR/HIRBuilder.ts index d82bfcca7e..5ed4b9e588 100644 --- a/compiler/forget/src/HIR/HIRBuilder.ts +++ b/compiler/forget/src/HIR/HIRBuilder.ts @@ -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, diff --git a/compiler/forget/src/HIR/MergeConsecutiveBlocks.ts b/compiler/forget/src/HIR/MergeConsecutiveBlocks.ts index fc84ccc776..5f83abd1ae 100644 --- a/compiler/forget/src/HIR/MergeConsecutiveBlocks.ts +++ b/compiler/forget/src/HIR/MergeConsecutiveBlocks.ts @@ -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(); 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]!; diff --git a/compiler/forget/src/HIR/PrintHIR.ts b/compiler/forget/src/HIR/PrintHIR.ts index 43115aa036..88becee2c7 100644 --- a/compiler/forget/src/HIR/PrintHIR.ts +++ b/compiler/forget/src/HIR/PrintHIR.ts @@ -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) { diff --git a/compiler/forget/src/Optimization/ConstantPropagation.ts b/compiler/forget/src/Optimization/ConstantPropagation.ts index 76316782ec..920421f1ba 100644 --- a/compiler/forget/src/Optimization/ConstantPropagation.ts +++ b/compiler/forget/src/Optimization/ConstantPropagation.ts @@ -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(); - 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 } diff --git a/compiler/forget/yarn.lock b/compiler/forget/yarn.lock index 84bcf2e1ec..13080a3355 100644 --- a/compiler/forget/yarn.lock +++ b/compiler/forget/yarn.lock @@ -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"