Scaffolding for early return from reactive scopes

Adds a new `earlyReturnValue` property on ReactiveScope which will be set if the 
scope had one or more early returns, with information about the temporary 
identifier that the early return value will be assigned to, as well as the label 
to be used for breaking (to simulate the early-return). The next PR shows the 
intended codegen.
This commit is contained in:
Joe Savona
2023-12-20 13:52:38 -08:00
parent 4c68da2e60
commit ed9d6a2ca1
4 changed files with 66 additions and 16 deletions
@@ -1053,10 +1053,35 @@ export function isMutableEffect(
export type ReactiveScope = {
id: ScopeId;
range: MutableRange;
/**
* The inputs to this reactive scope
*/
dependencies: ReactiveScopeDependencies;
/**
* The set of values produced by this scope. This may be empty
* for scopes that produce reassignments only.
*/
declarations: Map<IdentifierId, ReactiveScopeDeclaration>;
/**
* A mutable range may sometimes include a reassignment of some variable.
* This is the set of identifiers which are reassigned by this scope.
*/
reassignments: Set<Identifier>;
/**
* Reactive scopes may contain a return statement, which needs to be replayed
* whenever the inputs to the scope have not changed since the previous execution.
* If the reactive scope has an early return, this variable stores the temporary
* identifier to which the return value will be assigned. See PropagateEarlyReturns
* for more about how early returns in reactive scopes are compiled and represented.
*
* This value is null for scopes that do not contain early returns.
*/
earlyReturnValue: { value: IdentifierId; loc: SourceLocation } | null;
/*
* Some passes may merge scopes together. The merged set contains the
* ids of scopes that were merged into this one, for passes that need
@@ -451,11 +451,20 @@ function codegenReactiveScope(
const computationBlock = codegenBlock(cx, block);
computationBlock.body.push(...cacheStoreStatements);
const memoBlock = t.blockStatement(cacheLoadStatements);
const memoStatement = t.ifStatement(
testCondition,
computationBlock,
memoBlock
);
let memoStatement;
if (scope.earlyReturnValue !== null) {
// Has early return
CompilerError.throwTodo({
reason: `Codegen support for reactive scopes with early return`,
loc: scope.earlyReturnValue.loc,
description: null,
suggestions: null,
});
} else {
memoStatement = t.ifStatement(testCondition, computationBlock, memoBlock);
}
if (cx.env.config.enableMemoizationComments) {
if (changeExpressionComments.length) {
t.addComment(
@@ -199,6 +199,7 @@ export function inferReactiveScopeVariables(fn: HIRFunction): void {
dependencies: new Set(),
declarations: new Map(),
reassignments: new Set(),
earlyReturnValue: null,
merged: new Set(),
};
scopes.set(groupIdentifier, scope);
@@ -42,17 +42,32 @@ export function printReactiveFunction(fn: ReactiveFunction): string {
}
export function printReactiveScopeSummary(scope: ReactiveScope): string {
return `scope @${scope.id} [${scope.range.start}:${
scope.range.end
}] dependencies=[${Array.from(scope.dependencies)
.map((dep) => printDependency(dep))
.join(", ")}] declarations=[${Array.from(scope.declarations)
.map(([, decl]) =>
printIdentifier({ ...decl.identifier, scope: decl.scope })
)
.join(", ")}] reassignments=[${Array.from(scope.reassignments).map(
(reassign) => printIdentifier(reassign)
)}]`;
const items = [];
// If the scope has a return value it needs a label
items.push("scope");
items.push(`@${scope.id}`);
items.push(`[${scope.range.start}:${scope.range.end}]`);
items.push(
`dependencies=[${Array.from(scope.dependencies)
.map((dep) => printDependency(dep))
.join(", ")}]`
);
items.push(
`declarations=[${Array.from(scope.declarations)
.map(([, decl]) =>
printIdentifier({ ...decl.identifier, scope: decl.scope })
)
.join(", ")}]`
);
items.push(
`reassignments=[${Array.from(scope.reassignments).map((reassign) =>
printIdentifier(reassign)
)}]`
);
if (scope.earlyReturnValue !== null) {
items.push(`earlyReturn=${scope.earlyReturnValue.value}`);
}
return items.join(" ");
}
export function writeReactiveBlock(