Skip to content

Commit b361d98

Browse files
author
Vytautas Astrauskas
committed
Implement basic support for concurrency (Linux only).
1 parent d935f62 commit b361d98

File tree

23 files changed

+603
-104
lines changed

23 files changed

+603
-104
lines changed

src/diagnostics.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ pub enum NonHaltingDiagnostic {
4646

4747
/// Emit a custom diagnostic without going through the miri-engine machinery
4848
pub fn report_error<'tcx, 'mir>(
49-
ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
49+
ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
5050
mut e: InterpErrorInfo<'tcx>,
5151
) -> Option<i64> {
5252
use InterpError::*;
@@ -112,7 +112,7 @@ pub fn report_error<'tcx, 'mir>(
112112
/// Report an error or note (depending on the `error` argument) at the current frame's current statement.
113113
/// Also emits a full stacktrace of the interpreter stack.
114114
fn report_msg<'tcx, 'mir>(
115-
ecx: &InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
115+
ecx: &InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
116116
title: &str,
117117
span_msg: String,
118118
helps: &[String],
@@ -168,7 +168,7 @@ pub fn register_diagnostic(e: NonHaltingDiagnostic) {
168168
DIAGNOSTICS.with(|diagnostics| diagnostics.borrow_mut().push(e));
169169
}
170170

171-
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
171+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
172172
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
173173
/// Emit all diagnostics that were registed with `register_diagnostics`
174174
fn process_diagnostics(&self) {

src/eval.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ pub fn create_ecx<'mir, 'tcx: 'mir>(
5959
tcx: TyCtxt<'tcx>,
6060
main_id: DefId,
6161
config: MiriConfig,
62-
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'tcx>>, MPlaceTy<'tcx, Tag>)> {
62+
) -> InterpResult<'tcx, (InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>, MPlaceTy<'tcx, Tag>)> {
6363
let tcx_at = tcx.at(rustc_span::source_map::DUMMY_SP);
6464
let param_env = ty::ParamEnv::reveal_all();
6565
let layout_cx = LayoutCx { tcx, param_env };
@@ -201,7 +201,8 @@ pub fn eval_main<'tcx>(tcx: TyCtxt<'tcx>, main_id: DefId, config: MiriConfig) ->
201201
// Perform the main execution.
202202
let res: InterpResult<'_, i64> = (|| {
203203
// Main loop.
204-
while ecx.step()? {
204+
while ecx.schedule()? {
205+
assert!(ecx.step()?);
205206
ecx.process_diagnostics();
206207
}
207208
// Read the return code pointer *before* we run TLS destructors, to assert

src/helpers.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rand::RngCore;
1212

1313
use crate::*;
1414

15-
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
15+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
1616

1717
/// Gets an instance for a path.
1818
fn try_resolve_did<'mir, 'tcx>(tcx: TyCtxt<'tcx>, path: &[&str]) -> Option<DefId> {
@@ -264,7 +264,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
264264
unsafe_cell_action: F,
265265
}
266266

267-
impl<'ecx, 'mir, 'tcx, F> ValueVisitor<'mir, 'tcx, Evaluator<'tcx>>
267+
impl<'ecx, 'mir, 'tcx: 'mir, F> ValueVisitor<'mir, 'tcx, Evaluator<'mir, 'tcx>>
268268
for UnsafeCellVisitor<'ecx, 'mir, 'tcx, F>
269269
where
270270
F: FnMut(MPlaceTy<'tcx, Tag>) -> InterpResult<'tcx>,

src/intptrcast.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ impl Default for GlobalState {
4141
impl<'mir, 'tcx> GlobalState {
4242
pub fn int_to_ptr(
4343
int: u64,
44-
memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
44+
memory: &Memory<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
4545
) -> InterpResult<'tcx, Pointer<Tag>> {
4646
let global_state = memory.extra.intptrcast.borrow();
4747
let pos = global_state.int_to_ptr_map.binary_search_by_key(&int, |(addr, _)| *addr);
@@ -73,7 +73,7 @@ impl<'mir, 'tcx> GlobalState {
7373

7474
pub fn ptr_to_int(
7575
ptr: Pointer<Tag>,
76-
memory: &Memory<'mir, 'tcx, Evaluator<'tcx>>,
76+
memory: &Memory<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
7777
) -> InterpResult<'tcx, u64> {
7878
let mut global_state = memory.extra.intptrcast.borrow_mut();
7979
let global_state = &mut *global_state;

src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ extern crate rustc_ast;
1212
#[macro_use] extern crate rustc_middle;
1313
extern crate rustc_data_structures;
1414
extern crate rustc_hir;
15+
extern crate rustc_index;
1516
extern crate rustc_mir;
1617
extern crate rustc_span;
1718
extern crate rustc_target;
@@ -26,6 +27,7 @@ mod operator;
2627
mod range_map;
2728
mod shims;
2829
mod stacked_borrows;
30+
mod threads;
2931

3032
// Make all those symbols available in the same place as our own.
3133
pub use rustc_mir::interpret::*;
@@ -60,6 +62,7 @@ pub use crate::range_map::RangeMap;
6062
pub use crate::stacked_borrows::{
6163
EvalContextExt as StackedBorEvalContextExt, Item, Permission, PtrId, Stack, Stacks, Tag,
6264
};
65+
pub use crate::threads::EvalContextExt as ThreadsEvalContextExt;
6366

6467
/// Insert rustc arguments at the beginning of the argument list that Miri wants to be
6568
/// set per default, for maximal validation power.

src/machine.rs

Lines changed: 51 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ use rustc_target::abi::{LayoutOf, Size};
2525

2626
use crate::*;
2727

28+
pub use crate::threads::{ThreadId, ThreadSet, ThreadLocalStorage};
29+
2830
// Some global facts about the emulated machine.
2931
pub const PAGE_SIZE: u64 = 4 * 1024; // FIXME: adjust to target architecture
3032
pub const STACK_ADDR: u64 = 32 * PAGE_SIZE; // not really about the "stack", but where we start assigning integer addresses to allocations
@@ -81,6 +83,7 @@ pub struct AllocExtra {
8183
pub struct MemoryExtra {
8284
pub stacked_borrows: Option<stacked_borrows::MemoryExtra>,
8385
pub intptrcast: intptrcast::MemoryExtra,
86+
pub tls: ThreadLocalStorage,
8487

8588
/// Mapping extern static names to their canonical allocation.
8689
extern_statics: FxHashMap<Symbol, AllocId>,
@@ -107,6 +110,7 @@ impl MemoryExtra {
107110
extern_statics: FxHashMap::default(),
108111
rng: RefCell::new(rng),
109112
tracked_alloc_id,
113+
tls: Default::default(),
110114
}
111115
}
112116

@@ -169,7 +173,7 @@ impl<'mir, 'tcx: 'mir> PrimitiveLayouts<'tcx> {
169173
}
170174

171175
/// The machine itself.
172-
pub struct Evaluator<'tcx> {
176+
pub struct Evaluator<'mir, 'tcx> {
173177
/// Environment variables set by `setenv`.
174178
/// Miri does not expose env vars from the host to the emulated program.
175179
pub(crate) env_vars: EnvVars<'tcx>,
@@ -205,6 +209,9 @@ pub struct Evaluator<'tcx> {
205209
/// The "time anchor" for this machine's monotone clock (for `Instant` simulation).
206210
pub(crate) time_anchor: Instant,
207211

212+
/// The set of threads.
213+
pub(crate) threads: ThreadSet<'mir, 'tcx>,
214+
208215
/// Precomputed `TyLayout`s for primitive data types that are commonly used inside Miri.
209216
/// FIXME: Search through the rest of the codebase for more layout_of() calls that
210217
/// could be stored here.
@@ -235,12 +242,13 @@ impl<'tcx> Evaluator<'tcx> {
235242
panic_payload: None,
236243
time_anchor: Instant::now(),
237244
layouts,
245+
threads: Default::default(),
238246
}
239247
}
240248
}
241249

242250
/// A rustc InterpCx for Miri.
243-
pub type MiriEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, Evaluator<'tcx>>;
251+
pub type MiriEvalContext<'mir, 'tcx> = InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>;
244252

245253
/// A little trait that's useful to be inherited by extension traits.
246254
pub trait MiriEvalContextExt<'mir, 'tcx> {
@@ -259,7 +267,7 @@ impl<'mir, 'tcx> MiriEvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx>
259267
}
260268

261269
/// Machine hook implementations.
262-
impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
270+
impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'mir, 'tcx> {
263271
type MemoryKind = MiriMemoryKind;
264272

265273
type FrameExtra = FrameData<'tcx>;
@@ -275,6 +283,19 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
275283

276284
const CHECK_ALIGN: bool = true;
277285

286+
#[inline(always)]
287+
fn stack<'a>(
288+
ecx: &'a InterpCx<'mir, 'tcx, Self>
289+
) -> &'a [Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>] {
290+
ecx.active_thread_stack()
291+
}
292+
293+
fn stack_mut<'a>(
294+
ecx: &'a mut InterpCx<'mir, 'tcx, Self>
295+
) -> &'a mut Vec<Frame<'mir, 'tcx, Self::PointerTag, Self::FrameExtra>> {
296+
ecx.active_thread_stack_mut()
297+
}
298+
278299
#[inline(always)]
279300
fn enforce_validity(ecx: &InterpCx<'mir, 'tcx, Self>) -> bool {
280301
ecx.machine.validate
@@ -367,29 +388,39 @@ impl<'mir, 'tcx> Machine<'mir, 'tcx> for Evaluator<'tcx> {
367388

368389
fn canonical_alloc_id(mem: &Memory<'mir, 'tcx, Self>, id: AllocId) -> AllocId {
369390
let tcx = mem.tcx;
370-
// Figure out if this is an extern static, and if yes, which one.
371-
let def_id = match tcx.alloc_map.lock().get(id) {
372-
Some(GlobalAlloc::Static(def_id)) if tcx.is_foreign_item(def_id) => def_id,
391+
let alloc = tcx.alloc_map.lock().get(id);
392+
match alloc {
393+
Some(GlobalAlloc::Static(def_id)) if tcx.is_foreign_item(def_id) => {
394+
// Figure out if this is an extern static, and if yes, which one.
395+
let attrs = tcx.get_attrs(def_id);
396+
let link_name = match attr::first_attr_value_str_by_name(&attrs, sym::link_name) {
397+
Some(name) => name,
398+
None => tcx.item_name(def_id),
399+
};
400+
// Check if we know this one.
401+
if let Some(canonical_id) = mem.extra.extern_statics.get(&link_name) {
402+
trace!("canonical_alloc_id: {:?} ({}) -> {:?}", id, link_name, canonical_id);
403+
*canonical_id
404+
} else {
405+
// Return original id; `Memory::get_static_alloc` will throw an error.
406+
id
407+
}
408+
},
409+
Some(GlobalAlloc::Static(def_id)) if tcx.has_attr(def_id, sym::thread_local) => {
410+
// We have a thread local, so we need to get a unique allocation id for it.
411+
mem.extra.tls.get_or_register_allocation(*tcx, id)
412+
},
373413
_ => {
374414
// No need to canonicalize anything.
375-
return id;
415+
id
376416
}
377-
};
378-
let attrs = tcx.get_attrs(def_id);
379-
let link_name = match attr::first_attr_value_str_by_name(&attrs, sym::link_name) {
380-
Some(name) => name,
381-
None => tcx.item_name(def_id),
382-
};
383-
// Check if we know this one.
384-
if let Some(canonical_id) = mem.extra.extern_statics.get(&link_name) {
385-
trace!("canonical_alloc_id: {:?} ({}) -> {:?}", id, link_name, canonical_id);
386-
*canonical_id
387-
} else {
388-
// Return original id; `Memory::get_static_alloc` will throw an error.
389-
id
390417
}
391418
}
392419

420+
fn resolve_thread_local_allocation_id(extra: &Self::MemoryExtra, id: AllocId) -> AllocId {
421+
extra.tls.resolve_allocation(id)
422+
}
423+
393424
fn init_allocation_extra<'b>(
394425
memory_extra: &MemoryExtra,
395426
id: AllocId,

src/shims/dlsym.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ impl Dlsym {
2020
}
2121
}
2222

23-
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
23+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
2424
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
2525
fn call_dlsym(
2626
&mut self,

src/shims/env.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ pub struct EnvVars<'tcx> {
3535

3636
impl<'tcx> EnvVars<'tcx> {
3737
pub(crate) fn init<'mir>(
38-
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
38+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
3939
mut excluded_env_vars: Vec<String>,
4040
) -> InterpResult<'tcx> {
4141
let target_os = ecx.tcx.sess.target.target.target_os.as_str();
@@ -61,7 +61,7 @@ impl<'tcx> EnvVars<'tcx> {
6161
}
6262

6363
pub(crate) fn cleanup<'mir>(
64-
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
64+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
6565
) -> InterpResult<'tcx> {
6666
// Deallocate individual env vars.
6767
for (_name, ptr) in ecx.machine.env_vars.map.drain() {
@@ -78,7 +78,7 @@ impl<'tcx> EnvVars<'tcx> {
7878
fn alloc_env_var_as_c_str<'mir, 'tcx>(
7979
name: &OsStr,
8080
value: &OsStr,
81-
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
81+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
8282
) -> InterpResult<'tcx, Pointer<Tag>> {
8383
let mut name_osstring = name.to_os_string();
8484
name_osstring.push("=");
@@ -89,15 +89,15 @@ fn alloc_env_var_as_c_str<'mir, 'tcx>(
8989
fn alloc_env_var_as_wide_str<'mir, 'tcx>(
9090
name: &OsStr,
9191
value: &OsStr,
92-
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'tcx>>,
92+
ecx: &mut InterpCx<'mir, 'tcx, Evaluator<'mir, 'tcx>>,
9393
) -> InterpResult<'tcx, Pointer<Tag>> {
9494
let mut name_osstring = name.to_os_string();
9595
name_osstring.push("=");
9696
name_osstring.push(value);
9797
Ok(ecx.alloc_os_str_as_wide_str(name_osstring.as_os_str(), MiriMemoryKind::Env.into()))
9898
}
9999

100-
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
100+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
101101
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
102102
fn getenv(&mut self, name_op: OpTy<'tcx, Tag>) -> InterpResult<'tcx, Scalar<Tag>> {
103103
let this = self.eval_context_mut();

src/shims/foreign_items.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use rustc_ast::attr;
1212

1313
use crate::*;
1414

15-
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
15+
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
1616
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
1717
/// Returns the minimum alignment for the target architecture for allocations of the given size.
1818
fn min_align(&self, size: u64, kind: MiriMemoryKind) -> Align {

0 commit comments

Comments
 (0)