Skip to content

Conversation

josephsavona
Copy link
Member

@josephsavona josephsavona commented Apr 30, 2025

Stack from ghstack (oldest at bottom):

Handles a few edge cases relating to for..of terminals, which have a compound init value block:

  • For..of with a return statement inside would not memoize the rvalue of the init expression (the collection). This is because there was no direct dependency relationship between the value being returned and the outer scope. The fix is to track the set of active scopes and make return values depend on all the active scopes at the point of the return.
  • For..of that reassigns a variable which later escapes were also not memoizing the collection. This is similar: the escape analysis aliasing didn't consider scopes that reassign, so there was no dependency between the returned variable and the scope that did the reassigning. The fix is to establish that dependency relationship.

In both cases, adding the additional scopes as a dependency of the right variable ensures that the scope's dependencies are treated as escaping (needing memoization), ensuring the collection is memoized.

We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Apr 30, 2025
We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

ghstack-source-id: d4d58c3
Pull Request resolved: #33062
@josephsavona josephsavona marked this pull request as draft April 30, 2025 05:20
…rands"

We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Apr 30, 2025
We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

ghstack-source-id: f59837e
Pull Request resolved: #33062
…rands"

We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request Apr 30, 2025
We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

ghstack-source-id: a477f2c
Pull Request resolved: #33062
@josephsavona josephsavona requested a review from mofeiZ April 30, 2025 07:01
@josephsavona josephsavona marked this pull request as ready for review April 30, 2025 07:02
* - rvalues: places that are aliased by the instruction's lvalues.
* - level: the level of memoization to apply to this value
*/
function computeMemoizationInputs(
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this moves to become a method on CollectDependencies

Comment on lines +465 to +467
for (const instr of value.instructions) {
this.visitValueForMemoization(instr.id, instr.value, instr.lvalue);
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is the main change: not just traversing into sequence expressions, but processing the aliasing information into the state

Comment on lines +978 to +982
* If a scope reassigns any variables, set the chain of active scopes as a dependency
* of those variables. This ensures that if the variable escapes that we treat the
* reassignment scopes — and importantly their dependencies — as needing memoization.
*/
for (const reassignment of scope.scope.reassignments) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, thanks for the comment!

This ensures that if the variable escapes that we treat the reassignment scopes — and importantly their dependencies — as needing memoization.

I wonder how this was working previously.. If we were actually incorrectly pruning reassignment scopes, that sounds like a bug (e.g. if some reassignment scopes are pruned and others are kept due to declaring escaping values)

@@ -0,0 +1,16 @@
function useHook(nodeID, condition) {
const graph = useContext(GraphContext);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: can we add imports for useContext?

let value;
for (const key of Object.keys(node?.fields ?? {})) {
if (condition) {
// (1) We currently create a scope just for this instruction, then later prune the scope because
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh that makes sense. I guess loops are kind of the only case that we might want to lengthen instead of completely pruning scopes. Specifically, we should rewrite / lengthen a scope if it is the outermost one in a loop.

Since flattenReactiveLoops happens before other relevant passes (flatten hook calls, propagate scope deps), this seems pretty straightforward.

Nice find!

…rands"


Handles a few edge cases relating to for..of terminals, which have a compound `init` value block:
* For..of with a return statement inside would not memoize the rvalue of the init expression (the collection). This is because there was no direct dependency relationship between the value being returned and the outer scope. The fix is to track the set of active scopes and make return values depend on all the active scopes at the point of the return.
* For..of that reassigns a variable which later escapes were also not memoizing the collection. This is similar: the escape analysis aliasing didn't consider scopes that reassign, so there was no dependency between the returned variable and the scope that did the reassigning. The fix is to establish that dependency relationship.

In both cases, adding the additional scopes as a dependency of the right variable ensures that the scope's dependencies are treated as escaping (needing memoization), ensuring the collection is memoized.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request May 1, 2025
We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

ghstack-source-id: 09a2923
Pull Request resolved: #33062
@josephsavona josephsavona merged commit 2790dd4 into gh/josephsavona/73/base May 1, 2025
18 of 20 checks passed
josephsavona added a commit that referenced this pull request May 1, 2025
We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

ghstack-source-id: 09a2923
Pull Request resolved: #33062
@josephsavona josephsavona deleted the gh/josephsavona/73/head branch May 1, 2025 03:41
github-actions bot pushed a commit that referenced this pull request May 1, 2025
We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

ghstack-source-id: 09a2923
Pull Request resolved: #33062

DiffTrain build for [e9db3cc](e9db3cc)
github-actions bot pushed a commit to code/lib-react that referenced this pull request May 1, 2025
We weren't treating terminal operands as eligible for memoization in PruneNonEscapingScopes, which meant that they could end up un-memoized. Terminal operands can also be compound ReactiveValues like SequenceExpressions, so part of the fix is to make sure we don't just recurse into compound values but record the full aliasing information we would for top-level instructions.

Still WIP, this needs to handle terminals other than for..of.

ghstack-source-id: 09a2923
Pull Request resolved: facebook#33062

DiffTrain build for [e9db3cc](facebook@e9db3cc)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants