mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[HIR] Nonnullable block fallthroughs
ghstack-source-id: 6f24b60056c741e5d4e9836f91bc4fce0f9e8fdd Pull Request resolved: https://github.com/facebook/react-forget/pull/2865
This commit is contained in:
@@ -362,6 +362,8 @@ export type Terminal =
|
||||
| TryTerminal
|
||||
| ReactiveScopeTerminal;
|
||||
|
||||
export type TerminalWithFallthrough = Terminal & { fallthrough: BlockId };
|
||||
|
||||
function _staticInvariantTerminalHasLocation(
|
||||
terminal: Terminal
|
||||
): SourceLocation {
|
||||
@@ -376,6 +378,13 @@ function _staticInvariantTerminalHasInstructionId(
|
||||
return terminal.id;
|
||||
}
|
||||
|
||||
function _staticInvariantTerminalHasFallthrough(
|
||||
terminal: Terminal
|
||||
): BlockId | never | undefined {
|
||||
// If this fails, it is because a variant of Terminal is missing a fallthrough annotation
|
||||
return terminal.fallthrough;
|
||||
}
|
||||
|
||||
/*
|
||||
* Terminal nodes allowed for a value block
|
||||
* A terminal that couldn't be lowered correctly.
|
||||
@@ -384,6 +393,7 @@ export type UnsupportedTerminal = {
|
||||
kind: "unsupported";
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
fallthrough?: never;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -395,6 +405,7 @@ export type UnreachableTerminal = {
|
||||
kind: "unreachable";
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
fallthrough?: never;
|
||||
};
|
||||
|
||||
export type ThrowTerminal = {
|
||||
@@ -402,6 +413,7 @@ export type ThrowTerminal = {
|
||||
value: Place;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
fallthrough?: never;
|
||||
};
|
||||
export type Case = { test: Place | null; block: BlockId };
|
||||
|
||||
@@ -410,6 +422,7 @@ export type ReturnTerminal = {
|
||||
loc: SourceLocation;
|
||||
value: Place;
|
||||
id: InstructionId;
|
||||
fallthrough?: never;
|
||||
};
|
||||
|
||||
export type GotoTerminal = {
|
||||
@@ -418,6 +431,7 @@ export type GotoTerminal = {
|
||||
variant: GotoVariant;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
fallthrough?: never;
|
||||
};
|
||||
|
||||
export enum GotoVariant {
|
||||
@@ -431,7 +445,7 @@ export type IfTerminal = {
|
||||
test: Place;
|
||||
consequent: BlockId;
|
||||
alternate: BlockId;
|
||||
fallthrough: BlockId | null;
|
||||
fallthrough: BlockId;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
@@ -443,13 +457,14 @@ export type BranchTerminal = {
|
||||
alternate: BlockId;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
fallthrough?: never;
|
||||
};
|
||||
|
||||
export type SwitchTerminal = {
|
||||
kind: "switch";
|
||||
test: Place;
|
||||
cases: Array<Case>;
|
||||
fallthrough: BlockId | null;
|
||||
fallthrough: BlockId;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
@@ -521,7 +536,7 @@ export type TernaryTerminal = {
|
||||
export type LabelTerminal = {
|
||||
kind: "label";
|
||||
block: BlockId;
|
||||
fallthrough: BlockId | null;
|
||||
fallthrough: BlockId;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
@@ -555,7 +570,7 @@ export type TryTerminal = {
|
||||
handlerBinding: Place | null;
|
||||
handler: BlockId;
|
||||
// TODO: support `finally`
|
||||
fallthrough: BlockId | null;
|
||||
fallthrough: BlockId;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
};
|
||||
@@ -566,6 +581,7 @@ export type MaybeThrowTerminal = {
|
||||
handler: BlockId;
|
||||
id: InstructionId;
|
||||
loc: SourceLocation;
|
||||
fallthrough?: never;
|
||||
};
|
||||
|
||||
export type ReactiveScopeTerminal = {
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
import { Binding, NodePath } from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import { CompilerError } from "../CompilerError";
|
||||
import { assertExhaustive } from "../Utils/utils";
|
||||
import { Environment } from "./Environment";
|
||||
import { Global } from "./Globals";
|
||||
import {
|
||||
@@ -32,8 +31,8 @@ import {
|
||||
import { printInstruction } from "./PrintHIR";
|
||||
import {
|
||||
eachTerminalSuccessor,
|
||||
mapOptionalFallthroughs,
|
||||
mapTerminalSuccessors,
|
||||
terminalFallthrough,
|
||||
} from "./visitors";
|
||||
|
||||
/*
|
||||
@@ -329,7 +328,6 @@ export default class HIRBuilder {
|
||||
ir.blocks = rpoBlocks;
|
||||
|
||||
removeUnreachableForUpdates(ir);
|
||||
removeUnreachableFallthroughs(ir);
|
||||
removeDeadDoWhileStatements(ir);
|
||||
removeUnnecessaryTryCatch(ir);
|
||||
markInstructionIds(ir);
|
||||
@@ -629,24 +627,6 @@ export function removeUnreachableForUpdates(fn: HIR): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function removeUnreachableFallthroughs(func: HIR): void {
|
||||
const visited: Set<BlockId> = new Set();
|
||||
for (const [_, block] of func.blocks) {
|
||||
visited.add(block.id);
|
||||
}
|
||||
|
||||
// Cleanup any fallthrough blocks that weren't visited
|
||||
for (const [_, block] of func.blocks) {
|
||||
mapOptionalFallthroughs(block.terminal, (fallthrough) => {
|
||||
if (visited.has(fallthrough)) {
|
||||
return fallthrough;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function removeDeadDoWhileStatements(func: HIR): void {
|
||||
const visited: Set<BlockId> = new Set();
|
||||
for (const [_, block] of func.blocks) {
|
||||
@@ -691,14 +671,19 @@ export function reversePostorderBlocks(func: HIR): void {
|
||||
*/
|
||||
function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
|
||||
const visited: Set<BlockId> = new Set();
|
||||
const used: Set<BlockId> = new Set();
|
||||
const usedFallthroughs: Set<BlockId> = new Set();
|
||||
const postorder: Array<BlockId> = [];
|
||||
function visit(blockId: BlockId): void {
|
||||
if (visited.has(blockId)) {
|
||||
function visit(blockId: BlockId, isUsed: boolean): void {
|
||||
const wasUsed = used.has(blockId);
|
||||
const wasVisited = visited.has(blockId);
|
||||
visited.add(blockId);
|
||||
if (isUsed) {
|
||||
used.add(blockId);
|
||||
}
|
||||
if (wasVisited && (wasUsed || !isUsed)) {
|
||||
return;
|
||||
}
|
||||
visited.add(blockId);
|
||||
const block = func.blocks.get(blockId)!;
|
||||
const { terminal } = block;
|
||||
|
||||
/*
|
||||
* Note that we visit successors in reverse order. This ensures that when we
|
||||
@@ -710,7 +695,7 @@ function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
|
||||
* // bb1
|
||||
* x = 1;
|
||||
* } else {
|
||||
* // b2
|
||||
* // bb2
|
||||
* x = 2;
|
||||
* }
|
||||
* // bb3
|
||||
@@ -721,104 +706,50 @@ function getReversePostorderedBlocks(func: HIR): HIR["blocks"] {
|
||||
* program order for visual debugging. By visiting the successors in reverse order
|
||||
* (eg bb2 then bb1), we ensure that they get reversed back to the correct order.
|
||||
*/
|
||||
switch (terminal.kind) {
|
||||
case "return":
|
||||
case "throw": {
|
||||
// no-op, no successors
|
||||
break;
|
||||
}
|
||||
case "goto": {
|
||||
visit(terminal.block);
|
||||
break;
|
||||
}
|
||||
case "if": {
|
||||
/*
|
||||
* can ignore fallthrough, if its reachable it will be reached through
|
||||
* consequent/alternate
|
||||
*/
|
||||
const { consequent, alternate } = terminal;
|
||||
visit(alternate);
|
||||
visit(consequent);
|
||||
break;
|
||||
}
|
||||
case "branch": {
|
||||
const { consequent, alternate } = terminal;
|
||||
visit(alternate);
|
||||
visit(consequent);
|
||||
break;
|
||||
}
|
||||
case "switch": {
|
||||
/*
|
||||
* can ignore fallthrough, if its reachable it will be reached through
|
||||
* a case
|
||||
*/
|
||||
const { cases } = terminal;
|
||||
for (const case_ of [...cases].reverse()) {
|
||||
visit(case_.block);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "optional":
|
||||
case "ternary":
|
||||
case "logical": {
|
||||
visit(terminal.test);
|
||||
break;
|
||||
}
|
||||
case "do-while": {
|
||||
visit(terminal.loop);
|
||||
break;
|
||||
}
|
||||
case "while": {
|
||||
visit(terminal.test);
|
||||
break;
|
||||
}
|
||||
case "for":
|
||||
case "for-in":
|
||||
case "for-of": {
|
||||
visit(terminal.init);
|
||||
break;
|
||||
}
|
||||
case "label": {
|
||||
visit(terminal.block);
|
||||
break;
|
||||
}
|
||||
case "sequence": {
|
||||
visit(terminal.block);
|
||||
break;
|
||||
}
|
||||
case "maybe-throw": {
|
||||
visit(terminal.handler);
|
||||
visit(terminal.continuation);
|
||||
break;
|
||||
}
|
||||
case "try": {
|
||||
visit(terminal.block);
|
||||
break;
|
||||
}
|
||||
case "scope": {
|
||||
visit(terminal.block);
|
||||
break;
|
||||
}
|
||||
case "unreachable":
|
||||
case "unsupported": {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
terminal,
|
||||
`Unexpected terminal kind \`${(terminal as any).kind}\``
|
||||
);
|
||||
const block = func.blocks.get(blockId)!;
|
||||
const successors = [...eachTerminalSuccessor(block.terminal)].reverse();
|
||||
const fallthrough = terminalFallthrough(block.terminal);
|
||||
|
||||
/**
|
||||
* Fallthrough blocks are only used to record original program block structure. If the
|
||||
* fallthrough is actually reachable, it will be reached through terminal successors.
|
||||
* To retain program structure, we visit fallthrough blocks first (marking them as not
|
||||
* actually used yet) to ensure their block IDs emitted in the correct order.
|
||||
*/
|
||||
if (fallthrough != null) {
|
||||
if (isUsed) {
|
||||
usedFallthroughs.add(fallthrough);
|
||||
}
|
||||
visit(fallthrough, false);
|
||||
}
|
||||
for (const successor of successors) {
|
||||
visit(successor, isUsed);
|
||||
}
|
||||
|
||||
postorder.push(blockId);
|
||||
if (!wasVisited) {
|
||||
postorder.push(blockId);
|
||||
}
|
||||
}
|
||||
visit(func.entry);
|
||||
|
||||
const blocks = new Map();
|
||||
visit(func.entry, true);
|
||||
const blocks = new Map<BlockId, BasicBlock>();
|
||||
for (const blockId of postorder.reverse()) {
|
||||
blocks.set(blockId, func.blocks.get(blockId)!);
|
||||
const block = func.blocks.get(blockId)!;
|
||||
if (used.has(blockId)) {
|
||||
blocks.set(blockId, func.blocks.get(blockId)!);
|
||||
} else if (usedFallthroughs.has(blockId)) {
|
||||
blocks.set(blockId, {
|
||||
...block,
|
||||
instructions: [],
|
||||
terminal: {
|
||||
kind: "unreachable",
|
||||
id: block.terminal.id,
|
||||
loc: block.terminal.loc,
|
||||
},
|
||||
});
|
||||
}
|
||||
// otherwise this block is unreachable
|
||||
}
|
||||
|
||||
return blocks;
|
||||
}
|
||||
|
||||
@@ -847,6 +778,14 @@ export function markPredecessors(func: HIR): void {
|
||||
const visited: Set<BlockId> = new Set();
|
||||
function visit(blockId: BlockId, prevBlock: BasicBlock | null): void {
|
||||
const block = func.blocks.get(blockId)!;
|
||||
if (block == null) {
|
||||
return;
|
||||
}
|
||||
CompilerError.invariant(block != null, {
|
||||
reason: "unexpected missing block",
|
||||
description: `block ${blockId}`,
|
||||
loc: GeneratedSource,
|
||||
});
|
||||
if (prevBlock) {
|
||||
block.preds.add(prevBlock.id);
|
||||
}
|
||||
@@ -879,7 +818,7 @@ function getTargetIfIndirection(block: BasicBlock): number | null {
|
||||
|
||||
/*
|
||||
* Finds try terminals where the handler is unreachable, and converts the try
|
||||
* to a goto(terminal.fallthrough)
|
||||
* to a goto(terminal.block)
|
||||
*/
|
||||
export function removeUnnecessaryTryCatch(fn: HIR): void {
|
||||
for (const [, block] of fn.blocks) {
|
||||
@@ -887,6 +826,9 @@ export function removeUnnecessaryTryCatch(fn: HIR): void {
|
||||
block.terminal.kind === "try" &&
|
||||
!fn.blocks.has(block.terminal.handler)
|
||||
) {
|
||||
const handlerId = block.terminal.handler;
|
||||
const fallthroughId = block.terminal.fallthrough;
|
||||
const fallthrough = fn.blocks.get(fallthroughId);
|
||||
block.terminal = {
|
||||
kind: "goto",
|
||||
block: block.terminal.block,
|
||||
@@ -894,6 +836,15 @@ export function removeUnnecessaryTryCatch(fn: HIR): void {
|
||||
loc: block.terminal.loc,
|
||||
variant: GotoVariant.Break,
|
||||
};
|
||||
|
||||
if (fallthrough != null) {
|
||||
if (fallthrough.preds.size === 1 && fallthrough.preds.has(handlerId)) {
|
||||
// delete fallthrough
|
||||
fn.blocks.delete(fallthroughId);
|
||||
} else {
|
||||
fallthrough.preds.delete(handlerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,8 +13,8 @@ import {
|
||||
HIRFunction,
|
||||
Instruction,
|
||||
} from "./HIR";
|
||||
import { markPredecessors, removeUnreachableFallthroughs } from "./HIRBuilder";
|
||||
import { mapOptionalFallthroughs, terminalFallthrough } from "./visitors";
|
||||
import { markPredecessors } from "./HIRBuilder";
|
||||
import { terminalFallthrough, terminalHasFallthrough } from "./visitors";
|
||||
|
||||
/*
|
||||
* Merges sequences of blocks that will always execute consecutively —
|
||||
@@ -113,10 +113,11 @@ export function mergeConsecutiveBlocks(fn: HIRFunction): void {
|
||||
fn.body.blocks.delete(block.id);
|
||||
}
|
||||
markPredecessors(fn.body);
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
mapOptionalFallthroughs(block.terminal, (blockId) => merged.get(blockId));
|
||||
for (const [, { terminal }] of fn.body.blocks) {
|
||||
if (terminalHasFallthrough(terminal)) {
|
||||
terminal.fallthrough = merged.get(terminal.fallthrough);
|
||||
}
|
||||
}
|
||||
removeUnreachableFallthroughs(fn.body);
|
||||
}
|
||||
|
||||
class MergedBlocks {
|
||||
|
||||
@@ -23,7 +23,6 @@ export {
|
||||
markInstructionIds,
|
||||
markPredecessors,
|
||||
removeUnnecessaryTryCatch,
|
||||
removeUnreachableFallthroughs,
|
||||
reversePostorderBlocks,
|
||||
} from "./HIRBuilder";
|
||||
export { mergeConsecutiveBlocks } from "./MergeConsecutiveBlocks";
|
||||
|
||||
@@ -634,8 +634,7 @@ export function mapTerminalSuccessors(
|
||||
case "if": {
|
||||
const consequent = fn(terminal.consequent);
|
||||
const alternate = fn(terminal.alternate);
|
||||
const fallthrough =
|
||||
terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
|
||||
const fallthrough = fn(terminal.fallthrough);
|
||||
return {
|
||||
kind: "if",
|
||||
test: terminal.test,
|
||||
@@ -666,8 +665,7 @@ export function mapTerminalSuccessors(
|
||||
block: target,
|
||||
};
|
||||
});
|
||||
const fallthrough =
|
||||
terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
|
||||
const fallthrough = fn(terminal.fallthrough);
|
||||
return {
|
||||
kind: "switch",
|
||||
test: terminal.test,
|
||||
@@ -794,8 +792,7 @@ export function mapTerminalSuccessors(
|
||||
}
|
||||
case "label": {
|
||||
const block = fn(terminal.block);
|
||||
const fallthrough =
|
||||
terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
|
||||
const fallthrough = fn(terminal.fallthrough);
|
||||
return {
|
||||
kind: "label",
|
||||
block,
|
||||
@@ -829,8 +826,7 @@ export function mapTerminalSuccessors(
|
||||
case "try": {
|
||||
const block = fn(terminal.block);
|
||||
const handler = fn(terminal.handler);
|
||||
const fallthrough =
|
||||
terminal.fallthrough !== null ? fn(terminal.fallthrough) : null;
|
||||
const fallthrough = fn(terminal.fallthrough);
|
||||
return {
|
||||
kind: "try",
|
||||
block,
|
||||
@@ -866,12 +862,10 @@ export function mapTerminalSuccessors(
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper to get a terminal's fallthrough. The main reason to extract this as a helper
|
||||
* function is to ensure that we use an exhaustive switch to ensure that we add new terminal
|
||||
* variants as appropriate.
|
||||
*/
|
||||
export function terminalFallthrough(terminal: Terminal): BlockId | null {
|
||||
export function terminalHasFallthrough<
|
||||
T extends Terminal,
|
||||
U extends T & { fallthrough: BlockId }
|
||||
>(terminal: T): terminal is U {
|
||||
switch (terminal.kind) {
|
||||
case "maybe-throw":
|
||||
case "branch":
|
||||
@@ -880,7 +874,8 @@ export function terminalFallthrough(terminal: Terminal): BlockId | null {
|
||||
case "throw":
|
||||
case "unreachable":
|
||||
case "unsupported": {
|
||||
return null;
|
||||
const _: undefined = terminal.fallthrough;
|
||||
return false;
|
||||
}
|
||||
case "try":
|
||||
case "do-while":
|
||||
@@ -896,7 +891,8 @@ export function terminalFallthrough(terminal: Terminal): BlockId | null {
|
||||
case "ternary":
|
||||
case "while":
|
||||
case "scope": {
|
||||
return terminal.fallthrough;
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
return true;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
@@ -907,104 +903,16 @@ export function terminalFallthrough(terminal: Terminal): BlockId | null {
|
||||
}
|
||||
}
|
||||
|
||||
export function mapOptionalFallthroughs(
|
||||
terminal: Terminal,
|
||||
fn: (block: BlockId) => BlockId | null
|
||||
): void {
|
||||
switch (terminal.kind) {
|
||||
case "maybe-throw":
|
||||
case "branch":
|
||||
case "goto":
|
||||
case "return":
|
||||
case "throw":
|
||||
case "unreachable":
|
||||
case "unsupported": {
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* NOTE: TypeScript has a bug where it does not correctly model properties whose values are
|
||||
* non-null in some cases and nullable in other cases, if those cases are joined together.
|
||||
* Thus we use one block per case here to ensure that any changes to the types will cause
|
||||
* a compiler error.
|
||||
*/
|
||||
case "do-while": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "for-of": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "for-in": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "for": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "logical": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "optional": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "ternary": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "while": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "scope": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "switch": {
|
||||
if (terminal.fallthrough !== null) {
|
||||
terminal.fallthrough = fn(terminal.fallthrough);
|
||||
} else {
|
||||
terminal.fallthrough = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "if": {
|
||||
if (terminal.fallthrough !== null) {
|
||||
terminal.fallthrough = fn(terminal.fallthrough);
|
||||
} else {
|
||||
terminal.fallthrough = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "label": {
|
||||
if (terminal.fallthrough !== null) {
|
||||
terminal.fallthrough = fn(terminal.fallthrough);
|
||||
} else {
|
||||
terminal.fallthrough = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "sequence": {
|
||||
const _: BlockId = terminal.fallthrough;
|
||||
break;
|
||||
}
|
||||
case "try": {
|
||||
if (terminal.fallthrough !== null) {
|
||||
terminal.fallthrough = fn(terminal.fallthrough);
|
||||
} else {
|
||||
terminal.fallthrough = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
assertExhaustive(
|
||||
terminal,
|
||||
`Unexpected terminal kind \`${(terminal as any).kind}\``
|
||||
);
|
||||
}
|
||||
/*
|
||||
* Helper to get a terminal's fallthrough. The main reason to extract this as a helper
|
||||
* function is to ensure that we use an exhaustive switch to ensure that we add new terminal
|
||||
* variants as appropriate.
|
||||
*/
|
||||
export function terminalFallthrough(terminal: Terminal): BlockId | null {
|
||||
if (terminalHasFallthrough(terminal)) {
|
||||
return terminal.fallthrough;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,6 @@ import {
|
||||
markInstructionIds,
|
||||
markPredecessors,
|
||||
mergeConsecutiveBlocks,
|
||||
removeUnreachableFallthroughs,
|
||||
reversePostorderBlocks,
|
||||
} from "../HIR";
|
||||
import {
|
||||
@@ -72,7 +71,6 @@ function constantPropagationImpl(fn: HIRFunction, constants: Constants): void {
|
||||
* Re-run minification of the graph (incl reordering instruction ids)
|
||||
*/
|
||||
reversePostorderBlocks(fn.body);
|
||||
removeUnreachableFallthroughs(fn.body);
|
||||
removeUnreachableForUpdates(fn.body);
|
||||
removeDeadDoWhileStatements(fn.body);
|
||||
removeUnnecessaryTryCatch(fn.body);
|
||||
|
||||
@@ -15,7 +15,6 @@ import {
|
||||
assertConsistentIdentifiers,
|
||||
assertTerminalSuccessorsExist,
|
||||
mergeConsecutiveBlocks,
|
||||
removeUnreachableFallthroughs,
|
||||
reversePostorderBlocks,
|
||||
} from "../HIR";
|
||||
import {
|
||||
@@ -39,7 +38,6 @@ export function pruneMaybeThrows(fn: HIRFunction): void {
|
||||
* Re-run minification of the graph (incl reordering instruction ids)
|
||||
*/
|
||||
reversePostorderBlocks(fn.body);
|
||||
removeUnreachableFallthroughs(fn.body);
|
||||
removeUnreachableForUpdates(fn.body);
|
||||
removeDeadDoWhileStatements(fn.body);
|
||||
removeUnnecessaryTryCatch(fn.body);
|
||||
|
||||
+19
-20
@@ -112,7 +112,7 @@ class Driver {
|
||||
}
|
||||
case "if": {
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
this.cx.reachable(terminal.fallthrough) &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
@@ -176,7 +176,7 @@ class Driver {
|
||||
}
|
||||
case "switch": {
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
this.cx.reachable(terminal.fallthrough) &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
@@ -289,7 +289,7 @@ class Driver {
|
||||
}
|
||||
case "while": {
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
this.cx.reachable(terminal.fallthrough) &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
@@ -350,11 +350,9 @@ class Driver {
|
||||
? terminal.loop
|
||||
: null;
|
||||
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
const fallthroughId = !this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
|
||||
const scheduleId = this.cx.scheduleLoop(
|
||||
terminal.fallthrough,
|
||||
@@ -437,11 +435,9 @@ class Driver {
|
||||
? terminal.loop
|
||||
: null;
|
||||
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
const fallthroughId = !this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
|
||||
const scheduleId = this.cx.scheduleLoop(
|
||||
terminal.fallthrough,
|
||||
@@ -512,11 +508,9 @@ class Driver {
|
||||
? terminal.loop
|
||||
: null;
|
||||
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
const fallthroughId = !this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
|
||||
const scheduleId = this.cx.scheduleLoop(
|
||||
terminal.fallthrough,
|
||||
@@ -626,7 +620,7 @@ class Driver {
|
||||
}
|
||||
case "label": {
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
this.cx.reachable(terminal.fallthrough) &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
@@ -747,7 +741,7 @@ class Driver {
|
||||
}
|
||||
case "try": {
|
||||
const fallthroughId =
|
||||
terminal.fallthrough !== null &&
|
||||
this.cx.reachable(terminal.fallthrough) &&
|
||||
!this.cx.isScheduled(terminal.fallthrough)
|
||||
? terminal.fallthrough
|
||||
: null;
|
||||
@@ -1241,6 +1235,11 @@ class Context {
|
||||
this.#catchHandlers.add(block);
|
||||
}
|
||||
|
||||
reachable(id: BlockId): boolean {
|
||||
const block = this.ir.blocks.get(id)!;
|
||||
return block.terminal.kind !== "unreachable";
|
||||
}
|
||||
|
||||
/*
|
||||
* Record that the given block will be emitted (eg by the codegen of a parent node)
|
||||
* so that child nodes can avoid re-emitting it.
|
||||
|
||||
+25
-17
@@ -40,38 +40,46 @@ import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
import { mutate, setProperty, throwErrorWithMessageIf } from "shared-runtime";
|
||||
|
||||
function useFoo(t0) {
|
||||
const $ = useMemoCache(3);
|
||||
const $ = useMemoCache(6);
|
||||
const { value, cond } = t0;
|
||||
let y;
|
||||
let t1;
|
||||
if ($[0] !== value || $[1] !== cond) {
|
||||
t1 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
const y = [value];
|
||||
const x = { cond };
|
||||
try {
|
||||
mutate(x);
|
||||
throwErrorWithMessageIf(x.cond, "error");
|
||||
} catch {
|
||||
setProperty(x, "henderson");
|
||||
t1 = x;
|
||||
break bb0;
|
||||
y = [value];
|
||||
let x;
|
||||
if ($[4] !== cond) {
|
||||
x = { cond };
|
||||
try {
|
||||
mutate(x);
|
||||
throwErrorWithMessageIf(x.cond, "error");
|
||||
} catch {
|
||||
setProperty(x, "henderson");
|
||||
t1 = x;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
setProperty(x, "nevada");
|
||||
$[4] = cond;
|
||||
$[5] = x;
|
||||
} else {
|
||||
x = $[5];
|
||||
}
|
||||
|
||||
setProperty(x, "nevada");
|
||||
y.push(x);
|
||||
|
||||
t1 = y;
|
||||
break bb0;
|
||||
}
|
||||
$[0] = value;
|
||||
$[1] = cond;
|
||||
$[2] = t1;
|
||||
$[2] = y;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
y = $[2];
|
||||
t1 = $[3];
|
||||
}
|
||||
if (t1 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t1;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+1
-3
@@ -45,9 +45,6 @@ function Component(props) {
|
||||
t0 = e;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
t0 = null;
|
||||
break bb0;
|
||||
}
|
||||
$[0] = props.y;
|
||||
$[1] = props.e;
|
||||
@@ -58,6 +55,7 @@ function Component(props) {
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+8
-7
@@ -31,12 +31,13 @@ import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
const { throwInput } = require("shared-runtime");
|
||||
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(1);
|
||||
const $ = useMemoCache(2);
|
||||
let x;
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = Symbol.for("react.early_return_sentinel");
|
||||
bb0: {
|
||||
const x = [];
|
||||
x = [];
|
||||
try {
|
||||
throwInput(x);
|
||||
} catch (t1) {
|
||||
@@ -45,17 +46,17 @@ function Component(props) {
|
||||
t0 = e;
|
||||
break bb0;
|
||||
}
|
||||
|
||||
t0 = x;
|
||||
break bb0;
|
||||
}
|
||||
$[0] = t0;
|
||||
$[0] = x;
|
||||
$[1] = t0;
|
||||
} else {
|
||||
t0 = $[0];
|
||||
x = $[0];
|
||||
t0 = $[1];
|
||||
}
|
||||
if (t0 !== Symbol.for("react.early_return_sentinel")) {
|
||||
return t0;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
Reference in New Issue
Block a user