Skip to content

Commit 83667d6

Browse files
committed
abstract mono_hash_map through a trait, only miri actually needs the fancy one
1 parent 75ea7c7 commit 83667d6

File tree

6 files changed

+167
-136
lines changed

6 files changed

+167
-136
lines changed

src/librustc_mir/const_eval.rs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212

1313
use std::fmt;
1414
use std::error::Error;
15-
use std::borrow::Cow;
15+
use std::borrow::{Borrow, Cow};
16+
use std::hash::Hash;
17+
use std::collections::hash_map::Entry;
1618

1719
use rustc::hir::{self, def_id::DefId};
1820
use rustc::mir::interpret::ConstEvalErr;
@@ -21,13 +23,14 @@ use rustc::ty::{self, TyCtxt, Instance, query::TyCtxtAt};
2123
use rustc::ty::layout::{self, LayoutOf, TyLayout};
2224
use rustc::ty::subst::Subst;
2325
use rustc_data_structures::indexed_vec::IndexVec;
26+
use rustc_data_structures::fx::FxHashMap;
2427

2528
use syntax::ast::Mutability;
2629
use syntax::source_map::{Span, DUMMY_SP};
2730

2831
use rustc::mir::interpret::{
2932
EvalResult, EvalError, EvalErrorKind, GlobalId,
30-
Scalar, Allocation, ConstValue,
33+
Scalar, Allocation, AllocId, ConstValue,
3134
};
3235
use interpret::{self,
3336
Place, PlaceTy, MemPlace, OpTy, Operand, Value,
@@ -265,6 +268,67 @@ impl<'a, 'mir, 'tcx> CompileTimeInterpreter<'a, 'mir, 'tcx> {
265268
}
266269
}
267270

