Skip to content

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

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

Open
wants to merge 2 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
Member

@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
Member

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
Member

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
Member

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 +247
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)

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
mofeiZ added 2 commits April 29, 2025 18:38
(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 May 21, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

(will add more test cases)
mofeiZ added a commit that referenced this pull request May 21, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

(will add more test cases)
mofeiZ added a commit that referenced this pull request May 21, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

`
mofeiZ added a commit that referenced this pull request May 21, 2025
When collecting scope dependencies, mark each dependency with `reactive: true | false`. This prepares for later PRs #33326 and #32099 which rewrite scope dependencies into instructions.

Note that some reactive objects may have non-reactive properties, but we do not currently track this.

Technically, state[0] is reactive and state[1] is not. Currently, both would be marked as reactive.
```js
const state = useState();
```
mofeiZ added a commit that referenced this pull request May 21, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

`
mofeiZ added a commit that referenced this pull request May 21, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

`
mofeiZ added a commit that referenced this pull request May 21, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

`
mofeiZ added a commit that referenced this pull request May 21, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

`
mofeiZ added a commit that referenced this pull request May 22, 2025
Inferred effect dependencies now include optional chains.

This is a temporary solution while #32099 and its followups are worked on. Ideally, we should model reactive scope dependencies in the IR similarly to `ComputeIR` -- dependencies should be hoisted and all references rewritten to use the hoisted dependencies.

`
mofeiZ added a commit that referenced this pull request May 22, 2025
When collecting scope dependencies, mark each dependency with `reactive: true | false`. This prepares for later PRs #33326 and #32099 which rewrite scope dependencies into instructions.

Note that some reactive objects may have non-reactive properties, but we do not currently track this.

Technically, state[0] is reactive and state[1] is not. Currently, both would be marked as reactive.
```js
const state = useState();
```
mofeiZ added a commit that referenced this pull request May 22, 2025
When collecting scope dependencies, mark each dependency with `reactive:
true | false`. This prepares for later PRs
#33326 and
#32099 which rewrite scope
dependencies into instructions.

Note that some reactive objects may have non-reactive properties, but we
do not currently track this.

Technically, state[0] is reactive and state[1] is not. Currently, both
would be marked as reactive.
```js
const state = useState();
```
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33325).
* #33326
* __->__ #33325
* #32286
mofeiZ added a commit that referenced this pull request May 22, 2025
…33326)

Inferred effect dependencies now include optional chains.

This is a temporary solution while
#32099 and its followups are
worked on. Ideally, we should model reactive scope dependencies in the
IR similarly to `ComputeIR` -- dependencies should be hoisted and all
references rewritten to use the hoisted dependencies.

`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33326).
* __->__ #33326
* #33325
* #32286
github-actions bot pushed a commit that referenced this pull request May 22, 2025
…33326)

Inferred effect dependencies now include optional chains.

This is a temporary solution while
#32099 and its followups are
worked on. Ideally, we should model reactive scope dependencies in the
IR similarly to `ComputeIR` -- dependencies should be hoisted and all
references rewritten to use the hoisted dependencies.

`
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33326).
* __->__ #33326
* #33325
* #32286

DiffTrain build for [0d07288](0d07288)
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