-
Notifications
You must be signed in to change notification settings - Fork 46.9k
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] Maintain RPO and unique instruction ids when constructing scope terminals #30399
[compiler] Maintain RPO and unique instruction ids when constructing scope terminals #30399
Conversation
…g scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. [ghstack-poisoned]
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
…g scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 5a3e3dc25961ec39f3ff8788d9c59ad84ab8c6d0 Pull Request resolved: #30399
…constructing scope terminals" Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. [ghstack-poisoned]
…g scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 7fc6ee2492a24a1d6fa99795c4faec06bc81bd8a Pull Request resolved: #30399
…constructing scope terminals" Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. [ghstack-poisoned]
…g scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2127da749ff7d2f9f27144f6b6e484250389b0de Pull Request resolved: #30399
…nstructing scope terminals" Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. [ghstack-poisoned]
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2127da749ff7d2f9f27144f6b6e484250389b0de Pull Request resolved: #30399
/** | ||
* Step 5: | ||
* Fix scope and identifier ranges to account for renumbered instructions | ||
*/ |
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.
Hmm I don't think the range fixing logic covers all cases here. I wrote some validation in #30409, and we see quite a few cases in which identifiers have an attached mutable range that is neither (1) invalid e.g. end === start + 1
nor (2) a scope range.
There's roughly 3-4 classes:
- (fixed by [compiler][donotcommit] show differences between identifier + scope ranges #30409) when we merge scopes, we're currently not setting
identifier.mutableRange
to that of the scope. - There are lvalues for which we end up pruning the scope, but keep their previous mutable range (see method call example).
- Handful of other times we end up assigning a mutable range but not a scope: see props aliasing as a context variable and mutable ref examples
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 think currently, mutable ranges are referenced after this pass (see ValidateMemoizedEffectDeps
and ValidatePreserveExistingManualMemo
for examples).
Overall, I admit I'm a bit confused whether we need to ensure that identifier and scope ranges are always equivalent (i.e. pointing to the same object). My understanding is that this is unnecessary and we should instead deep copy when creating scopes in InferReactiveScopeVariables
. Currently, our aligning and merging logic extends the mutableRange
of some but not all identifiers
(cc @mvitousek as we were discussing a related topic yesterday)
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.
Yeah I was realizing after writing this that there were some other cases to cover. I think we need to just update all the passes that look at identifier or scope ranges (after this pass) to stop doing that and look at the scope’s effective range.
Alternatively I can do a more thorough tracking of how every scope needs to update its range before/after.
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.
update all the passes that look at identifier or scope ranges (after this pass) to stop doing that
Is this possible to do even before this pass? I.e., is it semantically necessary that identifiers report their own mutable ranges at all? It feels like it would be more consistent to always refer to an identifier's scope's mutable range. As it stands I am uncertain what it even means when they don't align to be honest. (Happy to split this into a separate conversation!)
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.
Well, it's necessary for identifiers to report their ranges so that we can construct scopes. But yeah, after that i don't see why we'd ever need to use identifier ranges.
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.
Addressed in #30428 - happy to squash that into this PR though
…nstructing scope terminals" Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. [ghstack-poisoned]
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: #30399
…ng scopes Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. [ghstack-poisoned]
…ng scopes Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: 9ad6f5dc1773ab0ef2b4cfd01be106c6d929be51 Pull Request resolved: #30428
… constructing scopes" Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. [ghstack-poisoned]
…ng scopes Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: 9d92032389d3cbca779823775baad441f06a7226 Pull Request resolved: #30428
…ble ranges after constructing scopes" Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. We already have fixtures that test for these edge cases: ## preserve-memo-validation/useMemo-dropped-infer-always-invalidating.ts This case passes the validation because the way Forget constructs the scopes matches what the user had: there is an unmemoized dependency (x, whose scope is pruned bc it has a hook call) whose range ends before the useMemo, and the useMemo is preserved: ```js const x = []; useHook(); x.push(props); return useMemo(() => [x], [x]); ``` Concretely: there is a `StartMemoize deps=x0`, but scope `0` is pruned so it's allowed. ## preserve-memo-validation/error.false-positive-useMemo-infer-mutate-deps.ts This case fails the validation because Forget constructs the scopes differently than the user code: the construction of `val` is part of the scope for `identity(val)` since Forget infers that `val` may be mutated there. So rather than `val` appearing as an unmemoized dependency of the memo block, it becomes part of the memo block. So we correctly reject it. ```js const val = [1, 2, 3]; return useMemo(() => { return identity(val); }, [val]); ``` Concretely, there is a `StartMemoize deps=val0`, but the StartMemoize instruction is within scope `0`, which hasn't completed yet, so it can't be a valid dependency and we reject it. # Sync Results I synced this stack: * Minimal changes to compilation output: P1491221074. The pattern is consistent: we previously had false positives where we reported that values that came from pruned scopes (due to hooks) were "unmemoized", whereas the new logic accounts for this with the pruned scope check. * No changes to lint output [ghstack-poisoned]
… constructing scopes" Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. We already have fixtures that test for these edge cases: ## preserve-memo-validation/useMemo-dropped-infer-always-invalidating.ts This case passes the validation because the way Forget constructs the scopes matches what the user had: there is an unmemoized dependency (x, whose scope is pruned bc it has a hook call) whose range ends before the useMemo, and the useMemo is preserved: ```js const x = []; useHook(); x.push(props); return useMemo(() => [x], [x]); ``` Concretely: there is a `StartMemoize deps=x0`, but scope `0` is pruned so it's allowed. ## preserve-memo-validation/error.false-positive-useMemo-infer-mutate-deps.ts This case fails the validation because Forget constructs the scopes differently than the user code: the construction of `val` is part of the scope for `identity(val)` since Forget infers that `val` may be mutated there. So rather than `val` appearing as an unmemoized dependency of the memo block, it becomes part of the memo block. So we correctly reject it. ```js const val = [1, 2, 3]; return useMemo(() => { return identity(val); }, [val]); ``` Concretely, there is a `StartMemoize deps=val0`, but the StartMemoize instruction is within scope `0`, which hasn't completed yet, so it can't be a valid dependency and we reject it. # Sync Results I synced this stack: * Minimal changes to compilation output: P1491221074. The pattern is consistent: we previously had false positives where we reported that values that came from pruned scopes (due to hooks) were "unmemoized", whereas the new logic accounts for this with the pruned scope check. * No changes to lint output [ghstack-poisoned]
…ng scopes Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: d7126aff6b733201d0f368180655cfea40017fa7 Pull Request resolved: #30428
…ble ranges after constructing scopes" Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. We already have fixtures that test for these edge cases: ## preserve-memo-validation/useMemo-dropped-infer-always-invalidating.ts This case passes the validation because the way Forget constructs the scopes matches what the user had: there is an unmemoized dependency (x, whose scope is pruned bc it has a hook call) whose range ends before the useMemo, and the useMemo is preserved: ```js const x = []; useHook(); x.push(props); return useMemo(() => [x], [x]); ``` Concretely: there is a `StartMemoize deps=x0`, but scope `0` is pruned so it's allowed. ## preserve-memo-validation/error.false-positive-useMemo-infer-mutate-deps.ts This case fails the validation because Forget constructs the scopes differently than the user code: the construction of `val` is part of the scope for `identity(val)` since Forget infers that `val` may be mutated there. So rather than `val` appearing as an unmemoized dependency of the memo block, it becomes part of the memo block. So we correctly reject it. ```js const val = [1, 2, 3]; return useMemo(() => { return identity(val); }, [val]); ``` Concretely, there is a `StartMemoize deps=val0`, but the StartMemoize instruction is within scope `0`, which hasn't completed yet, so it can't be a valid dependency and we reject it. # Sync Results I synced this stack: * Minimal changes to compilation output: P1491221074. The pattern is consistent: we previously had false positives where we reported that values that came from pruned scopes (due to hooks) were "unmemoized", whereas the new logic accounts for this with the pruned scope check. See the fixture added in the previous PR in the stack, and how it becomes compiled here. * No changes to lint output [ghstack-poisoned]
… constructing scopes" Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. We already have fixtures that test for these edge cases: ## preserve-memo-validation/useMemo-dropped-infer-always-invalidating.ts This case passes the validation because the way Forget constructs the scopes matches what the user had: there is an unmemoized dependency (x, whose scope is pruned bc it has a hook call) whose range ends before the useMemo, and the useMemo is preserved: ```js const x = []; useHook(); x.push(props); return useMemo(() => [x], [x]); ``` Concretely: there is a `StartMemoize deps=x0`, but scope `0` is pruned so it's allowed. ## preserve-memo-validation/error.false-positive-useMemo-infer-mutate-deps.ts This case fails the validation because Forget constructs the scopes differently than the user code: the construction of `val` is part of the scope for `identity(val)` since Forget infers that `val` may be mutated there. So rather than `val` appearing as an unmemoized dependency of the memo block, it becomes part of the memo block. So we correctly reject it. ```js const val = [1, 2, 3]; return useMemo(() => { return identity(val); }, [val]); ``` Concretely, there is a `StartMemoize deps=val0`, but the StartMemoize instruction is within scope `0`, which hasn't completed yet, so it can't be a valid dependency and we reject it. # Sync Results I synced this stack: * Minimal changes to compilation output: P1491221074. The pattern is consistent: we previously had false positives where we reported that values that came from pruned scopes (due to hooks) were "unmemoized", whereas the new logic accounts for this with the pruned scope check. See the fixture added in the previous PR in the stack, and how it becomes compiled here. * No changes to lint output [ghstack-poisoned]
…ng scopes Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: af5bfd88553de3e30621695f9d139c4dc5efb997 Pull Request resolved: #30428
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: #30399
…ng scopes Addresses discussion at #30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: af5bfd88553de3e30621695f9d139c4dc5efb997 Pull Request resolved: #30428
…ng scopes Addresses discussion at facebook#30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: af5bfd88553de3e30621695f9d139c4dc5efb997 Pull Request resolved: facebook#30428
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: facebook#30399
…ng scopes Addresses discussion at facebook#30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: af5bfd88553de3e30621695f9d139c4dc5efb997 Pull Request resolved: facebook#30428
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: facebook#30399
…ng scopes Addresses discussion at facebook#30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: af5bfd88553de3e30621695f9d139c4dc5efb997 Pull Request resolved: facebook#30428
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: facebook#30399
…ng scopes Addresses discussion at facebook#30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: af5bfd88553de3e30621695f9d139c4dc5efb997 Pull Request resolved: facebook#30428
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: facebook#30399
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: facebook#30399
…scope terminals Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this. This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where `markPredecessors()` handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids. ghstack-source-id: 2a99df02ac9d125b202cae369e2dc4dccefb0625 Pull Request resolved: facebook#30399
…ng scopes Addresses discussion at facebook#30399 (comment). Once we've constructed scopes it's invalid to use identifier mutable ranges. The only places we can do this which i can find are ValidateMemoizedEffectDeps (which is already flawed and disabled by default) and ValidatePreservedManualMemoization. I added a todo to the former, and fixed up the latter. The idea of the fix is that for StartMemo dependencies, if they needed to be memoized (identifier.scope != null) then that scope should exist and should have already completed. If they didn't need a scope or can't have one created (eg their range spans a hook), then their scope would be pruned. So if the scope is set, not pruned, and not completed, then it's an error. For declarations (FinishMemo) the existing logic applies unchanged. ghstack-source-id: af5bfd88553de3e30621695f9d139c4dc5efb997 Pull Request resolved: facebook#30428
Stack from ghstack (oldest at bottom):
Later passes may rely on HIR invariants such as blocks being in RPO or instructions having unique, ascending InstructionIds. However, BuildReactiveScopeTerminalsHIR doesn't currently gurantee this.
This PR updates that pass to first restore RPO, fixup predecessors (the previous logic tried to do this but failed on unreachable blocks, where
markPredecessors()
handles that case), and renumber instructions. Then it walks instructions and scopes to update identifier and scope ranges given the new instruction ids.