]> git.lizzy.rs Git - rust.git/blob - src/librustc_incremental/persist/hash.rs
ICH: Use 128-bit Blake2b hash instead of 64-bit SipHash for incr. comp. fingerprints.
[rust.git] / src / librustc_incremental / persist / hash.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use rustc::dep_graph::DepNode;
12 use rustc::hir::def_id::{CrateNum, DefId};
13 use rustc::hir::svh::Svh;
14 use rustc::ty::TyCtxt;
15 use rustc_data_structures::fnv::FnvHashMap;
16 use rustc_data_structures::flock;
17 use rustc_serialize::Decodable;
18 use rustc_serialize::opaque::Decoder;
19
20 use IncrementalHashesMap;
21 use ich::Fingerprint;
22 use super::data::*;
23 use super::fs::*;
24 use super::file_format;
25
26 pub struct HashContext<'a, 'tcx: 'a> {
27     pub tcx: TyCtxt<'a, 'tcx, 'tcx>,
28     incremental_hashes_map: &'a IncrementalHashesMap,
29     item_metadata_hashes: FnvHashMap<DefId, Fingerprint>,
30     crate_hashes: FnvHashMap<CrateNum, Svh>,
31 }
32
33 impl<'a, 'tcx> HashContext<'a, 'tcx> {
34     pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>,
35                incremental_hashes_map: &'a IncrementalHashesMap)
36                -> Self {
37         HashContext {
38             tcx: tcx,
39             incremental_hashes_map: incremental_hashes_map,
40             item_metadata_hashes: FnvHashMap(),
41             crate_hashes: FnvHashMap(),
42         }
43     }
44
45     pub fn is_hashable(dep_node: &DepNode<DefId>) -> bool {
46         match *dep_node {
47             DepNode::Krate |
48             DepNode::Hir(_) => true,
49             DepNode::MetaData(def_id) => !def_id.is_local(),
50             _ => false,
51         }
52     }
53
54     pub fn hash(&mut self, dep_node: &DepNode<DefId>) -> Option<Fingerprint> {
55         match *dep_node {
56             DepNode::Krate => {
57                 Some(self.incremental_hashes_map[dep_node])
58             }
59
60             // HIR nodes (which always come from our crate) are an input:
61             DepNode::Hir(def_id) => {
62                 assert!(def_id.is_local(),
63                         "cannot hash HIR for non-local def-id {:?} => {:?}",
64                         def_id,
65                         self.tcx.item_path_str(def_id));
66
67                 assert!(!self.tcx.map.is_inlined_def_id(def_id),
68                         "cannot hash HIR for inlined def-id {:?} => {:?}",
69                         def_id,
70                         self.tcx.item_path_str(def_id));
71
72                 Some(self.incremental_hashes_map[dep_node])
73             }
74
75             // MetaData from other crates is an *input* to us.
76             // MetaData nodes from *our* crates are an *output*; we
77             // don't hash them, but we do compute a hash for them and
78             // save it for others to use.
79             DepNode::MetaData(def_id) if !def_id.is_local() => {
80                 Some(self.metadata_hash(def_id))
81             }
82
83             _ => {
84                 // Other kinds of nodes represent computed by-products
85                 // that we don't hash directly; instead, they should
86                 // have some transitive dependency on a Hir or
87                 // MetaData node, so we'll just hash that
88                 None
89             }
90         }
91     }
92
93     fn metadata_hash(&mut self, def_id: DefId) -> Fingerprint {
94         debug!("metadata_hash(def_id={:?})", def_id);
95
96         assert!(!def_id.is_local());
97         loop {
98             // check whether we have a result cached for this def-id
99             if let Some(&hash) = self.item_metadata_hashes.get(&def_id) {
100                 debug!("metadata_hash: def_id={:?} hash={:?}", def_id, hash);
101                 return hash;
102             }
103
104             // check whether we did not find detailed metadata for this
105             // krate; in that case, we just use the krate's overall hash
106             if let Some(&svh) = self.crate_hashes.get(&def_id.krate) {
107                 debug!("metadata_hash: def_id={:?} crate_hash={:?}", def_id, svh);
108
109                 // micro-"optimization": avoid a cache miss if we ask
110                 // for metadata from this particular def-id again.
111                 let fingerprint = svh_to_fingerprint(svh);
112                 self.item_metadata_hashes.insert(def_id, fingerprint);
113
114                 return fingerprint;
115             }
116
117             // otherwise, load the data and repeat.
118             self.load_data(def_id.krate);
119             assert!(self.crate_hashes.contains_key(&def_id.krate));
120         }
121     }
122
123     fn load_data(&mut self, cnum: CrateNum) {
124         debug!("load_data(cnum={})", cnum);
125
126         let svh = self.tcx.sess.cstore.crate_hash(cnum);
127         let old = self.crate_hashes.insert(cnum, svh);
128         debug!("load_data: svh={}", svh);
129         assert!(old.is_none(), "loaded data for crate {:?} twice", cnum);
130
131         if let Some(session_dir) = find_metadata_hashes_for(self.tcx, cnum) {
132             debug!("load_data: session_dir={:?}", session_dir);
133
134             // Lock the directory we'll be reading  the hashes from.
135             let lock_file_path = lock_file_path(&session_dir);
136             let _lock = match flock::Lock::new(&lock_file_path,
137                                                false,   // don't wait
138                                                false,   // don't create the lock-file
139                                                false) { // shared lock
140                 Ok(lock) => lock,
141                 Err(err) => {
142                     debug!("Could not acquire lock on `{}` while trying to \
143                             load metadata hashes: {}",
144                             lock_file_path.display(),
145                             err);
146
147                     // Could not acquire the lock. The directory is probably in
148                     // in the process of being deleted. It's OK to just exit
149                     // here. It's the same scenario as if the file had not
150                     // existed in the first place.
151                     return
152                 }
153             };
154
155             let hashes_file_path = metadata_hash_import_path(&session_dir);
156
157             match file_format::read_file(&hashes_file_path)
158             {
159                 Ok(Some(data)) => {
160                     match self.load_from_data(cnum, &data, svh) {
161                         Ok(()) => { }
162                         Err(err) => {
163                             bug!("decoding error in dep-graph from `{}`: {}",
164                                  &hashes_file_path.display(), err);
165                         }
166                     }
167                 }
168                 Ok(None) => {
169                     // If the file is not found, that's ok.
170                 }
171                 Err(err) => {
172                     self.tcx.sess.err(
173                         &format!("could not load dep information from `{}`: {}",
174                                  hashes_file_path.display(), err));
175                 }
176             }
177         }
178     }
179
180     fn load_from_data(&mut self,
181                       cnum: CrateNum,
182                       data: &[u8],
183                       expected_svh: Svh) -> Result<(), String> {
184         debug!("load_from_data(cnum={})", cnum);
185
186         // Load up the hashes for the def-ids from this crate.
187         let mut decoder = Decoder::new(data, 0);
188         let svh_in_hashes_file = Svh::decode(&mut decoder)?;
189
190         if svh_in_hashes_file != expected_svh {
191             // We should not be able to get here. If we do, then
192             // `fs::find_metadata_hashes_for()` has messed up.
193             bug!("mismatch between SVH in crate and SVH in incr. comp. hashes")
194         }
195
196         let serialized_hashes = SerializedMetadataHashes::decode(&mut decoder)?;
197         for serialized_hash in serialized_hashes.hashes {
198             // the hashes are stored with just a def-index, which is
199             // always relative to the old crate; convert that to use
200             // our internal crate number
201             let def_id = DefId { krate: cnum, index: serialized_hash.def_index };
202
203             // record the hash for this dep-node
204             let old = self.item_metadata_hashes.insert(def_id, serialized_hash.hash);
205             debug!("load_from_data: def_id={:?} hash={}", def_id, serialized_hash.hash);
206             assert!(old.is_none(), "already have hash for {:?}", def_id);
207         }
208         Ok(())
209     }
210 }
211
212 fn svh_to_fingerprint(svh: Svh) -> Fingerprint {
213     Fingerprint::from_smaller_hash(svh.as_u64())
214 }