Skip to content

Commit c817e6e

Browse files
authored
Merge pull request #171 from RalfJung/println
Work with MIR-libstd
2 parents 53412ab + d06c165 commit c817e6e

23 files changed

+580
-132
lines changed

README.md

+23
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,29 @@ Then, inside your own project, use `cargo +nightly miri` to run your project, if
5454
a bin project, or run `cargo +nightly miri test` to run all tests in your project
5555
through miri.
5656

57+
## Running miri with full libstd
58+
59+
Per default libstd does not contain the MIR of non-polymorphic functions. When
60+
miri hits a call to such a function, execution terminates. To fix this, it is
61+
possible to compile libstd with full MIR:
62+
63+
```sh
64+
rustup component add rust-src
65+
chmod +x -R ~/.rustup/toolchains/*/lib/rustlib/src/rust/src/jemalloc/include/jemalloc/
66+
cargo install xargo
67+
cd xargo/
68+
RUSTFLAGS='-Zalways-encode-mir' xargo build
69+
```
70+
71+
Now you can run miri against the libstd compiled by xargo:
72+
73+
```sh
74+
cargo run --bin miri -- --sysroot ~/.xargo/HOST tests/run-pass/vecs.rs
75+
```
76+
77+
Notice that you will have to re-run the last step of the preparations above when
78+
your toolchain changes (e.g., when you update the nightly).
79+
5780
## Contributing and getting help
5881

5982
Check out the issues on this GitHub repository for some ideas. There's lots that

src/bin/miri.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) {
8484
if i.attrs.iter().any(|attr| attr.name().map_or(false, |n| n == "test")) {
8585
let did = self.1.hir.body_owner_def_id(body_id);
8686
println!("running test: {}", self.1.hir.def_path(did).to_string(self.1));
87-
miri::eval_main(self.1, did, self.0);
87+
miri::eval_main(self.1, did, None, self.0);
8888
self.2.session.abort_if_errors();
8989
}
9090
}
@@ -95,7 +95,9 @@ fn after_analysis<'a, 'tcx>(state: &mut CompileState<'a, 'tcx>) {
9595
state.hir_crate.unwrap().visit_all_item_likes(&mut Visitor(limits, tcx, state));
9696
} else if let Some((entry_node_id, _)) = *state.session.entry_fn.borrow() {
9797
let entry_def_id = tcx.hir.local_def_id(entry_node_id);
98-
miri::eval_main(tcx, entry_def_id, limits);
98+
let start_wrapper = tcx.lang_items.start_fn().and_then(|start_fn|
99+
if tcx.is_mir_available(start_fn) { Some(start_fn) } else { None });
100+
miri::eval_main(tcx, entry_def_id, start_wrapper, limits);
99101

100102
state.session.abort_if_errors();
101103
} else {

src/error.rs

+8
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ pub enum EvalError<'tcx> {
3838
},
3939
ExecutionTimeLimitReached,
4040
StackFrameLimitReached,
41+
OutOfTls,
42+
TlsOutOfBounds,
43+
AbiViolation(String),
4144
AlignmentCheckFailed {
4245
required: u64,
4346
has: u64,
@@ -101,6 +104,11 @@ impl<'tcx> Error for EvalError<'tcx> {
101104
"reached the configured maximum execution time",
102105
EvalError::StackFrameLimitReached =>
103106
"reached the configured maximum number of stack frames",
107+
EvalError::OutOfTls =>
108+
"reached the maximum number of representable TLS keys",
109+
EvalError::TlsOutOfBounds =>
110+
"accessed an invalid (unallocated) TLS key",
111+
EvalError::AbiViolation(ref msg) => msg,
104112
EvalError::AlignmentCheckFailed{..} =>
105113
"tried to execute a misaligned read or write",
106114
EvalError::CalledClosureAsFunction =>

src/eval_context.rs

+99-46
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ impl Default for ResourceLimits {
126126

127127
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
128128
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, limits: ResourceLimits) -> Self {
129+
// Register array drop glue code
129130
let source_info = mir::SourceInfo {
130131
span: DUMMY_SP,
131132
scope: mir::ARGUMENT_VISIBILITY_SCOPE
@@ -852,7 +853,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
852853
let fn_ptr = self.memory.create_fn_alloc(instance);
853854
self.write_value(Value::ByVal(PrimVal::Ptr(fn_ptr)), dest, dest_ty)?;
854855
},
855-
ref other => bug!("reify fn pointer on {:?}", other),
856+
ref other => bug!("closure fn pointer on {:?}", other),
856857
},
857858
}
858859
}
@@ -1557,6 +1558,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
15571558
}
15581559

