perf(react_compiler): compare value set pointers before probing in state merge (#24805)

## Context

Value sets in the aliasing inference state are `Rc<FxHashSet<ValueId>>`,
shared between states rather than copied (#24117). When two states meet
at a join, the merge still walks every variable and probes one set
against the other looking for a value the current state doesn't have.
Most of the time both sides point at the same `Rc`, so that probe is
comparing a set to itself.

## This PR

Check `Rc::ptr_eq` before probing. If the pointers match there is
nothing to add, so we skip the scan. Both merge paths did this check in
slightly different ways, so I pulled it into one helper. The sets are
never mutated in place, which is what makes the pointer check safe.

### Performance

| workload | main | this PR | delta |
| ----------------------------------------------------- | -------: |
-------: | ------: |
| 3.6k-line control-flow heavy component, instructions | 23.762 B |
22.034 B | -7.3% |
| same file, wall time |   4.55 s |   4.13 s |     -9% |
| 16.5k-file real corpus, instructions (1 thread) | 50.135 B | 49.697 B
| -0.87% |

This PR was assisted by Claude Code.
This commit is contained in:
Marius Schulz authored and GitHub committed 2026-07-22 18:46:45 +08:00
1 parent 23b904ff09
commit 98f2ecbb10
1 file changed
+13 -16
@@ -562,20 +562,12 @@ impl InferenceState {
// Merge variables present in both
for (id, this_values) in &self.variables {
if let Some(other_values) = other.variables.get(id) {
let mut has_new = false;
for ov in other_values.iter() {
if !this_values.contains(ov) {
has_new = true;
break;
}
}
if has_new {
let nvars = next_variables.get_or_insert_with(|| self.variables.clone());
let merged: FxHashSet<ValueId> =
this_values.union(other_values).copied().collect();
nvars.insert(*id, Rc::new(merged));
}
if let Some(other_values) = other.variables.get(id)
&& contributes_new_value(this_values, other_values)
{
let nvars = next_variables.get_or_insert_with(|| self.variables.clone());
let merged: FxHashSet<ValueId> = this_values.union(other_values).copied().collect();
nvars.insert(*id, Rc::new(merged));
}
}
// Add variables only in other
@@ -625,8 +617,7 @@ impl InferenceState {
for (id, other_values) in &other.variables {
match self.variables.get(id) {
Some(this_values) => {
let has_new = other_values.iter().any(|ov| !this_values.contains(ov));
if has_new {
if contributes_new_value(this_values, other_values) {
// Build the union as a fresh set exactly like `merge`, so
// the resulting iteration order matches.
let merged: FxHashSet<ValueId> =
@@ -665,6 +656,12 @@ fn is_superset(a: &ReasonSet, b: &ReasonSet) -> bool {
a.0 & b.0 == b.0
}
/// Whether `other` holds a value that `this` lacks. The sets are immutable and
/// shared, so an identical allocation can contribute nothing new.
fn contributes_new_value(this: &Rc<FxHashSet<ValueId>>, other: &Rc<FxHashSet<ValueId>>) -> bool {
!Rc::ptr_eq(this, other) && other.iter().any(|value| !this.contains(value))
}
#[derive(Debug, Clone, Copy)]
enum MutateVariant {
Mutate,