mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[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:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user