mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
[hir] Refactor useMemo inlining
Previously useMemo inlining created a new StoreLocal assignment (not
reassignment!) instruction for every return value. This breaks when the return
is inside a block (like an if-block) as the scope is tied to the block.
For example: ``` let x = useMemo(() => { if (...) { return { ... }; } })
``` would become: ``` if (...) { const temp = { ... }; } const x = temp; ```
This PR instead changes the inlining to declare a temporary in the function
prologue and then reassign values to it when replacing return statements.
``` let x = useMemo(() => { if (...) { return { ... }; } }) ```
becomes
``` let temp; if (...) { temp = { ... }; } const x = temp; ```
This commit is contained in:
@@ -12,11 +12,13 @@ import {
|
||||
Effect,
|
||||
Environment,
|
||||
FunctionExpression,
|
||||
GeneratedSource,
|
||||
GotoTerminal,
|
||||
GotoVariant,
|
||||
HIR,
|
||||
HIRFunction,
|
||||
IdentifierId,
|
||||
Identifier,
|
||||
InstructionKind,
|
||||
makeInstructionId,
|
||||
makeType,
|
||||
@@ -198,11 +200,20 @@ export function inlineUseMemo(fn: HIRFunction): void {
|
||||
}
|
||||
}
|
||||
|
||||
// We store the result in the useMemo temporary
|
||||
const result = instr.lvalue;
|
||||
|
||||
// Declare the useMemo temporary
|
||||
declareTemporary(fn.env, block, result);
|
||||
|
||||
// Promote the temporary with a name as we require this to persist
|
||||
promoteTemporary(result.identifier);
|
||||
|
||||
// Rewrite blocks from the lambda to replace any `return` with a
|
||||
// store the useMemo temporary and `goto` the continuation block
|
||||
// store to the result and `goto` the continuation block
|
||||
for (const [id, block] of body.loweredFunc.body.blocks) {
|
||||
block.preds.clear();
|
||||
rewriteBlock(fn.env, block, continuationBlockId, instr.lvalue);
|
||||
rewriteBlock(fn.env, block, continuationBlockId, result);
|
||||
fn.body.blocks.set(id, block);
|
||||
}
|
||||
|
||||
@@ -342,7 +353,7 @@ function rewriteBlock(
|
||||
},
|
||||
value: {
|
||||
kind: "StoreLocal",
|
||||
lvalue: { kind: InstructionKind.Const, place: { ...returnValue } },
|
||||
lvalue: { kind: InstructionKind.Reassign, place: { ...returnValue } },
|
||||
value: terminal.value,
|
||||
loc: terminal.loc,
|
||||
},
|
||||
@@ -356,3 +367,41 @@ function rewriteBlock(
|
||||
loc: block.terminal.loc,
|
||||
};
|
||||
}
|
||||
|
||||
function declareTemporary(
|
||||
env: Environment,
|
||||
block: BasicBlock,
|
||||
result: Place
|
||||
): void {
|
||||
block.instructions.push({
|
||||
id: makeInstructionId(0),
|
||||
loc: GeneratedSource,
|
||||
lvalue: {
|
||||
effect: Effect.Unknown,
|
||||
identifier: {
|
||||
id: env.nextIdentifierId,
|
||||
mutableRange: {
|
||||
start: makeInstructionId(0),
|
||||
end: makeInstructionId(0),
|
||||
},
|
||||
name: null,
|
||||
scope: null,
|
||||
type: makeType(),
|
||||
},
|
||||
kind: "Identifier",
|
||||
loc: GeneratedSource,
|
||||
},
|
||||
value: {
|
||||
kind: "DeclareLocal",
|
||||
lvalue: {
|
||||
place: result,
|
||||
kind: InstructionKind.Let,
|
||||
},
|
||||
loc: result.loc,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function promoteTemporary(temp: Identifier): void {
|
||||
temp.name = `t${temp.id}`;
|
||||
}
|
||||
|
||||
+12
-17
@@ -20,7 +20,8 @@ function Component(props) {
|
||||
```javascript
|
||||
// @inlineUseMemo
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(5);
|
||||
const $ = React.unstable_useMemoCache(4);
|
||||
let t16 = undefined;
|
||||
if (props.cond) {
|
||||
const c_0 = $[0] !== props.a;
|
||||
let t0;
|
||||
@@ -31,26 +32,20 @@ function Component(props) {
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = t0;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
t16 = t0;
|
||||
} else {
|
||||
const c_3 = $[3] !== props.b;
|
||||
let t2;
|
||||
if (c_3) {
|
||||
t2 = makeObject(props.b);
|
||||
$[3] = props.b;
|
||||
$[4] = t2;
|
||||
const c_2 = $[2] !== props.b;
|
||||
let t1;
|
||||
if (c_2) {
|
||||
t1 = makeObject(props.b);
|
||||
$[2] = props.b;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t2 = $[4];
|
||||
t1 = $[3];
|
||||
}
|
||||
t1 = t2;
|
||||
t16 = t1;
|
||||
}
|
||||
const x = t1;
|
||||
const x = t16;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -52,8 +52,8 @@ function Component(props) {
|
||||
} else {
|
||||
t2 = $[6];
|
||||
}
|
||||
const t54 = t2;
|
||||
const [a_0, b_0] = t54;
|
||||
const t24 = t2;
|
||||
const [a_0, b_0] = t24;
|
||||
const c_7 = $[7] !== a_0;
|
||||
const c_8 = $[8] !== b_0;
|
||||
let t3;
|
||||
|
||||
+4
-9
@@ -19,7 +19,8 @@ function component(a, b) {
|
||||
```javascript
|
||||
// @inlineUseMemo
|
||||
function component(a, b) {
|
||||
const $ = React.unstable_useMemoCache(3);
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
let t13;
|
||||
if (a) {
|
||||
const c_0 = $[0] !== b;
|
||||
let t0;
|
||||
@@ -30,15 +31,9 @@ function component(a, b) {
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
let t1;
|
||||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = t0;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
t13 = t0;
|
||||
}
|
||||
const x = t1;
|
||||
const x = t13;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -19,8 +19,8 @@ function Component(props) {
|
||||
```javascript
|
||||
// @inlineUseMemo
|
||||
function Component(props) {
|
||||
const t19 = props.value;
|
||||
const x = t19;
|
||||
const t8 = props.value;
|
||||
const x = t8;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@ function Component(props) {
|
||||
```javascript
|
||||
// @inlineUseMemo
|
||||
function Component(props) {
|
||||
const t32 = props.a && props.b;
|
||||
const x = t32;
|
||||
const t14 = props.a && props.b;
|
||||
const x = t14;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,24 +27,25 @@ function Component(props) {
|
||||
function Component(props) {
|
||||
const $ = React.unstable_useMemoCache(2);
|
||||
const c_0 = $[0] !== props;
|
||||
let t0;
|
||||
let t25;
|
||||
if (c_0) {
|
||||
const y = [];
|
||||
if (props.cond) {
|
||||
y.push(props.a);
|
||||
}
|
||||
t25 = undefined;
|
||||
if (props.cond2) {
|
||||
t0 = y;
|
||||
t25 = y;
|
||||
} else {
|
||||
y.push(props.b);
|
||||
t0 = y;
|
||||
t25 = y;
|
||||
}
|
||||
$[0] = props;
|
||||
$[1] = t0;
|
||||
$[1] = t25;
|
||||
} else {
|
||||
t0 = $[1];
|
||||
t25 = $[1];
|
||||
}
|
||||
const x = t0;
|
||||
const x = t25;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ function component(a) {
|
||||
} else {
|
||||
t0 = $[1];
|
||||
}
|
||||
const t23 = t0;
|
||||
const x = t23;
|
||||
const t9 = t0;
|
||||
const x = t9;
|
||||
const c_2 = $[2] !== x;
|
||||
let t1;
|
||||
if (c_2) {
|
||||
|
||||
+4
-3
@@ -24,16 +24,17 @@ function Component(props) {
|
||||
```javascript
|
||||
// @inlineUseMemo
|
||||
function Component(props) {
|
||||
let t13 = undefined;
|
||||
bb8: switch (props.key) {
|
||||
case "key": {
|
||||
const t28 = props.value;
|
||||
t13 = props.value;
|
||||
break bb8;
|
||||
}
|
||||
default: {
|
||||
const t28 = props.defaultValue;
|
||||
t13 = props.defaultValue;
|
||||
}
|
||||
}
|
||||
const x = t28;
|
||||
const x = t13;
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user