-
Notifications
You must be signed in to change notification settings - Fork 46.8k
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] Patch logic for aligning scopes to non-value blocks #29891
Conversation
[ghstack-poisoned]
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
ghstack-source-id: 18fa632d430e36b62a103de48475d03fac4a915b Pull Request resolved: #29891
Comparing: 133ada7...baf7098 Critical size changesIncludes critical production bundles, as well as any change greater than 2%:
Significant size changesIncludes any change greater than 0.2%: Expand to show
|
…alue blocks" [ghstack-poisoned]
Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. ghstack-source-id: d9051284cec15caa1e0a6840cd20ead7acff122a Pull Request resolved: #29891
…-value blocks" Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. [ghstack-poisoned]
Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. ghstack-source-id: d9051284cec15caa1e0a6840cd20ead7acff122a Pull Request resolved: #29891
…-value blocks" Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` \# This case gets handled correctly ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ \# But not this one! ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. [ghstack-poisoned]
Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` \# This case gets handled correctly ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ \# But not this one! ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. ghstack-source-id: d9051284cec15caa1e0a6840cd20ead7acff122a Pull Request resolved: #29891
Tested by syncing internally. See that we previously had many bailouts in P1413569380. After syncing this change, there are no bailouts of this category in P1413721911. Also observe that no compilation output changed (P1414504972) |
Awesome, thanks for the thorough testing! I’ll review in the morning |
@@ -41,7 +38,7 @@ | |||
* │return s; │◄──┘ | |||
* └───────────┘ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
important question: what did you use to make this diagram?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I used https://asciiflow.com with a little manual editing!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I need to take another read through this to internalize and might have more feedback, but let's ship given the thorough testing.
function retainWhere_Set<T>( | ||
items: Set<T>, | ||
predicate: (item: T) => boolean | ||
): void { | ||
for (const item of items) { | ||
if (!predicate(item)) { | ||
items.delete(item); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: let's move to utils
description: `No node for block bb${block.id} (${block.kind})`, | ||
}); | ||
const startingId = block.instructions[0]?.id ?? block.terminal.id; | ||
retainWhere_Set(activeScopes, (scope) => scope.range.end >= startingId); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This should be >
, right? End is exclusive. I think this probably happens to work just because any mutating instruction within the inner blocks will be followed by a terminal successor s.t. it doesn't matter, but it's more technically correct to use >
here.
id: makeInstructionId(0), | ||
}; | ||
blockNodes.set(fn.body.entry, rootNode); | ||
const activeInnerBlockRanges: Array<{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The naming of this was confusing, since it's really tracking outer block -> fallthrough ranges. We use this to extend the range of inner blocks, but it doesn't store inner block ranges. Let's rename?
…-value blocks" Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` \# This case gets handled correctly ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ \# But not this one! ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. [ghstack-poisoned]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice job, the algorithm makes sense to me, especially w the renaming to clarify intent. Tests look great but one question on a case of missing memoization (?)
try { | ||
let thing = null; | ||
if (cond) { | ||
thing = makeObject_Primitives(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just curious, why do we lose memoization on this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh memoization was pruned because we didn't return item
. Just updated the fixture to return item
to avoid pruning!
…-value blocks" Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` \# This case gets handled correctly ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ \# But not this one! ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. [ghstack-poisoned]
Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g. ```js // source a(); if (...) { b(); } c(); // HIR bb0: a() if test=... consequent=bb1 fallthrough=bb2 bb1: b() goto bb2 bb2: c() // AlignReactiveScopesToBlockScopesHIR nodes Root node (maps to both bb0 and bb2) |- bb1 |- ... ``` There are two issues with the existing implementation: 1. Only scopes that overlap with the beginning of a block are aligned correctly. This is because the traversal does not store information about the block-fallthrough pair for scopes that begin *within* the block-fallthrough range. ``` \# This case gets handled correctly ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ \# But not this one! ┌──────────────┐ │ │ block start block end scope start scope end │ │ └───────────────┘ ``` 2. Only scopes that are directly used by a block is considered. See the `align-scopes-nested-block-structure` fixture for details. ghstack-source-id: 327dec5019483666f81c8156ac0c666ccad511b3 Pull Request resolved: #29891
Stack from ghstack (oldest at bottom):
Our previous logic for aligning scopes to block scopes constructs a tree of block and scope nodes. We ensured that blocks always mapped to the same node as their fallthroughs. e.g.
There are two issues with the existing implementation:
align-scopes-nested-block-structure
fixture for details.