Skip to content

Commit 80e4d87

Browse files
committed
[compiler] Improve IIFE inlining (#33726)
We currently inline IIFEs by creating a temporary and a labeled block w the original code. The original return statements turn into an assignment to the temporary and break out of the label. However, many cases of IIFEs are due to inlining of manual `useMemo()`, and these cases often have only a single return statement. Here, the output is cleaner if we avoid the temporary and label - so that's what we do in this PR. Note that the most complex part of the change is actually around ValidatePreserveExistingMemo - we have some logic to track the IIFE temporary reassignmetns which needs to be updated to handle the simpler version of inlining. --- [//]: # (BEGIN SAPLING FOOTER) Stack created with [Sapling](https://sapling-scm.com). Best reviewed with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33726). * __->__ #33726 * #33725 DiffTrain build for [956d770](956d770)
1 parent c229707 commit 80e4d87

35 files changed

+180
-103
lines changed

compiled/eslint-plugin-react-hooks/index.js

Lines changed: 94 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27640,7 +27640,7 @@ function printInstructionValue(instrValue) {
2764027640
break;
2764127641
}
2764227642
case 'FinishMemoize': {
27643-
value = `FinishMemoize decl=${printPlace(instrValue.decl)}`;
27643+
value = `FinishMemoize decl=${printPlace(instrValue.decl)}${instrValue.pruned ? ' pruned' : ''}`;
2764427644
break;
2764527645
}
2764627646
default: {
@@ -39546,6 +39546,17 @@ function mergeConsecutiveBlocks(fn) {
3954639546
merged.merge(block.id, predecessorId);
3954739547
fn.body.blocks.delete(block.id);
3954839548
}
39549+
for (const [, block] of fn.body.blocks) {
39550+
for (const phi of block.phis) {
39551+
for (const [predecessorId, operand] of phi.operands) {
39552+
const mapped = merged.get(predecessorId);
39553+
if (mapped !== predecessorId) {
39554+
phi.operands.delete(predecessorId);
39555+
phi.operands.set(mapped, operand);
39556+
}
39557+
}
39558+
}
39559+
}
3954939560
markPredecessors(fn.body);
3955039561
for (const [, { terminal }] of fn.body.blocks) {
3955139562
if (terminalHasFallthrough(terminal)) {
@@ -48815,6 +48826,13 @@ class PruneScopesTransform extends ReactiveFunctionTransform {
4881548826
const ids = getOrInsertDefault(this.reassignments, value.lvalue.place.identifier.declarationId, new Set());
4881648827
ids.add(value.value.identifier);
4881748828
}
48829+
else if (value.kind === 'LoadLocal' &&
48830+
value.place.identifier.scope != null &&
48831+
instruction.lvalue != null &&
48832+
instruction.lvalue.identifier.scope == null) {
48833+
const ids = getOrInsertDefault(this.reassignments, instruction.lvalue.identifier.declarationId, new Set());
48834+
ids.add(value.place.identifier);
48835+
}
4881848836
else if (value.kind === 'FinishMemoize') {
4881948837
let decls;
4882048838
if (value.decl.identifier.scope == null) {
@@ -52688,21 +52706,60 @@ function inlineImmediatelyInvokedFunctionExpressions(fn) {
5268852706
};
5268952707
fn.body.blocks.set(continuationBlockId, continuationBlock);
5269052708
block.instructions.length = ii;
52691-
const newTerminal = {
52692-
block: body.loweredFunc.func.body.entry,
52693-
id: makeInstructionId(0),
52694-
kind: 'label',
52695-
fallthrough: continuationBlockId,
52696-
loc: block.terminal.loc,
52697-
};
52698-
block.terminal = newTerminal;
52699-
const result = instr.lvalue;
52700-
declareTemporary(fn.env, block, result);
52701-
promoteTemporary(result.identifier);
52702-
for (const [id, block] of body.loweredFunc.func.body.blocks) {
52703-
block.preds.clear();
52704-
rewriteBlock(fn.env, block, continuationBlockId, result);
52705-
fn.body.blocks.set(id, block);
52709+
if (hasSingleExitReturnTerminal(body.loweredFunc.func)) {
52710+
block.terminal = {
52711+
kind: 'goto',
52712+
block: body.loweredFunc.func.body.entry,
52713+
id: block.terminal.id,
52714+
loc: block.terminal.loc,
52715+
variant: GotoVariant.Break,
52716+
};
52717+
for (const block of body.loweredFunc.func.body.blocks.values()) {
52718+
if (block.terminal.kind === 'return') {
52719+
block.instructions.push({
52720+
id: makeInstructionId(0),
52721+
loc: block.terminal.loc,
52722+
lvalue: instr.lvalue,
52723+
value: {
52724+
kind: 'LoadLocal',
52725+
loc: block.terminal.loc,
52726+
place: block.terminal.value,
52727+
},
52728+
effects: null,
52729+
});
52730+
block.terminal = {
52731+
kind: 'goto',
52732+
block: continuationBlockId,
52733+
id: block.terminal.id,
52734+
loc: block.terminal.loc,
52735+
variant: GotoVariant.Break,
52736+
};
52737+
}
52738+
}
52739+
for (const [id, block] of body.loweredFunc.func.body.blocks) {
52740+
block.preds.clear();
52741+
fn.body.blocks.set(id, block);
52742+
}
52743+
}
52744+
else {
52745+
const newTerminal = {
52746+
block: body.loweredFunc.func.body.entry,
52747+
id: makeInstructionId(0),
52748+
kind: 'label',
52749+
fallthrough: continuationBlockId,
52750+
loc: block.terminal.loc,
52751+
};
52752+
block.terminal = newTerminal;
52753+
const result = instr.lvalue;
52754+
declareTemporary(fn.env, block, result);
52755+
if (result.identifier.name == null) {
52756+
promoteTemporary(result.identifier);
52757+
}
52758+
for (const [id, block] of body.loweredFunc.func.body.blocks) {
52759+
block.preds.clear();
52760+
rewriteBlock(fn.env, block, continuationBlockId, result);
52761+
fn.body.blocks.set(id, block);
52762+
}
5270652763
}
5270752764
queue.push(continuationBlock);
5270852765
continue queue;
@@ -52717,14 +52774,26 @@ function inlineImmediatelyInvokedFunctionExpressions(fn) {
5271752774
}
5271852775
}
5271952776
if (inlinedFunctions.size !== 0) {
52720-
for (const [, block] of fn.body.blocks) {
52777+
for (const block of fn.body.blocks.values()) {
5272152778
retainWhere(block.instructions, instr => !inlinedFunctions.has(instr.lvalue.identifier.id));
5272252779
}
5272352780
reversePostorderBlocks(fn.body);
5272452781
markInstructionIds(fn.body);
5272552782
markPredecessors(fn.body);
52783+
mergeConsecutiveBlocks(fn);
5272652784
}
5272752785
}
52786+
function hasSingleExitReturnTerminal(fn) {
52787+
let hasReturn = false;
52788+
let exitCount = 0;
52789+
for (const [, block] of fn.body.blocks) {
52790+
if (block.terminal.kind === 'return' || block.terminal.kind === 'throw') {
52791+
hasReturn || (hasReturn = block.terminal.kind === 'return');
52792+
exitCount++;
52793+
}
52794+
}
52795+
return exitCount === 1 && hasReturn;
52796+
}
5272852797
function rewriteBlock(env, block, returnTarget, returnValue) {
5272952798
const { terminal } = block;
5273052799
if (terminal.kind !== 'return') {
@@ -57161,6 +57230,14 @@ class Visitor extends ReactiveFunctionVisitor {
5716157230
const ids = getOrInsertDefault(state.manualMemoState.reassignments, value.lvalue.place.identifier.declarationId, new Set());
5716257231
ids.add(value.value.identifier);
5716357232
}
57233+
if (value.kind === 'LoadLocal' &&
57234+
value.place.identifier.scope != null &&
57235+
instruction.lvalue != null &&
57236+
instruction.lvalue.identifier.scope == null &&
57237+
state.manualMemoState != null) {
57238+
const ids = getOrInsertDefault(state.manualMemoState.reassignments, instruction.lvalue.identifier.declarationId, new Set());
57239+
ids.add(value.place.identifier);
57240+
}
5716457241
if (value.kind === 'StartMemoize') {
5716557242
let depsFromSource = null;
5716657243
if (value.deps != null) {

compiled/facebook-www/REVISION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
befc1246b07a04b401bc6e914b7f336a442dca1a
1+
956d770adf59e1f8a00a7b7c52b5727ef9e353e7
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
befc1246b07a04b401bc6e914b7f336a442dca1a
1+
956d770adf59e1f8a00a7b7c52b5727ef9e353e7

compiled/facebook-www/React-dev.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,7 @@ __DEV__ &&
14341434
exports.useTransition = function () {
14351435
return resolveDispatcher().useTransition();
14361436
};
1437-
exports.version = "19.2.0-www-classic-befc1246-20250708";
1437+
exports.version = "19.2.0-www-classic-956d770a-20250708";
14381438
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
14391439
"function" ===
14401440
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-dev.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1434,7 +1434,7 @@ __DEV__ &&
14341434
exports.useTransition = function () {
14351435
return resolveDispatcher().useTransition();
14361436
};
1437-
exports.version = "19.2.0-www-modern-befc1246-20250708";
1437+
exports.version = "19.2.0-www-modern-956d770a-20250708";
14381438
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
14391439
"function" ===
14401440
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-prod.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,4 +610,4 @@ exports.useSyncExternalStore = function (
610610
exports.useTransition = function () {
611611
return ReactSharedInternals.H.useTransition();
612612
};
613-
exports.version = "19.2.0-www-classic-befc1246-20250708";
613+
exports.version = "19.2.0-www-classic-956d770a-20250708";

compiled/facebook-www/React-prod.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -610,4 +610,4 @@ exports.useSyncExternalStore = function (
610610
exports.useTransition = function () {
611611
return ReactSharedInternals.H.useTransition();
612612
};
613-
exports.version = "19.2.0-www-modern-befc1246-20250708";
613+
exports.version = "19.2.0-www-modern-956d770a-20250708";

compiled/facebook-www/React-profiling.classic.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ exports.useSyncExternalStore = function (
614614
exports.useTransition = function () {
615615
return ReactSharedInternals.H.useTransition();
616616
};
617-
exports.version = "19.2.0-www-classic-befc1246-20250708";
617+
exports.version = "19.2.0-www-classic-956d770a-20250708";
618618
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
619619
"function" ===
620620
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/React-profiling.modern.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -614,7 +614,7 @@ exports.useSyncExternalStore = function (
614614
exports.useTransition = function () {
615615
return ReactSharedInternals.H.useTransition();
616616
};
617-
exports.version = "19.2.0-www-modern-befc1246-20250708";
617+
exports.version = "19.2.0-www-modern-956d770a-20250708";
618618
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
619619
"function" ===
620620
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

compiled/facebook-www/ReactART-dev.classic.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19299,10 +19299,10 @@ __DEV__ &&
1929919299
(function () {
1930019300
var internals = {
1930119301
bundleType: 1,
19302-
version: "19.2.0-www-classic-befc1246-20250708",
19302+
version: "19.2.0-www-classic-956d770a-20250708",
1930319303
rendererPackageName: "react-art",
1930419304
currentDispatcherRef: ReactSharedInternals,
19305-
reconcilerVersion: "19.2.0-www-classic-befc1246-20250708"
19305+
reconcilerVersion: "19.2.0-www-classic-956d770a-20250708"
1930619306
};
1930719307
internals.overrideHookState = overrideHookState;
1930819308
internals.overrideHookStateDeletePath = overrideHookStateDeletePath;
@@ -19336,7 +19336,7 @@ __DEV__ &&
1933619336
exports.Shape = Shape;
1933719337
exports.Surface = Surface;
1933819338
exports.Text = Text;
19339-
exports.version = "19.2.0-www-classic-befc1246-20250708";
19339+
exports.version = "19.2.0-www-classic-956d770a-20250708";
1934019340
"undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
1934119341
"function" ===
1934219342
typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&

0 commit comments

Comments
 (0)