Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[compiler][rewrite] Represent scope dependencies with value blocks #32099

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

mofeiZ
Copy link
Contributor

@mofeiZ mofeiZ commented Jan 16, 2025

(needs cleanup)

  • Scopes no longer store a flat list of their dependencies. Instead:
    • Scope terminals are effectively a goto for scope dependency instructions (represented as value blocks that terminate with a goto scopeBlock for HIR and a series of ReactiveInstructions for ReactiveIR)
    • Scopes themselves store dependencies: Array<Place>, which refer to temporaries written to by scope dependency instructions

Next steps:

  • new pass to dedupe scope dependency instructions after all dependency and scope pruning passes, effectively 'hoisting' dependencies out
  • more complex dependencies (unary ops like Boolean or Not, binary ops like !== or logical operators)

Stack created with Sapling. Best reviewed with ReviewStack.

Copy link
Contributor

@josephsavona josephsavona left a comment

Choose a reason for hiding this comment

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

i did a quick skim through, this is just some early feedback

@@ -3414,7 +3413,7 @@ function lowerExpressionToTemporary(
return lowerValueToTemporary(builder, value);
}

function lowerValueToTemporary(
export function lowerValueToTemporary(
Copy link
Contributor

Choose a reason for hiding this comment

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

not used outside this file, remove the export?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is used in propagateScopeDependencies, which creates new instructions / temporaries for scope dependency blocks

@@ -902,6 +904,7 @@ export class Environment {
this.#moduleTypes.set(REANIMATED_MODULE_NAME, reanimatedModuleType);
}

this.parentFunction = parentFunction;
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: splitting changes like this into a separate PR would make it easier to review and focus on the core dependency changes

Copy link
Contributor

Choose a reason for hiding this comment

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

I have not processed the changes here deeply, but my first instinct is to wonder why this pass should have to change at all?

Copy link
Contributor Author

@mofeiZ mofeiZ Jan 22, 2025

Choose a reason for hiding this comment

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

Since we're now representing scope dependencies with value blocks + a list of Places, two things need to change:

  1. Promoting unnamed Identifiers referenced by scope dependencies now requires explicitly traversing the value blocks (see this.visitBlock(scopeBlock.dependencyInstructions, ...)
  2. We now also need to perform some form of DCE -- after pruning passes, some instructions within scopeBlock.dependencyInstructions no longer correspond to real dependencies. Concretely, we need to figure out which base Identifiers should be excluded for promotion.
    Note that this isn't an issue for codegen as ReactiveScopeBlock.dependencyInstructions always contains non-named, non-null lvalues (which simply stores into a Temporaries: Map<Identifier, babel.t.Expression> sidemap for later inlining

mofeiZ added a commit that referenced this pull request Jan 22, 2025
Small patch to pass aliased context values into
`Object|ArrayExpression`s
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32093).
* #32099
* #32104
* #32098
* #32097
* #32096
* #32095
* #32094
* __->__ #32093
mofeiZ added a commit that referenced this pull request Jan 22, 2025
See test fixture
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32094).
* #32099
* #32104
* #32098
* #32097
* #32096
* #32095
* __->__ #32094
* #32093
mofeiZ added a commit that referenced this pull request Jan 22, 2025
See test fixture
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32095).
* #32099
* #32104
* #32098
* #32097
* #32096
* __->__ #32095
* #32094
* #32093
Comment on lines +197 to +200
getOrCreateIdentifier(
identifier: Identifier,
reactive: boolean,
): PropertyPathNode {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note that we now need to track reactivity, as we're creating new instructions to represent dependencies

@mofeiZ
Copy link
Contributor Author

mofeiZ commented Feb 15, 2025

Synced offline with @josephsavona, I'm going to hold off on landing this until after:

  1. We do a meta sync to check that this rewrite doesn't produce code / bailout changes
  2. I put up a poc for deduping dependencies and other hoistable instructions (e.g. ===, bool, etc)

LoweredFunction dependencies were exclusively used for dependency extraction (in `propagateScopeDeps`). Now that we have a `propagateScopeDepsHIR` that recursively traverses into nested functions, we can delete `dependencies` and their associated synthetic `LoadLocal`/`PropertyLoad` instructions.

[Internal snapshot diff](https://www.internalfb.com/phabricator/paste/view/P1716950202) for this change shows ~.2% of files changed. I [read through ~60 of the changed files](https://www.internalfb.com/phabricator/paste/view/P1733074307)
- most changes are due to better outlining (due to better DCE)
- a few changes in memo inference are due to changed ordering
```
// source
arr.map(() => contextVar.inner);

// previous instructions
$0 = LoadLocal arr
$1 = $0.map
// Below instructions are synthetic
$2 = LoadLocal contextVar
$3 = $2.inner
$4 = Function deps=$3 context=contextVar {
  ...
}
```
- a few changes are effectively bugfixes (see `aliased-nested-scope-fn-expr`)
This removes special casing for `PropertyStore` mutability inference within FunctionExpressions.
(needs cleanup)

- Scopes no longer store a flat list of their dependencies. Instead:
  - Scope terminals are effectively a `goto` for scope dependency instructions (represented as value blocks that terminate with a `goto scopeBlock` for HIR and a series of ReactiveInstructions for ReactiveIR)
  - Scopes themselves store `dependencies: Array<Place>`, which refer to temporaries written to by scope dependency instructions

Next steps:
- new pass to dedupe scope dependency instructions after all dependency and scope pruning passes, effectively 'hoisting' dependencies out
- more complex dependencies (unary ops like `Boolean` or `Not`, binary ops like `!==` or logical operators)
mofeiZ added a commit that referenced this pull request Feb 18, 2025
…ns (#32096)

LoweredFunction dependencies were exclusively used for dependency
extraction (in `propagateScopeDeps`). Now that we have a
`propagateScopeDepsHIR` that recursively traverses into nested
functions, we can delete `dependencies` and their associated synthetic
`LoadLocal`/`PropertyLoad` instructions.

[Internal snapshot
diff](https://www.internalfb.com/phabricator/paste/view/P1716950202) for
this change shows ~.2% of files changed. I [read through ~60 of the
changed
files](https://www.internalfb.com/phabricator/paste/view/P1733074307)
- most changes are due to better outlining (due to better DCE)
- a few changes in memo inference are due to changed ordering
```
// source
arr.map(() => contextVar.inner);

// previous instructions
$0 = LoadLocal arr
$1 = $0.map
// Below instructions are synthetic
$2 = LoadLocal contextVar
$3 = $2.inner
$4 = Function deps=$3 context=contextVar {
  ...
}
```
- a few changes are effectively bugfixes (see
`aliased-nested-scope-fn-expr`)
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/32096).
* #32099
* #32286
* #32104
* #32098
* #32097
* __->__ #32096
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CLA Signed React Core Team Opened by a member of the React Core Team
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants