mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
TryStatement: handle edge case control flows
Adds handling for some cases where the handler is unreachable (or is provably unreachable after analysis & optimization), where the try/catch can be flattened away: * The try block is empty. Nothing can throw, so the handler is unreachable. * The try block will always return. It can't return anything interesting (ie the result of a function call or variable load) since those could throw, but a try block that always return a primitive means the handler is unreachable. * The same, except where we only determine that the try block always returns via constant propagation.
This commit is contained in:
@@ -137,6 +137,9 @@ export function* run(
|
||||
deadCodeElimination(hir);
|
||||
yield log({ kind: "hir", name: "DeadCodeElimination", value: hir });
|
||||
|
||||
pruneMaybeThrows(hir);
|
||||
yield log({ kind: "hir", name: "PruneMaybeThrows", value: hir });
|
||||
|
||||
inferMutableRanges(hir);
|
||||
yield log({ kind: "hir", name: "InferMutableRanges", value: hir });
|
||||
|
||||
|
||||
@@ -316,6 +316,7 @@ export default class HIRBuilder {
|
||||
removeUnreachableForUpdates(ir);
|
||||
removeUnreachableFallthroughs(ir);
|
||||
removeDeadDoWhileStatements(ir);
|
||||
removeUnnecessaryTryCatch(ir);
|
||||
markInstructionIds(ir);
|
||||
markPredecessors(ir);
|
||||
|
||||
@@ -846,3 +847,24 @@ function getTargetIfIndirection(block: BasicBlock): number | null {
|
||||
? block.terminal.block
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds try terminals where the handler is unreachable, and converts the try
|
||||
* to a goto(terminal.fallthrough)
|
||||
*/
|
||||
export function removeUnnecessaryTryCatch(fn: HIR): void {
|
||||
for (const [, block] of fn.blocks) {
|
||||
if (
|
||||
block.terminal.kind === "try" &&
|
||||
!fn.blocks.has(block.terminal.handler)
|
||||
) {
|
||||
block.terminal = {
|
||||
kind: "goto",
|
||||
block: block.terminal.block,
|
||||
id: makeInstructionId(0),
|
||||
loc: block.terminal.loc,
|
||||
variant: GotoVariant.Break,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ export * from "./HIR";
|
||||
export {
|
||||
markInstructionIds,
|
||||
markPredecessors,
|
||||
removeUnnecessaryTryCatch,
|
||||
removeUnreachableFallthroughs,
|
||||
reversePostorderBlocks,
|
||||
} from "./HIRBuilder";
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
} from "../HIR";
|
||||
import {
|
||||
removeDeadDoWhileStatements,
|
||||
removeUnnecessaryTryCatch,
|
||||
removeUnreachableForUpdates,
|
||||
} from "../HIR/HIRBuilder";
|
||||
import { eliminateRedundantPhi } from "../SSA";
|
||||
@@ -66,6 +67,7 @@ function constantPropagationImpl(fn: HIRFunction, constants: Constants): void {
|
||||
removeUnreachableFallthroughs(fn.body);
|
||||
removeUnreachableForUpdates(fn.body);
|
||||
removeDeadDoWhileStatements(fn.body);
|
||||
removeUnnecessaryTryCatch(fn.body);
|
||||
markInstructionIds(fn.body);
|
||||
markPredecessors(fn.body);
|
||||
|
||||
|
||||
@@ -9,8 +9,20 @@ import {
|
||||
GotoVariant,
|
||||
HIRFunction,
|
||||
Instruction,
|
||||
assertConsistentIdentifiers,
|
||||
assertTerminalSuccessorsExist,
|
||||
mergeConsecutiveBlocks,
|
||||
removeUnreachableFallthroughs,
|
||||
reversePostorderBlocks,
|
||||
} from "../HIR";
|
||||
import {
|
||||
markInstructionIds,
|
||||
markPredecessors,
|
||||
removeDeadDoWhileStatements,
|
||||
removeUnnecessaryTryCatch,
|
||||
removeUnreachableForUpdates,
|
||||
} from "../HIR/HIRBuilder";
|
||||
import { eliminateRedundantPhi } from "../SSA";
|
||||
|
||||
/**
|
||||
* This pass prunes `maybe-throw` terminals for blocks that can provably *never* throw.
|
||||
@@ -20,7 +32,35 @@ import {
|
||||
export function pruneMaybeThrows(fn: HIRFunction): void {
|
||||
const didPrune = pruneMaybeThrowsImpl(fn);
|
||||
if (didPrune) {
|
||||
// If terminals have changed then blocks may have become newly unreachable.
|
||||
// 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);
|
||||
markInstructionIds(fn.body);
|
||||
markPredecessors(fn.body);
|
||||
|
||||
// Now that predecessors are updated, prune phi operands that can never be reached
|
||||
for (const [, block] of fn.body.blocks) {
|
||||
for (const phi of block.phis) {
|
||||
for (const [predecessor] of phi.operands) {
|
||||
if (!block.preds.has(predecessor)) {
|
||||
phi.operands.delete(predecessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// By removing some phi operands, there may be phis that were not previously
|
||||
// redundant but now are
|
||||
eliminateRedundantPhi(fn);
|
||||
// Finally, merge together any blocks that are now guaranteed to execute
|
||||
// consecutively
|
||||
mergeConsecutiveBlocks(fn);
|
||||
|
||||
assertConsistentIdentifiers(fn);
|
||||
assertTerminalSuccessorsExist(fn);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = props.default;
|
||||
try {
|
||||
} catch (e) {
|
||||
x = e;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
const x = props.default;
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
function Component(props) {
|
||||
let x = props.default;
|
||||
try {
|
||||
} catch (e) {
|
||||
x = e;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = props.default;
|
||||
try {
|
||||
// note: has to be a primitive, we want an instruction that cannot throw
|
||||
// to ensure there is no maybe-throw terminal
|
||||
const y = 42;
|
||||
return y;
|
||||
} catch (e) {
|
||||
x = e;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
return 42;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
function Component(props) {
|
||||
let x = props.default;
|
||||
try {
|
||||
// note: has to be a primitive, we want an instruction that cannot throw
|
||||
// to ensure there is no maybe-throw terminal
|
||||
const y = 42;
|
||||
return y;
|
||||
} catch (e) {
|
||||
x = e;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
|
||||
## Input
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
let x = props.default;
|
||||
const y = 42;
|
||||
try {
|
||||
// note: this constant propagates so that we know
|
||||
// the handler is unreachable
|
||||
return y;
|
||||
} catch (e) {
|
||||
x = e;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
function Component(props) {
|
||||
return 42;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
function Component(props) {
|
||||
let x = props.default;
|
||||
const y = 42;
|
||||
try {
|
||||
// note: this constant propagates so that we know
|
||||
// the handler is unreachable
|
||||
return y;
|
||||
} catch (e) {
|
||||
x = e;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
fn: Component,
|
||||
params: [{ default: 42 }],
|
||||
};
|
||||
Reference in New Issue
Block a user