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: Use types to decide which scopes are eligible for merging #29157

Merged
merged 8 commits into from
May 23, 2024

Conversation

josephsavona
Copy link
Contributor

@josephsavona josephsavona commented May 18, 2024

Stack from ghstack (oldest at bottom):

In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for merging, we previously looked specifically at the type of the instruction whose lvalue is declaration. So if a scope declaration was t0, we'd look for the instruction where t0 was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

[ghstack-poisoned]
Copy link

vercel bot commented May 18, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
react-compiler-playground ✅ Ready (Inspect) Visit Preview 💬 Add feedback May 23, 2024 0:12am

josephsavona added a commit that referenced this pull request May 18, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: 22e49606056cd37220892cc2298b1318147b97da
Pull Request resolved: #29157
@facebook-github-bot facebook-github-bot added the React Core Team Opened by a member of the React Core Team label May 18, 2024
@react-sizebot
Copy link

react-sizebot commented May 18, 2024

Comparing: bf046e8...6d4f5c8

Critical size changes

Includes critical production bundles, as well as any change greater than 2%:

Name +/- Base Current +/- gzip Base gzip Current gzip
oss-stable/react-dom/cjs/react-dom.production.js = 6.66 kB 6.66 kB = 1.82 kB 1.82 kB
oss-stable/react-dom/cjs/react-dom-client.production.js = 495.71 kB 495.71 kB = 88.78 kB 88.78 kB
oss-experimental/react-dom/cjs/react-dom.production.js = 6.67 kB 6.67 kB = 1.83 kB 1.83 kB
oss-experimental/react-dom/cjs/react-dom-client.production.js = 500.51 kB 500.51 kB = 89.47 kB 89.47 kB
facebook-www/ReactDOM-prod.classic.js = 592.86 kB 592.86 kB = 104.28 kB 104.28 kB
facebook-www/ReactDOM-prod.modern.js = 569.08 kB 569.08 kB = 100.69 kB 100.69 kB
test_utils/ReactAllWarnings.js Deleted 64.35 kB 0.00 kB Deleted 16.05 kB 0.00 kB

Significant size changes

Includes any change greater than 0.2%:

Expand to show
Name +/- Base Current +/- gzip Base gzip Current gzip
test_utils/ReactAllWarnings.js Deleted 64.35 kB 0.00 kB Deleted 16.05 kB 0.00 kB

Generated by 🚫 dangerJS against 6d4f5c8

…r merging"

In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request May 18, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: 675de0f4e14c4a93b2a63dd31363bac8a837dd2d
Pull Request resolved: #29157
@josephsavona josephsavona marked this pull request as draft May 18, 2024 00:23
…r merging"

In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request May 20, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: 6085abeddac8ced932fa36c8f111363fd18825a3
Pull Request resolved: #29157
…r merging"

In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request May 20, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: ab46a9ef50ef7342bdc09951bb8cd4d564ae1938
Pull Request resolved: #29157
…r merging"

In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request May 20, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: b2b2791ff9d93f618bfc063047766ca1d6f4215b
Pull Request resolved: #29157
…r merging"


In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for merging, we previously looked specifically at the type of the instruction whose lvalue is declaration. So if a scope declaration was `t0`, we'd look for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

NOTE: marking as draft because i have not yet verified all the fixtures changes (including checking that they are all have a snap fixture)

[ghstack-poisoned]
josephsavona added a commit that referenced this pull request May 20, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: f36a92a7519af6380fb70145795510f2220aa778
Pull Request resolved: #29157
@josephsavona josephsavona marked this pull request as ready for review May 20, 2024 22:06
…r merging"


In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for merging, we previously looked specifically at the type of the instruction whose lvalue is declaration. So if a scope declaration was `t0`, we'd look for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.


[ghstack-poisoned]
josephsavona added a commit that referenced this pull request May 21, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: 07d02d8d182b7fca75345a4f59f8a5b812868bcf
Pull Request resolved: #29157
Comment on lines +517 to +519
return [...scopeBlock.scope.declarations].some(([, decl]) =>
isAlwaysInvalidatingType(decl.identifier.type)
);
Copy link
Contributor

Choose a reason for hiding this comment

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

Haven't spent time trying to find a case in which this is an issue, but note that this also affects scopes that produce a "always-invalidating" type transitively, from a nested scope:

In this example, it seems that both scopes @0 and @1 are now "eligble for merging". It looks like the inner decl isn't always guaranteed to invalidate on outer_dep changes though. Maybe I'm missing something though

scope @0 deps=[inner_dep, outer_dep] decls=[inner, outer] {
  outer = makeValue(outer_dep);
  scope@1 deps=[inner_dep] decls=[inner] {
    inner = {inner_dep};
  }
  maybeWrite(outer);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting, i wasn't thinking about the nested case (more on that later) but about the transitive case. Something like: two values get merged into a single scope eg due to interleaving, x depends on a, y depends on b. Then some later value only depends on eg x, but that means it implicitly resets on changes to b as well:

scope @0 deps=[a, b] decls=[x, y] {
  // imagine this
  x = Array [  ]  
  y = Array [  ]
  x.push( a )
  y.push( b ) 
}
scope @1 deps=[y] decls=[z] {
  z = makeValue( y )
}

With this PR we'd now merge these scopes, and z will reset on changes to a even though y doesn't strictly depend on a. But...even if we didn't merge the scopes, y would still reset on changes to a, which would invalidate z anyway. So merging doesn't change the reset.

For the case you pointed out: the only way to have a subsequent scope depend on a value produced by an inner scope (that i can think of) is something like this:

function Component({ a, b, c }) {
  const x = [];
  let y;
  if (a) {
    y = [b];
  }
  x.push(c);

  const z = [y];

  return [z, x];
}

note y is declared outside the if, making it available for the subsequent z = [y] reference. That instruction sees the result of a phi node (due to the reassignment), and we don't currently infer types for phi nodes. So in practice, this case would not yet get merged.

I'll add this as a test case so we can find regressions.

…r merging"


In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for merging, we previously looked specifically at the type of the instruction whose lvalue is declaration. So if a scope declaration was `t0`, we'd look for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.


[ghstack-poisoned]
@josephsavona
Copy link
Contributor Author

Updated to add a test case confirming that the nested case memoizes as expected (ie does not inadvertently merge). I couldn't easily replicate that level of precision with useMemo, so i added a way to disable the non-forget execution in snap. This way we will get a test failure (not just a change to snapshot output) if we regress on the case you pointed out.

@josephsavona josephsavona merged commit 6d4f5c8 into gh/josephsavona/9/base May 23, 2024
49 of 50 checks passed
josephsavona added a commit that referenced this pull request May 23, 2024
In MergeReactiveScopesThatInvalidateTogether when deciding which scopes were eligible for mergin at all, we looked specifically at the instructions whose lvalue produces the declaration. So if a scope declaration was `t0`, we'd love for the instruction where `t0` was the lvalue and look at the instruction type to decide if it is eligible for merging.

Here, we use the inferred type instead (now that the inferred types support the same set of types of instructions we looked at before). This allows us to find more cases where scopes can be merged.

ghstack-source-id: 0e3e05f24ea0ac6e3c43046bc3e114f906747a04
Pull Request resolved: #29157
@josephsavona josephsavona deleted the gh/josephsavona/9/head branch May 23, 2024 16:45
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.

4 participants