-
Notifications
You must be signed in to change notification settings - Fork 349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cleanup overflow binop code #28
Changes from all commits
58b4fac
3ba4f6d
6376ef4
4f48bef
e3a2bf8
00eb198
874d683
d977642
e90ee16
a1082b9
ed4af21
b9ac85d
68469be
0821a15
3e3aeab
a088f10
001ae69
c7039db
65de5dd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Large diffs are not rendered by default.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,28 +10,41 @@ pub enum PrimVal { | |
U8(u8), U16(u16), U32(u32), U64(u64), | ||
|
||
AbstractPtr(Pointer), | ||
FnPtr(Pointer), | ||
IntegerPtr(u64), | ||
} | ||
|
||
pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, PrimVal> { | ||
/// returns the result of the operation and whether the operation overflowed | ||
pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> EvalResult<'tcx, (PrimVal, bool)> { | ||
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 Ok(($v(val), true)); | ||
} 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 +66,58 @@ 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 => { | ||
// these numbers are the maximum number a bitshift rhs could possibly have | ||
// e.g. u16 can be bitshifted by 0..16, so masking with 0b1111 (16 - 1) will ensure we are in that range | ||
let type_bits: u32 = match left { | ||
I8(_) | U8(_) => 8, | ||
I16(_) | U16(_) => 16, | ||
I32(_) | U32(_) => 32, | ||
I64(_) | U64(_) => 64, | ||
_ => unreachable!(), | ||
}; | ||
assert!(type_bits.is_power_of_two()); | ||
// turn into `u32` because `overflowing_sh{l,r}` only take `u32` | ||
let r = match right { | ||
I8(i) => i as u32, | ||
I16(i) => i as u32, | ||
I32(i) => i as u32, | ||
I64(i) => i as u32, | ||
U8(i) => i as u32, | ||
U16(i) => i as u32, | ||
U32(i) => i as u32, | ||
U64(i) => i as u32, | ||
_ => panic!("bad MIR: bitshift rhs is not integral"), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @solson Do you have things set up so you can use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That should be possible. I filed #32. |
||
}; | ||
// apply mask | ||
let r = r & (type_bits - 1); | ||
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, false)); | ||
}, | ||
_ => {}, | ||
} | ||
|
||
let val = match (left, right) { | ||
(I8(l), I8(r)) => int_binops!(I8, l, r), | ||
(I16(l), I16(r)) => int_binops!(I16, l, r), | ||
|
@@ -80,12 +145,23 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva | |
|
||
(IntegerPtr(l), IntegerPtr(r)) => int_binops!(IntegerPtr, l, r), | ||
|
||
(AbstractPtr(_), IntegerPtr(_)) | (IntegerPtr(_), AbstractPtr(_)) => | ||
return unrelated_ptr_ops(bin_op), | ||
(AbstractPtr(_), IntegerPtr(_)) | | ||
(IntegerPtr(_), AbstractPtr(_)) | | ||
(FnPtr(_), AbstractPtr(_)) | | ||
(AbstractPtr(_), FnPtr(_)) | | ||
(FnPtr(_), IntegerPtr(_)) | | ||
(IntegerPtr(_), FnPtr(_)) => | ||
unrelated_ptr_ops(bin_op)?, | ||
|
||
(FnPtr(l_ptr), FnPtr(r_ptr)) => match bin_op { | ||
Eq => Bool(l_ptr == r_ptr), | ||
Ne => Bool(l_ptr != r_ptr), | ||
_ => return Err(EvalError::Unimplemented(format!("unimplemented fn ptr comparison: {:?}", bin_op))), | ||
}, | ||
|
||
(AbstractPtr(l_ptr), AbstractPtr(r_ptr)) => { | ||
if l_ptr.alloc_id != r_ptr.alloc_id { | ||
return unrelated_ptr_ops(bin_op); | ||
return Ok((unrelated_ptr_ops(bin_op)?, false)); | ||
} | ||
|
||
let l = l_ptr.offset; | ||
|
@@ -105,7 +181,7 @@ pub fn binary_op<'tcx>(bin_op: mir::BinOp, left: PrimVal, right: PrimVal) -> Eva | |
(l, r) => return Err(EvalError::Unimplemented(format!("unimplemented binary op: {:?}, {:?}, {:?}", l, r, bin_op))), | ||
}; | ||
|
||
Ok(val) | ||
Ok((val, false)) | ||
} | ||
|
||
pub fn unary_op<'tcx>(un_op: mir::UnOp, val: PrimVal) -> EvalResult<'tcx, PrimVal> { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// just making sure that fn -> unsafe fn casts are handled by rustc so miri doesn't have to | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm confused by these two tests. Whether There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's fine, I just wanted to make sure it's not possible to cause a mir safe to unsafe cast. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I realized just after I posted that, this would matter for implementing casting. I guess that's fine. |
||
fn main() { | ||
fn f() {} | ||
|
||
let g = f as fn() as unsafe fn(i32); //~ERROR: non-scalar cast: `fn()` as `unsafe fn(i32)` | ||
|
||
unsafe { | ||
g(42); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
// just making sure that fn -> unsafe fn casts are handled by rustc so miri doesn't have to | ||
fn main() { | ||
fn f() {} | ||
|
||
let g = f as fn() as fn(i32) as unsafe fn(i32); //~ERROR: non-scalar cast: `fn()` as `fn(i32)` | ||
|
||
unsafe { | ||
g(42); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
//error-pattern: no mir for `std | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My
😃 I need to prepare that PR for enabling this in rustc optionally soon. |
||
|
||
fn main() { | ||
let x = std::env::args(); | ||
assert_eq!(x.count(), 1); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
fn main() { | ||
fn f() {} | ||
|
||
let g = f as fn() as unsafe fn(); | ||
unsafe { | ||
g(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yay. I hated this hack and the unsafe code I added with it.