@samitouri / QOS-React-2 / commits / 1014727d41

[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,