Fix block scoping issues from MergeConsecutiveBlocks

Fixes the issues from the previous PR. It's a simple fix — we don't merge 
consecutive blocks if the successor block is some terminal's fallthrough.
This commit is contained in:
Joe Savona
2024-01-23 11:41:00 -08:00
parent b2f44c103b
commit c92ad38375
6 changed files with 48 additions and 40 deletions
@@ -14,7 +14,7 @@ import {
Instruction,
} from "./HIR";
import { markPredecessors, removeUnreachableFallthroughs } from "./HIRBuilder";
import { mapOptionalFallthroughs } from "./visitors";
import { mapOptionalFallthroughs, terminalFallthrough } from "./visitors";
/*
* Merges sequences of blocks that will always execute consecutively —
@@ -30,7 +30,13 @@ import { mapOptionalFallthroughs } from "./visitors";
*/
export function mergeConsecutiveBlocks(fn: HIRFunction): void {
const merged = new MergedBlocks();
const fallthroughBlocks = new Set<BlockId>();
for (const [, block] of fn.body.blocks) {
const fallthrough = terminalFallthrough(block.terminal);
if (fallthrough !== null) {
fallthroughBlocks.add(fallthrough);
}
for (const instr of block.instructions) {
if (
instr.value.kind === "FunctionExpression" ||
@@ -40,11 +46,14 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
}
}
/*
* Can only merge blocks with a single predecessor, can't merge
* value blocks
*/
if (block.kind !== "block" || block.preds.size !== 1) {
if (
// Can only merge blocks with a single predecessor
block.preds.size !== 1 ||
// Value blocks cannot merge
block.kind !== "block" ||
// Merging across fallthroughs could move the predecessor out of its block scope
fallthroughBlocks.has(block.id)
) {
continue;
}
const originalPredecessorId = Array.from(block.preds)[0]!;
@@ -28,11 +28,14 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
function useHook(a, b) {
switch (a) {
bb1: switch (a) {
case 1: {
if (b == null) {
return;
}
console.log(b);
break bb1;
}
case 2: {
return;
@@ -41,8 +44,6 @@ function useHook(a, b) {
return;
}
}
console.log(b);
}
export const FIXTURE_ENTRYPOINT = {
@@ -51,4 +52,7 @@ export const FIXTURE_ENTRYPOINT = {
};
```
### Eval output
(kind: ok)
logs: ['foo']
@@ -35,23 +35,21 @@ import { useMemo, unstable_useMemoCache as useMemoCache } from "react";
function Component(props) {
const $ = useMemoCache(2);
let t22;
bb2: {
let t0;
if ($[0] !== props.value) {
t0 = { value: props.value };
$[0] = props.value;
$[1] = t0;
} else {
t0 = $[1];
let t0;
if ($[0] !== props.value) {
t0 = { value: props.value };
$[0] = props.value;
$[1] = t0;
} else {
t0 = $[1];
}
const handlers = t0;
bb2: switch (props.test) {
case true: {
console.log(handlers.value);
break bb2;
}
const handlers = t0;
switch (props.test) {
case true: {
console.log(handlers.value);
break bb2;
}
default: {
}
default: {
}
}
@@ -68,10 +66,5 @@ export const FIXTURE_ENTRYPOINT = {
```
### Eval output
(kind: exception) handlers.foo is not a function
logs: ['The above error occurred in the <WrapperTestComponent> component:\n' +
'\n' +
' at WrapperTestComponent (<project_root>/packages/sprout/dist/runner-evaluator.js:55:26)\n' +
'\n' +
'Consider adding an error boundary to your tree to customize error handling behavior.\n' +
'Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.']
(kind: ok) {"value":"hello"}
logs: ['hello']
@@ -79,6 +79,8 @@ function Component(props) {
x.push(42);
t37 = x;
break bb8;
} else {
console.log("fallthrough");
}
}
$[0] = t37;
@@ -88,8 +90,6 @@ function Component(props) {
if (t37 !== Symbol.for("react.early_return_sentinel")) {
return t37;
}
console.log("fallthrough");
let t0;
if ($[1] !== props.a) {
t0 = makeArray(props.a);
@@ -29,9 +29,9 @@ export const FIXTURE_ENTRYPOINT = {
function Component(props) {
let t16;
bb10: {
bb5: {
bb2: {
if (props.cond) {
break bb5;
break bb2;
}
t16 = props.a;
@@ -6,6 +6,11 @@
*/
const skipFilter = new Set([
/**
* Observable different in logging between Forget and non-Forget
*/
"early-return-no-declarations-reassignments-dependencies",
/**
* Category A:
* Tests with 0 parameters and 0 refs to external values
@@ -517,9 +522,6 @@ const skipFilter = new Set([
"bug-invalid-code-when-bailout",
"component-syntax-ref-gating.flow",
"block-scoping-switch-dead-code",
"block-scoping-switch-variable-scoping",
// 'react-forget-runtime' not yet supported
"flag-enable-emit-hook-guards",
]);