[compiler] More fine-grained memoization in change detection mode

[ghstack-poisoned]
This commit is contained in:
Mike Vitousek
2024-06-30 23:51:32 -07:00
parent f046ce99db
commit c22c4e727f
5 changed files with 138 additions and 79 deletions
@@ -653,6 +653,7 @@ function codegenReactiveScope(
const cacheLoadOldValueStatements: Array<t.Statement> = [];
const changeDetectionStatements: Array<t.Statement> = [];
const idempotenceDetectionStatements: Array<t.Statement> = [];
const restoreOldValueStatements: Array<t.Statement> = [];
for (const { name, index, value } of cacheLoads) {
const loadName = cx.synthesizeName(`old$${name.name}`);
@@ -669,6 +670,11 @@ function codegenReactiveScope(
t.variableDeclarator(t.identifier(loadName), slot),
])
);
if (scope.source) {
restoreOldValueStatements.push(
t.expressionStatement(t.assignmentExpression("=", t.cloneNode(name, true), t.identifier(loadName)))
);
}
changeDetectionStatements.push(
t.expressionStatement(
t.callExpression(t.identifier(detectionFunction), [
@@ -709,6 +715,7 @@ function codegenReactiveScope(
t.blockStatement([
...cacheLoadOldValueStatements,
...changeDetectionStatements,
...restoreOldValueStatements,
])
),
...cacheStoreStatements,
@@ -191,11 +191,10 @@ export function mayAllocate(env: Environment, instruction: Instruction, conserva
const { value } = instruction;
switch (value.kind) {
case "Destructure": {
return doesPatternContainSpreadElement(value.lvalue.pattern);
return doesPatternContainSpreadElement(value.lvalue.pattern) || env.config.enableChangeDetectionForDebugging != null;
}
case "PostfixUpdate":
case "PrefixUpdate":
case "Await":
case "DeclareLocal":
case "DeclareContext":
case "StoreLocal":
@@ -206,26 +205,29 @@ export function mayAllocate(env: Environment, instruction: Instruction, conserva
case "LoadContext":
case "StoreContext":
case "PropertyDelete":
case "ComputedLoad":
case "ComputedDelete":
case "JSXText":
case "TemplateLiteral":
case "Primitive":
case "GetIterator":
case "IteratorNext":
case "NextPropertyOf":
case "Debugger":
case "StartMemoize":
case "FinishMemoize":
case "UnaryExpression":
case "BinaryExpression":
case "PropertyLoad":
case "StoreGlobal": {
return false;
}
case "PropertyLoad":
case "NextPropertyOf":
case "ComputedLoad":
case "Await": {
return env.config.enableChangeDetectionForDebugging != null;
}
case "CallExpression":
case "MethodCall": {
return conservative || instruction.lvalue.identifier.type.kind !== "Primitive";
return conservative || instruction.lvalue.identifier.type.kind !== "Primitive" || env.config.enableChangeDetectionForDebugging != null;
}
case "RegExpLiteral":
case "PropertyStore":
@@ -39,43 +39,60 @@ function useOther(x) {
}
function Component(props) {
const $ = _c(4);
let t0;
{
t0 = f(props.x);
let condition = $[0] !== props.x;
if (!condition) {
let old$t0 = $[1];
$structuralCheck(old$t0, t0, "t0", "Component", "cached", "(8:8)");
}
$[0] = props.x;
$[1] = t0;
if (condition) {
t0 = f(props.x);
$structuralCheck($[1], t0, "t0", "Component", "recomputed", "(8:8)");
t0 = $[1];
}
}
const w = t0;
const z = useOther(w);
const [x] = useState(z);
const $ = _c(6);
const t0 = props.x;
let t1;
{
t1 = <div>{x}</div>;
let condition = $[2] !== x;
t1 = f(t0);
let condition = $[0] !== t0;
if (!condition) {
let old$t1 = $[3];
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(11:11)");
let old$t1 = $[1];
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(8:8)");
}
$[2] = x;
$[3] = t1;
$[0] = t0;
$[1] = t1;
if (condition) {
t1 = <div>{x}</div>;
$structuralCheck($[3], t1, "t1", "Component", "recomputed", "(11:11)");
t1 = $[3];
t1 = f(t0);
$structuralCheck($[1], t1, "t1", "Component", "recomputed", "(8:8)");
t1 = $[1];
}
}
return t1;
const w = t1;
const z = useOther(w);
const t2 = useState(z);
let x;
{
[x] = t2;
let condition = $[2] !== t2;
if (!condition) {
let old$x = $[3];
$structuralCheck(old$x, x, "x", "Component", "cached", "(10:10)");
}
$[2] = t2;
$[3] = x;
if (condition) {
[x] = t2;
$structuralCheck($[3], x, "x", "Component", "recomputed", "(10:10)");
x = $[3];
}
}
let t3;
{
t3 = <div>{x}</div>;
let condition = $[4] !== x;
if (!condition) {
let old$t3 = $[5];
$structuralCheck(old$t3, t3, "t3", "Component", "cached", "(11:11)");
}
$[4] = x;
$[5] = t3;
if (condition) {
t3 = <div>{x}</div>;
$structuralCheck($[5], t3, "t3", "Component", "recomputed", "(11:11)");
t3 = $[5];
}
}
return t3;
}
function f(x) {
@@ -20,7 +20,7 @@ import { c as _c } from "react/compiler-runtime"; // @enableChangeDetectionForDe
import { useState } from "react";
function Component(props) {
const $ = _c(3);
const $ = _c(5);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = f(props.x);
@@ -28,24 +28,40 @@ function Component(props) {
} else {
t0 = $[0];
}
const [x] = useState(t0);
let t1;
const t1 = useState(t0);
let x;
{
t1 = <div>{x}</div>;
let condition = $[1] !== x;
[x] = t1;
let condition = $[1] !== t1;
if (!condition) {
let old$t1 = $[2];
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(6:6)");
let old$x = $[2];
$structuralCheck(old$x, x, "x", "Component", "cached", "(5:5)");
}
$[1] = x;
$[2] = t1;
$[1] = t1;
$[2] = x;
if (condition) {
t1 = <div>{x}</div>;
$structuralCheck($[2], t1, "t1", "Component", "recomputed", "(6:6)");
t1 = $[2];
[x] = t1;
$structuralCheck($[2], x, "x", "Component", "recomputed", "(5:5)");
x = $[2];
}
}
return t1;
let t2;
{
t2 = <div>{x}</div>;
let condition = $[3] !== x;
if (!condition) {
let old$t2 = $[4];
$structuralCheck(old$t2, t2, "t2", "Component", "cached", "(6:6)");
}
$[3] = x;
$[4] = t2;
if (condition) {
t2 = <div>{x}</div>;
$structuralCheck($[4], t2, "t2", "Component", "recomputed", "(6:6)");
t2 = $[4];
}
}
return t2;
}
```
@@ -35,53 +35,70 @@ import { c as _c } from "react/compiler-runtime";
import { useState } from "react"; // @enableChangeDetectionForDebugging
function Component(props) {
const $ = _c(5);
let t0;
{
t0 = f(props.x);
let condition = $[0] !== props.x;
if (!condition) {
let old$t0 = $[1];
$structuralCheck(old$t0, t0, "t0", "Component", "cached", "(4:4)");
}
$[0] = props.x;
$[1] = t0;
if (condition) {
t0 = f(props.x);
$structuralCheck($[1], t0, "t0", "Component", "recomputed", "(4:4)");
t0 = $[1];
}
}
const w = t0;
const [x] = useState(w);
const $ = _c(7);
const t0 = props.x;
let t1;
{
t1 = (
t1 = f(t0);
let condition = $[0] !== t0;
if (!condition) {
let old$t1 = $[1];
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(4:4)");
}
$[0] = t0;
$[1] = t1;
if (condition) {
t1 = f(t0);
$structuralCheck($[1], t1, "t1", "Component", "recomputed", "(4:4)");
t1 = $[1];
}
}
const w = t1;
const t2 = useState(w);
let x;
{
[x] = t2;
let condition = $[2] !== t2;
if (!condition) {
let old$x = $[3];
$structuralCheck(old$x, x, "x", "Component", "cached", "(5:5)");
}
$[2] = t2;
$[3] = x;
if (condition) {
[x] = t2;
$structuralCheck($[3], x, "x", "Component", "recomputed", "(5:5)");
x = $[3];
}
}
let t3;
{
t3 = (
<div>
{x}
{w}
</div>
);
let condition = $[2] !== x || $[3] !== w;
let condition = $[4] !== x || $[5] !== w;
if (!condition) {
let old$t1 = $[4];
$structuralCheck(old$t1, t1, "t1", "Component", "cached", "(7:10)");
let old$t3 = $[6];
$structuralCheck(old$t3, t3, "t3", "Component", "cached", "(7:10)");
}
$[2] = x;
$[3] = w;
$[4] = t1;
$[4] = x;
$[5] = w;
$[6] = t3;
if (condition) {
t1 = (
t3 = (
<div>
{x}
{w}
</div>
);
$structuralCheck($[4], t1, "t1", "Component", "recomputed", "(7:10)");
t1 = $[4];
$structuralCheck($[6], t3, "t3", "Component", "recomputed", "(7:10)");
t3 = $[6];
}
}
return t1;
return t3;
}
function f(x) {