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

[hir] Introduce ValueKind.Context

This tracks whether a value is a context ref or generated from a context ref. This lets us track mutations to context refs and treat it separately as we want this to be more conservative than our existing inference. ValueKind.Context is exactly like ValueKind.Mutable but is more conservative.

Sathya Gunasekaran committed Feb 10, 2023 at 17:59 UTC fa57551dd51d6d3ad38b0a0bdb7543b41726c5dc
10 files changed +234 -4
compiler/forget/src/HIR/HIR.ts
+1
@@ -531,6 +531,7 @@ export enum ValueKind {
531 Frozen = "frozen",
532 Immutable = "immutable",
533 Mutable = "mutable",
534 + Context = "context",
535 }
536
537 /**
compiler/forget/src/Inference/InferReferenceEffects.ts
+31 -4
@@ -26,6 +26,7 @@ import {
26 } from "../HIR/PrintHIR";
27 import {
28 eachInstructionOperand,
29 + eachInstructionValueOperand,
30 eachTerminalOperand,
31 eachTerminalSuccessor,
32 } from "../HIR/visitors";
@@ -100,7 +101,7 @@ export default function inferReferenceEffects(fn: HIRFunction) {
101 properties: null,
102 loc: ref.loc,
103 };
103 - initialEnvironment.initialize(value, ValueKind.Mutable);
104 + initialEnvironment.initialize(value, ValueKind.Context);
105 initialEnvironment.define(ref, value);
106 }
107
@@ -274,6 +275,7 @@ class Environment {
275 case Effect.Freeze: {
276 if (
277 valueKind === ValueKind.Mutable ||
278 + valueKind === ValueKind.Context ||
279 valueKind === ValueKind.MaybeFrozen
280 ) {
281 effect = Effect.Freeze;
@@ -285,7 +287,10 @@ class Environment {
287 break;
288 }
289 case Effect.Mutate: {
288 - if (valueKind === ValueKind.Mutable) {
290 + if (
291 + valueKind === ValueKind.Mutable ||
292 + valueKind === ValueKind.Context
293 + ) {
294 effect = Effect.Mutate;
295 } else {
296 effect = Effect.Read;
@@ -488,6 +493,9 @@ class Environment {
493 * types. To ensure that any sequence of joins btw those three states yields the
494 * correct maybe-frozen, these two have to produce a frozen value.
495 * - <any> | maybe-frozen => maybe-frozen
496 + * - immutable | context => context
497 + * - mutable | context => context
498 + * - frozen | context => maybe-frozen
499 *
500 * ┌──────────────────────────┐
501 * │ Immutable │───┐
@@ -513,10 +521,21 @@ function mergeValues(a: ValueKind, b: ValueKind): ValueKind {
521 if (a === ValueKind.Frozen || b === ValueKind.Frozen) {
522 // frozen | mutable
523 return ValueKind.MaybeFrozen;
524 + } else if (a === ValueKind.Context || b === ValueKind.Context) {
525 + // context | mutable
526 + return ValueKind.Context;
527 } else {
528 // mutable | immutable
529 return ValueKind.Mutable;
530 }
531 + } else if (a === ValueKind.Context || b === ValueKind.Context) {
532 + if (a === ValueKind.Frozen || b === ValueKind.Frozen) {
533 + // frozen | context
534 + return ValueKind.MaybeFrozen;
535 + } else {
536 + // context | immutable
537 + return ValueKind.Context;
538 + }
539 } else {
540 // frozen | immutable
541 return ValueKind.Frozen;
@@ -664,7 +683,11 @@ function inferBlock(env: Environment, block: BasicBlock) {
683 continue;
684 }
685 case "PropertyStore": {
667 - env.reference(instrValue.value, Effect.Capture);
686 + const effect =
687 + env.kind(instrValue.object) === ValueKind.Context
688 + ? Effect.Mutate
689 + : Effect.Capture;
690 + env.reference(instrValue.value, effect);
691 env.reference(instrValue.object, Effect.Store);
692
693 const lvalue = instr.lvalue;
@@ -692,7 +715,11 @@ function inferBlock(env: Environment, block: BasicBlock) {
715 continue;
716 }
717 case "ComputedStore": {
695 - env.reference(instrValue.value, Effect.Capture);
718 + const effect =
719 + env.kind(instrValue.object) === ValueKind.Context
720 + ? Effect.Mutate
721 + : Effect.Capture;
722 + env.reference(instrValue.value, effect);
723 env.reference(instrValue.property, Effect.Capture);
724 env.reference(instrValue.object, Effect.Store);
725
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md new
+40
@@ -0,0 +1,40 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function component(a) {
6 + let x = { a };
7 + let y;
8 + (function () {
9 + y["x"] = x;
10 + })();
11 + mutate(y);
12 + return y;
13 +}
14 +
15 +```
16 +
17 +## Code
18 +
19 +```javascript
20 +function component(a) {
21 + const $ = React.unstable_useMemoCache();
22 + const c_0 = $[0] !== a;
23 + let y;
24 + if (c_0) {
25 + const x = { a: a };
26 + y = undefined;
27 + (function () {
28 + y["x"] = x;
29 + })();
30 + $[0] = a;
31 + $[1] = y;
32 + } else {
33 + y = $[1];
34 + }
35 + mutate(y);
36 + return y;
37 +}
38 +
39 +```
40 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js new
+9
@@ -0,0 +1,9 @@
1 +function component(a) {
2 + let x = { a };
3 + let y;
4 + (function () {
5 + y["x"] = x;
6 + })();
7 + mutate(y);
8 + return y;
9 +}
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md new
+40
@@ -0,0 +1,40 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function component(a) {
6 + let x = { a };
7 + let y;
8 + (function () {
9 + y.x = x;
10 + })();
11 + mutate(y);
12 + return y;
13 +}
14 +
15 +```
16 +
17 +## Code
18 +
19 +```javascript
20 +function component(a) {
21 + const $ = React.unstable_useMemoCache();
22 + const c_0 = $[0] !== a;
23 + let y;
24 + if (c_0) {
25 + const x = { a: a };
26 + y = undefined;
27 + (function () {
28 + y.x = x;
29 + })();
30 + $[0] = a;
31 + $[1] = y;
32 + } else {
33 + y = $[1];
34 + }
35 + mutate(y);
36 + return y;
37 +}
38 +
39 +```
40 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js new
+9
@@ -0,0 +1,9 @@
1 +function component(a) {
2 + let x = { a };
3 + let y;
4 + (function () {
5 + y.x = x;
6 + })();
7 + mutate(y);
8 + return y;
9 +}
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md new
+42
@@ -0,0 +1,42 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function component(a) {
6 + let x = { a };
7 + let y = {};
8 + (function () {
9 + let a = y;
10 + a["x"] = x;
11 + })();
12 + mutate(y);
13 + return y;
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +function component(a) {
22 + const $ = React.unstable_useMemoCache();
23 + const c_0 = $[0] !== a;
24 + let y;
25 + if (c_0) {
26 + const x = { a: a };
27 + y = {};
28 + (function () {
29 + let a = y;
30 + a["x"] = x;
31 + })();
32 + mutate(y);
33 + $[0] = a;
34 + $[1] = y;
35 + } else {
36 + y = $[1];
37 + }
38 + return y;
39 +}
40 +
41 +```
42 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js new
+10
@@ -0,0 +1,10 @@
1 +function component(a) {
2 + let x = { a };
3 + let y = {};
4 + (function () {
5 + let a = y;
6 + a["x"] = x;
7 + })();
8 + mutate(y);
9 + return y;
10 +}
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md new
+42
@@ -0,0 +1,42 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function component(a) {
6 + let x = { a };
7 + let y = {};
8 + (function () {
9 + let a = y;
10 + a.x = x;
11 + })();
12 + mutate(y);
13 + return y;
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +function component(a) {
22 + const $ = React.unstable_useMemoCache();
23 + const c_0 = $[0] !== a;
24 + let y;
25 + if (c_0) {
26 + const x = { a: a };
27 + y = {};
28 + (function () {
29 + let a = y;
30 + a.x = x;
31 + })();
32 + mutate(y);
33 + $[0] = a;
34 + $[1] = y;
35 + } else {
36 + y = $[1];
37 + }
38 + return y;
39 +}
40 +
41 +```
42 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js new
+10
@@ -0,0 +1,10 @@
1 +function component(a) {
2 + let x = { a };
3 + let y = {};
4 + (function () {
5 + let a = y;
6 + a.x = x;
7 + })();
8 + mutate(y);
9 + return y;
10 +}