15591560
pub(super) fn dump_local(&self, lvalue: Lvalue<'tcx>) {
1561+
// Debug output
15601562
if let Lvalue::Local { frame, local, field } = lvalue {
15611563
let mut allocs = Vec::new();
15621564
let mut msg = format!("{:?}", local);
@@ -1676,62 +1678,113 @@ impl<'tcx> Frame<'tcx> {
16761678

16771679
pub fn eval_main<'a, 'tcx: 'a>(
16781680
tcx: TyCtxt<'a, 'tcx, 'tcx>,
1679-
def_id: DefId,
1681+
main_id: DefId,
1682+
start_wrapper: Option<DefId>,
16801683
limits: ResourceLimits,
16811684
) {
1682-
let mut ecx = EvalContext::new(tcx, limits);
1683-
let instance = ty::Instance::mono(tcx, def_id);
1684-
let mir = ecx.load_mir(instance.def).expect("main function's MIR not found");
1685-
1686-
if !mir.return_ty.is_nil() || mir.arg_count != 0 {
1687-
let msg = "miri does not support main functions without `fn()` type signatures";
1688-
tcx.sess.err(&EvalError::Unimplemented(String::from(msg)).to_string());
1689-
return;
1690-
}
1691-
1692-
ecx.push_stack_frame(
1693-
instance,
1694-
DUMMY_SP,
1695-
mir,
1696-
Lvalue::from_ptr(Pointer::zst_ptr()),
1697-
StackPopCleanup::None,
1698-
).expect("could not allocate first stack frame");
1699-
1700-
loop {
1701-
match ecx.step() {
1702-
Ok(true) => {}
1703-
Ok(false) => {
1704-
let leaks = ecx.memory.leak_report();
1705-
if leaks != 0 {
1706-
tcx.sess.err("the evaluated program leaked memory");
1707-
}
1708-
return;
1685+
fn run_main<'a, 'tcx: 'a>(
1686+
ecx: &mut EvalContext<'a, 'tcx>,
1687+
main_id: DefId,
1688+
start_wrapper: Option<DefId>,
1689+
) -> EvalResult<'tcx> {
1690+
let main_instance = ty::Instance::mono(ecx.tcx, main_id);
1691+
let main_mir = ecx.load_mir(main_instance.def)?;
1692+
let mut cleanup_ptr = None; // Pointer to be deallocated when we are done
1693+
1694+
if !main_mir.return_ty.is_nil() || main_mir.arg_count != 0 {
1695+
return Err(EvalError::Unimplemented("miri does not support main functions without `fn()` type signatures".to_owned()));
1696+
}
1697+
1698+
if let Some(start_id) = start_wrapper {
1699+
let start_instance = ty::Instance::mono(ecx.tcx, start_id);
1700+
let start_mir = ecx.load_mir(start_instance.def)?;
1701+
1702+
if start_mir.arg_count != 3 {
1703+
return Err(EvalError::AbiViolation(format!("'start' lang item should have three arguments, but has {}", start_mir.arg_count)));
17091704
}
1710-
Err(e) => {
1711-
report(tcx, &ecx, e);
1712-
return;
1705+
1706+
// Return value
1707+
let ret_ptr = ecx.memory.allocate(ecx.tcx.data_layout.pointer_size.bytes(), ecx.tcx.data_layout.pointer_align.abi())?;
1708+
cleanup_ptr = Some(ret_ptr);
1709+
1710+
// Push our stack frame
1711+
ecx.push_stack_frame(
1712+
start_instance,
1713+
start_mir.span,
1714+
start_mir,
1715+
Lvalue::from_ptr(ret_ptr),
1716+
StackPopCleanup::None,
1717+
)?;
1718+
1719+
let mut args = ecx.frame().mir.args_iter();
1720+
1721+
// First argument: pointer to main()
1722+
let main_ptr = ecx.memory.create_fn_alloc(main_instance);
1723+
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
1724+
let main_ty = main_instance.def.def_ty(ecx.tcx);
1725+
let main_ptr_ty = ecx.tcx.mk_fn_ptr(main_ty.fn_sig());
1726+
ecx.write_value(Value::ByVal(PrimVal::Ptr(main_ptr)), dest, main_ptr_ty)?;
1727+
1728+
// Second argument (argc): 0
1729+
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
1730+
let ty = ecx.tcx.types.isize;
1731+
ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?;
1732+
1733+
// Third argument (argv): 0
1734+
let dest = ecx.eval_lvalue(&mir::Lvalue::Local(args.next().unwrap()))?;
1735+
let ty = ecx.tcx.mk_imm_ptr(ecx.tcx.mk_imm_ptr(ecx.tcx.types.u8));
1736+
ecx.write_value(Value::ByVal(PrimVal::Bytes(0)), dest, ty)?;
1737+
} else {
1738+
ecx.push_stack_frame(
1739+
main_instance,
1740+
main_mir.span,
1741+
main_mir,
1742+
Lvalue::from_ptr(Pointer::zst_ptr()),
1743+
StackPopCleanup::None,
1744+
)?;
1745+
}
1746+
1747+
while ecx.step()? {}
1748+
if let Some(cleanup_ptr) = cleanup_ptr {
1749+
ecx.memory.deallocate(cleanup_ptr)?;
1750+
}
1751+
return Ok(());
1752+
}
1753+
1754+
let mut ecx = EvalContext::new(tcx, limits);
1755+
match run_main(&mut ecx, main_id, start_wrapper) {
1756+
Ok(()) => {
1757+
let leaks = ecx.memory.leak_report();
1758+
if leaks != 0 {
1759+
tcx.sess.err("the evaluated program leaked memory");
17131760
}
17141761
}
1762+
Err(e) => {
1763+
report(tcx, &ecx, e);
1764+
}
17151765
}
17161766
}
17171767

17181768
fn report(tcx: TyCtxt, ecx: &EvalContext, e: EvalError) {
1719-
let frame = ecx.stack().last().expect("stackframe was empty");
1720-
let block = &frame.mir.basic_blocks()[frame.block];
1721-
let span = if frame.stmt < block.statements.len() {
1722-
block.statements[frame.stmt].source_info.span
1723-
} else {
1724-
block.terminator().source_info.span
1725-
};
1726-
let mut err = tcx.sess.struct_span_err(span, &e.to_string());
1727-
for &Frame { instance, span, .. } in ecx.stack().iter().rev() {
1728-
if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
1729-
err.span_note(span, "inside call to closure");
1730-
continue;
1769+
if let Some(frame) = ecx.stack().last() {
1770+
let block = &frame.mir.basic_blocks()[frame.block];
1771+
let span = if frame.stmt < block.statements.len() {
1772+
block.statements[frame.stmt].source_info.span
1773+
} else {
1774+
block.terminator().source_info.span
1775+
};
1776+
let mut err = tcx.sess.struct_span_err(span, &e.to_string());
1777+
for &Frame { instance, span, .. } in ecx.stack().iter().rev() {
1778+
if tcx.def_key(instance.def_id()).disambiguated_data.data == DefPathData::ClosureExpr {
1779+
err.span_note(span, "inside call to closure");
1780+
continue;
1781+
}
1782+
err.span_note(span, &format!("inside call to {}", instance));
17311783
}
1732-
err.span_note(span, &format!("inside call to {}", instance));
1784+
err.emit();
1785+
} else {
1786+
tcx.sess.err(&e.to_string());
17331787
}
1734-
err.emit();
17351788
}
17361789

17371790
// TODO(solson): Upstream these methods into rustc::ty::layout.

src/lvalue.rs

+9
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,15 @@ impl<'tcx> Global<'tcx> {
104104
initialized: false,
105105
}
106106
}
107+
108+
pub(super) fn initialized(ty: Ty<'tcx>, value: Value, mutable: bool) -> Self {
109+
Global {
110+
value,
111+
mutable,
112+
ty,
113+
initialized: true,
114+
}
115+
}
107116
}
108117

109118
impl<'a, 'tcx> EvalContext<'a, 'tcx> {

src/memory.rs

+73
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,18 @@ impl Pointer {
9898
pub fn never_ptr() -> Self {
9999
Pointer::new(NEVER_ALLOC_ID, 0)
100100
}
101+
102+
pub fn is_null_ptr(&self) -> bool {
103+
return *self == Pointer::from_int(0)
104+
}
105+
}
106+
107+
pub type TlsKey = usize;
108+
109+
#[derive(Copy, Clone, Debug)]
110+
pub struct TlsEntry<'tcx> {
111+
data: Pointer, // will eventually become a map from thread IDs to pointers
112+
dtor: Option<ty::Instance<'tcx>>,
101113
}
102114

103115
////////////////////////////////////////////////////////////////////////////////
@@ -149,6 +161,12 @@ pub struct Memory<'a, 'tcx> {
149161
/// A cache for basic byte allocations keyed by their contents. This is used to deduplicate
150162
/// allocations for string and bytestring literals.
151163
literal_alloc_cache: HashMap<Vec<u8>, AllocId>,
164+
165+
/// pthreads-style Thread-local storage. We only have one thread, so this is just a map from TLS keys (indices into the vector) to the pointer stored there.
166+
thread_local: HashMap<TlsKey, TlsEntry<'tcx>>,
167+
168+
/// The Key to use for the next thread-local allocation.
169+
next_thread_local: TlsKey,
152170
}
153171

154172
const ZST_ALLOC_ID: AllocId = AllocId(0);
@@ -167,6 +185,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
167185
packed: BTreeSet::new(),
168186
static_alloc: HashSet::new(),
169187
literal_alloc_cache: HashMap::new(),
188+
thread_local: HashMap::new(),
189+
next_thread_local: 0,
170190
}
171191
}
172192

@@ -345,6 +365,59 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
345365
pub(crate) fn clear_packed(&mut self) {
346366
self.packed.clear();
347367
}
368+
369+
pub(crate) fn create_tls_key(&mut self, dtor: Option<ty::Instance<'tcx>>) -> TlsKey {
370+
let new_key = self.next_thread_local;
371+
self.next_thread_local += 1;
372+
self.thread_local.insert(new_key, TlsEntry { data: Pointer::from_int(0), dtor });
373+
trace!("New TLS key allocated: {} with dtor {:?}", new_key, dtor);
374+
return new_key;
375+
}
376+
377+
pub(crate) fn delete_tls_key(&mut self, key: TlsKey) -> EvalResult<'tcx> {
378+
return match self.thread_local.remove(&key) {
379+
Some(_) => {
380+
trace!("TLS key {} removed", key);
381+
Ok(())
382+
},
383+
None => Err(EvalError::TlsOutOfBounds)
384+
}
385+
}
386+
387+
pub(crate) fn load_tls(&mut self, key: TlsKey) -> EvalResult<'tcx, Pointer> {
388+
return match self.thread_local.get(&key) {
389+
Some(&TlsEntry { data, .. }) => {
390+
trace!("TLS key {} loaded: {:?}", key, data);
391+
Ok(data)
392+
},
393+
None => Err(EvalError::TlsOutOfBounds)
394+
}
395+
}
396+
397+
pub(crate) fn store_tls(&mut self, key: TlsKey, new_data: Pointer) -> EvalResult<'tcx> {
398+
return match self.thread_local.get_mut(&key) {
399+
Some(&mut TlsEntry { ref mut data, .. }) => {
400+
trace!("TLS key {} stored: {:?}", key, new_data);
401+
*data = new_data;
402+
Ok(())
403+
},
404+
None => Err(EvalError::TlsOutOfBounds)
405+
}
406+
}
407+
408+
// Returns a dtor and its argument, if one is supposed to run
409+
pub(crate) fn fetch_tls_dtor(&mut self) -> Option<(ty::Instance<'tcx>, Pointer)> {
410+
for (_, &mut TlsEntry { ref mut data, dtor }) in self.thread_local.iter_mut() {
411+
if !data.is_null_ptr() {
412+
if let Some(dtor) = dtor {
413+
let old_data = *data;
414+
*data = Pointer::from_int(0);
415+
return Some((dtor, old_data));
416+
}
417+
}
418+
}
419+
return None;
420+
}
348421
}
349422

350423
// The derived `Ord` impl sorts first by the first field, then, if the fields are the same

0 commit comments

Comments
 (0)