Add a variant to ConstVal for storing miri results

This commit is contained in:
Oliver Schneider 2018-01-16 09:12:54 +01:00
parent cdcca78646
commit 38b5ddd39b
No known key found for this signature in database
GPG Key ID: A69F8D225B3AD7D9
18 changed files with 165 additions and 25 deletions

View File

@ -21,6 +21,7 @@ use std::mem;
use middle::region;
use traits;
use ty;
use mir;
impl<'gcx, T> HashStable<StableHashingContext<'gcx>>
for &'gcx ty::Slice<T>
@ -388,6 +389,9 @@ for ::middle::const_val::ConstVal<'gcx> {
def_id.hash_stable(hcx, hasher);
substs.hash_stable(hcx, hasher);
}
Value(ref value) => {
value.hash_stable(hcx, hasher);
}
}
}
}
@ -396,6 +400,26 @@ impl_stable_hash_for!(struct ::middle::const_val::ByteArray<'tcx> {
data
});
impl_stable_hash_for!(enum mir::interpret::Value {
ByVal(v),
ByValPair(a, b),
ByRef(ptr, align)
});
impl_stable_hash_for!(struct mir::interpret::MemoryPointer {
alloc_id,
offset
});
impl_stable_hash_for!(tuple_struct mir::interpret::AllocId{id});
impl_stable_hash_for!(struct mir::interpret::Pointer{primval});
impl_stable_hash_for!(enum mir::interpret::PrimVal {
Bytes(b),
Ptr(p),
Undef
});
impl_stable_hash_for!(struct ty::Const<'tcx> {
ty,
val

View File

@ -49,6 +49,7 @@
#![feature(core_intrinsics)]
#![feature(drain_filter)]
#![feature(dyn_trait)]
#![feature(entry_or_default)]
#![feature(from_ref)]
#![feature(fs_read_write)]
#![feature(i128)]

View File

@ -14,6 +14,7 @@ use hir::def_id::DefId;
use ty::{self, TyCtxt, layout};
use ty::subst::Substs;
use rustc_const_math::*;
use mir::interpret::Value;
use graphviz::IntoCow;
use errors::DiagnosticBuilder;
@ -38,6 +39,8 @@ pub enum ConstVal<'tcx> {
Function(DefId, &'tcx Substs<'tcx>),
Aggregate(ConstAggregate<'tcx>),
Unevaluated(DefId, &'tcx Substs<'tcx>),
/// A miri value, currently only produced if old ctfe fails, but miri succeeds
Value(Value),
}
#[derive(Copy, Clone, Debug, Hash, RustcEncodable, Eq, PartialEq)]

View File

@ -41,7 +41,7 @@ pub enum AccessKind {
}
/// Uniquely identifies a specific constant or static.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)]
pub struct GlobalId<'tcx> {
/// For a constant or static, the `Instance` of the item itself.
/// For a promoted global, the `Instance` of the function they belong to.
@ -101,7 +101,7 @@ pub trait PointerArithmetic: layout::HasDataLayout {
impl<T: layout::HasDataLayout> PointerArithmetic for T {}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[derive(Copy, Clone, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub struct MemoryPointer {
pub alloc_id: AllocId,
pub offset: u64,
@ -148,13 +148,16 @@ impl<'tcx> MemoryPointer {
#[derive(Copy, Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Debug)]
pub struct AllocId(pub u64);
impl ::rustc_serialize::UseSpecializedEncodable for AllocId {}
impl ::rustc_serialize::UseSpecializedDecodable for AllocId {}
impl fmt::Display for AllocId {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Debug, Eq, PartialEq, Hash)]
#[derive(Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)]
pub struct Allocation {
/// The actual bytes of the allocation.
/// Note that the bytes of a pointer represent the offset of the pointer
@ -188,7 +191,7 @@ impl Allocation {
type Block = u64;
const BLOCK_SIZE: u64 = 64;
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[derive(Clone, Debug, Eq, PartialEq, Hash, RustcEncodable, RustcDecodable)]
pub struct UndefMask {
blocks: Vec<Block>,
len: u64,

View File

@ -29,7 +29,7 @@ pub fn bytes_to_f64(bits: u128) -> ConstFloat {
/// For optimization of a few very common cases, there is also a representation for a pair of
/// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary
/// operations and fat pointers. This idea was taken from rustc's trans.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub enum Value {
ByRef(Pointer, Align),
ByVal(PrimVal),
@ -43,9 +43,9 @@ pub enum Value {
/// I (@oli-obk) believe it is less easy to mix up generic primvals and primvals that are just
/// the representation of pointers. Also all the sites that convert between primvals and pointers
/// are explicit now (and rare!)
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub struct Pointer {
primval: PrimVal,
pub primval: PrimVal,
}
impl<'tcx> Pointer {
@ -138,7 +138,7 @@ impl ::std::convert::From<MemoryPointer> for Pointer {
/// `memory::Allocation`. It is in many ways like a small chunk of a `Allocation`, up to 8 bytes in
/// size. Like a range of bytes in an `Allocation`, a `PrimVal` can either represent the raw bytes
/// of a simple value, a pointer into another `Allocation`, or be undefined.
#[derive(Clone, Copy, Debug)]
#[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)]
pub enum PrimVal {
/// The raw bytes of a simple value.
Bytes(u128),

View File

@ -1881,7 +1881,8 @@ fn fmt_const_val<W: Write>(fmt: &mut W, const_val: &ConstVal) -> fmt::Result {
Variant(def_id) |
Function(def_id, _) => write!(fmt, "{}", item_path_str(def_id)),
Aggregate(_) => bug!("`ConstVal::{:?}` should not be in MIR", const_val),
Unevaluated(..) => write!(fmt, "{:?}", const_val)
Unevaluated(..) => write!(fmt, "{:?}", const_val),
Value(val) => write!(fmt, "{:?}", val),
}
}

View File

@ -905,6 +905,11 @@ pub struct InterpretInterner<'tcx> {
/// Allows obtaining const allocs via a unique identifier
alloc_by_id: FxHashMap<interpret::AllocId, &'tcx interpret::Allocation>,
/// Reverse map of `alloc_cache`
///
/// Multiple globals may share the same memory
global_cache: FxHashMap<interpret::Pointer, Vec<interpret::GlobalId<'tcx>>>,
/// The AllocId to assign to the next new regular allocation.
/// Always incremented, never gets smaller.
next_id: interpret::AllocId,
@ -955,11 +960,25 @@ impl<'tcx> InterpretInterner<'tcx> {
global_id: interpret::GlobalId<'tcx>,
ptr: interpret::AllocId,
) {
if let interpret::PrimVal::Ptr(ptr) = ptr.primval {
assert!(ptr.offset == 0);
}
self.global_cache.entry(ptr).or_default().push(global_id);
if let Some(old) = self.alloc_cache.insert(global_id, ptr) {
bug!("tried to cache {:?}, but was already existing as {:#?}", global_id, old);
}
}
pub fn get_globals(
&self,
ptr: interpret::Pointer,
) -> &[interpret::GlobalId<'tcx>] {
match self.global_cache.get(&ptr) {
Some(v) => v,
None => &[],
}
}
pub fn intern_at_reserved(
&mut self,
id: interpret::AllocId,

View File

@ -224,6 +224,7 @@ impl FlagComputation {
ConstVal::ByteStr(_) |
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Value(_) |
ConstVal::Variant(_) => {}
ConstVal::Function(_, substs) => {
self.add_substs(substs);

View File

@ -17,13 +17,13 @@ use util::ppaux;
use std::fmt;
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct Instance<'tcx> {
pub def: InstanceDef<'tcx>,
pub substs: &'tcx Substs<'tcx>,
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub enum InstanceDef<'tcx> {
Item(DefId),
Intrinsic(DefId),

View File

@ -342,7 +342,7 @@ impl AddAssign for Size {
/// Each field is a power of two, giving the alignment a maximum
/// value of 2<sup>(2<sup>8</sup> - 1)</sup>, which is limited by LLVM to a i32, with
/// a maximum capacity of 2<sup>31</sup> - 1 or 2147483647.
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, RustcEncodable, RustcDecodable)]
pub struct Align {
abi: u8,
pref: u8,

View File

@ -1249,6 +1249,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> {
ConstVal::ByteStr(b) => ConstVal::ByteStr(b),
ConstVal::Bool(b) => ConstVal::Bool(b),
ConstVal::Char(c) => ConstVal::Char(c),
ConstVal::Value(v) => ConstVal::Value(v),
ConstVal::Variant(def_id) => ConstVal::Variant(def_id),
ConstVal::Function(def_id, substs) => {
ConstVal::Function(def_id, substs.fold_with(folder))
@ -1304,6 +1305,7 @@ impl<'tcx> TypeFoldable<'tcx> for ConstVal<'tcx> {
ConstVal::ByteStr(_) |
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Value(_) |
ConstVal::Variant(_) => false,
ConstVal::Function(_, substs) => substs.visit_with(visitor),
ConstVal::Aggregate(ConstAggregate::Struct(fields)) => {

View File

@ -146,6 +146,7 @@ fn push_const<'tcx>(stack: &mut TypeWalkerStack<'tcx>, constant: &'tcx ty::Const
ConstVal::ByteStr(_) |
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Value(_) |
ConstVal::Variant(_) => {}
ConstVal::Function(_, substs) => {
stack.extend(substs.types().rev());

View File

@ -224,6 +224,7 @@ impl<'a, 'gcx, 'tcx> WfPredicates<'a, 'gcx, 'tcx> {
ConstVal::Bool(_) |
ConstVal::Char(_) |
ConstVal::Variant(_) |
ConstVal::Value(_) |
ConstVal::Function(..) => {}
ConstVal::Aggregate(ConstAggregate::Struct(fields)) => {
for &(_, v) in fields {

View File

@ -121,6 +121,7 @@ fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result {
ConstVal::Variant(_) |
ConstVal::Function(..) |
ConstVal::Aggregate(_) |
ConstVal::Value(_) |
ConstVal::Unevaluated(..) => bug!("{:?} not printable in a pattern", value)
}
}

View File

@ -24,11 +24,12 @@ use rustc::hir::def_id::{CrateNum, DefId, DefIndex,
CRATE_DEF_INDEX, LOCAL_CRATE};
use rustc::ich::Fingerprint;
use rustc::middle::lang_items;
use rustc::mir;
use rustc::mir::{self, interpret};
use rustc::session::Session;
use rustc::ty::{self, Ty, TyCtxt};
use rustc::ty::codec::TyDecoder;
use rustc::mir::Mir;
use rustc::util::nodemap::FxHashMap;
use std::cell::Ref;
use std::collections::BTreeMap;
@ -54,6 +55,9 @@ pub struct DecodeContext<'a, 'tcx: 'a> {
last_filemap_index: usize,
lazy_state: LazyState,
// interpreter allocation cache
interpret_alloc_cache: FxHashMap<usize, interpret::AllocId>,
}
/// Abstract over the various ways one can create metadata decoders.
@ -72,6 +76,7 @@ pub trait Metadata<'a, 'tcx>: Copy {
tcx,
last_filemap_index: 0,
lazy_state: LazyState::NoNode,
interpret_alloc_cache: FxHashMap::default(),
}
}
}
@ -268,6 +273,45 @@ impl<'a, 'tcx> SpecializedDecoder<DefIndex> for DecodeContext<'a, 'tcx> {
}
}
impl<'a, 'tcx> SpecializedDecoder<interpret::AllocId> for DecodeContext<'a, 'tcx> {
fn specialized_decode(&mut self) -> Result<interpret::AllocId, Self::Error> {
const MAX1: usize = usize::max_value() - 1;
let mut interpret_interner = self.tcx.unwrap().interpret_interner.borrow_mut();
let pos = self.position();
match self.read_usize()? {
::std::usize::MAX => {
let allocation = interpret::Allocation::decode(self)?;
let id = interpret_interner.reserve();
let allocation = self.tcx.unwrap().intern_const_alloc(allocation);
interpret_interner.intern_at_reserved(id, allocation);
let id = interpret::AllocId(id);
self.interpret_alloc_cache.insert(pos, id);
let num = usize::decode(self)?;
let ptr = interpret::Pointer {
primval: interpret::PrimVal::Ptr(interpret::MemoryPointer {
alloc_id: id,
offset: 0,
}),
};
for _ in 0..num {
let glob = interpret::GlobalId::decode(self)?;
interpret_interner.cache(glob, ptr);
}
Ok(id)
},
MAX1 => {
let instance = ty::Instance::decode(self)?;
let id = interpret::AllocId(interpret_interner.create_fn_alloc(instance));
self.interpret_alloc_cache.insert(pos, id);
Ok(id)
},
shorthand => Ok(self.interpret_alloc_cache[&shorthand]),
}
}
}
impl<'a, 'tcx> SpecializedDecoder<Span> for DecodeContext<'a, 'tcx> {
fn specialized_decode(&mut self) -> Result<Span, Self::Error> {
let tag = u8::decode(self)?;

View File

@ -23,7 +23,7 @@ use rustc::middle::dependency_format::Linkage;
use rustc::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel,
metadata_symbol_name};
use rustc::middle::lang_items;
use rustc::mir;
use rustc::mir::{self, interpret};
use rustc::traits::specialization_graph;
use rustc::ty::{self, Ty, TyCtxt, ReprOptions, SymbolName};
use rustc::ty::codec::{self as ty_codec, TyEncoder};
@ -59,6 +59,7 @@ pub struct EncodeContext<'a, 'tcx: 'a> {
lazy_state: LazyState,
type_shorthands: FxHashMap<Ty<'tcx>, usize>,
predicate_shorthands: FxHashMap<ty::Predicate<'tcx>, usize>,
interpret_alloc_shorthands: FxHashMap<interpret::AllocId, usize>,
// This is used to speed up Span encoding.
filemap_cache: Lrc<FileMap>,
@ -186,6 +187,41 @@ impl<'a, 'tcx> SpecializedEncoder<Ty<'tcx>> for EncodeContext<'a, 'tcx> {
}
}
impl<'a, 'tcx> SpecializedEncoder<interpret::AllocId> for EncodeContext<'a, 'tcx> {
fn specialized_encode(&mut self, alloc_id: &interpret::AllocId) -> Result<(), Self::Error> {
if let Some(shorthand) = self.interpret_alloc_shorthands.get(alloc_id).cloned() {
return self.emit_usize(shorthand);
}
let start = self.position();
let interpret_interner = self.tcx.interpret_interner.borrow();
if let Some(alloc) = interpret_interner.get_alloc(alloc_id.0) {
usize::max_value().encode(self)?;
alloc.encode(self)?;
let globals = interpret_interner.get_globals(interpret::Pointer {
primval: interpret::PrimVal::Ptr(interpret::MemoryPointer {
alloc_id: *alloc_id,
offset: 0,
}),
});
globals.len().encode(self)?;
for glob in globals {
glob.encode(self)?;
}
} else if let Some(fn_instance) = interpret_interner.get_fn(alloc_id.0) {
(usize::max_value() - 1).encode(self)?;
fn_instance.encode(self)?;
} else {
bug!("alloc id without corresponding allocation: {}", alloc_id.0);
}
let len = self.position() - start * 7;
// Check that the shorthand is a not longer than the
// full encoding itself, i.e. it's an obvious win.
assert!(len >= 64 || (start as u64) < (1 << len));
self.interpret_alloc_shorthands.insert(*alloc_id, start);
Ok(())
}
}
impl<'a, 'tcx> SpecializedEncoder<ty::GenericPredicates<'tcx>> for EncodeContext<'a, 'tcx> {
fn specialized_encode(&mut self,
predicates: &ty::GenericPredicates<'tcx>)
@ -1699,6 +1735,7 @@ pub fn encode_metadata<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
type_shorthands: Default::default(),
predicate_shorthands: Default::default(),
filemap_cache: tcx.sess.codemap().files()[0].clone(),
interpret_alloc_shorthands: Default::default(),
};
// Encode the rustc version string in a predictable location.

View File

@ -241,24 +241,24 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}
pub(super) fn const_to_value(&mut self, const_val: &ConstVal<'tcx>, ty: Ty<'tcx>) -> EvalResult<'tcx, Value> {
use rustc::middle::const_val::ConstVal::*;
use rustc::middle::const_val::ConstVal;
let primval = match *const_val {
Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()),
ConstVal::Integral(const_int) => PrimVal::Bytes(const_int.to_u128_unchecked()),
Float(val) => PrimVal::Bytes(val.bits),
ConstVal::Float(val) => PrimVal::Bytes(val.bits),
Bool(b) => PrimVal::from_bool(b),
Char(c) => PrimVal::from_char(c),
ConstVal::Bool(b) => PrimVal::from_bool(b),
ConstVal::Char(c) => PrimVal::from_char(c),
Str(ref s) => return self.str_to_value(s),
ConstVal::Str(ref s) => return self.str_to_value(s),
ByteStr(ref bs) => {
ConstVal::ByteStr(ref bs) => {
let ptr = self.memory.allocate_cached(bs.data);
PrimVal::Ptr(ptr)
}
Unevaluated(def_id, substs) => {
ConstVal::Unevaluated(def_id, substs) => {
let instance = self.resolve(def_id, substs)?;
return Ok(self.read_global_as_value(GlobalId {
instance,
@ -266,10 +266,11 @@ impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
}, self.layout_of(ty)?));
}
Aggregate(..) |
Variant(_) => bug!("should not have aggregate or variant constants in MIR"),
ConstVal::Aggregate(..) |
ConstVal::Variant(_) => bug!("should not have aggregate or variant constants in MIR"),
// function items are zero sized and thus have no readable value
Function(..) => PrimVal::Undef,
ConstVal::Function(..) => PrimVal::Undef,
ConstVal::Value(val) => return Ok(val),
};
Ok(Value::ByVal(primval))

View File

@ -108,6 +108,7 @@ impl<'a, 'tcx> Const<'tcx> {
ConstVal::Unevaluated(..) => {
bug!("MIR must not use `{:?}` (aggregates are expanded to MIR rvalues)", cv)
}
ConstVal::Value(_) => unimplemented!(),
};
assert!(!ty.has_erasable_regions());