mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Redo of an earlier (pre-OSS) PR to infer types of phi nodes. There are a few pieces to this:
1. Update InferTypes to infer the type of `phi.id.type`, not the unused `phi.type`.
2. Update the algorithm to verify that all the phi types are actually equal, not just have the same kind.
3. Handle circular types by removing the cycle.
However, that reveals another issue: InferMutableRanges currently infers the results of `Store` effects _after_ its fixpoint loop. That was fine when a Store could never occur on a phi (since they wouldn't have a type to get a function signature from). Now though, we can have Store effects occur on phis, and we need to ensure that this correctly updates the mutable range of the phi operands - recursively. See new test that fails without the fixpoint loop.
ghstack-source-id: 2e1b02844d
Pull Request resolved: https://github.com/facebook/react/pull/30796
101 lines
2.9 KiB
TypeScript
101 lines
2.9 KiB
TypeScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
import {HIRFunction, Identifier} from '../HIR/HIR';
|
|
import {inferAliases} from './InferAlias';
|
|
import {inferAliasForPhis} from './InferAliasForPhis';
|
|
import {inferAliasForStores} from './InferAliasForStores';
|
|
import {inferMutableLifetimes} from './InferMutableLifetimes';
|
|
import {inferMutableRangesForAlias} from './InferMutableRangesForAlias';
|
|
import {inferTryCatchAliases} from './InferTryCatchAliases';
|
|
|
|
export function inferMutableRanges(ir: HIRFunction): void {
|
|
// Infer mutable ranges for non fields
|
|
inferMutableLifetimes(ir, false);
|
|
|
|
// Calculate aliases
|
|
const aliases = inferAliases(ir);
|
|
/*
|
|
* Calculate aliases for try/catch, where any value created
|
|
* in the try block could be aliased to the catch param
|
|
*/
|
|
inferTryCatchAliases(ir, aliases);
|
|
|
|
/*
|
|
* Eagerly canonicalize so that if nothing changes we can bail out
|
|
* after a single iteration
|
|
*/
|
|
let prevAliases: Map<Identifier, Identifier> = aliases.canonicalize();
|
|
while (true) {
|
|
// Infer mutable ranges for aliases that are not fields
|
|
inferMutableRangesForAlias(ir, aliases);
|
|
|
|
// Update aliasing information of fields
|
|
inferAliasForStores(ir, aliases);
|
|
|
|
// Update aliasing information of phis
|
|
inferAliasForPhis(ir, aliases);
|
|
|
|
const nextAliases = aliases.canonicalize();
|
|
if (areEqualMaps(prevAliases, nextAliases)) {
|
|
break;
|
|
}
|
|
prevAliases = nextAliases;
|
|
}
|
|
|
|
// Re-infer mutable ranges for all values
|
|
inferMutableLifetimes(ir, true);
|
|
|
|
/**
|
|
* The second inferMutableLifetimes() call updates mutable ranges
|
|
* of values to account for Store effects. Now we need to update
|
|
* all aliases of such values to extend their ranges as well. Note
|
|
* that the store only mutates the the directly aliased value and
|
|
* not any of its inner captured references. For example:
|
|
*
|
|
* ```
|
|
* let y;
|
|
* if (cond) {
|
|
* y = [];
|
|
* } else {
|
|
* y = [{}];
|
|
* }
|
|
* y.push(z);
|
|
* ```
|
|
*
|
|
* The Store effect from the `y.push` modifies the values that `y`
|
|
* directly aliases - the two arrays from the if/else branches -
|
|
* but does not modify values that `y` "contains" such as the
|
|
* object literal or `z`.
|
|
*/
|
|
prevAliases = aliases.canonicalize();
|
|
while (true) {
|
|
inferMutableRangesForAlias(ir, aliases);
|
|
inferAliasForPhis(ir, aliases);
|
|
const nextAliases = aliases.canonicalize();
|
|
if (areEqualMaps(prevAliases, nextAliases)) {
|
|
break;
|
|
}
|
|
prevAliases = nextAliases;
|
|
}
|
|
}
|
|
|
|
function areEqualMaps<T>(a: Map<T, T>, b: Map<T, T>): boolean {
|
|
if (a.size !== b.size) {
|
|
return false;
|
|
}
|
|
for (const [key, value] of a) {
|
|
if (!b.has(key)) {
|
|
return false;
|
|
}
|
|
if (b.get(key) !== value) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|