@samitouri / QOS-React-2 / commits / c7a84fc033

Foundation for PropertyStore (no lowering yet)

Adds a `InstructionValue::PropertyStore` variant and the minimal necessary handling across the passes to get things to compile. Lowering is in the next PR.

Joe Savona committed Dec 22, 2022 at 14:51 UTC c7a84fc033a7638728b9c983c6e7e84d16c966e3
16 files changed +197 -49
compiler/forget/src/HIR/Codegen.ts
+11
@@ -458,6 +458,17 @@ export function codegenInstructionValue(
458 value = node;
459 break;
460 }
461 + case "PropertyStore": {
462 + value = t.assignmentExpression(
463 + "=",
464 + t.memberExpression(
465 + codegenPlace(temp, instrValue.object),
466 + t.identifier(instrValue.property)
467 + ),
468 + codegenPlace(temp, instrValue.value)
469 + );
470 + break;
471 + }
472 case "PropertyLoad": {
473 value = t.memberExpression(
474 codegenPlace(temp, instrValue.object),
compiler/forget/src/HIR/HIR.ts
+1 -1
@@ -294,7 +294,7 @@ export type InstructionData =
294 | { kind: "JsxFragment"; children: Array<Place> }
295
296 // store `object.property = value`
297 - // | { kind: "PropertyStore"; object: Place; property: string; value: Place }
297 + | { kind: "PropertyStore"; object: Place; property: string; value: Place }
298 // load `object.property`
299 | { kind: "PropertyLoad"; object: Place; property: string }
300
compiler/forget/src/HIR/InferAliasForStores.ts
+4
@@ -36,6 +36,10 @@ export function inferAliasForStores(
36 }
37 break;
38 }
39 + case "PropertyStore": {
40 + maybeAlias(aliases, value.object, value.value, instr.id);
41 + break;
42 + }
43 }
44 }
45 }
compiler/forget/src/HIR/InferReferenceEffects.ts
+20 -13
@@ -578,6 +578,24 @@ function inferBlock(env: Environment, block: BasicBlock) {
578 valueKind = ValueKind.Immutable;
579 break;
580 }
581 + case "PropertyStore": {
582 + const effect = isObjectType(instrValue.object.identifier)
583 + ? Effect.Store
584 + : Effect.Mutate;
585 + env.reference(instrValue.value, Effect.Read);
586 + env.reference(instrValue.object, effect);
587 +
588 + const lvalue = instr.lvalue;
589 + if (lvalue !== null) {
590 + invariant(
591 + lvalue.place.memberPath === null,
592 + "PropertyLoad must always be saved to a temporary"
593 + );
594 + env.alias(lvalue.place, instrValue.value);
595 + lvalue.place.effect = Effect.Store;
596 + }
597 + continue;
598 + }
599 case "PropertyLoad": {
600 if (!env.isDefined(instrValue.object)) {
601 // TODO @josephsavona: improve handling of globals
@@ -611,19 +629,8 @@ function inferBlock(env: Environment, block: BasicBlock) {
629 const lvalue = instr.lvalue;
630 if (lvalue !== null) {
631 lvalue.place.effect = Effect.Mutate;
614 - if (
615 - lvalue.place.memberPath === null &&
616 - instrValue.memberPath === null
617 - ) {
618 - // direct aliasing: `a = b`;
619 - env.alias(lvalue.place, instrValue);
620 - } else {
621 - // no-op: `a.b.c = d`
622 - const effect = isObjectType(lvalue.place.identifier)
623 - ? Effect.Store
624 - : Effect.Mutate;
625 - env.reference(lvalue.place, effect);
626 - }
632 + // direct aliasing: `a = b`;
633 + env.alias(lvalue.place, instrValue);
634 }
635 continue;
636 }
compiler/forget/src/HIR/PrintHIR.ts
+6
@@ -271,6 +271,12 @@ export function printInstructionValue(instrValue: InstructionValue): string {
271 }`;
272 break;
273 }
274 + case "PropertyStore": {
275 + value = `PropertyStore ${printPlace(instrValue.object)}.${
276 + instrValue.property
277 + } = ${printPlace(instrValue.value)}`;
278 + break;
279 + }
280 default: {
281 assertExhaustive(
282 instrValue,
compiler/forget/src/HIR/visitors.ts
+10
@@ -45,6 +45,11 @@ export function* eachInstructionValueOperand(
45 yield instrValue.object;
46 break;
47 }
48 + case "PropertyStore": {
49 + yield instrValue.object;
50 + yield instrValue.value;
51 + break;
52 + }
53 case "UnaryExpression": {
54 yield instrValue.value;
55 break;
@@ -100,6 +105,11 @@ export function mapInstructionOperands(
105 instrValue.object = fn(instrValue.object);
106 break;
107 }
108 + case "PropertyStore": {
109 + instrValue.object = fn(instrValue.object);
110 + instrValue.value = fn(instrValue.value);
111 + break;
112 + }
113 case "Identifier": {
114 instr.value = fn(instrValue);
115 break;
compiler/forget/src/ReactiveScopes/InferReactiveScopeVariables.ts
+1
@@ -169,6 +169,7 @@ function mayAllocate(value: InstructionValue): boolean {
169 case "Primitive": {
170 return false;
171 }
172 + case "PropertyStore":
173 case "ArrayExpression":
174 case "CallExpression":
175 case "JsxExpression":
compiler/forget/src/ReactiveScopes/PropagateScopeDependencies.ts
+1
@@ -319,6 +319,7 @@ function valueKind(value: InstructionValue): DeclKind {
319 case "Primitive": {
320 return DeclKind.Const;
321 }
322 + case "PropertyStore":
323 case "PropertyLoad":
324 case "Identifier":
325 case "ArrayExpression":
compiler/forget/src/__tests__/fixtures/hir/alias-nested-member-path-mutate.expect.md
+21 -3
@@ -17,9 +17,27 @@ function component() {
17
18 ```javascript
19 function component() {
20 - const z = [];
21 - const y = {};
22 - y.z = z;
20 + const $ = React.useMemoCache();
21 + let z;
22 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23 + z = [];
24 + $[0] = z;
25 + } else {
26 + z = $[0];
27 + }
28 +
29 + const c_1 = $[1] !== z;
30 + let y;
31 +
32 + if (c_1) {
33 + y = {};
34 + y.z = z;
35 + $[1] = z;
36 + $[2] = y;
37 + } else {
38 + y = $[2];
39 + }
40 +
41 const x = {};
42 x.y = y;
43 mutate(x.y.z);
compiler/forget/src/__tests__/fixtures/hir/mutable-lifetime-with-aliasing.expect.md
+21 -2
@@ -45,8 +45,26 @@ function mutate(x, y) {}
45
46 ```javascript
47 function Component(props) {
48 - const a = {};
49 - const b = [a];
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 +
57 + const c_1 = $[1] !== a;
58 + let b;
59 +
60 + if (c_1) {
61 + b = [a];
62 + $[1] = a;
63 + $[2] = b;
64 + } else {
65 + b = $[2];
66 + }
67 +
68 const c = {};
69 const d = {
70 c: c,
@@ -54,6 +72,7 @@ function Component(props) {
72 const x = {};
73 x.b = b;
74 const y = mutate(x, d);
75 +
76 if (a) {
77 }
78
compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md
+33 -20
@@ -18,37 +18,50 @@ function Component(props) {
18 ```javascript
19 function Component(props) {
20 const $ = React.useMemoCache();
21 - const c_0 = $[0] !== props.p0;
21 let x;
23 - let child;
24 - if (c_0) {
22 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
23 x = {};
26 - const y = [];
24 + let y;
25 +
26 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
27 + y = [];
28 + $[1] = y;
29 + } else {
30 + y = $[1];
31 + }
32 +
33 x.y = y;
34 + $[0] = x;
35 + } else {
36 + x = $[0];
37 + }
38 +
39 + const c_2 = $[2] !== y;
40 + let child;
41 +
42 + if (c_2) {
43 child = <Component data={y}></Component>;
29 - x.y.push(props.p0);
30 - $[0] = props.p0;
31 - $[1] = x;
32 - $[2] = child;
44 + $[2] = y;
45 + $[3] = child;
46 } else {
34 - x = $[1];
35 - child = $[2];
47 + child = $[3];
48 }
49
38 - const c_3 = $[3] !== x;
39 - const c_4 = $[4] !== child;
40 - let t5;
50 + x.y.push(props.p0);
51 + const c_4 = $[4] !== x;
52 + const c_5 = $[5] !== child;
53 + let t6;
54
42 - if (c_3 || c_4) {
43 - t5 = <Component data={x}>{child}</Component>;
44 - $[3] = x;
45 - $[4] = child;
46 - $[5] = t5;
55 + if (c_4 || c_5) {
56 + t6 = <Component data={x}>{child}</Component>;
57 + $[4] = x;
58 + $[5] = child;
59 + $[6] = t6;
60 } else {
48 - t5 = $[5];
61 + t6 = $[6];
62 }
63
51 - return t5;
64 + return t6;
65 }
66
67 ```
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-alias-mutate-if.expect.md
+18 -2
@@ -30,10 +30,26 @@ function foo(a) {
30 x = b;
31
32 if (a) {
33 - const y = {};
33 + let y;
34 +
35 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
36 + y = {};
37 + $[2] = y;
38 + } else {
39 + y = $[2];
40 + }
41 +
42 x.y = y;
43 } else {
36 - const z = {};
44 + let z;
45 +
46 + if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
47 + z = {};
48 + $[3] = z;
49 + } else {
50 + z = $[3];
51 + }
52 +
53 x.z = z;
54 }
55
compiler/forget/src/__tests__/fixtures/hir/ssa-property-alias-mutate-if.expect.md
+18 -2
@@ -28,10 +28,26 @@ function foo(a) {
28 x = {};
29
30 if (a) {
31 - const y = {};
31 + let y;
32 +
33 + if ($[2] === Symbol.for("react.memo_cache_sentinel")) {
34 + y = {};
35 + $[2] = y;
36 + } else {
37 + y = $[2];
38 + }
39 +
40 x.y = y;
41 } else {
34 - const z = {};
42 + let z;
43 +
44 + if ($[3] === Symbol.for("react.memo_cache_sentinel")) {
45 + z = {};
46 + $[3] = z;
47 + } else {
48 + z = $[3];
49 + }
50 +
51 x.z = z;
52 }
53
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate-alias.expect.md
+9 -1
@@ -24,7 +24,15 @@ function foo() {
24 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
25 const a = {};
26 y = a;
27 - const x = [];
27 + let x;
28 +
29 + if ($[1] === Symbol.for("react.memo_cache_sentinel")) {
30 + x = [];
31 + $[1] = x;
32 + } else {
33 + x = $[1];
34 + }
35 +
36 y.x = x;
37 mutate(a);
38 $[0] = y;
compiler/forget/src/__tests__/fixtures/hir/ssa-property-mutate.expect.md
+14 -4
@@ -17,15 +17,25 @@ function foo() {
17 ```javascript
18 function foo() {
19 const $ = React.useMemoCache();
20 - let y;
20 + let x;
21 if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
22 - const x = [];
22 + x = [];
23 + $[0] = x;
24 + } else {
25 + x = $[0];
26 + }
27 +
28 + const c_1 = $[1] !== x;
29 + let y;
30 +
31 + if (c_1) {
32 y = {};
33 y.x = x;
34 mutate(y);
26 - $[0] = y;
35 + $[1] = x;
36 + $[2] = y;
37 } else {
28 - y = $[0];
38 + y = $[2];
39 }
40
41 return y;
compiler/forget/src/__tests__/fixtures/hir/transitive-alias-fields.expect.md
+9 -1
@@ -21,10 +21,18 @@ function component() {
21
22 ```javascript
23 function component() {
24 + const $ = React.useMemoCache();
25 const x = {};
26 const p = {};
27 const q = {};
27 - const y = {};
28 + let y;
29 + if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
30 + y = {};
31 + $[0] = y;
32 + } else {
33 + y = $[0];
34 + }
35 +
36 x.y = y;
37 p.y = x.y;
38 q.y = p.y;