@samitouri / QOS-React-2 / commits / 74a0d54db2

Account for stores that can occur in object/array literals

Joe Savona committed Dec 22, 2022 at 14:00 UTC 74a0d54db2e96ebcedfb5e39e41cb2bbd3acacef
11 files changed +167 -64
compiler/forget/src/HIR/InferAliasForStores.ts
+34 -13
@@ -4,9 +4,8 @@
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 */
7 -import invariant from "invariant";
7 import DisjointSet from "../Utils/DisjointSet";
9 -import { Effect, HIRFunction, Identifier } from "./HIR";
8 +import { Effect, HIRFunction, Identifier, InstructionId, Place } from "./HIR";
9
10 export function inferAliasForStores(
11 func: HIRFunction,
@@ -18,18 +17,40 @@ export function inferAliasForStores(
17 if (lvalue === null || lvalue.place.effect !== Effect.Store) {
18 continue;
19 }
21 -
22 - invariant(
23 - value.kind === "Identifier",
24 - "only identifiers can be aliased by stores"
25 - );
26 -
27 - if (
28 - lvalue.place.identifier.mutableRange.end > instr.id ||
29 - value.identifier.mutableRange.end > instr.id
30 - ) {
31 - aliases.union([lvalue.place.identifier, value.identifier]);
20 + switch (value.kind) {
21 + case "Identifier": {
22 + maybeAlias(aliases, lvalue.place, value, instr.id);
23 + break;
24 + }
25 + case "ArrayExpression": {
26 + for (const item of value.elements) {
27 + maybeAlias(aliases, lvalue.place, item, instr.id);
28 + }
29 + break;
30 + }
31 + case "ObjectExpression": {
32 + if (value.properties !== null) {
33 + for (const [, property] of value.properties) {
34 + maybeAlias(aliases, lvalue.place, property, instr.id);
35 + }
36 + }
37 + break;
38 + }
39 }
40 }
41 }
42 }
43 +
44 +function maybeAlias(
45 + aliases: DisjointSet<Identifier>,
46 + lvalue: Place,
47 + rvalue: Place,
48 + id: InstructionId
49 +): void {
50 + if (
51 + lvalue.identifier.mutableRange.end > id ||
52 + rvalue.identifier.mutableRange.end > id
53 + ) {
54 + aliases.union([lvalue.identifier, rvalue.identifier]);
55 + }
56 +}
compiler/forget/src/HIR/InferReferenceEffects.ts
+4 -1
@@ -516,6 +516,7 @@ function inferBlock(env: Environment, block: BasicBlock) {
516 for (const instr of block.instructions) {
517 const instrValue = instr.value;
518 let effectKind: Effect | null = null;
519 + let lvalueEffect = Effect.Mutate;
520 let valueKind: ValueKind;
521 switch (instrValue.kind) {
522 case "BinaryExpression": {
@@ -526,6 +527,7 @@ function inferBlock(env: Environment, block: BasicBlock) {
527 case "ArrayExpression": {
528 valueKind = ValueKind.Mutable;
529 effectKind = Effect.Read;
530 + lvalueEffect = Effect.Store;
531 break;
532 }
533 case "NewExpression": {
@@ -547,6 +549,7 @@ function inferBlock(env: Environment, block: BasicBlock) {
549 valueKind = ValueKind.Mutable;
550 // Object construction captures but does not modify the key/property values
551 effectKind = Effect.Read;
552 + lvalueEffect = Effect.Store;
553 break;
554 }
555 case "UnaryExpression": {
@@ -645,7 +648,7 @@ function inferBlock(env: Environment, block: BasicBlock) {
648 } else {
649 env.reference(instr.lvalue.place, Effect.Mutate);
650 }
648 - instr.lvalue.place.effect = Effect.Mutate;
651 + instr.lvalue.place.effect = lvalueEffect;
652 }
653 }
654
compiler/forget/src/__tests__/fixtures/hir/assignment-variations-complex-lvalue-array.expect.md new
+33
@@ -0,0 +1,33 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + const a = [[1]];
7 + const first = a.at(0);
8 + first.set(0, 2);
9 + return a;
10 +}
11 +
12 +```
13 +
14 +## Code
15 +
16 +```javascript
17 +function foo() {
18 + const $ = React.useMemoCache();
19 + let a;
20 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 + a = [[1]];
22 + const first = a.at(0);
23 + first.set(0, 2);
24 + $[0] = a;
25 + } else {
26 + a = $[0];
27 + }
28 +
29 + return a;
30 +}
31 +
32 +```
33 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/assignment-variations-complex-lvalue-array.js new
+6
@@ -0,0 +1,6 @@
1 +function foo() {
2 + const a = [[1]];
3 + const first = a.at(0);
4 + first.set(0, 2);
5 + return a;
6 +}
compiler/forget/src/__tests__/fixtures/hir/assignment-variations-complex-lvalue.expect.md
+6 -17
@@ -16,29 +16,18 @@ function g() {
16 ```javascript
17 function g() {
18 const $ = React.useMemoCache();
19 - let t0;
20 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 - t0 = {
22 - z: 1,
23 - };
24 - $[0] = t0;
25 - } else {
26 - t0 = $[0];
27 - }
28 -
29 - const c_1 = $[1] !== t0;
19 let x;
31 -
32 - if (c_1) {
20 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 x = {
34 - y: t0,
22 + y: {
23 + z: 1,
24 + },
25 };
26 x.z.y = x.y.z + 1;
27 x.z.y = x.y.z * 2;
38 - $[1] = t0;
39 - $[2] = x;
28 + $[0] = x;
29 } else {
41 - x = $[2];
30 + x = $[0];
31 }
32
33 return x;
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md
+2 -19
@@ -45,32 +45,15 @@ function mutate(x, y) {}
45
46 ```javascript
47 function Component(props) {
48 - const $ = React.useMemoCache();
49 - let a;
50 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
51 - a = {};
52 - $[0] = a;
53 - } else {
54 - a = $[0];
55 - }
56 -
48 + const a = {};
49 const b = [a];
58 - let c;
59 -
60 - if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
61 - c = {};
62 - $[1] = c;
63 - } else {
64 - c = $[1];
65 - }
66 -
50 + const c = {};
51 const d = {
52 c: c,
53 };
54 const x = {};
55 x.b = b;
56 const y = mutate(x, d);
73 -
57 if (a) {
58 }
59
compiler/forget/src/__tests__/fixtures/hir/ssa-property-call.expect.md
+4 -14
@@ -16,26 +16,16 @@ function foo() {
16 ```javascript
17 function foo() {
18 const $ = React.useMemoCache();
19 - let x;
20 - if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 - x = [];
22 - $[0] = x;
23 - } else {
24 - x = $[0];
25 - }
26 -
27 - const c_1 = $[1] !== x;
19 let y;
29 -
30 - if (c_1) {
20 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 + const x = [];
22 y = {
23 x: x,
24 };
25 y.x.push([]);
35 - $[1] = x;
36 - $[2] = y;
26 + $[0] = y;
27 } else {
38 - y = $[2];
28 + y = $[0];
29 }
30
31 return y;
compiler/forget/src/__tests__/fixtures/hir/store-via-call.expect.md new
+33
@@ -0,0 +1,33 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function foo() {
6 + const x = {};
7 + const y = foo(x);
8 + y.mutate();
9 + return x;
10 +}
11 +
12 +```
13 +
14 +## Code
15 +
16 +```javascript
17 +function foo() {
18 + const $ = React.useMemoCache();
19 + let x;
20 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 + x = {};
22 + const y = foo(x);
23 + y.mutate();
24 + $[0] = x;
25 + } else {
26 + x = $[0];
27 + }
28 +
29 + return x;
30 +}
31 +
32 +```
33 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/store-via-call.js new
+6
@@ -0,0 +1,6 @@
1 +function foo() {
2 + const x = {};
3 + const y = foo(x);
4 + y.mutate();
5 + return x;
6 +}
compiler/forget/src/__tests__/fixtures/hir/store-via-new.expect.md new
+33
@@ -0,0 +1,33 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Foo() {
6 + const x = {};
7 + const y = new Foo(x);
8 + y.mutate();
9 + return x;
10 +}
11 +
12 +```
13 +
14 +## Code
15 +
16 +```javascript
17 +function Foo() {
18 + const $ = React.useMemoCache();
19 + let x;
20 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
21 + x = {};
22 + const y = new Foo(x);
23 + y.mutate();
24 + $[0] = x;
25 + } else {
26 + x = $[0];
27 + }
28 +
29 + return x;
30 +}
31 +
32 +```
33 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/store-via-new.js new
+6
@@ -0,0 +1,6 @@
1 +function Foo() {
2 + const x = {};
3 + const y = new Foo(x);
4 + y.mutate();
5 + return x;
6 +}