Skip to content

Commit 635533b

Browse files
committed
manually implement Hash for DefId
This also reorders the fields to reduce the assembly operations for hashing and changes two UI tests that depended on the former ordering because of hashmap iteration order.
1 parent abba5ed commit 635533b

File tree

3 files changed

+48
-22
lines changed

3 files changed

+48
-22
lines changed

Diff for: compiler/rustc_span/src/def_id.rs

+31-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_macros::HashStable_Generic;
77
use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
88
use std::borrow::Borrow;
99
use std::fmt;
10+
use std::hash::{Hash, Hasher};
1011

1112
rustc_index::newtype_index! {
1213
pub struct CrateNum {
@@ -146,9 +147,6 @@ impl StableCrateId {
146147
/// Computes the stable ID for a crate with the given name and
147148
/// `-Cmetadata` arguments.
148149
pub fn new(crate_name: &str, is_exe: bool, mut metadata: Vec<String>) -> StableCrateId {
149-
use std::hash::Hash;
150-
use std::hash::Hasher;
151-
152150
let mut hasher = StableHasher::new();
153151
crate_name.hash(&mut hasher);
154152

@@ -205,10 +203,38 @@ impl<D: Decoder> Decodable<D> for DefIndex {
205203
/// index and a def index.
206204
///
207205
/// You can create a `DefId` from a `LocalDefId` using `local_def_id.to_def_id()`.
208-
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Copy)]
206+
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Copy)]
207+
// On below-64 bit systems we can simply use the derived `Hash` impl
208+
#[cfg_attr(not(target_pointer_width = "64"), derive(Hash))]
209+
// Note that the order is essential here, see below why
209210
pub struct DefId {
210-
pub krate: CrateNum,
211211
pub index: DefIndex,
212+
pub krate: CrateNum,
213+
}
214+
215+
// On 64-bit systems, we can hash the whole `DefId` as one `u64` instead of two `u32`s. This
216+
// improves performance without impairing `FxHash` quality. So the below code gets compiled to a
217+
// noop on little endian systems because the memory layout of `DefId` is as follows:
218+
//
219+
// ```
220+
// +-1--------------31-+-32-------------63-+
221+
// ! index ! krate !
222+
// +-------------------+-------------------+
223+
// ```
224+
//
225+
// The order here has direct impact on `FxHash` quality because we have far more `DefIndex` per
226+
// crate than we have `Crate`s within one compilation. Or in other words, this arrangement puts
227+
// more entropy in the low bits than the high bits. The reason this matters is that `FxHash`, which
228+
// is used throughout rustc, has problems distributing the entropy from the high bits, so reversing
229+
// the order would lead to a large number of collisions and thus far worse performance.
230+
//
231+
// On 64-bit big-endian systems, this compiles to a 64-bit rotation by 32 bits, which is still
232+
// faster than another `FxHash` round.
233+
#[cfg(target_pointer_width = "64")]
234+
impl Hash for DefId {
235+
fn hash<H: Hasher>(&self, h: &mut H) {
236+
(((self.krate.as_u32() as u64) << 32) | (self.index.as_u32() as u64)).hash(h)
237+
}
212238
}
213239

214240
impl DefId {

Diff for: src/test/ui/coherence/coherence-orphan.stderr

+11-11
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
2-
--> $DIR/coherence-orphan.rs:17:1
3-
|
4-
LL | impl !Send for Vec<isize> { }
5-
| ^^^^^^^^^^^^^^^----------
6-
| | |
7-
| | `Vec` is not defined in the current crate
8-
| impl doesn't use only types from inside the current crate
9-
|
10-
= note: define and implement a trait or new type instead
11-
121
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
132
--> $DIR/coherence-orphan.rs:10:1
143
|
@@ -21,6 +10,17 @@ LL | impl TheTrait<usize> for isize { }
2110
|
2211
= note: define and implement a trait or new type instead
2312

13+
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
14+
--> $DIR/coherence-orphan.rs:17:1
15+
|
16+
LL | impl !Send for Vec<isize> { }
17+
| ^^^^^^^^^^^^^^^----------
18+
| | |
19+
| | `Vec` is not defined in the current crate
20+
| impl doesn't use only types from inside the current crate
21+
|
22+
= note: define and implement a trait or new type instead
23+
2424
error: aborting due to 2 previous errors
2525

2626
For more information about this error, try `rustc --explain E0117`.

Diff for: src/test/ui/methods/method-ambig-two-traits-cross-crate.stderr

+6-6
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,20 @@ error[E0034]: multiple applicable items in scope
44
LL | fn main() { 1_usize.me(); }
55
| ^^ multiple `me` found
66
|
7-
note: candidate #1 is defined in an impl of the trait `Me2` for the type `usize`
7+
= note: candidate #1 is defined in an impl of the trait `Me` for the type `usize`
8+
note: candidate #2 is defined in an impl of the trait `Me2` for the type `usize`
89
--> $DIR/method-ambig-two-traits-cross-crate.rs:10:22
910
|
1011
LL | impl Me2 for usize { fn me(&self) -> usize { *self } }
1112
| ^^^^^^^^^^^^^^^^^^^^^
12-
= note: candidate #2 is defined in an impl of the trait `Me` for the type `usize`
1313
help: disambiguate the associated function for candidate #1
1414
|
15-
LL | fn main() { Me2::me(&1_usize); }
16-
| ~~~~~~~~~~~~~~~~~
17-
help: disambiguate the associated function for candidate #2
18-
|
1915
LL | fn main() { Me::me(&1_usize); }
2016
| ~~~~~~~~~~~~~~~~
17+
help: disambiguate the associated function for candidate #2
18+
|
19+
LL | fn main() { Me2::me(&1_usize); }
20+
| ~~~~~~~~~~~~~~~~~
2121

2222
error: aborting due to previous error
2323

0 commit comments

Comments
 (0)