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;