Fix scope merging in MergeOverlappingReactiveScopes

When `MergeOverlappingReactiveScopes` identified scopes to be merged, it was 
currently (oops, my bad) updating the _existing_ scope's id and range. Later, 
PropagateScopeDependencies marks outputs of a scope by updating that 
identifier's scope instance — so if that scope instance isn't shared, then the 
output is lost. This PR fixes MergeOverlappingReactiveScopes to correctly update 
all operands for a scope to have the same scope instance.
This commit is contained in:
Joe Savona
2023-01-19 10:10:21 -08:00
parent 7f7727d7e5
commit d295bb4516
6 changed files with 116 additions and 86 deletions
@@ -106,6 +106,7 @@ export function mergeOverlappingReactiveScopes(fn: ReactiveFunction): void {
context.enter(() => {
visitBlock(context, fn.body);
});
context.complete();
}
function visitBlock(context: Context, block: ReactiveBlock): void {
@@ -205,6 +206,7 @@ class Context {
scopes: Array<BlockScope> = [];
seenScopes: Set<ScopeId> = new Set();
joinedScopes: DisjointSet<ReactiveScope> = new DisjointSet();
operandScopes: Map<Place, ReactiveScope> = new Map();
visitId(id: InstructionId): void {
const currentBlock = this.scopes[this.scopes.length - 1]!;
@@ -223,6 +225,7 @@ class Context {
if (scope === null) {
return;
}
this.operandScopes.set(place, scope);
const currentBlock = this.scopes[this.scopes.length - 1]!;
// Fast-path for the first time we see a new scope
if (!this.seenScopes.has(scope.id)) {
@@ -290,19 +293,24 @@ class Context {
this.scopes.push(new BlockScope());
fn();
this.scopes.pop();
if (this.scopes.length === 0) {
this.joinedScopes.forEach((scope, groupScope) => {
if (scope !== groupScope) {
groupScope.range.start = makeInstructionId(
Math.min(groupScope.range.start, scope.range.start)
);
groupScope.range.end = makeInstructionId(
Math.max(groupScope.range.end, scope.range.end)
);
scope.range = groupScope.range;
scope.id = groupScope.id;
}
});
}
complete(): void {
this.joinedScopes.forEach((scope, groupScope) => {
if (scope !== groupScope) {
groupScope.range.start = makeInstructionId(
Math.min(groupScope.range.start, scope.range.start)
);
groupScope.range.end = makeInstructionId(
Math.max(groupScope.range.end, scope.range.end)
);
}
});
for (const [operand, originalScope] of this.operandScopes) {
const mergedScope = this.joinedScopes.find(originalScope);
if (mergedScope !== null) {
operand.identifier.scope = mergedScope;
}
}
}
}
@@ -17,11 +17,18 @@ function foo() {
```javascript
function foo() {
const x = { x: 0 };
const y = { z: 0 };
const z = { z: 0 };
x.x = x.x + (y.y = y.y * 1);
z.z = z.z + (y.y = y.y * (x.x = x.x & 3));
const $ = React.useMemoCache();
let z;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const x = { x: 0 };
const y = { z: 0 };
z = { z: 0 };
x.x = x.x + (y.y = y.y * 1);
z.z = z.z + (y.y = y.y * (x.x = x.x & 3));
$[0] = z;
} else {
z = $[0];
}
return z;
}
@@ -40,9 +40,10 @@ function Component(props) {
const c_1 = $[1] !== props.p1;
const c_2 = $[2] !== props.p2;
let a;
let b;
if (c_0 || c_1 || c_2) {
a = [];
const b = [];
b = [];
if (b) {
a.push(props.p0);
}
@@ -53,21 +54,23 @@ function Component(props) {
$[1] = props.p1;
$[2] = props.p2;
$[3] = a;
$[4] = b;
} else {
a = $[3];
b = $[4];
}
const c_4 = $[4] !== a;
const c_5 = $[5] !== b;
let t6;
if (c_4 || c_5) {
t6 = <Foo a={a} b={b}></Foo>;
$[4] = a;
$[5] = b;
$[6] = t6;
const c_5 = $[5] !== a;
const c_6 = $[6] !== b;
let t7;
if (c_5 || c_6) {
t7 = <Foo a={a} b={b}></Foo>;
$[5] = a;
$[6] = b;
$[7] = t7;
} else {
t6 = $[6];
t7 = $[7];
}
return t6;
return t7;
}
function Component(props) {
@@ -76,9 +79,10 @@ function Component(props) {
const c_1 = $[1] !== props.p1;
const c_2 = $[2] !== props.p2;
let a;
let b;
if (c_0 || c_1 || c_2) {
a = [];
const b = [];
b = [];
if (mayMutate(b)) {
a.push(props.p0);
}
@@ -89,21 +93,23 @@ function Component(props) {
$[1] = props.p1;
$[2] = props.p2;
$[3] = a;
$[4] = b;
} else {
a = $[3];
b = $[4];
}
const c_4 = $[4] !== a;
const c_5 = $[5] !== b;
let t6;
if (c_4 || c_5) {
t6 = <Foo a={a} b={b}></Foo>;
$[4] = a;
$[5] = b;
$[6] = t6;
const c_5 = $[5] !== a;
const c_6 = $[6] !== b;
let t7;
if (c_5 || c_6) {
t7 = <Foo a={a} b={b}></Foo>;
$[5] = a;
$[6] = b;
$[7] = t7;
} else {
t6 = $[6];
t7 = $[7];
}
return t6;
return t7;
}
function Foo() {}
@@ -61,9 +61,10 @@ function Component(props) {
const c_1 = $[1] !== props.b;
const c_2 = $[2] !== props.c;
let a;
let b;
if (c_0 || c_1 || c_2) {
a = compute(props.a);
const b = compute(props.b);
b = compute(props.b);
if (props.c) {
mutate(a);
mutate(b);
@@ -72,21 +73,23 @@ function Component(props) {
$[1] = props.b;
$[2] = props.c;
$[3] = a;
$[4] = b;
} else {
a = $[3];
b = $[4];
}
const c_4 = $[4] !== a;
const c_5 = $[5] !== b;
let t6;
if (c_4 || c_5) {
t6 = <Foo a={a} b={b}></Foo>;
$[4] = a;
$[5] = b;
$[6] = t6;
const c_5 = $[5] !== a;
const c_6 = $[6] !== b;
let t7;
if (c_5 || c_6) {
t7 = <Foo a={a} b={b}></Foo>;
$[5] = a;
$[6] = b;
$[7] = t7;
} else {
t6 = $[6];
t7 = $[7];
}
return t6;
return t7;
}
```
@@ -36,10 +36,11 @@ function Component(props) {
const c_0 = $[0] !== props.p0;
const c_1 = $[1] !== props.p2;
let x;
let y$0;
if (c_0 || c_1) {
x = [];
const y = undefined;
let y$0 = y;
y$0 = y;
bb1: switch (props.p0) {
case 1: {
break bb1;
@@ -47,11 +48,11 @@ function Component(props) {
case true: {
x.push(props.p2);
let y$1;
if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
if ($[4] === Symbol.for("react.memo_cache_sentinel")) {
y$1 = [];
$[3] = y$1;
$[4] = y$1;
} else {
y$1 = $[3];
y$1 = $[4];
}
y$0 = y$1;
break bb1;
@@ -67,31 +68,33 @@ function Component(props) {
$[0] = props.p0;
$[1] = props.p2;
$[2] = x;
$[3] = y$0;
} else {
x = $[2];
y$0 = $[3];
}
const c_4 = $[4] !== x;
const c_5 = $[5] !== x;
let child;
if (c_4) {
if (c_5) {
child = <Component data={x}></Component>;
$[4] = x;
$[5] = child;
$[5] = x;
$[6] = child;
} else {
child = $[5];
child = $[6];
}
y$0.push(props.p4);
const c_6 = $[6] !== y$0;
const c_7 = $[7] !== child;
let t8;
if (c_6 || c_7) {
t8 = <Component data={y$0}>{child}</Component>;
$[6] = y$0;
$[7] = child;
$[8] = t8;
const c_7 = $[7] !== y$0;
const c_8 = $[8] !== child;
let t9;
if (c_7 || c_8) {
t9 = <Component data={y$0}>{child}</Component>;
$[7] = y$0;
$[8] = child;
$[9] = t9;
} else {
t8 = $[8];
t9 = $[9];
}
return t8;
return t9;
}
```
@@ -32,10 +32,11 @@ function Component(props) {
const c_1 = $[1] !== props.p2;
const c_2 = $[2] !== props.p3;
let x;
let y$0;
if (c_0 || c_1 || c_2) {
x = [];
const y = undefined;
let y$0 = y;
y$0 = y;
switch (props.p0) {
case true: {
x.push(props.p2);
@@ -51,31 +52,33 @@ function Component(props) {
$[1] = props.p2;
$[2] = props.p3;
$[3] = x;
$[4] = y$0;
} else {
x = $[3];
y$0 = $[4];
}
const c_4 = $[4] !== x;
const c_5 = $[5] !== x;
let child;
if (c_4) {
if (c_5) {
child = <Component data={x}></Component>;
$[4] = x;
$[5] = child;
$[5] = x;
$[6] = child;
} else {
child = $[5];
child = $[6];
}
y$0.push(props.p4);
const c_6 = $[6] !== y$0;
const c_7 = $[7] !== child;
let t8;
if (c_6 || c_7) {
t8 = <Component data={y$0}>{child}</Component>;
$[6] = y$0;
$[7] = child;
$[8] = t8;
const c_7 = $[7] !== y$0;
const c_8 = $[8] !== child;
let t9;
if (c_7 || c_8) {
t9 = <Component data={y$0}>{child}</Component>;
$[7] = y$0;
$[8] = child;
$[9] = t9;
} else {
t8 = $[8];
t9 = $[9];
}
return t8;
return t9;
}
```