Skip to content

Merge FallbackImmediate and Fixpoint code paths#1063

Merged
MichaReiser merged 4 commits intosalsa-rs:masterfrom
MichaReiser:integrate-fallback-immediate-into-fixpoint
Jan 22, 2026
Merged

Merge FallbackImmediate and Fixpoint code paths#1063
MichaReiser merged 4 commits intosalsa-rs:masterfrom
MichaReiser:integrate-fallback-immediate-into-fixpoint

Conversation

@MichaReiser
Copy link
Contributor

@MichaReiser MichaReiser commented Jan 11, 2026

Summary

Spinned out of #1059 (comment)

This PR removes most special casing of FallbackImmediate and, instead, integrates it into the normal Fixpoint code. Not only should this fix the caching issues that FallbackImmediate has today, it also reduces overall complexity and makes it easier to make changes to how we handle cycles in Salsa (no need to update two completely different implementations).

This PR preserves the existing FallbackImmediate behavior where all heads participating in a query use their fallback value (and not just the cycle heads). I now believe that this behavior is essential for deterministic outputs, as pointed out by Carl in #798 (comment)

See

// If we're in the middle of a cycle and we have a fallback, use it instead.
// Cycle participants that don't have a fallback will be discarded in
// `validate_provisional()`.
let cycle_heads = std::mem::take(cycle_heads);
let active_query =
zalsa_local.push_query(database_key_index, IterationCount::initial());
new_value = C::cycle_initial(db, id, C::id_to_input(zalsa, id));
completed_query = active_query.pop();
// We need to set `cycle_heads` and `verified_final` because it needs to propagate to the callers.
// When verifying this, we will see we have fallback and mark ourselves verified.
completed_query.revisions.set_cycle_heads(cycle_heads);
completed_query.revisions.verified_final = AtomicBool::new(false);

This PR is based on top of #1062

One difference worth mentioning is that we now overapproximate the dependencies for queries with cycle_result, keeping all dependencies rather than just those up to the initial cycle. This is fine as it is, and I'm not aware of an easy way to remember when we hit the cycle and which dependencies we've seen up to this point.

@netlify
Copy link

netlify bot commented Jan 11, 2026

Deploy Preview for salsa-rs canceled.

Name Link
🔨 Latest commit df8855b
🔍 Latest deploy log https://app.netlify.com/projects/salsa-rs/deploys/6972232adfa53f000882cfa2

@MichaReiser MichaReiser changed the title integrate fallback immediate into fixpoint Merge FallbackImmediate and Fixpoint code paths Jan 11, 2026
@MichaReiser MichaReiser marked this pull request as ready for review January 11, 2026 10:34
// For Fixpoint, ask the recovery function what value to use and check convergence.
let value_converged = if C::CYCLE_STRATEGY == CycleRecoveryStrategy::FallbackImmediate {
// Use the fallback value instead of the computed value.
new_value = C::cycle_initial(db, id, C::id_to_input(zalsa, id));
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I opted for calling C::cycle_initial here again because it is easier than Cloning (would require a new method on C that panics for all other recovery strategies or we make CycleRecoverStrategy a trait). But I (or someone else ;)) can look into cloning if that's desired or, what would even be better, adding an API that allows us to "take" a value from a Memo (which would also allow us to pass an owned value to recover_from_cycle)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Having a way to take the value would also be nice in fetch_cold_cycle where we need to call remove_all_except (requiring Atomics) to remove the cycle heads from the previous iteration because we can't create a new memo with the same value but slightly different metadata.

I tried to implement this once but it touches on Salsa parts that I'm not at all familiar with, which is why I ended up going for atomics

// check if there's a provisional value for this query
// Note we don't `validate_may_be_provisional` the memo here as we want to reuse an
// existing provisional memo if it exists
let memo_guard = self.get_memo_from_table_for(zalsa, id, memo_ingredient_index);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I moved this into the cycle handling branch to help the compiler eliminate it for queries without cycle handling

@MichaReiser MichaReiser requested a review from Veykril January 11, 2026 10:57
@MichaReiser
Copy link
Contributor Author

CC: @ChayimFriedman2

}

#[salsa::tracked]
#[salsa::tracked(cycle_result=two_queries_cycle_result)]
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The test works regardless of whether we add cycle handling here but adding cycle handling is "more correct" because two_queries2 requires cycle handling if it is called before two_queries1

@MichaReiser MichaReiser force-pushed the integrate-fallback-immediate-into-fixpoint branch from a0ea2ed to 5b3b99b Compare January 19, 2026 14:32
@MichaReiser MichaReiser requested a review from carljm January 19, 2026 14:32
@MichaReiser MichaReiser force-pushed the integrate-fallback-immediate-into-fixpoint branch from 5b3b99b to 5c376e2 Compare January 21, 2026 14:20
@codspeed-hq
Copy link

codspeed-hq bot commented Jan 21, 2026

Merging this PR will degrade performance by 6.91%

❌ 2 regressed benchmarks
✅ 11 untouched benchmarks

⚠️ Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Benchmark BASE HEAD Efficiency
amortized[InternedInput] 2.2 µs 2.3 µs -5.12%
new[InternedInput] 4.3 µs 4.6 µs -6.91%

Comparing MichaReiser:integrate-fallback-immediate-into-fixpoint (df8855b) with master (93f1c6a)

Open in CodSpeed

Copy link
Contributor

@carljm carljm left a comment

Choose a reason for hiding this comment

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

Looks good to me, thank you.

@MichaReiser MichaReiser force-pushed the integrate-fallback-immediate-into-fixpoint branch from 5c376e2 to e82b78e Compare January 22, 2026 13:12
@MichaReiser MichaReiser enabled auto-merge January 22, 2026 13:20
@MichaReiser MichaReiser added this pull request to the merge queue Jan 22, 2026
Merged via the queue into salsa-rs:master with commit 186f83b Jan 22, 2026
13 of 14 checks passed
@MichaReiser MichaReiser deleted the integrate-fallback-immediate-into-fixpoint branch January 22, 2026 13:34
@github-actions github-actions bot mentioned this pull request Jan 22, 2026
@ChayimFriedman2
Copy link
Contributor

We have reports (rust-lang/rust-analyzer#21610 (comment)) that this PR made rust-analyzer constantly use high CPU and emit Salsa warnings.

@ibraheemdev
Copy link
Member

Micha is out until March, and I unfortunately am not very familiar with usage of FallbackImmediate. I believe #1061 depended on this PR, so it might not be very easy to revert.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants