Skip to content
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

Only use the new node hashmap for anonymous nodes. #112469

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

cjgillot
Copy link
Contributor

@cjgillot cjgillot commented Jun 9, 2023

Split from #109050

The duplication check is made opt-in with -Zincremental-verify-ich.

r? @michaelwoerister

@rustbot rustbot added A-query-system Area: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html) S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Jun 9, 2023
@rustbot
Copy link
Collaborator

rustbot commented Jun 9, 2023

Some changes occurred in compiler/rustc_codegen_cranelift

cc @bjorn3

Copy link
Member

@michaelwoerister michaelwoerister left a comment

Choose a reason for hiding this comment

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

Thanks for splitting this out into a more limited set of changes, @cjgillot! That should make it easier to make progress.

I've left a few comments below where I think changes are needed to preserve the semantics of the checks or to make things easier to understand. Otherwise the PR looks good to me, except for one thing:

The current version does not contain the lightweight check for duplicate dep-nodes during graph deserialization. That means that we would basically stop detecting duplicate dep-nodes altogether because the more accurate checks are now behind a flag that nobody uses. As long as (non-anonymous) dep-nodes are still defined to have a 1:1 relationship to query invocations, we want to know about DepNode collisions, right?

I'm not sure what's the best reaction to a DepNode collision being detected. Some options are:

  • Ignore the incremental cache and emit a warning, encouraging re-running with -Zverify-incremental-ich and creating a bug report.
  • Same as above, but only on nightly. Silently ignore the cache on non-nightly.
  • Instead of a warning, make the compiler ICE with message mentioning -Zverify-incremental-ich.
  • ICE on nightly, silently ignore cache on stable.

compiler/rustc_query_system/src/dep_graph/graph.rs Outdated Show resolved Hide resolved
compiler/rustc_query_system/src/dep_graph/graph.rs Outdated Show resolved Hide resolved
compiler/rustc_query_system/src/dep_graph/graph.rs Outdated Show resolved Hide resolved
compiler/rustc_query_system/src/dep_graph/graph.rs Outdated Show resolved Hide resolved
compiler/rustc_query_system/src/dep_graph/graph.rs Outdated Show resolved Hide resolved
@michaelwoerister michaelwoerister added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jun 16, 2023
@michaelwoerister
Copy link
Member

I think this looks great now!

I'd still like us to have some form of the duplicates check during deserialization, so that the signal on hash collisions doesn't go completely silent. However, I realize that it's kind of a hard UX question that I don't want this to be blocked on.

What do you think about this:

  1. We add the check and if a duplicate is detected, we just silently ignore the cache. That way we are on the safe side correctness-wise.
  2. We merge the PR with that behavior.
  3. We (or just I) re-visit the question of how to deal with detected duplicates once I'm back from vacation in two weeks.

@bors
Copy link
Contributor

bors commented Sep 7, 2023

☔ The latest upstream changes (presumably #110050) made this pull request unmergeable. Please resolve the merge conflicts.

@@ -1163,7 +1196,7 @@ impl<K: DepKind> CurrentDepGraph<K> {
record_graph,
record_stats,
)),
new_node_to_index: Sharded::new(|| {
anon_node_to_index: Sharded::new(|| {
FxHashMap::with_capacity_and_hasher(
new_node_count_estimate / sharded::SHARDS,
Copy link
Contributor

Choose a reason for hiding this comment

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

This size estimate is probably a bit off now, but I'm not sure what we'd replace it with.

@@ -1200,20 +1243,19 @@ impl<K: DepKind> CurrentDepGraph<K> {
edges: EdgesVec,
current_fingerprint: Fingerprint,
) -> DepNodeIndex {
Copy link
Contributor

Choose a reason for hiding this comment

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

This function should probably be renamed alloc_new_node as it no longer does interning.

if let Some(ref nodes_newly_allocated_in_current_session) =
self.nodes_newly_allocated_in_current_session
{
if !nodes_newly_allocated_in_current_session.lock().insert(key) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd put cold_path around this.

@Zoxc
Copy link
Contributor

Zoxc commented Sep 25, 2023

I have a branch that builds the index on-demand, which means by the time duplicates are detected, we can no longer ignore the incremental cache. That would only be compatible with the always ICE option.

@cjgillot
Copy link
Contributor Author

I have a branch that builds the index on-demand, which means by the time duplicates are detected, we can no longer ignore the incremental cache. That would only be compatible with the always ICE option.

That would also be compatible with the "drop duplicates from index" option, wouldn't it?

@Zoxc
Copy link
Contributor

Zoxc commented Sep 26, 2023

Yeah.

@michaelwoerister
Copy link
Member

I noticed that dropping duplicates from the index might not catch all cases: If the hash collision occurs between a node from the previous graph and a node that is newly allocated in the current session, looking up the new node would wrongly find the previous node, right?

I'm working on a proof of concept implementation of an alternative approach (inspired by @cjgillot's work in #109050), which does not have DepNodes and result fingerprints at all, except for reconstructible DepNodes (i.e. DefPathHashes, HirIds, etc). I hope to have the POC implementation ready later this week. It won't be nice enough to be merged but it should allow us to do a perf run so we can see if that approach is viable at all. If perf doesn't rule it out and nobody finds a fatal flaw in the approach, we could discuss in the wg-incr-comp Zulip, how it might fit with other in-progress work.

@michaelwoerister
Copy link
Member

michaelwoerister commented Dec 7, 2023

OK, I ran the experiment mentioned above in #118667 and I think we can safely call that a failure 🙂

Given that the quick check during deserialization also is not as complete as we thought1, I think it's time to re-evaluate. I'm proposing the following:

  • We default to not building the nodes_newly_allocated_in_current_session map for the production compiler, so that regular users don't have to pay the memory consumption cost of that.
  • When debug assertions are enabled, we do default to building the map and doing the associated assertions. But they still can be turned off by -Zincremental_verify_ich=no or forced with -Zincremental_verify_ich=yes, even in a production compiler.
  • In a separate PR, we add an additional check for query key hash collisions, that can be enabled independently of incr. comp. being turned on or not. We can then enable the check for various kinds of CI builds to get good coverage. This should help catch faulty HashStable implementations before they get merged. The check would also be off by default. It would just go through each query table separately, checking if the stable hashes of query keys collide.2 I can take care of implementing this and it would not block this PR.
  • We could still do the cheap collision check during deserialization and just silently throw away the cache if there is a collision. It would not catch everything but if it does find something, the cache is definitely corrupted in some way. I'm not sure about this point -- but with the additional checking described above, I don't think it is as important as before.

What do you think?

Footnotes

  1. Correct me if I'm wrong about this.

  2. We could turn the check off for any queries that are declared anonymous. That should be safe, as long as the query key type does not also occur in a query result type, where the hash collision would also cause trouble.

@Zoxc
Copy link
Contributor

Zoxc commented Mar 9, 2024

@cjgillot Do you mind if I pick up this PR?

@Zoxc
Copy link
Contributor

Zoxc commented Mar 9, 2024

  • In a separate PR, we add an additional check for query key hash collisions, that can be enabled independently of incr. comp. being turned on or not. We can then enable the check for various kinds of CI builds to get good coverage.

It seems this would be a good idea anyway as seems to be quite a few hash issues sneaking through CI already: https://github.com/rust-lang/rust/issues?q=is%3Aissue+is%3Aopen+forcing+query+with+already+existing

@Zoxc
Copy link
Contributor

Zoxc commented Mar 9, 2024

I noticed that dropping duplicates from the index might not catch all cases

I don't think it would catch less cases that we currently do, it just catches them later. It also does not catch all cases. We're trying to catch mismatches between Eq and HashStable impls for query keys, but we only have the HashStable result from the previous session, limiting us to catching mismatches from the current session without also storing the query keys in the incremental cache.

bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 9, 2024
Verify that query keys result in unique dep nodes

This implements checking that query keys result into unique dep nodes as mentioned in rust-lang#112469.

We could do a perf check to see how expensive this is.

r? `@michaelwoerister`
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 10, 2024
Verify that query keys result in unique dep nodes

This implements checking that query keys result into unique dep nodes as mentioned in rust-lang#112469.

We could do a perf check to see how expensive this is.

r? `@michaelwoerister`
bors added a commit to rust-lang-ci/rust that referenced this pull request Mar 13, 2024
…rister

Verify that query keys result in unique dep nodes

This implements checking that query keys result into unique dep nodes as mentioned in rust-lang#112469.

We could do a perf check to see how expensive this is.

r? `@michaelwoerister`
github-actions bot pushed a commit to rust-lang/miri that referenced this pull request Mar 14, 2024
Verify that query keys result in unique dep nodes

This implements checking that query keys result into unique dep nodes as mentioned in rust-lang/rust#112469.

We could do a perf check to see how expensive this is.

r? `@michaelwoerister`
lnicola pushed a commit to lnicola/rust-analyzer that referenced this pull request Apr 7, 2024
Verify that query keys result in unique dep nodes

This implements checking that query keys result into unique dep nodes as mentioned in rust-lang/rust#112469.

We could do a perf check to see how expensive this is.

r? `@michaelwoerister`
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-query-system Area: The rustc query system (https://rustc-dev-guide.rust-lang.org/query.html) S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants