diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 67f30f53a6810..04ffb9af29ed5 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -15,11 +15,13 @@ pub use self::value::{PrimVal, PrimValKind, Value, Pointer}; use std::collections::BTreeMap; use std::fmt; use mir; -use ty; +use hir::def_id::DefId; +use ty::{self, TyCtxt}; use ty::layout::{self, Align, HasDataLayout}; use middle::region; use std::iter; use syntax::ast::Mutability; +use rustc_serialize::{Encoder, Decoder, Decodable, Encodable}; #[derive(Clone, Debug, PartialEq)] pub enum Lock { @@ -152,6 +154,84 @@ pub struct AllocId(pub u64); impl ::rustc_serialize::UseSpecializedEncodable for AllocId {} impl ::rustc_serialize::UseSpecializedDecodable for AllocId {} +pub const ALLOC_DISCRIMINANT: usize = 0; +pub const FN_DISCRIMINANT: usize = 1; + +pub fn specialized_encode_alloc_id< + 'a, 'tcx, + E: Encoder, +>( + encoder: &mut E, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + alloc_id: AllocId, + shorthand: Option, +) -> Result<(), E::Error> { + if let Some(shorthand) = shorthand { + return shorthand.encode(encoder); + } + if let Some(alloc) = tcx.interpret_interner.get_alloc(alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, alloc); + ALLOC_DISCRIMINANT.encode(encoder)?; + alloc.encode(encoder)?; + tcx.interpret_interner + .get_corresponding_static_def_id(alloc_id) + .encode(encoder)?; + } else if let Some(fn_instance) = tcx.interpret_interner.get_fn(alloc_id) { + trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); + FN_DISCRIMINANT.encode(encoder)?; + fn_instance.encode(encoder)?; + } else { + bug!("alloc id without corresponding allocation: {}", alloc_id); + } + Ok(()) +} + +pub fn specialized_decode_alloc_id< + 'a, 'tcx, + D: Decoder, + CACHE: FnOnce(&mut D, usize, AllocId), + SHORT: FnOnce(&mut D, usize) -> Result +>( + decoder: &mut D, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + pos: usize, + cache: CACHE, + short: SHORT, +) -> Result { + match usize::decode(decoder)? { + ALLOC_DISCRIMINANT => { + let alloc_id = tcx.interpret_interner.reserve(); + trace!("creating alloc id {:?} at {}", alloc_id, pos); + // insert early to allow recursive allocs + cache(decoder, pos, alloc_id); + + let allocation = Allocation::decode(decoder)?; + trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); + let allocation = tcx.intern_const_alloc(allocation); + tcx.interpret_interner.intern_at_reserved(alloc_id, allocation); + + if let Some(glob) = Option::::decode(decoder)? { + tcx.interpret_interner.cache(glob, alloc_id); + } + + Ok(alloc_id) + }, + FN_DISCRIMINANT => { + trace!("creating fn alloc id at {}", pos); + let instance = ty::Instance::decode(decoder)?; + trace!("decoded fn alloc instance: {:?}", instance); + let id = tcx.interpret_interner.create_fn_alloc(instance); + trace!("created fn alloc id: {:?}", id); + cache(decoder, pos, id); + Ok(id) + }, + shorthand => { + trace!("loading shorthand {}", shorthand); + short(decoder, shorthand) + }, + } +} + impl fmt::Display for AllocId { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.0) diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index 35e874b74d9ae..e56d8f8e818dd 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -75,6 +75,13 @@ pub struct OnDiskCache<'sess> { // A map from dep-node to the position of any associated diagnostics in // `serialized_data`. prev_diagnostics_index: FxHashMap, + + // A cache to ensure we don't read allocations twice + interpret_alloc_cache: RefCell>, + + // A map from positions to size of the serialized allocation + // so we can skip over already processed allocations + interpret_alloc_size: RefCell>, } // This type is used only for (de-)serialization. @@ -140,6 +147,8 @@ impl<'sess> OnDiskCache<'sess> { query_result_index: footer.query_result_index.into_iter().collect(), prev_diagnostics_index: footer.diagnostics_index.into_iter().collect(), synthetic_expansion_infos: RefCell::new(FxHashMap()), + interpret_alloc_cache: RefCell::new(FxHashMap::default()), + interpret_alloc_size: RefCell::new(FxHashMap::default()), } } @@ -155,6 +164,8 @@ impl<'sess> OnDiskCache<'sess> { query_result_index: FxHashMap(), prev_diagnostics_index: FxHashMap(), synthetic_expansion_infos: RefCell::new(FxHashMap()), + interpret_alloc_cache: RefCell::new(FxHashMap::default()), + interpret_alloc_size: RefCell::new(FxHashMap::default()), } } @@ -381,7 +392,8 @@ impl<'sess> OnDiskCache<'sess> { file_index_to_file: &self.file_index_to_file, file_index_to_stable_id: &self.file_index_to_stable_id, synthetic_expansion_infos: &self.synthetic_expansion_infos, - interpret_alloc_cache: FxHashMap::default(), + interpret_alloc_cache: &self.interpret_alloc_cache, + interpret_alloc_size: &self.interpret_alloc_size, }; match decode_tagged(&mut decoder, dep_node_index) { @@ -443,7 +455,8 @@ struct CacheDecoder<'a, 'tcx: 'a, 'x> { synthetic_expansion_infos: &'x RefCell>, file_index_to_file: &'x RefCell>>, file_index_to_stable_id: &'x FxHashMap, - interpret_alloc_cache: FxHashMap, + interpret_alloc_cache: &'x RefCell>, + interpret_alloc_size: &'x RefCell>, } impl<'a, 'tcx, 'x> CacheDecoder<'a, 'tcx, 'x> { @@ -565,47 +578,37 @@ implement_ty_decoder!( CacheDecoder<'a, 'tcx, 'x> ); impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { fn specialized_decode(&mut self) -> Result { - const MAX1: usize = usize::max_value() - 1; let tcx = self.tcx; let pos = TyDecoder::position(self); - match usize::decode(self)? { - ::std::usize::MAX => { - let alloc_id = tcx.interpret_interner.reserve(); - trace!("creating alloc id {:?} at {}", alloc_id, pos); - // insert early to allow recursive allocs - self.interpret_alloc_cache.insert(pos, alloc_id); - - let allocation = interpret::Allocation::decode(self)?; - trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); - let allocation = self.tcx.intern_const_alloc(allocation); - tcx.interpret_interner.intern_at_reserved(alloc_id, allocation); - - if let Some(glob) = Option::::decode(self)? { - trace!("connecting alloc {:?} with {:?}", alloc_id, glob); - tcx.interpret_interner.cache(glob, alloc_id); - } - - Ok(alloc_id) - }, - MAX1 => { - trace!("creating fn alloc id at {}", pos); - let instance = ty::Instance::decode(self)?; - trace!("decoded fn alloc instance: {:?}", instance); - let id = tcx.interpret_interner.create_fn_alloc(instance); - trace!("created fn alloc id: {:?}", id); - self.interpret_alloc_cache.insert(pos, id); - Ok(id) + trace!("specialized_decode_alloc_id: {:?}", pos); + if let Some(cached) = self.interpret_alloc_cache.borrow().get(&pos).cloned() { + // if there's no end position we are currently deserializing a recursive + // allocation + if let Some(end) = self.interpret_alloc_size.borrow().get(&pos).cloned() { + trace!("{} already cached as {:?}", pos, cached); + // skip ahead + self.opaque.set_position(end); + return Ok(cached) + } + } + let id = interpret::specialized_decode_alloc_id( + self, + tcx, + pos, + |this, pos, alloc_id| { + assert!(this.interpret_alloc_cache.borrow_mut().insert(pos, alloc_id).is_none()); }, - shorthand => { - trace!("loading shorthand {}", shorthand); - if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { - return Ok(alloc_id); - } - trace!("shorthand {} not cached, loading entire allocation", shorthand); + |this, shorthand| { // need to load allocation - self.with_position(shorthand, |this| interpret::AllocId::decode(this)) - }, - } + this.with_position(shorthand, |this| interpret::AllocId::decode(this)) + } + )?; + assert!(self + .interpret_alloc_size + .borrow_mut() + .insert(pos, TyDecoder::position(self)) + .is_none()); + Ok(id) } } impl<'a, 'tcx, 'x> SpecializedDecoder for CacheDecoder<'a, 'tcx, 'x> { @@ -806,30 +809,27 @@ impl<'enc, 'a, 'tcx, E> SpecializedEncoder for CacheEncoder< where E: 'enc + ty_codec::TyEncoder { fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { - trace!("encoding {:?} at {}", alloc_id, self.position()); - if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { - trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); - return shorthand.encode(self); - } - let start = self.position(); - // cache the allocation shorthand now, because the allocation itself might recursively - // point to itself. - self.interpret_alloc_shorthands.insert(*alloc_id, start); - if let Some(alloc) = self.tcx.interpret_interner.get_alloc(*alloc_id) { - trace!("encoding {:?} with {:#?}", alloc_id, alloc); - usize::max_value().encode(self)?; - alloc.encode(self)?; - self.tcx.interpret_interner - .get_corresponding_static_def_id(*alloc_id) - .encode(self)?; - } else if let Some(fn_instance) = self.tcx.interpret_interner.get_fn(*alloc_id) { - trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); - (usize::max_value() - 1).encode(self)?; - fn_instance.encode(self)?; - } else { - bug!("alloc id without corresponding allocation: {}", alloc_id); - } - Ok(()) + use std::collections::hash_map::Entry; + let tcx = self.tcx; + let pos = self.position(); + let shorthand = match self.interpret_alloc_shorthands.entry(*alloc_id) { + Entry::Occupied(entry) => Some(entry.get().clone()), + Entry::Vacant(entry) => { + // ensure that we don't place any AllocIds at the very beginning + // of the metadata file, because that would end up making our indices + // not special. It is essentially impossible for that to happen, + // but let's make sure + assert!(pos != interpret::ALLOC_DISCRIMINANT && pos != interpret::FN_DISCRIMINANT); + entry.insert(pos); + None + }, + }; + interpret::specialized_encode_alloc_id( + self, + tcx, + *alloc_id, + shorthand, + ) } } diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index d83d6f26393d1..e1c17e8260a79 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -57,6 +57,9 @@ pub struct DecodeContext<'a, 'tcx: 'a> { // interpreter allocation cache interpret_alloc_cache: FxHashMap, + // a cache for sizes of interpreter allocations + // needed to skip already deserialized allocations + interpret_alloc_size: FxHashMap, } /// Abstract over the various ways one can create metadata decoders. @@ -76,6 +79,7 @@ pub trait Metadata<'a, 'tcx>: Copy { last_filemap_index: 0, lazy_state: LazyState::NoNode, interpret_alloc_cache: FxHashMap::default(), + interpret_alloc_size: FxHashMap::default(), } } } @@ -281,46 +285,34 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result { - const MAX1: usize = usize::max_value() - 1; - let tcx = self.tcx.unwrap(); + let tcx = self.tcx.expect("need tcx for AllocId decoding"); let pos = self.position(); - match usize::decode(self)? { - ::std::usize::MAX => { - let alloc_id = tcx.interpret_interner.reserve(); - trace!("creating alloc id {:?} at {}", alloc_id, pos); - // insert early to allow recursive allocs - self.interpret_alloc_cache.insert(pos, alloc_id); - - let allocation = interpret::Allocation::decode(self)?; - trace!("decoded alloc {:?} {:#?}", alloc_id, allocation); - let allocation = self.tcx.unwrap().intern_const_alloc(allocation); - tcx.interpret_interner.intern_at_reserved(alloc_id, allocation); - - if let Some(glob) = Option::::decode(self)? { - tcx.interpret_interner.cache(glob, alloc_id); - } - - Ok(alloc_id) - }, - MAX1 => { - trace!("creating fn alloc id at {}", pos); - let instance = ty::Instance::decode(self)?; - trace!("decoded fn alloc instance: {:?}", instance); - let id = tcx.interpret_interner.create_fn_alloc(instance); - trace!("created fn alloc id: {:?}", id); - self.interpret_alloc_cache.insert(pos, id); - Ok(id) - }, - shorthand => { - trace!("loading shorthand {}", shorthand); - if let Some(&alloc_id) = self.interpret_alloc_cache.get(&shorthand) { - return Ok(alloc_id); - } - trace!("shorthand {} not cached, loading entire allocation", shorthand); - // need to load allocation - self.with_position(shorthand, |this| interpret::AllocId::decode(this)) - }, + if let Some(cached) = self.interpret_alloc_cache.get(&pos).cloned() { + // if there's no end position we are currently deserializing a recursive + // allocation + if let Some(end) = self.interpret_alloc_size.get(&pos).cloned() { + trace!("{} already cached as {:?}", pos, cached); + // skip ahead + self.opaque.set_position(end); + return Ok(cached) + } } + let id = interpret::specialized_decode_alloc_id( + self, + tcx, + pos, + |this, pos, alloc_id| { this.interpret_alloc_cache.insert(pos, alloc_id); }, + |this, shorthand| { + // need to load allocation + this.with_position(shorthand, |this| interpret::AllocId::decode(this)) + } + )?; + let end_pos = self.position(); + assert!(self + .interpret_alloc_size + .insert(pos, end_pos) + .is_none()); + Ok(id) } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index d959482417489..0da23c2caf4ae 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -196,30 +196,26 @@ impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedEncoder for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> { - trace!("encoding {:?} at {}", alloc_id, self.position()); - if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() { - trace!("encoding {:?} as shorthand to {}", alloc_id, shorthand); - return shorthand.encode(self); - } - let start = self.position(); - // cache the allocation shorthand now, because the allocation itself might recursively - // point to itself. - self.interpret_alloc_shorthands.insert(*alloc_id, start); - if let Some(alloc) = self.tcx.interpret_interner.get_alloc(*alloc_id) { - trace!("encoding {:?} with {:#?}", alloc_id, alloc); - usize::max_value().encode(self)?; - alloc.encode(self)?; - self.tcx.interpret_interner - .get_corresponding_static_def_id(*alloc_id) - .encode(self)?; - } else if let Some(fn_instance) = self.tcx.interpret_interner.get_fn(*alloc_id) { - trace!("encoding {:?} with {:#?}", alloc_id, fn_instance); - (usize::max_value() - 1).encode(self)?; - fn_instance.encode(self)?; - } else { - bug!("alloc id without corresponding allocation: {}", alloc_id); - } - Ok(()) + use std::collections::hash_map::Entry; + let tcx = self.tcx; + let pos = self.position(); + let shorthand = match self.interpret_alloc_shorthands.entry(*alloc_id) { + Entry::Occupied(entry) => Some(entry.get().clone()), + Entry::Vacant(entry) => { + // ensure that we don't place any AllocIds at the very beginning + // of the metadata file, because that would end up making our 0 and 1 indices + // not special. This is essentially impossible, but let's make sure + assert!(pos != 0 && pos != 1); + entry.insert(pos); + None + }, + }; + interpret::specialized_encode_alloc_id( + self, + tcx, + *alloc_id, + shorthand, + ) } } diff --git a/src/test/incremental/static_refering_to_other_static/issue-49081.rs b/src/test/incremental/static_refering_to_other_static/issue-49081.rs new file mode 100644 index 0000000000000..6345b456523ed --- /dev/null +++ b/src/test/incremental/static_refering_to_other_static/issue-49081.rs @@ -0,0 +1,18 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// https://github.com/rust-lang/rust/issues/49081 + +// revisions:rpass1 rpass2 + +pub static A: i32 = 42; +pub static B: &i32 = &A; + +fn main() {}