1
Fork 0

implement overflowing ops

This commit is contained in:
Oliver Schneider 2016-06-17 13:09:20 +02:00
parent 8db0bc0ce9
commit 58b4fac1ce
No known key found for this signature in database
GPG key ID: 56D6EEA0FC67AC46
5 changed files with 118 additions and 23 deletions

View file

@ -3,6 +3,9 @@ use std::fmt;
use rustc::mir::repr as mir;
use rustc::ty::BareFnTy;
use memory::Pointer;
use rustc_const_math::ConstMathErr;
use syntax::codemap::Span;
use primval::PrimVal;
#[derive(Clone, Debug)]
pub enum EvalError<'tcx> {
@ -24,6 +27,10 @@ pub enum EvalError<'tcx> {
Unimplemented(String),
DerefFunctionPointer,
ExecuteMemory,
ArrayIndexOutOfBounds(Span, u64, u64),
Math(Span, ConstMathErr),
InvalidBitShiftRhs(PrimVal),
Overflow(PrimVal, PrimVal, mir::BinOp, PrimVal),
}
pub type EvalResult<'tcx, T> = Result<T, EvalError<'tcx>>;
@ -58,6 +65,14 @@ impl<'tcx> Error for EvalError<'tcx> {
"tried to dereference a function pointer",
EvalError::ExecuteMemory =>
"tried to treat a memory pointer as a function pointer",
EvalError::ArrayIndexOutOfBounds(..) =>
"array index out of bounds",
EvalError::Math(..) =>
"mathematical operation failed",
EvalError::InvalidBitShiftRhs(..) =>
"bit shift rhs negative or not an int",
EvalError::Overflow(..) =>
"mathematical operation overflowed",
}
}
@ -73,6 +88,12 @@ impl<'tcx> fmt::Display for EvalError<'tcx> {
},
EvalError::FunctionPointerTyMismatch(expected, got) =>
write!(f, "tried to call a function of type {:?} through a function pointer of type {:?}", expected, got),
EvalError::ArrayIndexOutOfBounds(span, len, index) =>
write!(f, "array index {} out of bounds {} at {:?}", index, len, span),
EvalError::Math(span, ref err) =>
write!(f, "mathematical operation at {:?} failed with {:?}", span, err),
EvalError::Overflow(l, r, op, val) =>
write!(f, "mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val),
_ => write!(f, "{}", self.description()),
}
}

View file

