From fa57551dd51d6d3ad38b0a0bdb7543b41726c5dc Mon Sep 17 00:00:00 2001 From: Sathya Gunasekaran Date: Fri, 10 Feb 2023 17:59:56 +0000 Subject: [PATCH] [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. --- compiler/forget/src/HIR/HIR.ts | 1 + .../src/Inference/InferReferenceEffects.ts | 35 ++++++++++++++-- ...uring-func-alias-computed-mutate.expect.md | 40 ++++++++++++++++++ .../capturing-func-alias-computed-mutate.js | 9 ++++ .../hir/capturing-func-alias-mutate.expect.md | 40 ++++++++++++++++++ .../hir/capturing-func-alias-mutate.js | 9 ++++ ...c-alias-receiver-computed-mutate.expect.md | 42 +++++++++++++++++++ ...ing-func-alias-receiver-computed-mutate.js | 10 +++++ ...uring-func-alias-receiver-mutate.expect.md | 42 +++++++++++++++++++ .../capturing-func-alias-receiver-mutate.js | 10 +++++ 10 files changed, 234 insertions(+), 4 deletions(-) create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js diff --git a/compiler/forget/src/HIR/HIR.ts b/compiler/forget/src/HIR/HIR.ts index e6e7b8d589..3570e62324 100644 --- a/compiler/forget/src/HIR/HIR.ts +++ b/compiler/forget/src/HIR/HIR.ts @@ -531,6 +531,7 @@ export enum ValueKind { Frozen = "frozen", Immutable = "immutable", Mutable = "mutable", + Context = "context", } /** diff --git a/compiler/forget/src/Inference/InferReferenceEffects.ts b/compiler/forget/src/Inference/InferReferenceEffects.ts index 246b23617a..0a35a12c91 100644 --- a/compiler/forget/src/Inference/InferReferenceEffects.ts +++ b/compiler/forget/src/Inference/InferReferenceEffects.ts @@ -26,6 +26,7 @@ import { } from "../HIR/PrintHIR"; import { eachInstructionOperand, + eachInstructionValueOperand, eachTerminalOperand, eachTerminalSuccessor, } from "../HIR/visitors"; @@ -100,7 +101,7 @@ export default function inferReferenceEffects(fn: HIRFunction) { properties: null, loc: ref.loc, }; - initialEnvironment.initialize(value, ValueKind.Mutable); + initialEnvironment.initialize(value, ValueKind.Context); initialEnvironment.define(ref, value); } @@ -274,6 +275,7 @@ class Environment { case Effect.Freeze: { if ( valueKind === ValueKind.Mutable || + valueKind === ValueKind.Context || valueKind === ValueKind.MaybeFrozen ) { effect = Effect.Freeze; @@ -285,7 +287,10 @@ class Environment { break; } case Effect.Mutate: { - if (valueKind === ValueKind.Mutable) { + if ( + valueKind === ValueKind.Mutable || + valueKind === ValueKind.Context + ) { effect = Effect.Mutate; } else { effect = Effect.Read; @@ -488,6 +493,9 @@ class Environment { * types. To ensure that any sequence of joins btw those three states yields the * correct maybe-frozen, these two have to produce a frozen value. * - | maybe-frozen => maybe-frozen + * - immutable | context => context + * - mutable | context => context + * - frozen | context => maybe-frozen * * ┌──────────────────────────┐ * │ Immutable │───┐ @@ -513,10 +521,21 @@ function mergeValues(a: ValueKind, b: ValueKind): ValueKind { if (a === ValueKind.Frozen || b === ValueKind.Frozen) { // frozen | mutable return ValueKind.MaybeFrozen; + } else if (a === ValueKind.Context || b === ValueKind.Context) { + // context | mutable + return ValueKind.Context; } else { // mutable | immutable return ValueKind.Mutable; } + } else if (a === ValueKind.Context || b === ValueKind.Context) { + if (a === ValueKind.Frozen || b === ValueKind.Frozen) { + // frozen | context + return ValueKind.MaybeFrozen; + } else { + // context | immutable + return ValueKind.Context; + } } else { // frozen | immutable return ValueKind.Frozen; @@ -664,7 +683,11 @@ function inferBlock(env: Environment, block: BasicBlock) { continue; } case "PropertyStore": { - env.reference(instrValue.value, Effect.Capture); + const effect = + env.kind(instrValue.object) === ValueKind.Context + ? Effect.Mutate + : Effect.Capture; + env.reference(instrValue.value, effect); env.reference(instrValue.object, Effect.Store); const lvalue = instr.lvalue; @@ -692,7 +715,11 @@ function inferBlock(env: Environment, block: BasicBlock) { continue; } case "ComputedStore": { - env.reference(instrValue.value, Effect.Capture); + const effect = + env.kind(instrValue.object) === ValueKind.Context + ? Effect.Mutate + : Effect.Capture; + env.reference(instrValue.value, effect); env.reference(instrValue.property, Effect.Capture); env.reference(instrValue.object, Effect.Store); diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md new file mode 100644 index 0000000000..d936f8018a --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.expect.md @@ -0,0 +1,40 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y; + (function () { + y["x"] = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = undefined; + (function () { + y["x"] = x; + })(); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + mutate(y); + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js new file mode 100644 index 0000000000..d3a669613b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-computed-mutate.js @@ -0,0 +1,9 @@ +function component(a) { + let x = { a }; + let y; + (function () { + y["x"] = x; + })(); + mutate(y); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md new file mode 100644 index 0000000000..2402cdfb39 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.expect.md @@ -0,0 +1,40 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y; + (function () { + y.x = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = undefined; + (function () { + y.x = x; + })(); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + mutate(y); + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js new file mode 100644 index 0000000000..6da8bf9d1a --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-mutate.js @@ -0,0 +1,9 @@ +function component(a) { + let x = { a }; + let y; + (function () { + y.x = x; + })(); + mutate(y); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md new file mode 100644 index 0000000000..53055ae54b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a["x"] = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = {}; + (function () { + let a = y; + a["x"] = x; + })(); + mutate(y); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js new file mode 100644 index 0000000000..dba7a25cf1 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-computed-mutate.js @@ -0,0 +1,10 @@ +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a["x"] = x; + })(); + mutate(y); + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md new file mode 100644 index 0000000000..4116b7bb76 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.expect.md @@ -0,0 +1,42 @@ + +## Input + +```javascript +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a.x = x; + })(); + mutate(y); + return y; +} + +``` + +## Code + +```javascript +function component(a) { + const $ = React.unstable_useMemoCache(); + const c_0 = $[0] !== a; + let y; + if (c_0) { + const x = { a: a }; + y = {}; + (function () { + let a = y; + a.x = x; + })(); + mutate(y); + $[0] = a; + $[1] = y; + } else { + y = $[1]; + } + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js new file mode 100644 index 0000000000..0599917f1b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-func-alias-receiver-mutate.js @@ -0,0 +1,10 @@ +function component(a) { + let x = { a }; + let y = {}; + (function () { + let a = y; + a.x = x; + })(); + mutate(y); + return y; +}