1
Fork 0

Merge remote-tracking branch 'origin/master' into alignment

This commit is contained in:
Oliver Schneider 2016-07-07 13:17:08 +02:00
commit 4c258d1ed2
No known key found for this signature in database
GPG key ID: 56D6EEA0FC67AC46
6 changed files with 138 additions and 24 deletions

View file

@ -4,7 +4,6 @@ extern crate getopts;
extern crate miri;
extern crate rustc;
extern crate rustc_driver;
extern crate rustc_plugin;
extern crate env_logger;
extern crate log_settings;
extern crate syntax;
@ -54,9 +53,9 @@ impl<'a> CompilerCalls<'a> for MiriCompilerCalls {
match item.node {
MetaItemKind::NameValue(ref name, ref value) => {
match &**name {
"memory_size" => memory_size = extract_str(value).parse::<u64>().expect("not a number"),
"step_limit" => step_limit = extract_str(value).parse::<u64>().expect("not a number"),
"stack_limit" => stack_limit = extract_str(value).parse::<u64>().expect("not a number"),
"memory_size" => memory_size = extract_str(value).parse().expect("not a number"),
"step_limit" => step_limit = extract_str(value).parse().expect("not a number"),
"stack_limit" => stack_limit = extract_str(value).parse().expect("not a number"),
_ => state.session.span_err(item.span, "unknown miri attribute"),
}
}

View file

@ -30,9 +30,9 @@ pub enum EvalError<'tcx> {
Math(Span, ConstMathErr),
InvalidChar(u32),
OutOfMemory {
allocation_size: u64,
memory_size: u64,
memory_usage: u64,
allocation_size: usize,
memory_size: usize,
memory_usage: usize,
},
ExecutionTimeLimitReached,
StackFrameLimitReached,

View file

@ -136,8 +136,7 @@ enum ConstantKind {
}
impl<'a, 'tcx> EvalContext<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: u64, stack_limit: u64) -> Self {
assert_eq!(stack_limit as usize as u64, stack_limit);
pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir_map: &'a MirMap<'tcx>, memory_size: usize, stack_limit: usize) -> Self {
EvalContext {
tcx: tcx,
mir_map: mir_map,
@ -145,7 +144,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
memory: Memory::new(&tcx.data_layout, memory_size),
statics: HashMap::new(),
stack: Vec::new(),
stack_limit: stack_limit as usize,
stack_limit: stack_limit,
}
}
@ -175,7 +174,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
// TODO(solson): Try making const_to_primval instead.
fn const_to_ptr(&mut self, const_val: &const_val::ConstVal) -> EvalResult<'tcx, Pointer> {
use rustc::middle::const_val::ConstVal::*;
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize};
use rustc_const_math::{ConstInt, ConstIsize, ConstUsize, ConstFloat};
macro_rules! i2p {
($i:ident, $n:expr) => {{
let ptr = self.memory.allocate($n, $n)?;
@ -184,7 +183,17 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}}
}
match *const_val {
Float(_f) => unimplemented!(),
Float(ConstFloat::F32(f)) => {
let ptr = self.memory.allocate(4, 4)?;
self.memory.write_f32(ptr, f)?;
Ok(ptr)
},
Float(ConstFloat::F64(f)) => {
let ptr = self.memory.allocate(8, 8)?;
self.memory.write_f64(ptr, f)?;
Ok(ptr)
},
Float(ConstFloat::FInfer{..}) => unreachable!(),
Integral(ConstInt::Infer(_)) => unreachable!(),
Integral(ConstInt::InferSigned(_)) => unreachable!(),
Integral(ConstInt::I8(i)) => i2p!(i, 1),
@ -856,7 +865,7 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
}
pub fn read_primval(&mut self, ptr: Pointer, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> {
use syntax::ast::{IntTy, UintTy};
use syntax::ast::{IntTy, UintTy, FloatTy};
let val = match (self.memory.pointer_size(), &ty.sty) {
(_, &ty::TyBool) => PrimVal::Bool(self.memory.read_bool(ptr)?),
(_, &ty::TyChar) => {
@ -881,6 +890,9 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
(8, &ty::TyUint(UintTy::Us)) |
(_, &ty::TyUint(UintTy::U64)) => PrimVal::U64(self.memory.read_uint(ptr, 8)? as u64),
(_, &ty::TyFloat(FloatTy::F32)) => PrimVal::F32(self.memory.read_f32(ptr)?),
(_, &ty::TyFloat(FloatTy::F64)) => PrimVal::F64(self.memory.read_f64(ptr)?),
(_, &ty::TyFnDef(def_id, substs, fn_ty)) => {
PrimVal::FnPtr(self.memory.create_fn_ptr(def_id, substs, fn_ty))
},
@ -954,9 +966,9 @@ pub fn eval_main<'a, 'tcx: 'a>(
tcx: TyCtxt<'a, 'tcx, 'tcx>,
mir_map: &'a MirMap<'tcx>,
node_id: ast::NodeId,
memory_size: u64,
memory_size: usize,
step_limit: u64,
stack_limit: u64,
stack_limit: usize,
) {
let mir = mir_map.map.get(&node_id).expect("no mir for main function");
let def_id = tcx.map.local_def_id(node_id);

View file

@ -87,9 +87,9 @@ pub struct Memory<'a, 'tcx> {
/// Actual memory allocations (arbitrary bytes, may contain pointers into other allocations)
alloc_map: HashMap<AllocId, Allocation>,
/// Number of virtual bytes allocated
memory_usage: u64,
memory_usage: usize,
/// Maximum number of virtual bytes that may be allocated
memory_size: u64,
memory_size: usize,
/// Function "allocations". They exist solely so pointers have something to point to, and
/// we can figure out what they point to.
functions: HashMap<AllocId, FunctionDefinition<'tcx>>,
@ -102,7 +102,7 @@ pub struct Memory<'a, 'tcx> {
const ZST_ALLOC_ID: AllocId = AllocId(0);
impl<'a, 'tcx> Memory<'a, 'tcx> {
pub fn new(layout: &'a TargetDataLayout, max_memory: u64) -> Self {
pub fn new(layout: &'a TargetDataLayout, max_memory: usize) -> Self {
let mut mem = Memory {
alloc_map: HashMap::new(),
functions: HashMap::new(),
@ -161,14 +161,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
// make sure we can offset the result pointer by the worst possible alignment
// this allows cheaply checking for alignment directly in the pointer
let least_aligned_size = size + align;
if self.memory_size - self.memory_usage < size as u64 {
if self.memory_size - self.memory_usage < size {
return Err(EvalError::OutOfMemory {
allocation_size: least_aligned_size as u64,
allocation_size: least_aligned_size,
memory_size: self.memory_size,
memory_usage: self.memory_usage,
});
}
self.memory_usage += size as u64;
self.memory_usage += size;
let alloc = Allocation {
bytes: vec![0; least_aligned_size],
relocations: BTreeMap::new(),
@ -201,14 +201,14 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
if least_aligned_size > size {
let amount = least_aligned_size - size;
self.memory_usage += amount as u64;
self.memory_usage += amount;
let alloc = self.get_mut(ptr.alloc_id)?;
alloc.bytes.extend(iter::repeat(0).take(amount));
alloc.undef_mask.grow(amount, false);
} else if size > least_aligned_size {
// it's possible to cause miri to use arbitrary amounts of memory that aren't detectable
// through the memory_usage value, by allocating a lot and reallocating to zero
self.memory_usage -= (size - least_aligned_size) as u64;
self.memory_usage -= size - least_aligned_size;
self.clear_relocations(ptr.offset(least_aligned_size as isize), size - least_aligned_size)?;
let alloc = self.get_mut(ptr.alloc_id)?;
alloc.bytes.truncate(least_aligned_size);
@ -232,7 +232,7 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
}
if let Some(alloc) = self.alloc_map.remove(&ptr.alloc_id) {
self.memory_usage -= alloc.bytes.len() as u64;
self.memory_usage -= alloc.bytes.len();
} else {
debug!("deallocated a pointer twice: {}", ptr.alloc_id);
// TODO(solson): Report error about erroneous free. This is blocked on properly tracking
@ -457,6 +457,8 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
PrimVal::U64(n) => self.write_uint(ptr, n as u64, 8),
PrimVal::Char(c) => self.write_uint(ptr, c as u64, 4),
PrimVal::IntegerPtr(n) => self.write_uint(ptr, n as u64, pointer_size),
PrimVal::F32(f) => self.write_f32(ptr, f),
PrimVal::F64(f) => self.write_f64(ptr, f),
PrimVal::FnPtr(_p) |
PrimVal::AbstractPtr(_p) => unimplemented!(),
}
@ -530,6 +532,32 @@ impl<'a, 'tcx> Memory<'a, 'tcx> {
let size = self.pointer_size();
self.write_uint(ptr, n, size)
}
pub fn write_f32(&mut self, ptr: Pointer, f: f32) -> EvalResult<'tcx, ()> {
ptr.check_align(self.layout.f32_align.abi() as usize)?;
let endianess = self.endianess();
let b = self.get_bytes_mut(ptr, 4)?;
write_target_f32(endianess, b, f).unwrap();
Ok(())
}
pub fn write_f64(&mut self, ptr: Pointer, f: f64) -> EvalResult<'tcx, ()> {
ptr.check_align(self.layout.f64_align.abi() as usize)?;
let endianess = self.endianess();
let b = self.get_bytes_mut(ptr, 8)?;
write_target_f64(endianess, b, f).unwrap();
Ok(())
}
pub fn read_f32(&self, ptr: Pointer) -> EvalResult<'tcx, f32> {
ptr.check_align(self.layout.f32_align.abi() as usize)?;
self.get_bytes(ptr, 4).map(|b| read_target_f32(self.endianess(), b).unwrap())
}
pub fn read_f64(&self, ptr: Pointer) -> EvalResult<'tcx, f64> {
ptr.check_align(self.layout.f64_align.abi() as usize)?;
self.get_bytes(ptr, 8).map(|b| read_target_f64(self.endianess(), b).unwrap())
}
}
/// Relocations
@ -652,6 +680,36 @@ fn read_target_int(endianess: layout::Endian, mut source: &[u8]) -> Result<i64,
}
}
////////////////////////////////////////////////////////////////////////////////
// Methods to access floats in the target endianess
////////////////////////////////////////////////////////////////////////////////
fn write_target_f32(endianess: layout::Endian, mut target: &mut [u8], data: f32) -> Result<(), byteorder::Error> {
match endianess {
layout::Endian::Little => target.write_f32::<LittleEndian>(data),
layout::Endian::Big => target.write_f32::<BigEndian>(data),
}
}
fn write_target_f64(endianess: layout::Endian, mut target: &mut [u8], data: f64) -> Result<(), byteorder::Error> {
match endianess {
layout::Endian::Little => target.write_f64::<LittleEndian>(data),
layout::Endian::Big => target.write_f64::<BigEndian>(data),
}
}
fn read_target_f32(endianess: layout::Endian, mut source: &[u8]) -> Result<f32, byteorder::Error> {
match endianess {
layout::Endian::Little => source.read_f32::<LittleEndian>(),
layout::Endian::Big => source.read_f32::<BigEndian>(),
}
}
fn read_target_f64(endianess: layout::Endian, mut source: &[u8]) -> Result<f64, byteorder::Error> {
match endianess {
layout::Endian::Little => source.read_f64::<LittleEndian>(),
layout::Endian::Big => source.read_f64::<BigEndian>(),
}
}
////////////////////////////////////////////////////////////////////////////////
// Undefined byte tracking
////////////////////////////////////////////////////////////////////////////////
@ -722,6 +780,7 @@ impl UndefMask {
fn truncate(&mut self, length: usize) {
self.len = length;
self.blocks.truncate(self.len / BLOCK_SIZE + 1);
self.blocks.shrink_to_fit();
}
}

View file

@ -13,6 +13,8 @@ pub enum PrimVal {
FnPtr(Pointer),
IntegerPtr(u64),
Char(char),
F32(f32), F64(f64),
}
/// returns the result of the operation and whether the operation overflowed
@ -57,6 +59,32 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
})
}
macro_rules! float_binops {
($v:ident, $l:ident, $r:ident) => ({
match bin_op {
Add => $v($l + $r),
Sub => $v($l - $r),
Mul => $v($l * $r),
Div => $v($l / $r),
Rem => $v($l % $r),
// invalid float ops
BitXor => unreachable!(),
BitAnd => unreachable!(),
BitOr => unreachable!(),
Shl => unreachable!(),
Shr => unreachable!(),
Eq => Bool($l == $r),
Ne => Bool($l != $r),
Lt => Bool($l < $r),
Le => Bool($l <= $r),
Gt => Bool($l > $r),
Ge => Bool($l >= $r),
}
})
}
fn unrelated_ptr_ops<'tcx>(bin_op: mir::BinOp) -> EvalResult<'tcx, PrimVal> {
use rustc::mir::repr::BinOp::*;
match bin_op {
@ -128,6 +156,8 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
(U16(l), U16(r)) => int_binops!(U16, l, r),
(U32(l), U32(r)) => int_binops!(U32, l, r),
(U64(l), U64(r)) => int_binops!(U64, l, r),
(F32(l), F32(r)) => float_binops!(F32, l, r),
(F64(l), F64(r)) => float_binops!(F64, l, r),
(Char(l), Char(r)) => match bin_op {
Eq => Bool(l == r),
Ne => Bool(l != r),
@ -211,6 +241,9 @@ pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVa
(Not, U16(n)) => Ok(U16(!n)),
(Not, U32(n)) => Ok(U32(!n)),
(Not, U64(n)) => Ok(U64(!n)),
(Neg, F64(n)) => Ok(F64(-n)),
(Neg, F32(n)) => Ok(F32(-n)),
_ => Err(EvalError::Unimplemented(format!("unimplemented unary op: {:?}, {:?}", un_op, val))),
}
}

11
tests/run-pass/floats.rs Normal file
View file

@ -0,0 +1,11 @@
fn main() {
assert_eq!(6.0_f32*6.0_f32, 36.0_f32);
assert_eq!(6.0_f64*6.0_f64, 36.0_f64);
assert_eq!(-{5.0_f32}, -5.0_f32);
assert!((5.0_f32/0.0).is_infinite());
assert!((-5.0_f32).sqrt().is_nan());
let x: u64 = unsafe { std::mem::transmute(42.0_f64) };
let y: f64 = unsafe { std::mem::transmute(x) };
assert_eq!(y, 42.0_f64);
}