mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Remove unnecessary scopes for value blocks
The example earlier in the stack had unreachable code in the output because there was an unnecessary memoization block around an assignment. This was a holdover from before we moved the logic to expand mutable ranges for phis from LeaveSSA to InferMutableRanges. We were conservatively assigning a mutable range to all variables with a phi, even those that didn't strictly need one. Removing the range extension logic in LeaveSSA fixed the issue, but uncovered the fact that AlignReactiveScopesToBlockScopes was missing a case to handle optionals. ## Test Plan Synced internally and ran a snapshot/comparison of compilation before/after (P1197734337 for those curious). The majority of components get fewer memo slots thanks to not needing to memoize non-allocating value block expressions like ternaries/optionals. In a few cases, the fact that we're no longer assigning a mutable range for value blocks (unless there is actually a mutation!) means we get more fine-grained memoization and increase the number of memoization blocks. So overall this appears to be correct, improve memoization, and reduce code size.
This commit is contained in:
+1
@@ -84,6 +84,7 @@ class Visitor extends ReactiveFunctionVisitor<Context> {
|
||||
|
||||
override visitInstruction(instr: ReactiveInstruction, state: Context): void {
|
||||
switch (instr.value.kind) {
|
||||
case "OptionalExpression":
|
||||
case "SequenceExpression":
|
||||
case "ConditionalExpression":
|
||||
case "LogicalExpression": {
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
InstructionKind,
|
||||
LValue,
|
||||
LValuePattern,
|
||||
makeInstructionId,
|
||||
Phi,
|
||||
Place,
|
||||
} from "../HIR/HIR";
|
||||
@@ -312,50 +311,6 @@ export function leaveSSA(fn: HIRFunction): void {
|
||||
} else {
|
||||
reassignmentPhis.push({ phi, block: phiBlock });
|
||||
}
|
||||
const hasBackEdge = backEdgePhis.has(phi);
|
||||
const isPhiMutatedAfterCreation: boolean =
|
||||
phi.id.mutableRange.end >
|
||||
(phiBlock.instructions.at(0)?.id ?? phiBlock.terminal.id);
|
||||
|
||||
/*
|
||||
* Named variables whose phi doesn't have a back-edge can potentially be independenly
|
||||
* memoized, depending on whether the phi is after its creation.
|
||||
*/
|
||||
if (phi.id.name !== null && !hasBackEdge) {
|
||||
if (!isPhiMutatedAfterCreation) {
|
||||
/*
|
||||
* Simple case: predecesor-only values flowing into a phi, which is never modified:
|
||||
* adjust the phi's range to clarify that the identifier does not mutate
|
||||
*/
|
||||
phi.id.mutableRange.start = terminal.id;
|
||||
phi.id.mutableRange.end = makeInstructionId(terminal.id + 1);
|
||||
} else {
|
||||
/*
|
||||
* Predecessor only values flow into a phi, which is modified later:
|
||||
* all operands flow into the phi and can be modified, must extend their ranges
|
||||
*/
|
||||
for (const [, operand] of phi.operands) {
|
||||
operand.mutableRange.end = phi.id.mutableRange.end;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* Otherwise this is a temporary phi (logical or ternary) or occurs in a loop. In either
|
||||
* case we can't independently memoize any of the values: unify their ranges to span the
|
||||
* min(start) to max(end) so that we create a single scope for all the computation.
|
||||
*/
|
||||
let start = block.terminal.id as number;
|
||||
let end = Number.MIN_SAFE_INTEGER;
|
||||
const operands = [phi.id, ...phi.operands.values()];
|
||||
for (const operand of operands) {
|
||||
start = Math.min(start, operand.mutableRange.start);
|
||||
end = Math.max(end, operand.mutableRange.end);
|
||||
}
|
||||
for (const operand of operands) {
|
||||
operand.mutableRange.start = makeInstructionId(start);
|
||||
operand.mutableRange.end = makeInstructionId(end);
|
||||
}
|
||||
}
|
||||
}
|
||||
const fallthroughId = terminalFallthrough(terminal);
|
||||
|
||||
+22
-30
@@ -56,7 +56,7 @@ function useFragment(_arg1, _arg2) {
|
||||
}
|
||||
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(16);
|
||||
const $ = useMemoCache(14);
|
||||
const post = useFragment(graphql`...`, props.post);
|
||||
let media;
|
||||
let allUrls;
|
||||
@@ -65,48 +65,40 @@ function Component(props) {
|
||||
allUrls = [];
|
||||
|
||||
const { media: t0, comments: t1, urls: t2 } = post;
|
||||
media = t0 === undefined ? null : t0;
|
||||
let t3;
|
||||
if ($[4] !== t0) {
|
||||
t3 = t0 === undefined ? null : t0;
|
||||
$[4] = t0;
|
||||
if ($[4] !== t1) {
|
||||
t3 = t1 === undefined ? [] : t1;
|
||||
$[4] = t1;
|
||||
$[5] = t3;
|
||||
} else {
|
||||
t3 = $[5];
|
||||
}
|
||||
media = t3;
|
||||
const comments = t3;
|
||||
let t4;
|
||||
if ($[6] !== t1) {
|
||||
t4 = t1 === undefined ? [] : t1;
|
||||
$[6] = t1;
|
||||
if ($[6] !== t2) {
|
||||
t4 = t2 === undefined ? [] : t2;
|
||||
$[6] = t2;
|
||||
$[7] = t4;
|
||||
} else {
|
||||
t4 = $[7];
|
||||
}
|
||||
const comments = t4;
|
||||
const urls = t4;
|
||||
let t5;
|
||||
if ($[8] !== t2) {
|
||||
t5 = t2 === undefined ? [] : t2;
|
||||
$[8] = t2;
|
||||
$[9] = t5;
|
||||
} else {
|
||||
t5 = $[9];
|
||||
}
|
||||
const urls = t5;
|
||||
let t6;
|
||||
if ($[10] !== comments.length) {
|
||||
t6 = (e) => {
|
||||
if ($[8] !== comments.length) {
|
||||
t5 = (e) => {
|
||||
if (!comments.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(comments.length);
|
||||
};
|
||||
$[10] = comments.length;
|
||||
$[11] = t6;
|
||||
$[8] = comments.length;
|
||||
$[9] = t5;
|
||||
} else {
|
||||
t6 = $[11];
|
||||
t5 = $[9];
|
||||
}
|
||||
onClick = t6;
|
||||
onClick = t5;
|
||||
|
||||
allUrls.push(...urls);
|
||||
$[0] = post;
|
||||
@@ -119,14 +111,14 @@ function Component(props) {
|
||||
onClick = $[3];
|
||||
}
|
||||
let t0;
|
||||
if ($[12] !== media || $[13] !== allUrls || $[14] !== onClick) {
|
||||
if ($[10] !== media || $[11] !== allUrls || $[12] !== onClick) {
|
||||
t0 = <Stringify media={media} allUrls={allUrls} onClick={onClick} />;
|
||||
$[12] = media;
|
||||
$[13] = allUrls;
|
||||
$[14] = onClick;
|
||||
$[15] = t0;
|
||||
$[10] = media;
|
||||
$[11] = allUrls;
|
||||
$[12] = onClick;
|
||||
$[13] = t0;
|
||||
} else {
|
||||
t0 = $[15];
|
||||
t0 = $[13];
|
||||
}
|
||||
return t0;
|
||||
}
|
||||
|
||||
+2
-12
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(10);
|
||||
const $ = useMemoCache(6);
|
||||
let t0;
|
||||
if ($[0] !== props.a) {
|
||||
t0 = [props.a];
|
||||
@@ -52,17 +52,7 @@ function Component(props) {
|
||||
t2 = $[5];
|
||||
}
|
||||
const c = t2;
|
||||
let t3;
|
||||
if ($[6] !== a || $[7] !== b || $[8] !== c) {
|
||||
t3 = (a && b) || c;
|
||||
$[6] = a;
|
||||
$[7] = b;
|
||||
$[8] = c;
|
||||
$[9] = t3;
|
||||
} else {
|
||||
t3 = $[9];
|
||||
}
|
||||
return t3;
|
||||
return (a && b) || c;
|
||||
}
|
||||
|
||||
export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
+2
-11
@@ -14,7 +14,7 @@ function Component(props) {
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(5);
|
||||
const $ = useMemoCache(2);
|
||||
let t0;
|
||||
if ($[0] !== props) {
|
||||
t0 = makeObject(props);
|
||||
@@ -24,16 +24,7 @@ function Component(props) {
|
||||
t0 = $[1];
|
||||
}
|
||||
const object = t0;
|
||||
let t1;
|
||||
if ($[2] !== object || $[3] !== props) {
|
||||
t1 = object?.[props.key];
|
||||
$[2] = object;
|
||||
$[3] = props;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
return t1;
|
||||
return object?.[props.key];
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+2
-10
@@ -14,7 +14,7 @@ function Component(props) {
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(props) {
|
||||
const $ = useMemoCache(3);
|
||||
const $ = useMemoCache(1);
|
||||
let t0;
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t0 = makeObject();
|
||||
@@ -23,15 +23,7 @@ function Component(props) {
|
||||
t0 = $[0];
|
||||
}
|
||||
const x = t0;
|
||||
let t1;
|
||||
if ($[1] !== props) {
|
||||
t1 = x.y?.[props.a?.[props.b?.[props.c]]];
|
||||
$[1] = props;
|
||||
$[2] = t1;
|
||||
} else {
|
||||
t1 = $[2];
|
||||
}
|
||||
return t1;
|
||||
return x.y?.[props.a?.[props.b?.[props.c]]];
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
+8
-16
@@ -54,7 +54,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```javascript
|
||||
import { unstable_useMemoCache as useMemoCache } from "react";
|
||||
function Component(t0) {
|
||||
const $ = useMemoCache(11);
|
||||
const $ = useMemoCache(8);
|
||||
const { label, highlightedItem } = t0;
|
||||
const serverTime = useServerTime();
|
||||
let t1;
|
||||
@@ -63,16 +63,8 @@ function Component(t0) {
|
||||
const highlight = new Highlight(highlightedItem);
|
||||
|
||||
const time = serverTime.get();
|
||||
let t2;
|
||||
if ($[5] !== time || $[6] !== label) {
|
||||
t2 = time / 1000 || label;
|
||||
$[5] = time;
|
||||
$[6] = label;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[7];
|
||||
}
|
||||
timestampLabel = t2;
|
||||
|
||||
timestampLabel = time / 1000 || label;
|
||||
|
||||
t1 = highlight.render();
|
||||
$[0] = highlightedItem;
|
||||
@@ -85,18 +77,18 @@ function Component(t0) {
|
||||
timestampLabel = $[4];
|
||||
}
|
||||
let t2;
|
||||
if ($[8] !== t1 || $[9] !== timestampLabel) {
|
||||
if ($[5] !== t1 || $[6] !== timestampLabel) {
|
||||
t2 = (
|
||||
<>
|
||||
{t1}
|
||||
{timestampLabel}
|
||||
</>
|
||||
);
|
||||
$[8] = t1;
|
||||
$[9] = timestampLabel;
|
||||
$[10] = t2;
|
||||
$[5] = t1;
|
||||
$[6] = timestampLabel;
|
||||
$[7] = t2;
|
||||
} else {
|
||||
t2 = $[10];
|
||||
t2 = $[7];
|
||||
}
|
||||
return t2;
|
||||
}
|
||||
|
||||
+18
-33
@@ -49,60 +49,45 @@ import {
|
||||
import { ValidateMemoization, identity } from "shared-runtime";
|
||||
|
||||
function Component(t0) {
|
||||
const $ = useMemoCache(10);
|
||||
const $ = useMemoCache(7);
|
||||
const { value } = t0;
|
||||
let t1;
|
||||
bb13: {
|
||||
if (value == null) {
|
||||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = null;
|
||||
break bb13;
|
||||
$[0] = t1;
|
||||
} else {
|
||||
t1 = $[0];
|
||||
}
|
||||
t1 = null;
|
||||
break bb13;
|
||||
}
|
||||
try {
|
||||
let t3;
|
||||
if ($[1] !== value) {
|
||||
if ($[0] !== value) {
|
||||
t3 = { value };
|
||||
$[1] = value;
|
||||
$[2] = t3;
|
||||
$[0] = value;
|
||||
$[1] = t3;
|
||||
} else {
|
||||
t3 = $[2];
|
||||
}
|
||||
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = t3;
|
||||
$[3] = t1;
|
||||
} else {
|
||||
t1 = $[3];
|
||||
t3 = $[1];
|
||||
}
|
||||
t1 = t3;
|
||||
} catch (t2) {
|
||||
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
|
||||
t1 = null;
|
||||
$[4] = t1;
|
||||
} else {
|
||||
t1 = $[4];
|
||||
}
|
||||
t1 = null;
|
||||
}
|
||||
}
|
||||
const result = t1;
|
||||
let t2;
|
||||
if ($[5] !== value) {
|
||||
if ($[2] !== value) {
|
||||
t2 = [value];
|
||||
$[5] = value;
|
||||
$[6] = t2;
|
||||
$[2] = value;
|
||||
$[3] = t2;
|
||||
} else {
|
||||
t2 = $[6];
|
||||
t2 = $[3];
|
||||
}
|
||||
let t3;
|
||||
if ($[7] !== t2 || $[8] !== result) {
|
||||
if ($[4] !== t2 || $[5] !== result) {
|
||||
t3 = <ValidateMemoization inputs={t2} output={result} />;
|
||||
$[7] = t2;
|
||||
$[8] = result;
|
||||
$[9] = t3;
|
||||
$[4] = t2;
|
||||
$[5] = result;
|
||||
$[6] = t3;
|
||||
} else {
|
||||
t3 = $[9];
|
||||
t3 = $[6];
|
||||
}
|
||||
return t3;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user