From 9048f5923745ace73cc9210d4a4bb0bfdedb0d8d Mon Sep 17 00:00:00 2001 From: Joe Savona Date: Thu, 9 Mar 2023 09:51:43 -0800 Subject: [PATCH] Prune scopes whose values dont escape Adds a new pass that uses escape analysis and React-specific heuristics to tune the amount of memoization applied. Specifically, the pass ensures that we only memoize: * Values which escape (are directly returned or transitively aliased by a returned value) * ...and that are not JSX elements * OR values which are _dependencies_ of scopes that produce an escaping value. The latter case is necessary to avoid breaking memoization of an escaping value bc a scope happened to have a non-escaping dependency. ## Algorithm 1. First we build up a graph, a mapping of IdentifierId to a node describing all the scopes and inputs involved in creating that identifier. Individual nodes are marked as definitely aliased, conditionally aliased, or unaliased: a. Arrays, objects, function calls all produce a new value and are always marked as aliased b. Conditional and logical expressions (and a few others) are conditinally aliased, depending on whether their result value is aliased. c. JSX is always unaliased (though its props children may be) 2. The same pass which builds the graph also stores the set of returned identifiers 3. We traverse the graph starting from the returned identifiers and mark reachable dependencies as escaping, based on the combination of the parent node's type and its children (eg a conditional node with an aliased dep promotes to aliased). 4. Finally we prune scopes whose outputs weren't marked. --- compiler/forget/src/CompilerPipeline.ts | 8 + .../ReactiveScopes/PrintReactiveFunction.ts | 4 +- .../ReactiveScopes/PruneNonEscapingScopes.ts | 581 ++++++++++++++++++ compiler/forget/src/ReactiveScopes/index.ts | 1 + ...g.capturing-function-renamed-ref.expect.md | 14 +- ...ng-primitive-as-dep-nested-scope.expect.md | 47 +- .../hir/allocating-primitive-as-dep.expect.md | 27 +- .../src/__tests__/fixtures/hir/call.expect.md | 11 +- .../hir/capture-func-passed-to-jsx.expect.md | 13 +- ...turing-function-member-expr-call.expect.md | 15 +- .../fixtures/hir/component.expect.md | 45 +- .../fixtures/hir/concise-arrow-expr.expect.md | 13 +- .../hir/conditional-on-mutable.expect.md | 30 +- .../hir/constant-propagation-for.expect.md | 13 +- .../hir/constant-propagation-while.expect.md | 13 +- .../fixtures/hir/constructor.expect.md | 11 +- .../fixtures/hir/controlled-input.expect.md | 15 +- .../__tests__/fixtures/hir/dce-loop.expect.md | 15 +- .../destructure-direct-reassignment.expect.md | 18 +- .../hir/escape-analysis-jsx-child.expect.md | 59 ++ .../fixtures/hir/escape-analysis-jsx-child.js | 11 + .../hir/escape-analysis-logical.expect.md | 68 ++ .../fixtures/hir/escape-analysis-logical.js | 8 + ...nterleaved-allocating-dependency.expect.md | 57 ++ ...aping-interleaved-allocating-dependency.js | 15 + ...interleaved-primitive-dependency.expect.md | 51 ++ ...caping-interleaved-primitive-dependency.js | 17 + ...pe-analysis-not-conditional-test.expect.md | 23 + .../escape-analysis-not-conditional-test.js | 5 + .../hir/escape-analysis-not-if-test.expect.md | 33 + .../hir/escape-analysis-not-if-test.js | 10 + .../escape-analysis-not-switch-case.expect.md | 33 + .../hir/escape-analysis-not-switch-case.js | 10 + .../escape-analysis-not-switch-test.expect.md | 33 + .../hir/escape-analysis-not-switch-test.js | 10 + .../fixtures/hir/for-logical.expect.md | 25 +- ...ing-test-export-default-function.expect.md | 24 +- ...test-export-function-and-default.expect.md | 24 +- .../hir/gating-test-export-function.expect.md | 24 +- .../fixtures/hir/gating-test.expect.md | 24 +- .../fixtures/hir/hook-call.expect.md | 23 +- .../hir/independent-across-if.expect.md | 15 +- .../fixtures/hir/independent.expect.md | 15 +- .../hir/interdependent-across-if.expect.md | 15 +- .../fixtures/hir/interdependent.expect.md | 15 +- .../fixtures/hir/jsx-fragment.expect.md | 36 +- .../fixtures/hir/jsx-spread.expect.md | 36 +- .../hir/logical-expression-object.expect.md | 43 +- .../fixtures/hir/logical-expression.expect.md | 38 +- .../hir/mutable-liverange-loop.expect.md | 10 +- ...ng-scopes-shadowing-within-block.expect.md | 14 +- .../hir/property-assignment.expect.md | 15 +- .../fixtures/hir/reactive-scopes-if.expect.md | 13 +- .../fixtures/hir/reactive-scopes.expect.md | 13 +- .../hir/reassignment-conditional.expect.md | 15 +- .../reassignment-separate-scopes.expect.md | 57 +- .../fixtures/hir/reassignment.expect.md | 15 +- ...ce-reactive-cond-deps-cfg-ifelse.expect.md | 13 +- ...d-deps-cfg-nested-ifelse-missing.expect.md | 22 +- ...tive-cond-deps-cfg-nested-ifelse.expect.md | 33 +- ...ond-deps-cfg-switch-missing-case.expect.md | 13 +- ...-deps-cfg-switch-missing-default.expect.md | 13 +- ...ce-reactive-cond-deps-cfg-switch.expect.md | 13 +- ...uce-reactive-cond-deps-no-uncond.expect.md | 13 +- ...eactive-cond-deps-promote-uncond.expect.md | 13 +- ...eactive-cond-deps-subpath-order1.expect.md | 13 +- ...eactive-cond-deps-subpath-order2.expect.md | 13 +- ...ctive-cond-deps-superpath-order1.expect.md | 13 +- ...ctive-cond-deps-superpath-order2.expect.md | 13 +- ...s-dep-and-redeclare-maybe-frozen.expect.md | 85 +-- ...me-variable-as-dep-and-redeclare.expect.md | 81 +-- .../fixtures/hir/ssa-call-jsx-2.expect.md | 20 +- .../fixtures/hir/ssa-call-jsx.expect.md | 11 +- .../ssa-cascading-eliminated-phis.expect.md | 13 +- .../hir/ssa-for-trivial-update.expect.md | 13 +- .../__tests__/fixtures/hir/ssa-for.expect.md | 13 +- .../fixtures/hir/ssa-leave-case.expect.md | 25 +- .../fixtures/hir/ssa-while.expect.md | 13 +- .../hir/switch-non-final-default.expect.md | 27 +- .../__tests__/fixtures/hir/switch.expect.md | 27 +- ...emporary-at-start-of-value-block.expect.md | 12 +- .../fixtures/hir/ternary-expression.expect.md | 36 +- .../__tests__/fixtures/hir/timers.expect.md | 41 +- .../hir/type-binary-operator.expect.md | 19 +- .../type-test-return-type-inference.expect.md | 26 +- .../fixtures/hir/unary-expr.expect.md | 73 +-- .../hir/use-callback-simple.expect.md | 13 +- .../fixtures/hir/useMemo-simple.expect.md | 13 +- .../fixtures/hir/while-property.expect.md | 17 +- 89 files changed, 1361 insertions(+), 1248 deletions(-) create mode 100644 compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.js create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.expect.md create mode 100644 compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.js diff --git a/compiler/forget/src/CompilerPipeline.ts b/compiler/forget/src/CompilerPipeline.ts index 39196724be..6aff1039f7 100644 --- a/compiler/forget/src/CompilerPipeline.ts +++ b/compiler/forget/src/CompilerPipeline.ts @@ -32,6 +32,7 @@ import { mergeOverlappingReactiveScopes, promoteUsedTemporaries, propagateScopeDependencies, + pruneNonEscapingScopes, pruneNonReactiveDependencies, pruneUnusedLabels, pruneUnusedLValues, @@ -145,6 +146,13 @@ export function* run( value: reactiveFunction, }); + pruneNonEscapingScopes(reactiveFunction); + yield log({ + kind: "reactive", + name: "PruneNonEscapingDependencies", + value: reactiveFunction, + }); + pruneNonReactiveDependencies(reactiveFunction); yield log({ kind: "reactive", diff --git a/compiler/forget/src/ReactiveScopes/PrintReactiveFunction.ts b/compiler/forget/src/ReactiveScopes/PrintReactiveFunction.ts index 74fad09b1e..a8efaa269a 100644 --- a/compiler/forget/src/ReactiveScopes/PrintReactiveFunction.ts +++ b/compiler/forget/src/ReactiveScopes/PrintReactiveFunction.ts @@ -84,9 +84,7 @@ function printReactiveInstruction( const id = `[${instruction.id}]`; if (instruction.lvalue !== null) { - writer.write( - `${id} ${printIdentifier(instruction.lvalue.identifier)} = ` - ); + writer.write(`${id} ${printPlace(instruction.lvalue)} = `); printReactiveValue(writer, instruction.value); writer.newline(); } else { diff --git a/compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts b/compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts new file mode 100644 index 0000000000..0c6f8dd8ff --- /dev/null +++ b/compiler/forget/src/ReactiveScopes/PruneNonEscapingScopes.ts @@ -0,0 +1,581 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import invariant from "invariant"; +import prettyFormat from "pretty-format"; +import { CompilerError } from "../CompilerError"; +import { + Effect, + IdentifierId, + InstructionId, + Place, + ReactiveFunction, + ReactiveInstruction, + ReactiveScopeBlock, + ReactiveStatement, + ReactiveTerminal, + ReactiveTerminalStatement, + ReactiveValue, + ScopeId, +} from "../HIR"; +import { eachInstructionLValue } from "../HIR/visitors"; +import { log } from "../Utils/logger"; +import { assertExhaustive } from "../Utils/utils"; +import { getPlaceScope } from "./BuildReactiveBlocks"; +import { printReactiveFunction } from "./PrintReactiveFunction"; +import { + eachReactiveValueOperand, + ReactiveFunctionTransform, + ReactiveFunctionVisitor, + Transformed, + visitReactiveFunction, +} from "./visitors"; + +/** + * This pass prunes reactive scopes that are not necessary to bound downstream computation. + * Specifically, the pass identifies the set of identifiers which are directly returned by + * the function and/or transitively aliased by a return value - ie, values that "escape". + * + * Example to build intuition: + * + * ```javascript + * function Component(props) { + * const a = {}; // not aliased or returned: *not* memoized + * const b = {}; // aliased by c, which is returned: memoized + * const c = [b]; // directly returned: memoized + * return c; + * } + * ``` + * + * However, this logic alone is insufficient for two reasons: + * - Statically memoizing JSX elements *may* be inefficient compared to using dynamic + * memoization with `React.memo()`. Static memoization may be JIT'd and can look at + * the precise props w/o dynamic iteration, but incurs potentially large code-size + * overhead. Dynamic memoization with `React.memo()` incurs potentially increased + * runtime overhead for smaller code size. We plan to experiment with both variants + * for JSX. + * - Because we merge values whose mutations _interleave_ into a single scope, there + * can be cases where a non-escaping value needs to be memoized anyway to avoid breaking + * a memoization input. As a rule, for any scope that has a memoized output, all of that + * scope's transitive dependencies must also be memoized _even if they don't escape_. + * Failing to memoize them would cause the scope to invalidate more often than necessary + * and break downstream memoization. + * + * Example of this second case: + * + * ```javascript + * function Component(props) { + * // a can be independently memoized but it doesn't escape, so naively we may think its + * // safe to not memoize. but not memoizing would break caching of b, which does + * // escape. + * const a = [props.a]; + * + * // b and c are interleaved and grouped into a single scope, + * // but they are independent values. c does not escape, but + * // we need to ensure that a is memoized or else b will invalidate + * // on every render since a is a dependency. + * const b = []; + * const c = {}; + * c.a = a; + * b.push(props.b); + * + * return b; + * } + * ``` + * + * ## Algorithm + * + * 1. First we build up a graph, a mapping of IdentifierId to a node describing all the + * scopes and inputs involved in creating that identifier. Individual nodes are marked + * as definitely aliased, conditionally aliased, or unaliased: + * a. Arrays, objects, function calls all produce a new value and are always marked as aliased + * b. Conditional and logical expressions (and a few others) are conditinally aliased, + * depending on whether their result value is aliased. + * c. JSX is always unaliased (though its props children may be) + * 2. The same pass which builds the graph also stores the set of returned identifiers. + * 3. We traverse the graph starting from the returned identifiers and mark reachable dependencies + * as escaping, based on the combination of the parent node's type and its children (eg a + * conditional node with an aliased dep promotes to aliased). + * 4. Finally we prune scopes whose outputs weren't marked. + */ +export function pruneNonEscapingScopes(fn: ReactiveFunction): void { + // First build up a map of which instructions are involved in creating which values, + // and which values are returned. + const state = new State(); + if (fn.id !== null) { + state.declare(fn.id.id); + } + for (const param of fn.params) { + state.declare(param.identifier.id); + } + visitReactiveFunction(fn, new CollectDependenciesVisitor(), state); + + log(() => prettyFormat(state)); + + // Then walk outward from the returned values and find all captured operands. + // This forms the set of identifiers which should be memoized. + const memoized = computeMemoizedIdentifiers(state); + + log(() => prettyFormat(memoized)); + + log(() => printReactiveFunction(fn)); + + // Prune scopes that do not declare/reassign any escaping values + visitReactiveFunction(fn, new PruneScopesTransform(), memoized); +} + +// Describes how to determine whether a value should be memoized, relative to dependees and dependencies +enum MemoizationLevel { + // The value should be memoized if it escapes + Memoized = "Memoized", + // Values that are memoized if their dependencies are memoized (used for logical/ternary and + // other expressions that propagate dependencies wo changing them) + Conditional = "Conditional", + // Values that cannot be compared with Object.is, but which by default don't need to be memoized + // unless forced + Unmemoized = "Unmemoized", + // The value will never be memoized: used for values that can be cheaply compared w Object.is + Never = "Never", +} + +// Given an identifier that appears as an lvalue multiple times with different memoization levels, +// determines the final memoization level. +function joinAliases( + kind1: MemoizationLevel, + kind2: MemoizationLevel +): MemoizationLevel { + if ( + kind1 === MemoizationLevel.Memoized || + kind2 === MemoizationLevel.Memoized + ) { + return MemoizationLevel.Memoized; + } else if ( + kind1 === MemoizationLevel.Conditional || + kind2 === MemoizationLevel.Conditional + ) { + return MemoizationLevel.Conditional; + } else if ( + kind1 === MemoizationLevel.Unmemoized || + kind2 === MemoizationLevel.Unmemoized + ) { + return MemoizationLevel.Unmemoized; + } else { + return MemoizationLevel.Never; + } +} + +// A node in the graph describing the memoization level of a given identifier as well as its dependencies and scopes. +type IdentifierNode = { + level: MemoizationLevel; + memoized: boolean; + dependencies: Set; + scopes: Set; + seen: boolean; +}; + +// A scope node describing its dependencies +type ScopeNode = { + dependencies: Array; + seen: boolean; +}; + +// Stores the identifier and scope graphs, set of returned identifiers, etc +class State { + // Maps lvalues for LoadLocal to the identifier being loaded, to resolve indirections + // in subsequent lvalues/rvalues + definitions: Map = new Map(); + + identifiers: Map = new Map(); + scopes: Map = new Map(); + returned: Set = new Set(); + + /** + * Declare a new identifier, used for function id and params + */ + declare(id: IdentifierId): void { + this.identifiers.set(id, { + level: MemoizationLevel.Never, + memoized: false, + dependencies: new Set(), + scopes: new Set(), + seen: false, + }); + } + + /** + * Associates the identifier with its scope, if there is one and it is active for the given instruction id: + * - Records the scope and its dependencies + * - Associates the identifier with this scope + */ + visitOperand( + id: InstructionId, + place: Place, + identifier: IdentifierId + ): void { + const scope = getPlaceScope(id, place); + if (scope !== null) { + let node = this.scopes.get(scope.id); + if (node === undefined) { + node = { + dependencies: [...scope.dependencies].map((dep) => dep.identifier.id), + seen: false, + }; + this.scopes.set(scope.id, node); + } + const identifierNode = this.identifiers.get(identifier); + invariant( + identifierNode !== undefined, + "Expected identifier to be initialized" + ); + identifierNode.scopes.add(scope.id); + } + } +} + +/** + * Given a state derived from visiting the function, walks the graph from the returned nodes + * to determine which other values should be memoized. Returns a set of all identifiers + * that should be memoized. + */ +function computeMemoizedIdentifiers(state: State): Set { + const memoized = new Set(); + + // Visit an identifier, optionally forcing it to be memoized + function visit(id: IdentifierId, forceMemoize: boolean = false): boolean { + const node = state.identifiers.get(id); + invariant(node !== undefined, "Expected a node for all identifiers"); + if (node.seen) { + return node.memoized; + } + node.seen = true; + + // Note: in case of cycles we temporarily mark the identifier as non-memoized, + // this is reset later after processing dependencies + node.memoized = false; + + // Visit dependencies, determine if any of them are memoized + let hasMemoizedDependency = false; + for (const dep of node.dependencies) { + const isDepMemoized = visit(dep); + hasMemoizedDependency ||= isDepMemoized; + } + + if ( + node.level === MemoizationLevel.Memoized || + (node.level === MemoizationLevel.Conditional && + (hasMemoizedDependency || forceMemoize)) || + (node.level === MemoizationLevel.Unmemoized && forceMemoize) + ) { + node.memoized = true; + memoized.add(id); + for (const scope of node.scopes) { + forceMemoizeScopeDependencies(scope); + } + } + return node.memoized; + } + + // Force all the scope's optionally-memoizeable dependencies (not "Never") to be memoized + function forceMemoizeScopeDependencies(id: ScopeId): void { + const node = state.scopes.get(id); + invariant(node !== undefined, "Expected a node for all scopes"); + if (node.seen) { + return; + } + node.seen = true; + + for (const dep of node.dependencies) { + visit(dep, true); + } + return; + } + + // Walk from the "roots" aka returned identifiers. + for (const returned of state.returned) { + visit(returned); + } + + return memoized; +} + +/** + * Given a value, returns a description of how it should be memoized: + * - lvalues: optional extra places that are lvalue-like in the sense of + * aliasing the rvalues + * - rvalues: places that are aliased by the instruction's lvalues. + * - level: the level of memoization to apply to this value + */ +function computeMemoizationInputs(value: ReactiveValue): { + // can optionally return a custom set of lvalues per instruction + lvalues: Array | null; + rvalues: Array; + level: MemoizationLevel; +} { + switch (value.kind) { + case "ConditionalExpression": { + return { + lvalues: null, + rvalues: [ + // Conditionals do not alias their test value. + ...computeMemoizationInputs(value.consequent).rvalues, + ...computeMemoizationInputs(value.alternate).rvalues, + ], + // Only need to memoize if the rvalues are memoized + level: MemoizationLevel.Conditional, + }; + } + case "LogicalExpression": { + return { + lvalues: null, + rvalues: [ + ...computeMemoizationInputs(value.left).rvalues, + ...computeMemoizationInputs(value.right).rvalues, + ], + // Only need to memoize if the rvalues are memoized + level: MemoizationLevel.Conditional, + }; + } + case "SequenceExpression": { + return { + lvalues: null, + // Only the final value of the sequence is a true rvalue: + // values from the sequence's instructions are evaluated + // as separate nodes + rvalues: computeMemoizationInputs(value.value).rvalues, + // Only memoize if the final value was memoized + level: MemoizationLevel.Conditional, + }; + } + case "JsxExpression": { + const operands: Array = []; + operands.push(value.tag); + for (const prop of value.props) { + if (prop.kind === "JsxAttribute") { + operands.push(prop.place); + } else { + operands.push(prop.argument); + } + } + if (value.children !== null) { + for (const child of value.children) { + operands.push(child); + } + } + return { + lvalues: null, + rvalues: operands, + // JSX elements themselves are not memoized unless forced to + // avoid breaking downstream memoization + level: MemoizationLevel.Unmemoized, + }; + } + case "JsxFragment": { + return { + lvalues: null, + rvalues: value.children, + // JSX elements themselves are not memoized unless forced to + // avoid breaking downstream memoization + level: MemoizationLevel.Unmemoized, + }; + } + case "ComputedDelete": + case "PropertyDelete": + case "LoadGlobal": + case "TemplateLiteral": + case "Primitive": + case "JSXText": + case "BinaryExpression": + case "UnaryExpression": { + return { + lvalues: null, + rvalues: [], + // All of these instructions return a primitive value and never need to be memoized + level: MemoizationLevel.Never, + }; + } + case "TypeCastExpression": { + return { + lvalues: null, + // Indirection for the inner value, memoized if the value is + rvalues: [value.value], + level: MemoizationLevel.Conditional, + }; + } + case "LoadLocal": { + return { + lvalues: null, + // Indirection for the inner value, memoized if the value is + rvalues: [value.place], + level: MemoizationLevel.Conditional, + }; + } + case "Destructure": + case "StoreLocal": { + return { + lvalues: null, + // Indirection for the inner value, memoized if the value is + rvalues: [value.value], + level: MemoizationLevel.Conditional, + }; + } + case "ComputedLoad": + case "PropertyLoad": { + return { + lvalues: null, + // Only the object is aliased to the result, and the result only needs to be + // memoized if the object is + rvalues: [value.object], + level: MemoizationLevel.Conditional, + }; + } + case "ComputedStore": { + // The object being stored to acts as an lvalue (it aliases the value), but + // the computed key is not aliased + return { + lvalues: [value.object], + rvalues: [value.value], + level: MemoizationLevel.Conditional, + }; + } + case "FunctionExpression": + case "TaggedTemplateExpression": + case "CallExpression": + case "ArrayExpression": + case "NewExpression": + case "ObjectExpression": + case "ComputedCall": + case "PropertyCall": + case "PropertyStore": { + // All of these instructions may produce new values which must be memoized if + // reachable from a return value. Any mutable rvalue may alias any other rvalue + const operands = [...eachReactiveValueOperand(value)]; + return { + lvalues: operands.filter((operand) => isMutableEffect(operand.effect)), + rvalues: operands, + level: MemoizationLevel.Memoized, + }; + } + case "UnsupportedNode": { + CompilerError.invariant(`Unexpected unsupported node`, value.loc); + } + default: { + assertExhaustive(value, `Unexpected value kind '${(value as any).kind}'`); + } + } +} + +/** + * Populates the input state with the set of returned identifiers and information about each + * identifier's and scope's dependencies. + */ +class CollectDependenciesVisitor extends ReactiveFunctionVisitor { + override visitInstruction( + instruction: ReactiveInstruction, + state: State + ): void { + this.traverseInstruction(instruction, state); + + // Determe the level of memoization for this value and the lvalues/rvalues + const aliasing = computeMemoizationInputs(instruction.value); + + // Associate all the rvalues with the instruction's scope if it has one + for (const operand of aliasing.rvalues) { + const operandId = + state.definitions.get(operand.identifier.id) ?? operand.identifier.id; + state.visitOperand(instruction.id, operand, operandId); + } + + // Add the operands as dependencies of all lvalues. + const lvalues = + aliasing.lvalues !== null + ? [...eachInstructionLValue(instruction), ...aliasing.lvalues] + : [...eachInstructionLValue(instruction)]; + for (const lvalue of lvalues) { + const lvalueId = + state.definitions.get(lvalue.identifier.id) ?? lvalue.identifier.id; + let node = state.identifiers.get(lvalueId); + if (node === undefined) { + node = { + level: MemoizationLevel.Never, + memoized: false, + dependencies: new Set(), + scopes: new Set(), + seen: false, + }; + state.identifiers.set(lvalueId, node); + } + node.level = joinAliases(node.level, aliasing.level); + // This looks like NxM iterations but in practice all instructions with multiple + // lvalues have only a single rvalue + for (const operand of aliasing.rvalues) { + const operandId = + state.definitions.get(operand.identifier.id) ?? operand.identifier.id; + if (operandId === lvalueId) { + continue; + } + node.dependencies.add(operandId); + } + + state.visitOperand(instruction.id, lvalue, lvalueId); + } + + if (instruction.value.kind === "LoadLocal" && instruction.lvalue !== null) { + state.definitions.set( + instruction.lvalue.identifier.id, + instruction.value.place.identifier.id + ); + } + } + + override visitTerminal( + stmt: ReactiveTerminalStatement, + state: State + ): void { + this.traverseTerminal(stmt, state); + + if (stmt.terminal.kind === "return" && stmt.terminal.value !== null) { + state.returned.add(stmt.terminal.value.identifier.id); + } + } +} + +/** + * Prune reactive scopes that do not have any memoized outputs + */ +class PruneScopesTransform extends ReactiveFunctionTransform< + Set +> { + override transformScope( + scope: ReactiveScopeBlock, + state: Set + ): Transformed { + this.visitScope(scope, state); + const hasMemoizedOutput = + Array.from(scope.scope.declarations.keys()).some((id) => state.has(id)) || + Array.from(scope.scope.reassignments).some((identifier) => + state.has(identifier.id) + ); + if (hasMemoizedOutput) { + return { kind: "keep" }; + } else { + return { kind: "replace-many", value: scope.instructions }; + } + } +} + +function isMutableEffect(effect: Effect): boolean { + switch (effect) { + case Effect.Capture: + case Effect.Mutate: + case Effect.Store: { + return true; + } + default: { + return false; + } + } +} diff --git a/compiler/forget/src/ReactiveScopes/index.ts b/compiler/forget/src/ReactiveScopes/index.ts index ba8ef4385b..ad5b55756c 100644 --- a/compiler/forget/src/ReactiveScopes/index.ts +++ b/compiler/forget/src/ReactiveScopes/index.ts @@ -16,6 +16,7 @@ export { mergeOverlappingReactiveScopes } from "./MergeOverlappingReactiveScopes export { printReactiveFunction } from "./PrintReactiveFunction"; export { promoteUsedTemporaries } from "./PromoteUsedTemporaries"; export { propagateScopeDependencies } from "./PropagateScopeDependencies"; +export { pruneNonEscapingScopes } from "./PruneNonEscapingScopes"; export { pruneNonReactiveDependencies } from "./PruneNonReactiveDependencies"; export { pruneTemporaryLValues as pruneUnusedLValues } from "./PruneTemporaryLValues"; export { pruneUnusedLabels } from "./PruneUnusedLabels"; diff --git a/compiler/forget/src/__tests__/fixtures/hir/_bug.capturing-function-renamed-ref.expect.md b/compiler/forget/src/__tests__/fixtures/hir/_bug.capturing-function-renamed-ref.expect.md index 736833c494..d2f4fdc4b9 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/_bug.capturing-function-renamed-ref.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/_bug.capturing-function-renamed-ref.expect.md @@ -19,7 +19,7 @@ function component(a, b) { ```javascript function component(a, b) { - const $ = React.unstable_useMemoCache(4); + const $ = React.unstable_useMemoCache(2); const c_0 = $[0] !== a; let t0; if (c_0) { @@ -30,16 +30,8 @@ function component(a, b) { t0 = $[1]; } const z = t0; - const c_2 = $[2] !== b; - let t1; - if (c_2) { - t1 = { b }; - $[2] = b; - $[3] = t1; - } else { - t1 = $[3]; - } - const z_0 = t1; + + const z_0 = { b }; (function () { mutate(z); })(); diff --git a/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep-nested-scope.expect.md b/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep-nested-scope.expect.md index 4dc4c0d670..1b9703f762 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep-nested-scope.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep-nested-scope.expect.md @@ -24,7 +24,7 @@ function AllocatingPrimitiveAsDepNested(props) { // Correctness: // - y depends on either bar(props.b) or bar(props.b) + 1 function AllocatingPrimitiveAsDepNested(props) { - const $ = React.unstable_useMemoCache(11); + const $ = React.unstable_useMemoCache(9); const c_0 = $[0] !== props.b; const c_1 = $[1] !== props.a; let x; @@ -32,26 +32,17 @@ function AllocatingPrimitiveAsDepNested(props) { if (c_0 || c_1) { x = {}; mutate(x); - const c_4 = $[4] !== props.b; - let t0; + const t0 = bar(props.b) + 1; + const c_4 = $[4] !== t0; + let t1; if (c_4) { - t0 = bar(props.b); - $[4] = props.b; - $[5] = t0; + t1 = foo(t0); + $[4] = t0; + $[5] = t1; } else { - t0 = $[5]; + t1 = $[5]; } - const t1 = t0 + 1; - const c_6 = $[6] !== t1; - let t2; - if (c_6) { - t2 = foo(t1); - $[6] = t1; - $[7] = t2; - } else { - t2 = $[7]; - } - y = t2; + y = t1; mutate(x, props.a); $[0] = props.b; $[1] = props.a; @@ -61,18 +52,18 @@ function AllocatingPrimitiveAsDepNested(props) { x = $[2]; y = $[3]; } - const c_8 = $[8] !== x; - const c_9 = $[9] !== y; - let t3; - if (c_8 || c_9) { - t3 = [x, y]; - $[8] = x; - $[9] = y; - $[10] = t3; + const c_6 = $[6] !== x; + const c_7 = $[7] !== y; + let t2; + if (c_6 || c_7) { + t2 = [x, y]; + $[6] = x; + $[7] = y; + $[8] = t2; } else { - t3 = $[10]; + t2 = $[8]; } - return t3; + return t2; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep.expect.md b/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep.expect.md index 7836f17a83..0e1a15a95d 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/allocating-primitive-as-dep.expect.md @@ -21,27 +21,18 @@ function AllocatingPrimitiveAsDep(props) { // Correctness: // - y depends on either bar(props.b) or bar(props.b) + 1 function AllocatingPrimitiveAsDep(props) { - const $ = React.unstable_useMemoCache(4); - const c_0 = $[0] !== props; - let t0; + const $ = React.unstable_useMemoCache(2); + const t0 = bar(props).b + 1; + const c_0 = $[0] !== t0; + let t1; if (c_0) { - t0 = bar(props); - $[0] = props; - $[1] = t0; + t1 = foo(t0); + $[0] = t0; + $[1] = t1; } else { - t0 = $[1]; + t1 = $[1]; } - const t1 = t0.b + 1; - const c_2 = $[2] !== t1; - let t2; - if (c_2) { - t2 = foo(t1); - $[2] = t1; - $[3] = t2; - } else { - t2 = $[3]; - } - const y = t2; + const y = t1; return y; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/call.expect.md b/compiler/forget/src/__tests__/fixtures/hir/call.expect.md index 7af3132878..60377a0666 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/call.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/call.expect.md @@ -21,7 +21,7 @@ function Component(props) { function foo() {} function Component(props) { - const $ = React.unstable_useMemoCache(3); + const $ = React.unstable_useMemoCache(2); let a; let b; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { @@ -36,14 +36,7 @@ function Component(props) { a = $[0]; b = $[1]; } - let t0; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t0 =
; - $[2] = t0; - } else { - t0 = $[2]; - } - return t0; + return
; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/capture-func-passed-to-jsx.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capture-func-passed-to-jsx.expect.md index acae2e4956..be0199a9b0 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/capture-func-passed-to-jsx.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/capture-func-passed-to-jsx.expect.md @@ -20,7 +20,7 @@ function component(a, b) { ```javascript function component(a, b) { - const $ = React.unstable_useMemoCache(9); + const $ = React.unstable_useMemoCache(7); const c_0 = $[0] !== b; let t0; if (c_0) { @@ -56,16 +56,7 @@ function component(a, b) { t2 = $[6]; } const x = t2; - const c_7 = $[7] !== x; - let t3; - if (c_7) { - t3 = ; - $[7] = x; - $[8] = t3; - } else { - t3 = $[8]; - } - const t = t3; + const t = ; mutate(x); return t; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/capturing-function-member-expr-call.expect.md b/compiler/forget/src/__tests__/fixtures/hir/capturing-function-member-expr-call.expect.md index 2dc57b8057..f727b5e205 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/capturing-function-member-expr-call.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/capturing-function-member-expr-call.expect.md @@ -20,7 +20,7 @@ function component({ mutator }) { ```javascript function component(t27) { - const $ = React.unstable_useMemoCache(7); + const $ = React.unstable_useMemoCache(4); const { mutator } = t27; const c_0 = $[0] !== mutator; let t0; @@ -46,18 +46,7 @@ function component(t27) { t1 = $[3]; } const hide = t1; - const c_4 = $[4] !== poke; - const c_5 = $[5] !== hide; - let t2; - if (c_4 || c_5) { - t2 = ; - $[4] = poke; - $[5] = hide; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/component.expect.md b/compiler/forget/src/__tests__/fixtures/hir/component.expect.md index 0acc2fedb3..2518ff4e3d 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/component.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/component.expect.md @@ -35,7 +35,7 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(10); + const $ = React.unstable_useMemoCache(3); const items = props.items; const maxItems = props.maxItems; const c_0 = $[0] !== maxItems; @@ -44,16 +44,7 @@ function Component(props) { if (c_0 || c_1) { renderedItems = []; const seen = new Set(); - const c_3 = $[3] !== maxItems; - let t0; - if (c_3) { - t0 = Math.max(0, maxItems); - $[3] = maxItems; - $[4] = t0; - } else { - t0 = $[4]; - } - const max = t0; + const max = Math.max(0, maxItems); for (let i = 0; i < items.length; i = i + 1, i) { const item = items.at(i); if (item == null || seen.has(item)) { @@ -74,32 +65,12 @@ function Component(props) { } const count = renderedItems.length; - const c_5 = $[5] !== count; - let t1; - if (c_5) { - t1 =

{count} Items

; - $[5] = count; - $[6] = t1; - } else { - t1 = $[6]; - } - const c_7 = $[7] !== t1; - const c_8 = $[8] !== renderedItems; - let t2; - if (c_7 || c_8) { - t2 = ( -
- {t1} - {renderedItems} -
- ); - $[7] = t1; - $[8] = renderedItems; - $[9] = t2; - } else { - t2 = $[9]; - } - return t2; + return ( +
+ {

{count} Items

} + {renderedItems} +
+ ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/concise-arrow-expr.expect.md b/compiler/forget/src/__tests__/fixtures/hir/concise-arrow-expr.expect.md index 517503efd5..006ec5f533 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/concise-arrow-expr.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/concise-arrow-expr.expect.md @@ -14,7 +14,7 @@ function component() { ```javascript function component() { - const $ = React.unstable_useMemoCache(4); + const $ = React.unstable_useMemoCache(2); const [x, setX] = useState(0); const c_0 = $[0] !== setX; let t0; @@ -26,16 +26,7 @@ function component() { t0 = $[1]; } const handler = t0; - const c_2 = $[2] !== handler; - let t1; - if (c_2) { - t1 = ; - $[2] = handler; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md b/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md index f80aacf62e..990b07d7f5 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/conditional-on-mutable.expect.md @@ -35,7 +35,7 @@ function mayMutate() {} ```javascript function ComponentA(props) { - const $ = React.unstable_useMemoCache(6); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props; let a; let b; @@ -55,22 +55,11 @@ function ComponentA(props) { a = $[1]; b = $[2]; } - const c_3 = $[3] !== a; - const c_4 = $[4] !== b; - let t0; - if (c_3 || c_4) { - t0 = ; - $[3] = a; - $[4] = b; - $[5] = t0; - } else { - t0 = $[5]; - } - return t0; + return ; } function ComponentB(props) { - const $ = React.unstable_useMemoCache(6); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props; let a; let b; @@ -90,18 +79,7 @@ function ComponentB(props) { a = $[1]; b = $[2]; } - const c_3 = $[3] !== a; - const c_4 = $[4] !== b; - let t0; - if (c_3 || c_4) { - t0 = ; - $[3] = a; - $[4] = b; - $[5] = t0; - } else { - t0 = $[5]; - } - return t0; + return ; } function Foo() {} diff --git a/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-for.expect.md b/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-for.expect.md index 257f5ff027..8da0c3f1e8 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-for.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-for.expect.md @@ -16,16 +16,9 @@ function foo() { ```javascript function foo() { - const $ = React.unstable_useMemoCache(1); - let y; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - y = 0; - for (const x = 100; false; 100) { - y = y + 1; - } - $[0] = y; - } else { - y = $[0]; + let y = 0; + for (const x = 100; false; 100) { + y = y + 1; } return y; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-while.expect.md b/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-while.expect.md index 9aae7d26bf..38668aa3db 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-while.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/constant-propagation-while.expect.md @@ -17,16 +17,9 @@ function foo() { ```javascript function foo() { - const $ = React.unstable_useMemoCache(1); - let y; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - y = 0; - while (false) { - y = y + 1; - } - $[0] = y; - } else { - y = $[0]; + let y = 0; + while (false) { + y = y + 1; } return y; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md b/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md index 08b57f01d8..3c0d0d9f28 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/constructor.expect.md @@ -21,7 +21,7 @@ function Component(props) { function Foo() {} function Component(props) { - const $ = React.unstable_useMemoCache(3); + const $ = React.unstable_useMemoCache(2); let a; let b; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { @@ -35,14 +35,7 @@ function Component(props) { a = $[0]; b = $[1]; } - let t0; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t0 =
; - $[2] = t0; - } else { - t0 = $[2]; - } - return t0; + return
; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/controlled-input.expect.md b/compiler/forget/src/__tests__/fixtures/hir/controlled-input.expect.md index 417dcef789..dfdf8c1b09 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/controlled-input.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/controlled-input.expect.md @@ -14,7 +14,7 @@ function component() { ```javascript function component() { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(2); const [x, setX] = useState(0); const c_0 = $[0] !== setX; let t0; @@ -26,18 +26,7 @@ function component() { t0 = $[1]; } const handler = t0; - const c_2 = $[2] !== handler; - const c_3 = $[3] !== x; - let t1; - if (c_2 || c_3) { - t1 = ; - $[2] = handler; - $[3] = x; - $[4] = t1; - } else { - t1 = $[4]; - } - return t1; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/dce-loop.expect.md b/compiler/forget/src/__tests__/fixtures/hir/dce-loop.expect.md index 87ae30f017..2fec874a11 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/dce-loop.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/dce-loop.expect.md @@ -18,18 +18,9 @@ function foo(props) { ```javascript function foo(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.max; - let y; - if (c_0) { - y = 0; - while (y < props.max) { - y = y + 1; - } - $[0] = props.max; - $[1] = y; - } else { - y = $[1]; + let y = 0; + while (y < props.max) { + y = y + 1; } return y; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/destructure-direct-reassignment.expect.md b/compiler/forget/src/__tests__/fixtures/hir/destructure-direct-reassignment.expect.md index 599a3be6d0..ec9c7e3bf8 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/destructure-direct-reassignment.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/destructure-direct-reassignment.expect.md @@ -16,22 +16,8 @@ function foo(props) { ```javascript function foo(props) { - const $ = React.unstable_useMemoCache(4); - const c_0 = $[0] !== props.a; - const c_1 = $[1] !== props.b; - let x; - let y; - if (c_0 || c_1) { - ({ x, y } = { x: props.a, y: props.b }); - console.log(x); - $[0] = props.a; - $[1] = props.b; - $[2] = x; - $[3] = y; - } else { - x = $[2]; - y = $[3]; - } + let { x, y } = { x: props.a, y: props.b }; + console.log(x); x = props.c; return x + y; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.expect.md new file mode 100644 index 0000000000..7f41bd83cd --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.expect.md @@ -0,0 +1,59 @@ + +## Input + +```javascript +function foo(a, b, c) { + const x = []; + if (a) { + const y = []; + if (b) { + y.push(c); + } + x.push(
{y}
); + } + return x; +} + +``` + +## Code + +```javascript +function foo(a, b, c) { + const $ = React.unstable_useMemoCache(7); + const c_0 = $[0] !== a; + const c_1 = $[1] !== b; + const c_2 = $[2] !== c; + let x; + if (c_0 || c_1 || c_2) { + x = []; + if (a) { + const c_4 = $[4] !== b; + const c_5 = $[5] !== c; + let y; + if (c_4 || c_5) { + y = []; + if (b) { + y.push(c); + } + $[4] = b; + $[5] = c; + $[6] = y; + } else { + y = $[6]; + } + + x.push(
{y}
); + } + $[0] = a; + $[1] = b; + $[2] = c; + $[3] = x; + } else { + x = $[3]; + } + return x; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.js new file mode 100644 index 0000000000..7df580429c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-jsx-child.js @@ -0,0 +1,11 @@ +function foo(a, b, c) { + const x = []; + if (a) { + const y = []; + if (b) { + y.push(c); + } + x.push(
{y}
); + } + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.expect.md new file mode 100644 index 0000000000..cb67370a9b --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.expect.md @@ -0,0 +1,68 @@ + +## Input + +```javascript +function Component(props) { + const a = [props.a]; + const b = [props.b]; + const c = [props.c]; + // We don't do constant folding for non-primitive values (yet) so we consider + // that any of a, b, or c could return here + return (a && b) || c; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(10); + const c_0 = $[0] !== props.a; + let t0; + if (c_0) { + t0 = [props.a]; + $[0] = props.a; + $[1] = t0; + } else { + t0 = $[1]; + } + const a = t0; + const c_2 = $[2] !== props.b; + let t1; + if (c_2) { + t1 = [props.b]; + $[2] = props.b; + $[3] = t1; + } else { + t1 = $[3]; + } + const b = t1; + const c_4 = $[4] !== props.c; + let t2; + if (c_4) { + t2 = [props.c]; + $[4] = props.c; + $[5] = t2; + } else { + t2 = $[5]; + } + const c = t2; + const c_6 = $[6] !== a; + const c_7 = $[7] !== b; + const c_8 = $[8] !== c; + let t3; + if (c_6 || c_7 || c_8) { + t3 = (a && b) || c; + $[6] = a; + $[7] = b; + $[8] = c; + $[9] = t3; + } else { + t3 = $[9]; + } + return t3; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.js new file mode 100644 index 0000000000..4582a56fa9 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-logical.js @@ -0,0 +1,8 @@ +function Component(props) { + const a = [props.a]; + const b = [props.b]; + const c = [props.c]; + // We don't do constant folding for non-primitive values (yet) so we consider + // that any of a, b, or c could return here + return (a && b) || c; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.expect.md new file mode 100644 index 0000000000..42b5bf284f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.expect.md @@ -0,0 +1,57 @@ + +## Input + +```javascript +function Component(props) { + // a can be independently memoized, is not mutated later + const a = [props.a]; + + // b and c are interleaved and grouped into a single scope, + // but they are independent values. c does not escape, but + // we need to ensure that a is memoized or else b will invalidate + // on every render since a is a dependency. + const b = []; + const c = {}; + c.a = a; + b.push(props.b); + + return b; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(5); + const c_0 = $[0] !== props.a; + let t0; + if (c_0) { + t0 = [props.a]; + $[0] = props.a; + $[1] = t0; + } else { + t0 = $[1]; + } + const a = t0; + const c_2 = $[2] !== a; + const c_3 = $[3] !== props.b; + let b; + if (c_2 || c_3) { + b = []; + const c = {}; + c.a = a; + + b.push(props.b); + $[2] = a; + $[3] = props.b; + $[4] = b; + } else { + b = $[4]; + } + return b; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.js new file mode 100644 index 0000000000..a7646fcc95 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-allocating-dependency.js @@ -0,0 +1,15 @@ +function Component(props) { + // a can be independently memoized, is not mutated later + const a = [props.a]; + + // b and c are interleaved and grouped into a single scope, + // but they are independent values. c does not escape, but + // we need to ensure that a is memoized or else b will invalidate + // on every render since a is a dependency. + const b = []; + const c = {}; + c.a = a; + b.push(props.b); + + return b; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.expect.md new file mode 100644 index 0000000000..66801c7447 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.expect.md @@ -0,0 +1,51 @@ + +## Input + +```javascript +function Component(props) { + // a does not need to be memoized ever, even though it's a + // dependency of c, which exists in a scope that has a memoized + // output. it doesn't need to be memoized bc the value is a primitive type. + const a = props.a + props.b; + + // b and c are interleaved and grouped into a single scope, + // but they are independent values. c does not escape, but + // we need to ensure that a is memoized or else b will invalidate + // on every render since a is a dependency. + const b = []; + const c = {}; + c.a = a; + b.push(props.c); + + return b; +} + +``` + +## Code + +```javascript +function Component(props) { + const $ = React.unstable_useMemoCache(3); + + const a = props.a + props.b; + const c_0 = $[0] !== a; + const c_1 = $[1] !== props.c; + let b; + if (c_0 || c_1) { + b = []; + const c = {}; + c.a = a; + + b.push(props.c); + $[0] = a; + $[1] = props.c; + $[2] = b; + } else { + b = $[2]; + } + return b; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.js new file mode 100644 index 0000000000..b8d3bd280c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-non-escaping-interleaved-primitive-dependency.js @@ -0,0 +1,17 @@ +function Component(props) { + // a does not need to be memoized ever, even though it's a + // dependency of c, which exists in a scope that has a memoized + // output. it doesn't need to be memoized bc the value is a primitive type. + const a = props.a + props.b; + + // b and c are interleaved and grouped into a single scope, + // but they are independent values. c does not escape, but + // we need to ensure that a is memoized or else b will invalidate + // on every render since a is a dependency. + const b = []; + const c = {}; + c.a = a; + b.push(props.c); + + return b; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.expect.md new file mode 100644 index 0000000000..0b4d20a76a --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.expect.md @@ -0,0 +1,23 @@ + +## Input + +```javascript +function Component(props) { + const x = [props.a]; + const y = x ? props.b : props.c; + return y; +} + +``` + +## Code + +```javascript +function Component(props) { + const x = [props.a]; + const y = x ? props.b : props.c; + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.js new file mode 100644 index 0000000000..d59b67810f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-conditional-test.js @@ -0,0 +1,5 @@ +function Component(props) { + const x = [props.a]; + const y = x ? props.b : props.c; + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.expect.md new file mode 100644 index 0000000000..ffd3f0dc09 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +function Component(props) { + const x = [props.a]; + let y; + if (x) { + y = props.b; + } else { + y = props.c; + } + return y; +} + +``` + +## Code + +```javascript +function Component(props) { + const x = [props.a]; + let y = undefined; + if (x) { + y = props.b; + } else { + y = props.c; + } + return y; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.js new file mode 100644 index 0000000000..e999f4e143 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-if-test.js @@ -0,0 +1,10 @@ +function Component(props) { + const x = [props.a]; + let y; + if (x) { + y = props.b; + } else { + y = props.c; + } + return y; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.expect.md new file mode 100644 index 0000000000..ee3e6f944f --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +function Component(props) { + const a = [props.a]; + let x = props.b; + switch (props.c) { + case a: { + x = props.d; + } + } + return x; +} + +``` + +## Code + +```javascript +function Component(props) { + const a = [props.a]; + let x = props.b; + switch (props.c) { + case a: { + x = props.d; + } + } + return x; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.js new file mode 100644 index 0000000000..bf577624e4 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-case.js @@ -0,0 +1,10 @@ +function Component(props) { + const a = [props.a]; + let x = props.b; + switch (props.c) { + case a: { + x = props.d; + } + } + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.expect.md b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.expect.md new file mode 100644 index 0000000000..229f49f56c --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.expect.md @@ -0,0 +1,33 @@ + +## Input + +```javascript +function Component(props) { + const a = [props.a]; + let x = props.b; + switch (a) { + case true: { + x = props.c; + } + } + return x; +} + +``` + +## Code + +```javascript +function Component(props) { + const a = [props.a]; + let x = props.b; + switch (a) { + case true: { + x = props.c; + } + } + return x; +} + +``` + \ No newline at end of file diff --git a/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.js b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.js new file mode 100644 index 0000000000..58ceaff461 --- /dev/null +++ b/compiler/forget/src/__tests__/fixtures/hir/escape-analysis-not-switch-test.js @@ -0,0 +1,10 @@ +function Component(props) { + const a = [props.a]; + let x = props.b; + switch (a) { + case true: { + x = props.c; + } + } + return x; +} diff --git a/compiler/forget/src/__tests__/fixtures/hir/for-logical.expect.md b/compiler/forget/src/__tests__/fixtures/hir/for-logical.expect.md index e465c701e6..afbc402bfc 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/for-logical.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/for-logical.expect.md @@ -21,23 +21,14 @@ function foo(props) { ```javascript function foo(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props; - let y; - if (c_0) { - y = 0; - for ( - let x = 0; - x > props.min && x < props.max; - x = x + (props.cond ? props.increment : 2), x - ) { - x = x * 2; - y = y + x; - } - $[0] = props; - $[1] = y; - } else { - y = $[1]; + let y = 0; + for ( + let x = 0; + x > props.min && x < props.max; + x = x + (props.cond ? props.increment : 2), x + ) { + x = x * 2; + y = y + x; } return y; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-default-function.expect.md b/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-default-function.expect.md index 8ce6c365f5..6518388ae8 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-default-function.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-default-function.expect.md @@ -28,17 +28,7 @@ function Bar_uncompiled(props) { return
{props.bar}
; } function Bar_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 =
{props.bar}
; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return
{props.bar}
; } const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled; export default Bar; @@ -52,17 +42,7 @@ function Foo_uncompiled(props) { return {props.bar}; } function Foo_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 = {props.bar}; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return {props.bar}; } const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled; diff --git a/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function-and-default.expect.md b/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function-and-default.expect.md index e4bfcfe42e..6c12dcde2c 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function-and-default.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function-and-default.expect.md @@ -28,17 +28,7 @@ function Bar_uncompiled(props) { return
{props.bar}
; } function Bar_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 =
{props.bar}
; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return
{props.bar}
; } const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled; export default Bar; @@ -52,17 +42,7 @@ function Foo_uncompiled(props) { return {props.bar}; } function Foo_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 = {props.bar}; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return {props.bar}; } export const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled; diff --git a/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function.expect.md b/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function.expect.md index 81b8dd9e8e..36363fb738 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/gating-test-export-function.expect.md @@ -28,17 +28,7 @@ function Bar_uncompiled(props) { return
{props.bar}
; } function Bar_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 =
{props.bar}
; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return
{props.bar}
; } export const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled; @@ -51,17 +41,7 @@ function Foo_uncompiled(props) { return {props.bar}; } function Foo_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 = {props.bar}; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return {props.bar}; } export const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled; diff --git a/compiler/forget/src/__tests__/fixtures/hir/gating-test.expect.md b/compiler/forget/src/__tests__/fixtures/hir/gating-test.expect.md index 19260edb97..624514d94e 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/gating-test.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/gating-test.expect.md @@ -28,17 +28,7 @@ function Bar_uncompiled(props) { return
{props.bar}
; } function Bar_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 =
{props.bar}
; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return
{props.bar}
; } const Bar = isForgetEnabled ? Bar_forget : Bar_uncompiled; @@ -51,17 +41,7 @@ function Foo_uncompiled(props) { return {props.bar}; } function Foo_forget(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props.bar; - let t0; - if (c_0) { - t0 = {props.bar}; - $[0] = props.bar; - $[1] = t0; - } else { - t0 = $[1]; - } - return t0; + return {props.bar}; } const Foo = isForgetEnabled ? Foo_forget : Foo_uncompiled; diff --git a/compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md b/compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md index 4ecc4cc7fa..b7a764d207 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/hook-call.expect.md @@ -26,7 +26,7 @@ function useFreeze() {} function foo() {} function Component(props) { - const $ = React.unstable_useMemoCache(3); + const $ = React.unstable_useMemoCache(1); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = []; @@ -37,21 +37,12 @@ function Component(props) { const x = t0; const y = useFreeze(x); foo(y, x); - const c_1 = $[1] !== y; - let t1; - if (c_1) { - t1 = ( - - {x} - {y} - - ); - $[1] = y; - $[2] = t1; - } else { - t1 = $[2]; - } - return t1; + return ( + + {x} + {y} + + ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md index 34633c17d8..68f656e264 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/independent-across-if.expect.md @@ -56,7 +56,7 @@ function Foo() {} * return = */ function Component(props) { - const $ = React.unstable_useMemoCache(8); + const $ = React.unstable_useMemoCache(5); const c_0 = $[0] !== props.a; const c_1 = $[1] !== props.b; const c_2 = $[2] !== props.c; @@ -78,18 +78,7 @@ function Component(props) { a = $[3]; b = $[4]; } - const c_5 = $[5] !== a; - const c_6 = $[6] !== b; - let t0; - if (c_5 || c_6) { - t0 = ; - $[5] = a; - $[6] = b; - $[7] = t0; - } else { - t0 = $[7]; - } - return t0; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/independent.expect.md b/compiler/forget/src/__tests__/fixtures/hir/independent.expect.md index 9a344bd0d9..c9cc9692f4 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/independent.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/independent.expect.md @@ -38,7 +38,7 @@ function Foo() {} * return = */ function Component(props) { - const $ = React.unstable_useMemoCache(7); + const $ = React.unstable_useMemoCache(4); const c_0 = $[0] !== props.a; let t0; if (c_0) { @@ -59,18 +59,7 @@ function Component(props) { t1 = $[3]; } const b = t1; - const c_4 = $[4] !== a; - const c_5 = $[5] !== b; - let t2; - if (c_4 || c_5) { - t2 = ; - $[4] = a; - $[5] = b; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + return ; } function compute() {} diff --git a/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md index 0d0f6cab93..9339072294 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/interdependent-across-if.expect.md @@ -45,7 +45,7 @@ function Foo() {} * return = */ function Component(props) { - const $ = React.unstable_useMemoCache(8); + const $ = React.unstable_useMemoCache(5); const c_0 = $[0] !== props.a; const c_1 = $[1] !== props.b; const c_2 = $[2] !== props.c; @@ -66,18 +66,7 @@ function Component(props) { a = $[3]; b = $[4]; } - const c_5 = $[5] !== a; - const c_6 = $[6] !== b; - let t0; - if (c_5 || c_6) { - t0 = ; - $[5] = a; - $[6] = b; - $[7] = t0; - } else { - t0 = $[7]; - } - return t0; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md b/compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md index 9c7bc4c7c3..fa63c5ee4f 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/interdependent.expect.md @@ -37,7 +37,7 @@ function Foo() {} * return = */ function Component(props) { - const $ = React.unstable_useMemoCache(7); + const $ = React.unstable_useMemoCache(4); const c_0 = $[0] !== props.a; const c_1 = $[1] !== props.b; let a; @@ -54,18 +54,7 @@ function Component(props) { a = $[2]; b = $[3]; } - const c_4 = $[4] !== a; - const c_5 = $[5] !== b; - let t0; - if (c_4 || c_5) { - t0 = ; - $[4] = a; - $[5] = b; - $[6] = t0; - } else { - t0 = $[6]; - } - return t0; + return ; } function compute() {} diff --git a/compiler/forget/src/__tests__/fixtures/hir/jsx-fragment.expect.md b/compiler/forget/src/__tests__/fixtures/hir/jsx-fragment.expect.md index 09b15eefae..896216a662 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/jsx-fragment.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/jsx-fragment.expect.md @@ -19,36 +19,12 @@ function Foo(props) { ```javascript function Foo(props) { - const $ = React.unstable_useMemoCache(4); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = <>Text; - $[0] = t0; - } else { - t0 = $[0]; - } - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 =
{t0}
; - $[1] = t1; - } else { - t1 = $[1]; - } - const c_2 = $[2] !== props.greeting; - let t2; - if (c_2) { - t2 = ( - <> - Hello {props.greeting} - {t1} - - ); - $[2] = props.greeting; - $[3] = t2; - } else { - t2 = $[3]; - } - return t2; + return ( + <> + Hello {props.greeting} + {
{<>Text}
} + + ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/jsx-spread.expect.md b/compiler/forget/src/__tests__/fixtures/hir/jsx-spread.expect.md index 525aaa437c..2dfe91d80a 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/jsx-spread.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/jsx-spread.expect.md @@ -14,37 +14,19 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(7); - const c_0 = $[0] !== props; - let t0; - if (c_0) { - t0 = props.cond ? props.foo : props.bar; - $[0] = props; - $[1] = t0; - } else { - t0 = $[1]; - } - const c_2 = $[2] !== t0; + const $ = React.unstable_useMemoCache(2); + + const t0 = props.cond ? props.foo : props.bar; + const c_0 = $[0] !== t0; let t1; - if (c_2) { + if (c_0) { t1 = { bar: t0 }; - $[2] = t0; - $[3] = t1; + $[0] = t0; + $[1] = t1; } else { - t1 = $[3]; + t1 = $[1]; } - const c_4 = $[4] !== props; - const c_5 = $[5] !== t1; - let t2; - if (c_4 || c_5) { - t2 = ; - $[4] = props; - $[5] = t1; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/logical-expression-object.expect.md b/compiler/forget/src/__tests__/fixtures/hir/logical-expression-object.expect.md index 95a3c75e61..f0f7daea29 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/logical-expression-object.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/logical-expression-object.expect.md @@ -19,39 +19,22 @@ function component(props) { ```javascript function component(props) { - const $ = React.unstable_useMemoCache(7); - const c_0 = $[0] !== props; + const $ = React.unstable_useMemoCache(3); + + const a = props.a || (props.b && props.c && props.d); + const b = (props.a && props.b && props.c) || props.d; + const c_0 = $[0] !== a; + const c_1 = $[1] !== b; let t0; - if (c_0) { - t0 = props.a || (props.b && props.c && props.d); - $[0] = props; - $[1] = t0; + if (c_0 || c_1) { + t0 = { a, b }; + $[0] = a; + $[1] = b; + $[2] = t0; } else { - t0 = $[1]; + t0 = $[2]; } - const a = t0; - const c_2 = $[2] !== props; - let t1; - if (c_2) { - t1 = (props.a && props.b && props.c) || props.d; - $[2] = props; - $[3] = t1; - } else { - t1 = $[3]; - } - const b = t1; - const c_4 = $[4] !== a; - const c_5 = $[5] !== b; - let t2; - if (c_4 || c_5) { - t2 = { a, b }; - $[4] = a; - $[5] = b; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + return t0; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md b/compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md index 8c6244e68d..8dad2ec6ac 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/logical-expression.expect.md @@ -14,41 +14,9 @@ function component(props) { ```javascript function component(props) { - const $ = React.unstable_useMemoCache(8); - const c_0 = $[0] !== props; - let t0; - if (c_0) { - t0 = props.a || (props.b && props.c && props.d); - $[0] = props; - $[1] = t0; - } else { - t0 = $[1]; - } - const a = t0; - const c_2 = $[2] !== props; - let t1; - if (c_2) { - t1 = (props.a && props.b && props.c) || props.d; - $[2] = props; - $[3] = t1; - } else { - t1 = $[3]; - } - const b = t1; - const c_4 = $[4] !== a; - const c_5 = $[5] !== b; - const c_6 = $[6] !== props; - let t2; - if (c_4 || c_5 || c_6) { - t2 = a ? b : props.c; - $[4] = a; - $[5] = b; - $[6] = props; - $[7] = t2; - } else { - t2 = $[7]; - } - return t2; + const a = props.a || (props.b && props.c && props.d); + const b = (props.a && props.b && props.c) || props.d; + return a ? b : props.c; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md b/compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md index 465e44b685..62501bfb6f 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/mutable-liverange-loop.expect.md @@ -41,17 +41,9 @@ function mutate() {} function cond() {} function Component(props) { - const $ = React.unstable_useMemoCache(1); const a = {}; const b = {}; - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = {}; - $[0] = t0; - } else { - t0 = $[0]; - } - const c = t0; + const c = {}; const d = {}; while (true) { mutate(a, b); diff --git a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md index d24c7ef2f9..8a4627755b 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/overlapping-scopes-shadowing-within-block.expect.md @@ -21,7 +21,7 @@ function foo(a, b, c) { ```javascript function foo(a, b, c) { - const $ = React.unstable_useMemoCache(9); + const $ = React.unstable_useMemoCache(7); const c_0 = $[0] !== a; const c_1 = $[1] !== b; const c_2 = $[2] !== c; @@ -43,16 +43,8 @@ function foo(a, b, c) { } else { y = $[6]; } - const c_7 = $[7] !== y; - let t0; - if (c_7) { - t0 =
{y}
; - $[7] = y; - $[8] = t0; - } else { - t0 = $[8]; - } - x.push(t0); + + x.push(
{y}
); } $[0] = a; $[1] = b; diff --git a/compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md b/compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md index 0589203765..643cbcac2e 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/property-assignment.expect.md @@ -17,7 +17,7 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(6); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props.p0; let x; let child; @@ -35,18 +35,7 @@ function Component(props) { x = $[1]; child = $[2]; } - const c_3 = $[3] !== x; - const c_4 = $[4] !== child; - let t0; - if (c_3 || c_4) { - t0 = {child}; - $[3] = x; - $[4] = child; - $[5] = t0; - } else { - t0 = $[5]; - } - return t0; + return {child}; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md index 3faf4cc51f..1ec13b7c14 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes-if.expect.md @@ -20,7 +20,7 @@ function foo(a, b, c) { ```javascript function foo(a, b, c) { - const $ = React.unstable_useMemoCache(8); + const $ = React.unstable_useMemoCache(6); const c_0 = $[0] !== a; const c_1 = $[1] !== b; const c_2 = $[2] !== c; @@ -38,16 +38,7 @@ function foo(a, b, c) { } else { y = $[5]; } - const c_6 = $[6] !== y; - let t0; - if (c_6) { - t0 =
{y}
; - $[6] = y; - $[7] = t0; - } else { - t0 = $[7]; - } - x.push(t0); + x.push(
{y}
); } else { x.push(c); } diff --git a/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes.expect.md index 006beeecae..d70fbe3f80 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reactive-scopes.expect.md @@ -19,7 +19,7 @@ function f(a, b) { ```javascript function f(a, b) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== a.length; const c_1 = $[1] !== b; let x; @@ -36,16 +36,7 @@ function f(a, b) { } else { x = $[2]; } - const c_3 = $[3] !== x; - let t0; - if (c_3) { - t0 =
{x}
; - $[3] = x; - $[4] = t0; - } else { - t0 = $[4]; - } - return t0; + return
{x}
; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md index c7552085ba..3eaac56f77 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reassignment-conditional.expect.md @@ -23,7 +23,7 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(6); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props.p0; let x; if (c_0) { @@ -47,18 +47,7 @@ function Component(props) { } y.push(props.p2); - const c_3 = $[3] !== x; - const c_4 = $[4] !== y; - let t1; - if (c_3 || c_4) { - t1 = ; - $[3] = x; - $[4] = y; - $[5] = t1; - } else { - t1 = $[5]; - } - return t1; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md index 79d36dd99e..315c9f16d1 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reassignment-separate-scopes.expect.md @@ -34,7 +34,7 @@ function foo(a, b, c) { ```javascript function foo(a, b, c) { - const $ = React.unstable_useMemoCache(11); + const $ = React.unstable_useMemoCache(6); const c_0 = $[0] !== a; let x; if (c_0) { @@ -47,58 +47,39 @@ function foo(a, b, c) { } else { x = $[1]; } - const c_2 = $[2] !== x; - let t0; - if (c_2) { - t0 =
{x}
; - $[2] = x; - $[3] = t0; - } else { - t0 = $[3]; - } - const y = t0; + + const y =
{x}
; bb3: switch (b) { case 0: { - const c_4 = $[4] !== b; - if (c_4) { + const c_2 = $[2] !== b; + if (c_2) { x = []; x.push(b); - $[4] = b; - $[5] = x; + $[2] = b; + $[3] = x; } else { - x = $[5]; + x = $[3]; } break bb3; } default: { - const c_6 = $[6] !== c; - if (c_6) { + const c_4 = $[4] !== c; + if (c_4) { x = []; x.push(c); - $[6] = c; - $[7] = x; + $[4] = c; + $[5] = x; } else { - x = $[7]; + x = $[5]; } } } - const c_8 = $[8] !== y; - const c_9 = $[9] !== x; - let t1; - if (c_8 || c_9) { - t1 = ( -
- {y} - {x} -
- ); - $[8] = y; - $[9] = x; - $[10] = t1; - } else { - t1 = $[10]; - } - return t1; + return ( +
+ {y} + {x} +
+ ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md index 0b3212b74d..82cd2284a5 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reassignment.expect.md @@ -21,7 +21,7 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(8); + const $ = React.unstable_useMemoCache(5); const c_0 = $[0] !== props.p0; const c_1 = $[1] !== props.p1; let x; @@ -48,18 +48,7 @@ function Component(props) { x = $[2]; y = $[3]; } - const c_5 = $[5] !== x; - const c_6 = $[6] !== y; - let t1; - if (c_5 || c_6) { - t1 = ; - $[5] = x; - $[6] = y; - $[7] = t1; - } else { - t1 = $[7]; - } - return t1; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-ifelse.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-ifelse.expect.md index 0ced780c0b..53db1825ea 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-ifelse.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-ifelse.expect.md @@ -26,22 +26,13 @@ function TestCondDepInDirectIfElse(props, other) { // paths function TestCondDepInDirectIfElse(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props.a.b; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { + if (foo(other)) { x.b = props.a.b; } else { x.c = props.a.b; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse-missing.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse-missing.expect.md index df4ad11cf7..fcaafdcadd 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse-missing.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse-missing.expect.md @@ -26,30 +26,14 @@ function TestCondDepInNestedIfElse(props, other) { // scope that produces x if it is not accessed in every path function TestCondDepInNestedIfElse(props, other) { - const $ = React.unstable_useMemoCache(6); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { - let t1; - if ($[5] === Symbol.for("react.memo_cache_sentinel")) { - t1 = bar(); - $[5] = t1; - } else { - t1 = $[5]; - } - if (t1) { + if (foo(other)) { + if (bar()) { x.a = props.a.b; } } else { diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse.expect.md index 66d954e2e2..2cb8a10eae 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-nested-ifelse.expect.md @@ -32,45 +32,20 @@ function TestCondDepInNestedIfElse(props, other) { // paths function TestCondDepInNestedIfElse(props, other) { - const $ = React.unstable_useMemoCache(8); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props.a.b; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { - let t1; - if ($[5] === Symbol.for("react.memo_cache_sentinel")) { - t1 = bar(); - $[5] = t1; - } else { - t1 = $[5]; - } - if (t1) { + if (foo(other)) { + if (bar()) { x.a = props.a.b; } else { x.b = props.a.b; } } else { - const c_6 = $[6] !== other; - let t2; - if (c_6) { - t2 = baz(other); - $[6] = other; - $[7] = t2; - } else { - t2 = $[7]; - } - if (t2) { + if (baz(other)) { x.c = props.a.b; } else { x.d = props.a.b; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-case.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-case.expect.md index 8093fe3055..032817f562 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-case.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-case.expect.md @@ -30,22 +30,13 @@ function TestCondDepInSwitchMissingCase(props, other) { // scope that produces x if it is not accessed in every path function TestCondDepInSwitchMissingCase(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - bb1: switch (t0) { + bb1: switch (foo(other)) { case 1: { x.a = props.a.b; break bb1; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-default.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-default.expect.md index dca154ab40..7c854282cb 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-default.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch-missing-default.expect.md @@ -27,22 +27,13 @@ function TestCondDepInSwitchMissingDefault(props, other) { // scope that produces x if it is not accessed in the default case. function TestCondDepInSwitchMissingDefault(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - bb1: switch (t0) { + bb1: switch (foo(other)) { case 1: { x.a = props.a.b; break bb1; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch.expect.md index 3016700db5..8ad41ad70e 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-cfg-switch.expect.md @@ -31,22 +31,13 @@ function TestCondDepInSwitch(props, other) { // paths function TestCondDepInSwitch(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props.a.b; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - bb1: switch (t0) { + bb1: switch (foo(other)) { case 1: { x.a = props.a.b; break bb1; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-no-uncond.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-no-uncond.expect.md index 8b60cacc88..933fc1eac1 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-no-uncond.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-no-uncond.expect.md @@ -21,22 +21,13 @@ function TestOnlyConditionalDependencies(props, other) { // When an object's properties are only read conditionally, we should // track the base object as a dependency. function TestOnlyConditionalDependencies(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { + if (foo(other)) { x.b = props.a.b; x.c = props.a.b.c; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-promote-uncond.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-promote-uncond.expect.md index 0dc9bc311e..98235bb24e 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-promote-uncond.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-promote-uncond.expect.md @@ -23,23 +23,14 @@ function TestPromoteUnconditionalAccessToDependency(props, other) { // in its subpath or superpath, we should find the nearest unconditional access // and promote it to an unconditional dependency. function TestPromoteUnconditionalAccessToDependency(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props.a; const c_1 = $[1] !== other; let x; if (c_0 || c_1) { x = {}; x.a = props.a.a.a; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { + if (foo(other)) { x.c = props.a.b.c; } $[0] = props.a; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order1.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order1.expect.md index 6b15c1d174..b766b54e6c 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order1.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order1.expect.md @@ -27,23 +27,14 @@ function TestConditionalSubpath1(props, other) { // deps: {`props.a`, `props.a.b`} can further reduce to just `props.a` // ordering of accesses should not matter function TestConditionalSubpath1(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props.a; const c_1 = $[1] !== other; let x; if (c_0 || c_1) { x = {}; x.b = props.a.b; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { + if (foo(other)) { x.a = props.a; } $[0] = props.a; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order2.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order2.expect.md index e7a9a37f80..2a1925d1f2 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order2.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-subpath-order2.expect.md @@ -27,22 +27,13 @@ function TestConditionalSubpath2(props, other) { // deps: {`props.a`, `props.a.b`} can further reduce to just `props.a` // ordering of accesses should not matter function TestConditionalSubpath2(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props.a; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { + if (foo(other)) { x.a = props.a; } x.b = props.a.b; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order1.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order1.expect.md index ac67e5dd5e..0acc4dc3f1 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order1.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order1.expect.md @@ -25,23 +25,14 @@ function TestConditionalSuperpath1(props, other) { // as a dependency // ordering of accesses should not matter function TestConditionalSuperpath1(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props.a; const c_1 = $[1] !== other; let x; if (c_0 || c_1) { x = {}; x.a = props.a; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { + if (foo(other)) { x.b = props.a.b; } $[0] = props.a; diff --git a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order2.expect.md b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order2.expect.md index 52fdceb006..fc32e61f3e 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order2.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/reduce-reactive-cond-deps-superpath-order2.expect.md @@ -25,22 +25,13 @@ function TestConditionalSuperpath2(props, other) { // as a dependency // ordering of accesses should not matter function TestConditionalSuperpath2(props, other) { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== other; const c_1 = $[1] !== props.a; let x; if (c_0 || c_1) { x = {}; - const c_3 = $[3] !== other; - let t0; - if (c_3) { - t0 = foo(other); - $[3] = other; - $[4] = t0; - } else { - t0 = $[4]; - } - if (t0) { + if (foo(other)) { x.b = props.a.b; } x.a = props.a; diff --git a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md index c4f6052007..055923ee83 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare-maybe-frozen.expect.md @@ -46,7 +46,7 @@ function foo(props) { // note: comments are for the ideal scopes, not what is currently // emitted function foo(props) { - const $ = React.unstable_useMemoCache(16); + const $ = React.unstable_useMemoCache(7); const c_0 = $[0] !== props.a; let x; if (c_0) { @@ -57,70 +57,39 @@ function foo(props) { } else { x = $[1]; } - const c_2 = $[2] !== props.showHeader; - const c_3 = $[3] !== x; - let t0; - if (c_2 || c_3) { - t0 = props.showHeader ?
{x}
: null; - $[2] = props.showHeader; - $[3] = x; - $[4] = t0; - } else { - t0 = $[4]; - } - const header = t0; - const c_5 = $[5] !== x; - const c_6 = $[6] !== props.b; - const c_7 = $[7] !== props.c; + + const header = props.showHeader ?
{x}
: null; + const c_2 = $[2] !== x; + const c_3 = $[3] !== props.b; + const c_4 = $[4] !== props.c; let y; - if (c_5 || c_6 || c_7) { + if (c_2 || c_3 || c_4) { y = [x]; x = []; y.push(props.b); x.push(props.c); - $[5] = x; - $[6] = props.b; - $[7] = props.c; - $[8] = y; - $[9] = x; + $[2] = x; + $[3] = props.b; + $[4] = props.c; + $[5] = y; + $[6] = x; } else { - y = $[8]; - x = $[9]; + y = $[5]; + x = $[6]; } - const c_10 = $[10] !== x; - const c_11 = $[11] !== y; - let t1; - if (c_10 || c_11) { - t1 = ( -
- {x} - {y} -
- ); - $[10] = x; - $[11] = y; - $[12] = t1; - } else { - t1 = $[12]; - } - const content = t1; - const c_13 = $[13] !== header; - const c_14 = $[14] !== content; - let t2; - if (c_13 || c_14) { - t2 = ( - <> - {header} - {content} - - ); - $[13] = header; - $[14] = content; - $[15] = t2; - } else { - t2 = $[15]; - } - return t2; + + const content = ( +
+ {x} + {y} +
+ ); + return ( + <> + {header} + {content} + + ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md index dc4c090b37..fa6a6227b1 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/same-variable-as-dep-and-redeclare.expect.md @@ -46,7 +46,7 @@ function foo(props) { // note: comments are for the ideal scopes, not what is currently // emitted function foo(props) { - const $ = React.unstable_useMemoCache(15); + const $ = React.unstable_useMemoCache(7); const c_0 = $[0] !== props.a; let x; if (c_0) { @@ -57,68 +57,39 @@ function foo(props) { } else { x = $[1]; } + + const header =
{x}
; const c_2 = $[2] !== x; - let t0; - if (c_2) { - t0 =
{x}
; - $[2] = x; - $[3] = t0; - } else { - t0 = $[3]; - } - const header = t0; - const c_4 = $[4] !== x; - const c_5 = $[5] !== props.b; - const c_6 = $[6] !== props.c; + const c_3 = $[3] !== props.b; + const c_4 = $[4] !== props.c; let y; - if (c_4 || c_5 || c_6) { + if (c_2 || c_3 || c_4) { y = [x]; x = []; y.push(props.b); x.push(props.c); - $[4] = x; - $[5] = props.b; - $[6] = props.c; - $[7] = y; - $[8] = x; + $[2] = x; + $[3] = props.b; + $[4] = props.c; + $[5] = y; + $[6] = x; } else { - y = $[7]; - x = $[8]; + y = $[5]; + x = $[6]; } - const c_9 = $[9] !== x; - const c_10 = $[10] !== y; - let t1; - if (c_9 || c_10) { - t1 = ( -
- {x} - {y} -
- ); - $[9] = x; - $[10] = y; - $[11] = t1; - } else { - t1 = $[11]; - } - const content = t1; - const c_12 = $[12] !== header; - const c_13 = $[13] !== content; - let t2; - if (c_12 || c_13) { - t2 = ( - <> - {header} - {content} - - ); - $[12] = header; - $[13] = content; - $[14] = t2; - } else { - t2 = $[14]; - } - return t2; + + const content = ( +
+ {x} + {y} +
+ ); + return ( + <> + {header} + {content} + + ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md index 8326b7e3fd..8d33b08ebb 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx-2.expect.md @@ -25,21 +25,14 @@ function Component(props) { function foo() {} function Component(props) { - const $ = React.unstable_useMemoCache(4); + const $ = React.unstable_useMemoCache(2); let a; let b; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { a = []; b = {}; foo(a, b); - let t0; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t0 = foo(); - $[2] = t0; - } else { - t0 = $[2]; - } - if (t0) { + if (foo()) { } foo(a, b); @@ -49,14 +42,7 @@ function Component(props) { a = $[0]; b = $[1]; } - let t1; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t1 =
; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return
; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md index e786729615..8a59efbd00 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-call-jsx.expect.md @@ -21,7 +21,7 @@ function Component(props) { function foo() {} function Component(props) { - const $ = React.unstable_useMemoCache(3); + const $ = React.unstable_useMemoCache(2); let a; let b; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { @@ -36,14 +36,7 @@ function Component(props) { a = $[0]; b = $[1]; } - let t0; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t0 =
; - $[2] = t0; - } else { - t0 = $[2]; - } - return t0; + return
; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-cascading-eliminated-phis.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-cascading-eliminated-phis.expect.md index 88538e4925..6451c8f766 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-cascading-eliminated-phis.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-cascading-eliminated-phis.expect.md @@ -24,22 +24,13 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(4); + const $ = React.unstable_useMemoCache(2); let x = 0; const c_0 = $[0] !== props; let values; if (c_0) { values = []; - const c_2 = $[2] !== props; - let t0; - if (c_2) { - t0 = props.a || props.b; - $[2] = props; - $[3] = t0; - } else { - t0 = $[3]; - } - const y = t0; + const y = props.a || props.b; values.push(y); if (props.c) { x = 1; diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-for-trivial-update.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-for-trivial-update.expect.md index 9ce7eb94ba..add2bb00dd 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-for-trivial-update.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-for-trivial-update.expect.md @@ -16,16 +16,9 @@ function foo() { ```javascript function foo() { - const $ = React.unstable_useMemoCache(1); - let x; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = 1; - for (const i = 0; true; 0) { - x = x + 1; - } - $[0] = x; - } else { - x = $[0]; + let x = 1; + for (const i = 0; true; 0) { + x = x + 1; } return x; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md index 0504fe30d6..6ce00eed13 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-for.expect.md @@ -16,16 +16,9 @@ function foo() { ```javascript function foo() { - const $ = React.unstable_useMemoCache(1); - let x; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = 1; - for (let i = 0; i < 10; i = i + 1, i) { - x = x + 1; - } - $[0] = x; - } else { - x = $[0]; + let x = 1; + for (let i = 0; i < 10; i = i + 1, i) { + x = x + 1; } return x; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-leave-case.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-leave-case.expect.md index 72c1e52d44..a9fe1e2ee3 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-leave-case.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-leave-case.expect.md @@ -23,7 +23,7 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(6); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props; let x; let y; @@ -41,23 +41,12 @@ function Component(props) { x = $[1]; y = $[2]; } - const c_3 = $[3] !== x; - const c_4 = $[4] !== y; - let t0; - if (c_3 || c_4) { - t0 = ( - - {x} - {y} - - ); - $[3] = x; - $[4] = y; - $[5] = t0; - } else { - t0 = $[5]; - } - return t0; + return ( + + {x} + {y} + + ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md index 21c3ed6f41..8b6a3ca0ed 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ssa-while.expect.md @@ -17,16 +17,9 @@ function foo() { ```javascript function foo() { - const $ = React.unstable_useMemoCache(1); - let x; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - x = 1; - while (x < 10) { - x = x + 1; - } - $[0] = x; - } else { - x = $[0]; + let x = 1; + while (x < 10) { + x = x + 1; } return x; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md b/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md index 617fbc88fb..edf573ba87 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/switch-non-final-default.expect.md @@ -32,7 +32,7 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(9); + const $ = React.unstable_useMemoCache(4); const c_0 = $[0] !== props; let x; let y; @@ -69,29 +69,10 @@ function Component(props) { x = $[1]; y = $[2]; } - const c_4 = $[4] !== x; - let t1; - if (c_4) { - t1 = ; - $[4] = x; - $[5] = t1; - } else { - t1 = $[5]; - } - const child = t1; + + const child = ; y.push(props.p4); - const c_6 = $[6] !== y; - const c_7 = $[7] !== child; - let t2; - if (c_6 || c_7) { - t2 = {child}; - $[6] = y; - $[7] = child; - $[8] = t2; - } else { - t2 = $[8]; - } - return t2; + return {child}; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md b/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md index 1ab3f13f01..beccdd160d 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/switch.expect.md @@ -27,7 +27,7 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(8); + const $ = React.unstable_useMemoCache(3); const c_0 = $[0] !== props; let x; let y; @@ -50,29 +50,10 @@ function Component(props) { x = $[1]; y = $[2]; } - const c_3 = $[3] !== x; - let t0; - if (c_3) { - t0 = ; - $[3] = x; - $[4] = t0; - } else { - t0 = $[4]; - } - const child = t0; + + const child = ; y.push(props.p4); - const c_5 = $[5] !== y; - const c_6 = $[6] !== child; - let t1; - if (c_5 || c_6) { - t1 = {child}; - $[5] = y; - $[6] = child; - $[7] = t1; - } else { - t1 = $[7]; - } - return t1; + return {child}; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/temporary-at-start-of-value-block.expect.md b/compiler/forget/src/__tests__/fixtures/hir/temporary-at-start-of-value-block.expect.md index 29f79e4370..7aadfcb57b 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/temporary-at-start-of-value-block.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/temporary-at-start-of-value-block.expect.md @@ -14,17 +14,7 @@ function component(props) { ```javascript function component(props) { - const $ = React.unstable_useMemoCache(2); - const c_0 = $[0] !== props; - let t0; - if (c_0) { - t0 = isMenuShown ? {props.a ? props.b : props.c} : null; - $[0] = props; - $[1] = t0; - } else { - t0 = $[1]; - } - const x = t0; + const x = isMenuShown ? {props.a ? props.b : props.c} : null; return x; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/ternary-expression.expect.md b/compiler/forget/src/__tests__/fixtures/hir/ternary-expression.expect.md index 9ff10bb632..fb6e412c66 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/ternary-expression.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/ternary-expression.expect.md @@ -14,39 +14,9 @@ function ternary(props) { ```javascript function ternary(props) { - const $ = React.unstable_useMemoCache(7); - const c_0 = $[0] !== props; - let t0; - if (c_0) { - t0 = props.a && props.b ? props.c || props.d : props.e ?? props.f; - $[0] = props; - $[1] = t0; - } else { - t0 = $[1]; - } - const a = t0; - const c_2 = $[2] !== props; - let t1; - if (c_2) { - t1 = props.a ? (props.b && props.c ? props.d : props.e) : props.f; - $[2] = props; - $[3] = t1; - } else { - t1 = $[3]; - } - const b = t1; - const c_4 = $[4] !== a; - const c_5 = $[5] !== b; - let t2; - if (c_4 || c_5) { - t2 = a ? b : null; - $[4] = a; - $[5] = b; - $[6] = t2; - } else { - t2 = $[6]; - } - return t2; + const a = props.a && props.b ? props.c || props.d : props.e ?? props.f; + const b = props.a ? (props.b && props.c ? props.d : props.e) : props.f; + return a ? b : null; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/timers.expect.md b/compiler/forget/src/__tests__/fixtures/hir/timers.expect.md index 414007af95..ff5ed5784f 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/timers.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/timers.expect.md @@ -19,43 +19,22 @@ function Component(props) { ```javascript function Component(props) { - const $ = React.unstable_useMemoCache(4); + const $ = React.unstable_useMemoCache(1); + const start = performance.now(); let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = performance.now(); + t0 = Date.now(); $[0] = t0; } else { t0 = $[0]; } - const start = t0; - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = Date.now(); - $[1] = t1; - } else { - t1 = $[1]; - } - const now = t1; - let t2; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t2 = performance.now(); - $[2] = t2; - } else { - t2 = $[2]; - } - const time = t2 - start; - let t3; - if ($[3] === Symbol.for("react.memo_cache_sentinel")) { - t3 = ( -
- rendering took {time} at {now} -
- ); - $[3] = t3; - } else { - t3 = $[3]; - } - return t3; + const now = t0; + const time = performance.now() - start; + return ( +
+ rendering took {time} at {now} +
+ ); } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/type-binary-operator.expect.md b/compiler/forget/src/__tests__/fixtures/hir/type-binary-operator.expect.md index 087ff35c9a..a3e5be659b 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/type-binary-operator.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/type-binary-operator.expect.md @@ -16,23 +16,8 @@ function component() { ```javascript function component() { - const $ = React.unstable_useMemoCache(2); - let t0; - if ($[0] === Symbol.for("react.memo_cache_sentinel")) { - t0 = some(); - $[0] = t0; - } else { - t0 = $[0]; - } - const a = t0; - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = someOther(); - $[1] = t1; - } else { - t1 = $[1]; - } - const b = t1; + const a = some(); + const b = someOther(); if (a > b) { } } diff --git a/compiler/forget/src/__tests__/fixtures/hir/type-test-return-type-inference.expect.md b/compiler/forget/src/__tests__/fixtures/hir/type-test-return-type-inference.expect.md index 0a4b769a0f..19f69faef0 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/type-test-return-type-inference.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/type-test-return-type-inference.expect.md @@ -19,7 +19,11 @@ function component() { ```javascript function component() { - const $ = React.unstable_useMemoCache(3); + const $ = React.unstable_useMemoCache(1); + const x = foo(); + const y = foo(); + if (x > y) { + } let t0; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t0 = foo(); @@ -27,25 +31,7 @@ function component() { } else { t0 = $[0]; } - const x = t0; - let t1; - if ($[1] === Symbol.for("react.memo_cache_sentinel")) { - t1 = foo(); - $[1] = t1; - } else { - t1 = $[1]; - } - const y = t1; - if (x > y) { - } - let t2; - if ($[2] === Symbol.for("react.memo_cache_sentinel")) { - t2 = foo(); - $[2] = t2; - } else { - t2 = $[2]; - } - const z_0 = t2; + const z_0 = t0; return z_0; } diff --git a/compiler/forget/src/__tests__/fixtures/hir/unary-expr.expect.md b/compiler/forget/src/__tests__/fixtures/hir/unary-expr.expect.md index 868956d994..05638fcee1 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/unary-expr.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/unary-expr.expect.md @@ -20,58 +20,37 @@ function component(a) { ```javascript function component(a) { - const $ = React.unstable_useMemoCache(14); - const c_0 = $[0] !== a; - let t0; - let t; - let z; - let p; - let q; - if (c_0) { - t = { t: a }; - z = +t.t; - q = -t.t; - p = void t.t; - t0 = delete t.t; - $[0] = a; - $[1] = t0; - $[2] = t; - $[3] = z; - $[4] = p; - $[5] = q; - } else { - t0 = $[1]; - t = $[2]; - z = $[3]; - p = $[4]; - q = $[5]; - } - const n = t0; + const $ = React.unstable_useMemoCache(8); + const t = { t: a }; + const z = +t.t; + const q = -t.t; + const p = void t.t; + const n = delete t.t; const m = !t.t; const e = ~t.t; const f = typeof t.t; - const c_6 = $[6] !== z; - const c_7 = $[7] !== p; - const c_8 = $[8] !== q; - const c_9 = $[9] !== n; - const c_10 = $[10] !== m; - const c_11 = $[11] !== e; - const c_12 = $[12] !== f; - let t1; - if (c_6 || c_7 || c_8 || c_9 || c_10 || c_11 || c_12) { - t1 = { z, p, q, n, m, e, f }; - $[6] = z; - $[7] = p; - $[8] = q; - $[9] = n; - $[10] = m; - $[11] = e; - $[12] = f; - $[13] = t1; + const c_0 = $[0] !== z; + const c_1 = $[1] !== p; + const c_2 = $[2] !== q; + const c_3 = $[3] !== n; + const c_4 = $[4] !== m; + const c_5 = $[5] !== e; + const c_6 = $[6] !== f; + let t0; + if (c_0 || c_1 || c_2 || c_3 || c_4 || c_5 || c_6) { + t0 = { z, p, q, n, m, e, f }; + $[0] = z; + $[1] = p; + $[2] = q; + $[3] = n; + $[4] = m; + $[5] = e; + $[6] = f; + $[7] = t0; } else { - t1 = $[13]; + t0 = $[7]; } - return t1; + return t0; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/use-callback-simple.expect.md b/compiler/forget/src/__tests__/fixtures/hir/use-callback-simple.expect.md index 27e04507db..4228e8a584 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/use-callback-simple.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/use-callback-simple.expect.md @@ -15,7 +15,7 @@ function component() { ```javascript function component() { - const $ = React.unstable_useMemoCache(5); + const $ = React.unstable_useMemoCache(3); const [count, setCount] = useState(0); const c_0 = $[0] !== setCount; const c_1 = $[1] !== count; @@ -29,16 +29,7 @@ function component() { t0 = $[2]; } const increment = t0; - const c_3 = $[3] !== increment; - let t1; - if (c_3) { - t1 = ; - $[3] = increment; - $[4] = t1; - } else { - t1 = $[4]; - } - return t1; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/useMemo-simple.expect.md b/compiler/forget/src/__tests__/fixtures/hir/useMemo-simple.expect.md index ef1baed8c5..182a54a307 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/useMemo-simple.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/useMemo-simple.expect.md @@ -13,7 +13,7 @@ function component(a) { ```javascript function component(a) { - const $ = React.unstable_useMemoCache(4); + const $ = React.unstable_useMemoCache(2); const c_0 = $[0] !== a; let t0; if (c_0) { @@ -24,16 +24,7 @@ function component(a) { t0 = $[1]; } const x = t0; - const c_2 = $[2] !== x; - let t1; - if (c_2) { - t1 = ; - $[2] = x; - $[3] = t1; - } else { - t1 = $[3]; - } - return t1; + return ; } ``` diff --git a/compiler/forget/src/__tests__/fixtures/hir/while-property.expect.md b/compiler/forget/src/__tests__/fixtures/hir/while-property.expect.md index b19dd0c1da..087dcd7cbe 100644 --- a/compiler/forget/src/__tests__/fixtures/hir/while-property.expect.md +++ b/compiler/forget/src/__tests__/fixtures/hir/while-property.expect.md @@ -16,20 +16,9 @@ function foo(a, b) { ```javascript function foo(a, b) { - const $ = React.unstable_useMemoCache(3); - const c_0 = $[0] !== a.b.c; - const c_1 = $[1] !== b; - let x; - if (c_0 || c_1) { - x = 0; - while (a.b.c) { - x = x + b; - } - $[0] = a.b.c; - $[1] = b; - $[2] = x; - } else { - x = $[2]; + let x = 0; + while (a.b.c) { + x = x + b; } return x; }