From 5a7f4412eed61ac82880d67d2bc5275c5dee4922 Mon Sep 17 00:00:00 2001 From: Dylan MacKenzie Date: Fri, 29 Jun 2018 12:48:55 -0700 Subject: [PATCH] Implement `Eq` and `Hash` for MemoryData and Evaluator In order to implement infinite loop detection while executing MIR, both the implementor of `Machine` (`Evaluator`) and its associated type (`MemoryData`), must implement `Eq` and `Hash`. This PR adds the required trait implementations. It's possible that the `Hash` implementations need to be improved; only the `env_vars` field of `Evaluator` and the `thread_local` field of `MemoryData` are actually being hashed. Omitting fields from a `Hash` implementation is not incorrect, but could lead to collisions if the ignored fields are changing constantly. Perhaps I should instead derive `Hash` on a few more fields related to MIR validation? --- src/lib.rs | 41 ++++++++++++++++++++++++++++++++++++++--- src/locks.rs | 2 +- src/range_map.rs | 2 +- 3 files changed, 40 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e3fd5b8a06..ffb17527ad 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -26,10 +26,13 @@ use rustc::hir::def_id::DefId; use rustc::mir; use rustc::middle::const_val; +use rustc_data_structures::fx::FxHasher; + use syntax::ast::Mutability; use syntax::codemap::Span; use std::collections::{HashMap, BTreeMap}; +use std::hash::{Hash, Hasher}; pub use rustc::mir::interpret::*; pub use rustc_mir::interpret::*; @@ -295,7 +298,7 @@ pub fn eval_main<'a, 'tcx: 'a>( } } -#[derive(Default)] +#[derive(Clone, Default, PartialEq, Eq)] pub struct Evaluator<'tcx> { /// Environment variables set by `setenv` /// Miri does not expose env vars from the host to the emulated program @@ -305,15 +308,34 @@ pub struct Evaluator<'tcx> { pub(crate) suspended: HashMap>>, } +impl<'tcx> Hash for Evaluator<'tcx> { + fn hash(&self, state: &mut H) { + let Evaluator { + env_vars, + suspended: _, + } = self; + + env_vars.iter() + .map(|(env, ptr)| { + let mut h = FxHasher::default(); + env.hash(&mut h); + ptr.hash(&mut h); + h.finish() + }) + .fold(0u64, |acc, hash| acc.wrapping_add(hash)) + .hash(state); + } +} + pub type TlsKey = u128; -#[derive(Copy, Clone, Debug)] +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub struct TlsEntry<'tcx> { data: Scalar, // Will eventually become a map from thread IDs to `Scalar`s, if we ever support more than one thread. dtor: Option>, } -#[derive(Default)] +#[derive(Clone, Default, PartialEq, Eq)] pub struct MemoryData<'tcx> { /// The Key to use for the next thread-local allocation. next_thread_local: TlsKey, @@ -330,6 +352,19 @@ pub struct MemoryData<'tcx> { statics: HashMap, AllocId>, } +impl<'tcx> Hash for MemoryData<'tcx> { + fn hash(&self, state: &mut H) { + let MemoryData { + next_thread_local: _, + thread_local, + locks: _, + statics: _, + } = self; + + thread_local.hash(state); + } +} + impl<'mir, 'tcx: 'mir> Machine<'mir, 'tcx> for Evaluator<'tcx> { type MemoryData = MemoryData<'tcx>; type MemoryKinds = memory::MemoryKind; diff --git a/src/locks.rs b/src/locks.rs index a463f8ba57..9947609d37 100644 --- a/src/locks.rs +++ b/src/locks.rs @@ -7,7 +7,7 @@ use rustc::ty::layout::Size; //////////////////////////////////////////////////////////////////////////////// /// Information about a lock that is currently held. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct LockInfo<'tcx> { /// Stores for which lifetimes (of the original write lock) we got /// which suspensions. diff --git a/src/range_map.rs b/src/range_map.rs index 5cdcbe3512..118be32a29 100644 --- a/src/range_map.rs +++ b/src/range_map.rs @@ -7,7 +7,7 @@ use std::collections::BTreeMap; use std::ops; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq, Eq)] pub struct RangeMap { map: BTreeMap, }