diff --git a/src/librustc/ty/codec.rs b/src/librustc/ty/codec.rs new file mode 100644 index 00000000000..1c793920bf2 --- /dev/null +++ b/src/librustc/ty/codec.rs @@ -0,0 +1,243 @@ +// Copyright 2017 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. + +// This module contains some shared code for encoding and decoding various +// things from the `ty` module, and in particular implements support for +// "shorthands" which allow to have pointers back into the already encoded +// stream instead of re-encoding the same thing twice. +// +// The functionality in here is shared between persisting to crate metadata and +// persisting to incr. comp. caches. + +use hir::def_id::{DefId, CrateNum}; +use middle::const_val::ByteArray; +use rustc_data_structures::fx::FxHashMap; +use rustc_serialize::{Decodable, Decoder, Encoder, Encodable}; +use std::hash::Hash; +use std::intrinsics; +use ty::{self, Ty, TyCtxt}; +use ty::subst::Substs; + +/// The shorthand encoding uses an enum's variant index `usize` +/// and is offset by this value so it never matches a real variant. +/// This offset is also chosen so that the first byte is never < 0x80. +pub const SHORTHAND_OFFSET: usize = 0x80; + +pub trait EncodableWithShorthand: Clone + Eq + Hash { + type Variant: Encodable; + fn variant(&self) -> &Self::Variant; +} + +impl<'tcx> EncodableWithShorthand for Ty<'tcx> { + type Variant = ty::TypeVariants<'tcx>; + fn variant(&self) -> &Self::Variant { + &self.sty + } +} + +impl<'tcx> EncodableWithShorthand for ty::Predicate<'tcx> { + type Variant = ty::Predicate<'tcx>; + fn variant(&self) -> &Self::Variant { + self + } +} + +pub trait TyEncoder: Encoder { + fn position(&self) -> usize; +} + +/// Encode the given value or a previously cached shorthand. +pub fn encode_with_shorthand(encoder: &mut E, + value: &T, + cache: M) + -> Result<(), E::Error> + where E: TyEncoder, + M: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, + T: EncodableWithShorthand, +{ + let existing_shorthand = cache(encoder).get(value).cloned(); + if let Some(shorthand) = existing_shorthand { + return encoder.emit_usize(shorthand); + } + + let variant = value.variant(); + + let start = encoder.position(); + variant.encode(encoder)?; + let len = encoder.position() - start; + + // The shorthand encoding uses the same usize as the + // discriminant, with an offset so they can't conflict. + let discriminant = unsafe { intrinsics::discriminant_value(variant) }; + assert!(discriminant < SHORTHAND_OFFSET as u64); + let shorthand = start + SHORTHAND_OFFSET; + + // Get the number of bits that leb128 could fit + // in the same space as the fully encoded type. + let leb128_bits = len * 7; + + // Check that the shorthand is a not longer than the + // full encoding itself, i.e. it's an obvious win. + if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { + cache(encoder).insert(value.clone(), shorthand); + } + + Ok(()) +} + +pub fn encode_predicates<'tcx, E, C>(encoder: &mut E, + predicates: &ty::GenericPredicates<'tcx>, + cache: C) + -> Result<(), E::Error> + where E: TyEncoder, + C: for<'b> Fn(&'b mut E) -> &'b mut FxHashMap, usize>, +{ + predicates.parent.encode(encoder)?; + predicates.predicates.len().encode(encoder)?; + for predicate in &predicates.predicates { + encode_with_shorthand(encoder, predicate, &cache)? + } + Ok(()) +} + +pub trait TyDecoder<'a, 'tcx: 'a>: Decoder { + + fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx>; + + fn peek_byte(&self) -> u8; + + fn cached_ty_for_shorthand(&mut self, + shorthand: usize, + or_insert_with: F) + -> Result, Self::Error> + where F: FnOnce(&mut Self) -> Result, Self::Error>; + + fn with_position(&mut self, pos: usize, f: F) -> R + where F: FnOnce(&mut Self) -> R; + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum; + + fn positioned_at_shorthand(&self) -> bool { + (self.peek_byte() & (SHORTHAND_OFFSET as u8)) != 0 + } +} + +pub fn decode_cnum<'a, 'tcx, D>(decoder: &mut D) -> Result + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let cnum = CrateNum::from_u32(u32::decode(decoder)?); + Ok(decoder.map_encoded_cnum_to_current(cnum)) +} + +pub fn decode_ty<'a, 'tcx, D>(decoder: &mut D) -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + // Handle shorthands first, if we have an usize > 0x80. + // if self.opaque.data[self.opaque.position()] & 0x80 != 0 { + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.cached_ty_for_shorthand(shorthand, |decoder| { + decoder.with_position(shorthand, Ty::decode) + }) + } else { + let tcx = decoder.tcx(); + Ok(tcx.mk_ty(ty::TypeVariants::decode(decoder)?)) + } +} + +pub fn decode_predicates<'a, 'tcx, D>(decoder: &mut D) + -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(ty::GenericPredicates { + parent: Decodable::decode(decoder)?, + predicates: (0..decoder.read_usize()?).map(|_| { + // Handle shorthands first, if we have an usize > 0x80. + if decoder.positioned_at_shorthand() { + let pos = decoder.read_usize()?; + assert!(pos >= SHORTHAND_OFFSET); + let shorthand = pos - SHORTHAND_OFFSET; + + decoder.with_position(shorthand, ty::Predicate::decode) + } else { + ty::Predicate::decode(decoder) + } + }) + .collect::, _>>()?, + }) +} + +pub fn decode_substs<'a, 'tcx, D>(decoder: &mut D) -> Result<&'tcx Substs<'tcx>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let len = decoder.read_usize()?; + let tcx = decoder.tcx(); + Ok(tcx.mk_substs((0..len).map(|_| Decodable::decode(decoder)))?) +} + +pub fn decode_region<'a, 'tcx, D>(decoder: &mut D) -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(decoder.tcx().mk_region(Decodable::decode(decoder)?)) +} + +pub fn decode_ty_slice<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::Slice>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let len = decoder.read_usize()?; + Ok(decoder.tcx().mk_type_list((0..len).map(|_| Decodable::decode(decoder)))?) +} + +pub fn decode_adt_def<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::AdtDef, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let def_id = DefId::decode(decoder)?; + Ok(decoder.tcx().adt_def(def_id)) +} + +pub fn decode_existential_predicate_slice<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::Slice>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + let len = decoder.read_usize()?; + Ok(decoder.tcx() + .mk_existential_predicates((0..len).map(|_| Decodable::decode(decoder)))?) +} + +pub fn decode_byte_array<'a, 'tcx, D>(decoder: &mut D) + -> Result, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(ByteArray { + data: decoder.tcx().alloc_byte_array(&Vec::decode(decoder)?) + }) +} + +pub fn decode_const<'a, 'tcx, D>(decoder: &mut D) + -> Result<&'tcx ty::Const<'tcx>, D::Error> + where D: TyDecoder<'a, 'tcx>, + 'tcx: 'a, +{ + Ok(decoder.tcx().mk_const(Decodable::decode(decoder)?)) +} diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 129c81c5cd6..99885e3e637 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -89,6 +89,7 @@ pub use self::maps::queries; pub mod adjustment; pub mod binding; pub mod cast; +pub mod codec; pub mod error; mod erase_regions; pub mod fast_reject; diff --git a/src/librustc_metadata/decoder.rs b/src/librustc_metadata/decoder.rs index 65cf15e5a0e..fed0f526033 100644 --- a/src/librustc_metadata/decoder.rs +++ b/src/librustc_metadata/decoder.rs @@ -25,6 +25,7 @@ use rustc::ich::Fingerprint; use rustc::middle::lang_items; use rustc::session::Session; use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::codec::{self as ty_codec, TyDecoder}; use rustc::ty::subst::Substs; use rustc::util::nodemap::DefIdSet; @@ -143,16 +144,6 @@ impl<'a, 'tcx> DecodeContext<'a, 'tcx> { self.cdata.expect("missing CrateMetadata in DecodeContext") } - fn with_position R, R>(&mut self, pos: usize, f: F) -> R { - let new_opaque = opaque::Decoder::new(self.opaque.data, pos); - let old_opaque = mem::replace(&mut self.opaque, new_opaque); - let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode); - let r = f(self); - self.opaque = old_opaque; - self.lazy_state = old_state; - r - } - fn read_lazy_distance(&mut self, min_size: usize) -> Result::Error> { let distance = self.read_usize()?; let position = match self.lazy_state { @@ -208,6 +199,60 @@ impl<'doc, 'tcx> Decoder for DecodeContext<'doc, 'tcx> { } } + +impl<'a, 'tcx: 'a> TyDecoder<'a, 'tcx> for DecodeContext<'a, 'tcx> { + + fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.tcx.expect("missing TyCtxt in DecodeContext") + } + + fn peek_byte(&self) -> u8 { + self.opaque.data[self.opaque.position()] + } + + fn cached_ty_for_shorthand(&mut self, + shorthand: usize, + or_insert_with: F) + -> Result, Self::Error> + where F: FnOnce(&mut Self) -> Result, Self::Error> + { + let tcx = self.tcx(); + + let key = ty::CReaderCacheKey { + cnum: self.cdata().cnum, + pos: shorthand, + }; + + if let Some(&ty) = tcx.rcache.borrow().get(&key) { + return Ok(ty); + } + + let ty = or_insert_with(self)?; + tcx.rcache.borrow_mut().insert(key, ty); + Ok(ty) + } + + fn with_position(&mut self, pos: usize, f: F) -> R + where F: FnOnce(&mut Self) -> R + { + let new_opaque = opaque::Decoder::new(self.opaque.data, pos); + let old_opaque = mem::replace(&mut self.opaque, new_opaque); + let old_state = mem::replace(&mut self.lazy_state, LazyState::NoNode); + let r = f(self); + self.opaque = old_opaque; + self.lazy_state = old_state; + r + } + + fn map_encoded_cnum_to_current(&self, cnum: CrateNum) -> CrateNum { + if cnum == LOCAL_CRATE { + self.cdata().cnum + } else { + self.cdata().cnum_map.borrow()[cnum] + } + } +} + impl<'a, 'tcx, T> SpecializedDecoder> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result, Self::Error> { Ok(Lazy::with_position(self.read_lazy_distance(Lazy::::min_size())?)) @@ -302,73 +347,37 @@ impl<'a, 'tcx> SpecializedDecoder for DecodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result, Self::Error> { - let tcx = self.tcx(); - - // Handle shorthands first, if we have an usize > 0x80. - if self.opaque.data[self.opaque.position()] & 0x80 != 0 { - let pos = self.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let key = ty::CReaderCacheKey { - cnum: self.cdata().cnum, - pos: pos - SHORTHAND_OFFSET, - }; - if let Some(ty) = tcx.rcache.borrow().get(&key).cloned() { - return Ok(ty); - } - - let ty = self.with_position(key.pos, Ty::decode)?; - tcx.rcache.borrow_mut().insert(key, ty); - Ok(ty) - } else { - Ok(tcx.mk_ty(ty::TypeVariants::decode(self)?)) - } + ty_codec::decode_ty(self) } } - impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result, Self::Error> { - Ok(ty::GenericPredicates { - parent: Decodable::decode(self)?, - predicates: (0..self.read_usize()?).map(|_| { - // Handle shorthands first, if we have an usize > 0x80. - if self.opaque.data[self.opaque.position()] & 0x80 != 0 { - let pos = self.read_usize()?; - assert!(pos >= SHORTHAND_OFFSET); - let pos = pos - SHORTHAND_OFFSET; - - self.with_position(pos, ty::Predicate::decode) - } else { - ty::Predicate::decode(self) - } - }) - .collect::, _>>()?, - }) + ty_codec::decode_predicates(self) } } impl<'a, 'tcx> SpecializedDecoder<&'tcx Substs<'tcx>> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result<&'tcx Substs<'tcx>, Self::Error> { - Ok(self.tcx().mk_substs((0..self.read_usize()?).map(|_| Decodable::decode(self)))?) + ty_codec::decode_substs(self) } } impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result, Self::Error> { - Ok(self.tcx().mk_region(Decodable::decode(self)?)) + ty_codec::decode_region(self) } } impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::Slice>> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result<&'tcx ty::Slice>, Self::Error> { - Ok(self.tcx().mk_type_list((0..self.read_usize()?).map(|_| Decodable::decode(self)))?) + ty_codec::decode_ty_slice(self) } } impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::AdtDef> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result<&'tcx ty::AdtDef, Self::Error> { - let def_id = DefId::decode(self)?; - Ok(self.tcx().adt_def(def_id)) + ty_codec::decode_adt_def(self) } } @@ -376,22 +385,19 @@ impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::Slice for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result<&'tcx ty::Slice>, Self::Error> { - Ok(self.tcx().mk_existential_predicates((0..self.read_usize()?) - .map(|_| Decodable::decode(self)))?) + ty_codec::decode_existential_predicate_slice(self) } } impl<'a, 'tcx> SpecializedDecoder> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result, Self::Error> { - Ok(ByteArray { - data: self.tcx().alloc_byte_array(&Vec::decode(self)?) - }) + ty_codec::decode_byte_array(self) } } impl<'a, 'tcx> SpecializedDecoder<&'tcx ty::Const<'tcx>> for DecodeContext<'a, 'tcx> { fn specialized_decode(&mut self) -> Result<&'tcx ty::Const<'tcx>, Self::Error> { - Ok(self.tcx().mk_const(Decodable::decode(self)?)) + ty_codec::decode_const(self) } } diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 6b49be3e121..bf2c61c13c3 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -25,14 +25,13 @@ use rustc::middle::lang_items; use rustc::mir; use rustc::traits::specialization_graph; use rustc::ty::{self, Ty, TyCtxt, ReprOptions}; +use rustc::ty::codec::{self as ty_codec, TyEncoder}; use rustc::session::config::{self, CrateTypeProcMacro}; use rustc::util::nodemap::{FxHashMap, NodeSet}; use rustc_serialize::{Encodable, Encoder, SpecializedEncoder, opaque}; -use std::hash::Hash; -use std::intrinsics; use std::io::prelude::*; use std::io::Cursor; use std::path::Path; @@ -119,7 +118,7 @@ impl<'a, 'tcx, T> SpecializedEncoder> for EncodeContext<'a, 'tcx> { impl<'a, 'tcx> SpecializedEncoder> for EncodeContext<'a, 'tcx> { fn specialized_encode(&mut self, ty: &Ty<'tcx>) -> Result<(), Self::Error> { - self.encode_with_shorthand(ty, &ty.sty, |ecx| &mut ecx.type_shorthands) + ty_codec::encode_with_shorthand(self, ty, |ecx| &mut ecx.type_shorthands) } } @@ -127,21 +126,18 @@ impl<'a, 'tcx> SpecializedEncoder> for EncodeContext fn specialized_encode(&mut self, predicates: &ty::GenericPredicates<'tcx>) -> Result<(), Self::Error> { - predicates.parent.encode(self)?; - predicates.predicates.len().encode(self)?; - for predicate in &predicates.predicates { - self.encode_with_shorthand(predicate, predicate, |ecx| &mut ecx.predicate_shorthands)? - } - Ok(()) + ty_codec::encode_predicates(self, predicates, |ecx| &mut ecx.predicate_shorthands) + } +} + +impl<'a, 'tcx> TyEncoder for EncodeContext<'a, 'tcx> { + fn position(&self) -> usize { + self.opaque.position() } } impl<'a, 'tcx> EncodeContext<'a, 'tcx> { - pub fn position(&self) -> usize { - self.opaque.position() - } - fn emit_node R, R>(&mut self, f: F) -> R { assert_eq!(self.lazy_state, LazyState::NoNode); let pos = self.position(); @@ -204,44 +200,6 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { }) } - /// Encode the given value or a previously cached shorthand. - fn encode_with_shorthand(&mut self, - value: &T, - variant: &U, - map: M) - -> Result<(), ::Error> - where M: for<'b> Fn(&'b mut Self) -> &'b mut FxHashMap, - T: Clone + Eq + Hash, - U: Encodable - { - let existing_shorthand = map(self).get(value).cloned(); - if let Some(shorthand) = existing_shorthand { - return self.emit_usize(shorthand); - } - - let start = self.position(); - variant.encode(self)?; - let len = self.position() - start; - - // The shorthand encoding uses the same usize as the - // discriminant, with an offset so they can't conflict. - let discriminant = unsafe { intrinsics::discriminant_value(variant) }; - assert!(discriminant < SHORTHAND_OFFSET as u64); - let shorthand = start + SHORTHAND_OFFSET; - - // Get the number of bits that leb128 could fit - // in the same space as the fully encoded type. - let leb128_bits = len * 7; - - // Check that the shorthand is a not longer than the - // full encoding itself, i.e. it's an obvious win. - if leb128_bits >= 64 || (shorthand as u64) < (1 << leb128_bits) { - map(self).insert(value.clone(), shorthand); - } - - Ok(()) - } - // Encodes something that corresponds to a single DepNode::GlobalMetaData // and registers the Fingerprint in the `metadata_hashes` map. pub fn tracked<'x, DATA, R>(&'x mut self, diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 54dbb68667b..20bdfaea0d0 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -15,7 +15,6 @@ #![feature(box_patterns)] #![feature(conservative_impl_trait)] -#![feature(core_intrinsics)] #![feature(i128_type)] #![feature(proc_macro_internals)] #![feature(quote)] diff --git a/src/librustc_metadata/schema.rs b/src/librustc_metadata/schema.rs index dad0d26d271..e1d127d3516 100644 --- a/src/librustc_metadata/schema.rs +++ b/src/librustc_metadata/schema.rs @@ -53,11 +53,6 @@ pub const METADATA_VERSION: u8 = 4; pub const METADATA_HEADER: &'static [u8; 12] = &[0, 0, 0, 0, b'r', b'u', b's', b't', 0, 0, 0, METADATA_VERSION]; -/// The shorthand encoding uses an enum's variant index `usize` -/// and is offset by this value so it never matches a real variant. -/// This offset is also chosen so that the first byte is never < 0x80. -pub const SHORTHAND_OFFSET: usize = 0x80; - /// A value of type T referred to by its absolute position /// in the metadata, and which can be decoded lazily. ///