@ -474,15 +474,23 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
self.frame_mut().block = target;
}
Assert { ref cond, expected, ref msg, target, cleanup } => {
let actual_ptr = self.eval_operand(cond)?;
let actual = self.memory.read_bool(actual_ptr)?;
if actual == expected {
Assert { ref cond, expected, ref msg, target, .. } => {
let cond_ptr = self.eval_operand(cond)?;
if expected == self.memory.read_bool(cond_ptr)? {
self.frame_mut().block = target;
} else {
panic!("unimplemented: jump to {:?} and print {:?}", cleanup, msg);
return match *msg {
mir::AssertMessage::BoundsCheck { ref len, ref index } => {
let len = self.eval_operand(len).expect("can't eval len");
let len = self.memory.read_usize(len).expect("can't read len");
let index = self.eval_operand(index).expect("can't eval index");
let index = self.memory.read_usize(index).expect("can't read index");
Err(EvalError::ArrayIndexOutOfBounds(terminator.source_info.span, len, index))
},
mir::AssertMessage::Math(ref err) => Err(EvalError::Math(terminator.source_info.span, err.clone())),
}
}
},
DropAndReplace { .. } => unimplemented!(),
Resume => unimplemented!(),
@ -922,13 +930,28 @@ impl<'a, 'tcx> EvalContext<'a, 'tcx> {
let right_ty = self.operand_ty(right);
let right_val = self.read_primval(right_ptr, right_ty)?;
let val = primval::binary_op(bin_op, left_val, right_val)?;
self.memory.write_primval(dest, val)?;
use rustc::ty::layout::Layout::*;
let tup_layout = match *dest_layout {
Univariant { ref variant, .. } => variant,
_ => panic!("checked bin op returns something other than a tuple"),
};
// FIXME(solson): Find the result type size properly. Perhaps refactor out
// Projection calculations so we can do the equivalent of `dest.1` here.
let s = self.type_size(left_ty);
self.memory.write_bool(dest.offset(s as isize), false)?;
match primval::binary_op(bin_op, left_val, right_val) {
Ok(val) => {
let offset = tup_layout.field_offset(0).bytes() as isize;
self.memory.write_primval(dest.offset(offset), val)?;
let offset = tup_layout.field_offset(1).bytes() as isize;
self.memory.write_bool(dest.offset(offset), false)?;
},
Err(EvalError::Overflow(l, r, op, val)) => {
debug!("mathematical operation overflowed: {:?} {} {:?} => {:?}", l, op.to_hir_binop().as_str(), r, val);
let offset = tup_layout.field_offset(0).bytes() as isize;
self.memory.write_primval(dest.offset(offset), val)?;
let offset = tup_layout.field_offset(1).bytes() as isize;
self.memory.write_bool(dest.offset(offset), true)?;
},
Err(other) => return Err(other),
}
}
UnaryOp(un_op, ref operand) => {

View file

@ -14,6 +14,7 @@
extern crate rustc_data_structures;
extern crate rustc_mir;
extern crate rustc_trans;
extern crate rustc_const_math;
extern crate syntax;
#[macro_use] extern crate log;
extern crate log_settings;

View file

@ -17,21 +17,32 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
use rustc::mir::repr::BinOp::*;
use self::PrimVal::*;
macro_rules! overflow {
($v:ident, $v2:ident, $l:ident, $op:ident, $r:ident) => ({
let (val, of) = $l.$op($r);
if of {
return Err(EvalError::Overflow($v($l), $v2($r), bin_op, $v(val)));
} else {
$v(val)
}
})
}
macro_rules! int_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),
Add => overflow!($v, $v, $l, overflowing_add, $r),
Sub => overflow!($v, $v, $l, overflowing_sub, $r),
Mul => overflow!($v, $v, $l, overflowing_mul, $r),
Div => overflow!($v, $v, $l, overflowing_div, $r),
Rem => overflow!($v, $v, $l, overflowing_rem, $r),
BitXor => $v($l ^ $r),
BitAnd => $v($l & $r),
BitOr => $v($l | $r),
// TODO(solson): Can have differently-typed RHS.
Shl => $v($l << $r),
Shr => $v($l >> $r),
// these have already been handled
Shl => unreachable!(),
Shr => unreachable!(),
Eq => Bool($l == $r),
Ne => Bool($l != $r),
@ -53,6 +64,45 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva
}
}
match bin_op {
// can have rhs with a different numeric type
Shl | Shr => {
let r = match right {
I8(i) if i >= 0 => i as u32,
I16(i) if i >= 0 => i as u32,
I32(i) if i >= 0 => i as u32,
I64(i) if i >= 0 && i as i32 as i64 == i => i as u32,
U8(i) => i as u32,
U16(i) => i as u32,
U32(i) => i,
U64(i) if i as u32 as u64 == i => i as u32,
_ => return Err(EvalError::InvalidBitShiftRhs(right)),
};
macro_rules! shift {
($v:ident, $l:ident, $r:ident) => ({
match bin_op {
Shl => overflow!($v, U32, $l, overflowing_shl, $r),
Shr => overflow!($v, U32, $l, overflowing_shr, $r),
_ => unreachable!(),
}
})
}
let val = match left {
I8(l) => shift!(I8, l, r),
I16(l) => shift!(I16, l, r),
I32(l) => shift!(I32, l, r),
I64(l) => shift!(I64, l, r),
U8(l) => shift!(U8, l, r),
U16(l) => shift!(U16, l, r),
U32(l) => shift!(U32, l, r),
U64(l) => shift!(U64, l, r),
_ => unreachable!(),
};
return Ok(val);
},
_ => {},
}
let val = match (left, right) {
(I8(l), I8(r)) => int_binops!(I8, l, r),
(I16(l), I16(r)) => int_binops!(I16, l, r),

View file

@ -79,11 +79,11 @@ fn compile_test() {
writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
},
}
}
let stderr = std::io::stderr();
writeln!(stderr.lock(), "").unwrap();
});
if failed {
panic!("some tests failed");
}
}
let stderr = std::io::stderr();
writeln!(stderr.lock(), "").unwrap();
});
}