Flatten scopes within loops

We can't independently memoize values created within a loop, so this pass 
flattens scopes within loops. Right now this just flattens the scope away 
without propagating any dependency (or output) information, follow-ups will 
extend it to do that.
This commit is contained in:
Joe Savona
2022-12-14 08:42:37 -08:00
parent be9b6356df
commit 5f3dc0b6c7
3 changed files with 147 additions and 3 deletions
@@ -0,0 +1,144 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
import { assertExhaustive } from "../Common/utils";
import { ReactiveBasicBlock, ReactiveBlock, ReactiveFunction } from "./HIR";
/**
* Given a reactive function, flattens any scopes contained within a loop construct.
* We won't initially support memoization within loops though this is possible in the future.
*/
export function flattenReactiveLoops(fn: ReactiveFunction): void {
visit(fn.body, false);
}
function visit(block: ReactiveBasicBlock, shouldFlatten: boolean): void {
let i = 0;
while (i < block.length) {
const item = block[i]!;
switch (item.kind) {
case "block": {
if (shouldFlatten) {
const successors = block.splice(i + 1);
block.pop(); // remove the current element
flatten(item, block);
i = block.length;
block.push(...successors);
} else {
visit(item.instructions, false);
i++;
}
break;
}
case "instruction": {
i++;
break;
}
case "terminal": {
const terminal = item.terminal;
switch (terminal.kind) {
case "break":
case "continue":
case "return":
case "throw": {
break;
}
case "for": {
visit(terminal.loop, true);
break;
}
case "while": {
visit(terminal.loop, true);
break;
}
case "if": {
visit(terminal.consequent, shouldFlatten);
if (terminal.alternate !== null) {
visit(terminal.alternate, shouldFlatten);
}
break;
}
case "switch": {
for (const case_ of terminal.cases) {
if (case_.block !== undefined) {
visit(case_.block, shouldFlatten);
}
}
break;
}
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind '${(terminal as any).kind}'`
);
}
}
i++;
break;
}
default: {
assertExhaustive(item, `Unexpected item`);
}
}
}
}
function flatten(scope: ReactiveBlock, block: ReactiveBasicBlock): void {
for (const item of scope.instructions) {
switch (item.kind) {
case "block": {
flatten(item, block);
break;
}
case "terminal": {
const terminal = item.terminal;
switch (terminal.kind) {
case "break":
case "continue":
case "return":
case "throw": {
break;
}
case "for": {
visit(terminal.loop, true);
break;
}
case "while": {
visit(terminal.loop, true);
break;
}
case "if": {
visit(terminal.consequent, true);
if (terminal.alternate !== null) {
visit(terminal.alternate, true);
}
break;
}
case "switch": {
for (const case_ of terminal.cases) {
if (case_.block !== undefined) {
visit(case_.block, true);
}
}
break;
}
default: {
assertExhaustive(
terminal,
`Unexpected terminal kind '${(terminal as any).kind}'`
);
}
}
block.push(item);
break;
}
default: {
block.push(item);
}
}
}
}
@@ -157,9 +157,7 @@ function Component(
}
[22] Call mutate seen$33_@0.add(mutate item$40_@0:TPrimitive)
[23] Const mutate $49:TPrimitive = "div"
scope @3 [24:25] deps=[read $49:TPrimitive] {
[24] Const mutate $50_@3 = JSX <read $49:TPrimitive>{freeze item$40_@0:TPrimitive}</read $49:TPrimitive>
}
[24] Const mutate $50_@3 = JSX <read $49:TPrimitive>{freeze item$40_@0:TPrimitive}</read $49:TPrimitive>
[25] Call mutate renderedItems$32_@0.push(read $50_@3)
[26] Const mutate $55:TPrimitive = Binary read renderedItems$32_@0.length >= read max$35_@2:TPrimitive
if (read $55:TPrimitive) {
@@ -15,6 +15,7 @@ import invariant from "invariant";
import path from "path";
import prettier from "prettier";
import { buildReactiveFunction } from "../HIR/BuildReactiveFunction";
import { flattenReactiveLoops } from "../HIR/FlattenReactiveLoops";
import { toggleLogging } from "../HIR/logger";
import run from "../HIR/Pipeline";
import { printFunction } from "../HIR/PrintHIR";
@@ -148,6 +149,7 @@ function transform(text: string, file: string): Array<TestOutput> {
});
const reactiveFunction = buildReactiveFunction(ir);
flattenReactiveLoops(reactiveFunction);
const scopes = printReactiveFunction(reactiveFunction);
const textHIR = printFunction(ir);