271+
impl<K: Hash + Eq, V> interpret::AllocMap<K, V> for FxHashMap<K, V> {
272+
#[inline(always)]
273+
fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
274+
where K: Borrow<Q>
275+
{
276+
FxHashMap::contains_key(self, k)
277+
}
278+
279+
#[inline(always)]
280+
fn insert(&mut self, k: K, v: V) -> Option<V>
281+
{
282+
FxHashMap::insert(self, k, v)
283+
}
284+
285+
#[inline(always)]
286+
fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
287+
where K: Borrow<Q>
288+
{
289+
FxHashMap::remove(self, k)
290+
}
291+
292+
#[inline(always)]
293+
fn filter_map_collect<T>(&self, mut f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T> {
294+
self.iter()
295+
.filter_map(move |(k, v)| f(k, &*v))
296+
.collect()
297+
}
298+
299+
#[inline(always)]
300+
fn get_or<E>(
301+
&self,
302+
k: K,
303+
vacant: impl FnOnce() -> Result<V, E>
304+
) -> Result<&V, E>
305+
{
306+
match self.get(&k) {
307+
Some(v) => Ok(v),
308+
None => {
309+
vacant()?;
310+
bug!("The CTFE machine shouldn't ever need to extend the alloc_map when reading")
311+
}
312+
}
313+
}
314+
315+
#[inline(always)]
316+
fn get_mut_or<E>(
317+
&mut self,
318+
k: K,
319+
vacant: impl FnOnce() -> Result<V, E>
320+
) -> Result<&mut V, E>
321+
{
322+
match self.entry(k) {
323+
Entry::Occupied(e) => Ok(e.into_mut()),
324+
Entry::Vacant(e) => {
325+
let v = vacant()?;
326+
Ok(e.insert(v))
327+
}
328+
}
329+
}
330+
}
331+
268332
type CompileTimeEvalContext<'a, 'mir, 'tcx> =
269333
EvalContext<'a, 'mir, 'tcx, CompileTimeInterpreter<'a, 'mir, 'tcx>>;
270334

@@ -275,6 +339,8 @@ impl<'a, 'mir, 'tcx> interpret::Machine<'a, 'mir, 'tcx>
275339
type MemoryKinds = !;
276340
type PointerTag = ();
277341

342+
type MemoryMap = FxHashMap<AllocId, (MemoryKind<!>, Allocation<()>)>;
343+
278344
const STATIC_KIND: Option<!> = None; // no copying of statics allowed
279345
const ENFORCE_VALIDITY: bool = false; // for now, we don't
280346

src/librustc_mir/interpret/machine.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,68 @@
1212
//! This separation exists to ensure that no fancy miri features like
1313
//! interpreting common C functions leak into CTFE.
1414
15-
use std::borrow::Cow;
15+
use std::borrow::{Borrow, Cow};
1616
use std::hash::Hash;
1717

1818
use rustc::hir::def_id::DefId;
19-
use rustc::mir::interpret::{Allocation, EvalResult, Scalar};
19+
use rustc::mir::interpret::{Allocation, AllocId, EvalResult, Scalar};
2020
use rustc::mir;
2121
use rustc::ty::{self, layout::TyLayout, query::TyCtxtAt};
2222

23-
use super::{EvalContext, PlaceTy, OpTy};
23+
use super::{EvalContext, PlaceTy, OpTy, MemoryKind};
24+
25+
/// The functionality needed by memory to manage its allocations
26+
pub trait AllocMap<K: Hash + Eq, V> {
27+
/// Test if the map contains the given key.
28+
/// Deliberately takes `&mut` because that is sufficient, and some implementations
29+
/// can be more efficient then (using `RefCell::get_mut`).
30+
fn contains_key<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> bool
31+
where K: Borrow<Q>;
32+
33+
/// Insert new entry into the map.
34+
fn insert(&mut self, k: K, v: V) -> Option<V>;
35+
36+
/// Remove entry from the map.
37+
fn remove<Q: ?Sized + Hash + Eq>(&mut self, k: &Q) -> Option<V>
38+
where K: Borrow<Q>;
39+
40+
/// Return data based the keys and values in the map.
41+
fn filter_map_collect<T>(&self, f: impl FnMut(&K, &V) -> Option<T>) -> Vec<T>;
42+
43+
/// Return a reference to entry `k`. If no such entry exists, call
44+
/// `vacant` and either forward its error, or add its result to the map
45+
/// and return a reference to *that*.
46+
fn get_or<E>(
47+
&self,
48+
k: K,
49+
vacant: impl FnOnce() -> Result<V, E>
50+
) -> Result<&V, E>;
51+
52+
/// Return a mutable reference to entry `k`. If no such entry exists, call
53+
/// `vacant` and either forward its error, or add its result to the map
54+
/// and return a reference to *that*.
55+
fn get_mut_or<E>(
56+
&mut self,
57+
k: K,
58+
vacant: impl FnOnce() -> Result<V, E>
59+
) -> Result<&mut V, E>;
60+
}
2461

2562
/// Methods of this trait signifies a point where CTFE evaluation would fail
2663
/// and some use case dependent behaviour can instead be applied.
27-
/// FIXME: We should be able to get rid of the 'a here if we can get rid of the 'a in
28-
/// `snapshot::EvalSnapshot`.
2964
pub trait Machine<'a, 'mir, 'tcx>: Sized {
3065
/// Additional data that can be accessed via the Memory
3166
type MemoryData;
3267

3368
/// Additional memory kinds a machine wishes to distinguish from the builtin ones
3469
type MemoryKinds: ::std::fmt::Debug + Copy + Eq;
3570

71+
/// Memory's allocation map
72+
type MemoryMap:
73+
AllocMap<AllocId, (MemoryKind<Self::MemoryKinds>, Allocation<Self::PointerTag>)> +
74+
Default +
75+
Clone;
76+
3677
/// Tag tracked alongside every pointer. This is inert for now, in preparation for
3778
/// a future implementation of "Stacked Borrows"
3879
/// <https://www.ralfj.de/blog/2018/08/07/stacked-borrows.html>.

src/librustc_mir/interpret/memory.rs

Lines changed: 42 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,23 @@
1616
//! integer. It is crucial that these operations call `check_align` *before*
1717
//! short-circuiting the empty case!
1818
19-
use std::collections::hash_map::Entry;
2019
use std::collections::VecDeque;
2120
use std::ptr;
2221
use std::borrow::Cow;
2322

2423
use rustc::ty::{self, Instance, ParamEnv, query::TyCtxtAt};
2524
use rustc::ty::layout::{self, Align, TargetDataLayout, Size, HasDataLayout};
26-
use rustc::mir::interpret::{Pointer, AllocId, Allocation, ConstValue, GlobalId,
27-
EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
28-
truncate};
25+
use rustc::mir::interpret::{
26+
Pointer, AllocId, Allocation, ConstValue, GlobalId,
27+
EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
28+
truncate
29+
};
2930
pub use rustc::mir::interpret::{write_target_uint, read_target_uint};
3031
use rustc_data_structures::fx::{FxHashSet, FxHashMap};
3132

3233
use syntax::ast::Mutability;
3334

34-
use super::{Machine, MonoHashMap, ScalarMaybeUndef};
35+
use super::{Machine, AllocMap, ScalarMaybeUndef};
3536

3637
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
3738
pub enum MemoryKind<T> {
@@ -52,13 +53,13 @@ pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'a, 'mir, 'tcx>> {
5253
/// Allocations local to this instance of the miri engine. The kind
5354
/// helps ensure that the same mechanism is used for allocation and
5455
/// deallocation. When an allocation is not found here, it is a
55-
/// static and looked up in the `tcx` for read access. If this machine
56-
/// does pointer provenance tracking, the type of alloctions in `tcx`
57-
/// and here do not match, so we have a `MonoHashMap` to be able to
58-
/// put the "mapped" allocation into `alloc_map` even on a read access.
56+
/// static and looked up in the `tcx` for read access. Some machines may
57+
/// have to mutate this map even on a read-only access to a static (because
58+
/// they do pointer provenance tracking and the allocations in `tcx` have
59+
/// the wrong type), so we let the machine override this type.
5960
/// Either way, if the machine allows writing to a static, doing so will
6061
/// create a copy of the static allocation here.
61-
alloc_map: MonoHashMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<M::PointerTag>)>,
62+
alloc_map: M::MemoryMap,
6263

6364
/// To be able to compare pointers with NULL, and to check alignment for accesses
6465
/// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
@@ -106,7 +107,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
106107
pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
107108
Memory {
108109
data,
109-
alloc_map: MonoHashMap::default(),
110+
alloc_map: Default::default(),
110111
dead_alloc_map: FxHashMap::default(),
111112
tcx,
112113
}
@@ -419,30 +420,32 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
419420
&mut self,
420421
id: AllocId,
421422
) -> EvalResult<'tcx, &mut Allocation<M::PointerTag>> {
422-
Ok(match self.alloc_map.entry(id) {
423-
// Normal alloc?
424-
Entry::Occupied(alloc) => {
425-
let alloc = &mut alloc.into_mut().1;
426-
if alloc.mutability == Mutability::Immutable {
427-
return err!(ModifiedConstantMemory);
428-
}
429-
alloc
423+
let tcx = self.tcx;
424+
let a = self.alloc_map.get_mut_or(id, || {
425+
// Need to make a copy, even if `get_static_alloc` is able
426+
// to give us a cheap reference.
427+
let alloc = Self::get_static_alloc(tcx, id)?;
428+
if alloc.mutability == Mutability::Immutable {
429+
return err!(ModifiedConstantMemory);
430430
}
431-
// Static.
432-
Entry::Vacant(entry) => {
433-
// Need to make a copy, even if `get_static_alloc` is able
434-
// to give us a cheap reference.
435-
let alloc = Self::get_static_alloc(self.tcx, id)?;
436-
if alloc.mutability == Mutability::Immutable {
431+
let kind = M::STATIC_KIND.expect(
432+
"I got an owned allocation that I have to copy but the machine does \
433+
not expect that to happen"
434+
);
435+
Ok((MemoryKind::Machine(kind), alloc.into_owned()))
436+
});
437+
// Unpack the error type manually because type inference doesn't
438+
// work otherwise (and we cannot help it because `impl Trait`)
439+
match a {
440+
Err(e) => Err(e),
441+
Ok(a) => {
442+
let a = &mut a.1;
443+
if a.mutability == Mutability::Immutable {
437444
return err!(ModifiedConstantMemory);
438445
}
439-
let kind = M::STATIC_KIND.expect(
440-
"I got an owned allocation that I have to copy but the machine does \
441-
not expect that to happen"
442-
);
443-
&mut entry.insert(Box::new((MemoryKind::Machine(kind), alloc.into_owned()))).1
446+
Ok(a)
444447
}
445-
})
448+
}
446449
}
447450

448451
pub fn get_fn(&self, ptr: Pointer<M::PointerTag>) -> EvalResult<'tcx, Instance<'tcx>> {
@@ -534,8 +537,8 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
534537
let msg = format!("Alloc {:<5} ", format!("{}:", id));
535538

536539
// normal alloc?
537-
match self.alloc_map.get(&id) {
538-
Some((kind, alloc)) => {
540+
match self.alloc_map.get_or(id, || Err(())) {
541+
Ok((kind, alloc)) => {
539542
let extra = match kind {
540543
MemoryKind::Stack => " (stack)".to_owned(),
541544
MemoryKind::Vtable => " (vtable)".to_owned(),
@@ -546,7 +549,7 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
546549
msg, alloc, extra
547550
);
548551
},
549-
None => {
552+
Err(()) => {
550553
// static alloc?
551554
match self.tcx.alloc_map.lock().get(id) {
552555
Some(AllocType::Memory(alloc)) => {
@@ -664,7 +667,11 @@ impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
664667
}
665668

666669
/// Interning (for CTFE)
667-
impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx, PointerTag=()>> Memory<'a, 'mir, 'tcx, M> {
670+
impl<'a, 'mir, 'tcx, M> Memory<'a, 'mir, 'tcx, M>
671+
where
672+
M: Machine<'a, 'mir, 'tcx, PointerTag=()>,
673+
M::MemoryMap: AllocMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation<()>)>,
674+
{
668675
/// mark an allocation as static and initialized, either mutable or not
669676
pub fn intern_static(
670677
&mut self,

src/librustc_mir/interpret/mod.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ mod terminator;
2323
mod traits;
2424
mod validity;
2525
mod intrinsics;
26-
mod mono_hash_map;
2726

2827
pub use self::eval_context::{
2928
EvalContext, Frame, StackPopCleanup, LocalValue,
@@ -33,10 +32,8 @@ pub use self::place::{Place, PlaceTy, MemPlace, MPlaceTy};
3332

3433
pub use self::memory::{Memory, MemoryKind};
3534

36-
pub use self::machine::Machine;
35+
pub use self::machine::{Machine, AllocMap};
3736

3837
pub use self::operand::{ScalarMaybeUndef, Value, ValTy, Operand, OpTy};
3938

4039
pub use self::validity::RefTracking;
41-
42-
pub use self::mono_hash_map::MonoHashMap;

0 commit comments

Comments
 (0)