2 use rustc::ty::{self, layout::{Size, TyLayout}};
3 use syntax::ast::FloatTy;
4 use rustc_apfloat::ieee::{Double, Single};
5 use rustc_apfloat::Float;
6 use rustc::mir::interpret::{EvalResult, Scalar};
8 use super::{InterpretCx, PlaceTy, Immediate, Machine, ImmTy};
11 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
12 /// Applies the binary operation `op` to the two operands and writes a tuple of the result
13 /// and a boolean signifying the potential overflow to the destination.
14 pub fn binop_with_overflow(
17 left: ImmTy<'tcx, M::PointerTag>,
18 right: ImmTy<'tcx, M::PointerTag>,
19 dest: PlaceTy<'tcx, M::PointerTag>,
20 ) -> EvalResult<'tcx> {
21 let (val, overflowed) = self.binary_op(op, left, right)?;
22 let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
23 self.write_immediate(val, dest)
26 /// Applies the binary operation `op` to the arguments and writes the result to the
28 pub fn binop_ignore_overflow(
31 left: ImmTy<'tcx, M::PointerTag>,
32 right: ImmTy<'tcx, M::PointerTag>,
33 dest: PlaceTy<'tcx, M::PointerTag>,
34 ) -> EvalResult<'tcx> {
35 let (val, _overflowed) = self.binary_op(op, left, right)?;
36 self.write_scalar(val, dest)
40 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
46 ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
47 use rustc::mir::BinOp::*;
49 let res = match bin_op {
56 _ => bug!("Invalid operation on char: {:?}", bin_op),
58 return Ok((Scalar::from_bool(res), false));
66 ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
67 use rustc::mir::BinOp::*;
69 let res = match bin_op {
79 _ => bug!("Invalid operation on bool: {:?}", bin_op),
81 return Ok((Scalar::from_bool(res), false));
88 // passing in raw bits
91 ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
92 use rustc::mir::BinOp::*;
94 macro_rules! float_math {
95 ($ty:path, $size:expr) => {{
96 let l = <$ty>::from_bits(l);
97 let r = <$ty>::from_bits(r);
98 let bitify = |res: ::rustc_apfloat::StatusAnd<$ty>|
99 Scalar::from_uint(res.value.to_bits(), Size::from_bytes($size));
100 let val = match bin_op {
101 Eq => Scalar::from_bool(l == r),
102 Ne => Scalar::from_bool(l != r),
103 Lt => Scalar::from_bool(l < r),
104 Le => Scalar::from_bool(l <= r),
105 Gt => Scalar::from_bool(l > r),
106 Ge => Scalar::from_bool(l >= r),
107 Add => bitify(l + r),
108 Sub => bitify(l - r),
109 Mul => bitify(l * r),
110 Div => bitify(l / r),
111 Rem => bitify(l % r),
112 _ => bug!("invalid float op: `{:?}`", bin_op),
114 return Ok((val, false));
118 FloatTy::F32 => float_math!(Single, 4),
119 FloatTy::F64 => float_math!(Double, 8),
126 // passing in raw bits
128 left_layout: TyLayout<'tcx>,
130 right_layout: TyLayout<'tcx>,
131 ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
132 use rustc::mir::BinOp::*;
134 // Shift ops can have an RHS with a different numeric type.
135 if bin_op == Shl || bin_op == Shr {
136 let signed = left_layout.abi.is_signed();
137 let mut oflo = (r as u32 as u128) != r;
138 let mut r = r as u32;
139 let size = left_layout.size;
140 oflo |= r >= size.bits() as u32;
142 r %= size.bits() as u32;
144 let result = if signed {
145 let l = self.sign_extend(l, left_layout) as i128;
146 let result = match bin_op {
149 _ => bug!("it has already been checked that this is a shift op"),
156 _ => bug!("it has already been checked that this is a shift op"),
159 let truncated = self.truncate(result, left_layout);
160 return Ok((Scalar::from_uint(truncated, size), oflo));
163 // For the remaining ops, the types must be the same on both sides
164 if left_layout.ty != right_layout.ty {
166 "unimplemented asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
173 return err!(Unimplemented(msg));
176 // Operations that need special treatment for signed integers
177 if left_layout.abi.is_signed() {
178 let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
179 Lt => Some(i128::lt),
180 Le => Some(i128::le),
181 Gt => Some(i128::gt),
182 Ge => Some(i128::ge),
185 if let Some(op) = op {
186 let l = self.sign_extend(l, left_layout) as i128;
187 let r = self.sign_extend(r, right_layout) as i128;
188 return Ok((Scalar::from_bool(op(&l, &r)), false));
190 let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
191 Div if r == 0 => return err!(DivisionByZero),
192 Rem if r == 0 => return err!(RemainderByZero),
193 Div => Some(i128::overflowing_div),
194 Rem => Some(i128::overflowing_rem),
195 Add => Some(i128::overflowing_add),
196 Sub => Some(i128::overflowing_sub),
197 Mul => Some(i128::overflowing_mul),
200 if let Some(op) = op {
201 let l128 = self.sign_extend(l, left_layout) as i128;
202 let r = self.sign_extend(r, right_layout) as i128;
203 let size = left_layout.size;
207 if r == -1 && l == (1 << (size.bits() - 1)) {
208 return Ok((Scalar::from_uint(l, size), true));
213 trace!("{}, {}, {}", l, l128, r);
214 let (result, mut oflo) = op(l128, r);
215 trace!("{}, {}", result, oflo);
216 if !oflo && size.bits() != 128 {
217 let max = 1 << (size.bits() - 1);
218 oflo = result >= max || result < -max;
220 // this may be out-of-bounds for the result type, so we have to truncate ourselves
221 let result = result as u128;
222 let truncated = self.truncate(result, left_layout);
223 return Ok((Scalar::from_uint(truncated, size), oflo));
227 let size = left_layout.size;
230 let val = match bin_op {
231 Eq => Scalar::from_bool(l == r),
232 Ne => Scalar::from_bool(l != r),
234 Lt => Scalar::from_bool(l < r),
235 Le => Scalar::from_bool(l <= r),
236 Gt => Scalar::from_bool(l > r),
237 Ge => Scalar::from_bool(l >= r),
239 BitOr => Scalar::from_uint(l | r, size),
240 BitAnd => Scalar::from_uint(l & r, size),
241 BitXor => Scalar::from_uint(l ^ r, size),
243 Add | Sub | Mul | Rem | Div => {
244 debug_assert!(!left_layout.abi.is_signed());
245 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
246 Add => u128::overflowing_add,
247 Sub => u128::overflowing_sub,
248 Mul => u128::overflowing_mul,
249 Div if r == 0 => return err!(DivisionByZero),
250 Rem if r == 0 => return err!(RemainderByZero),
251 Div => u128::overflowing_div,
252 Rem => u128::overflowing_rem,
255 let (result, oflo) = op(l, r);
256 let truncated = self.truncate(result, left_layout);
257 return Ok((Scalar::from_uint(truncated, size), oflo || truncated != result));
262 "unimplemented binary op {:?}: {:?}, {:?} (both {:?})",
268 return err!(Unimplemented(msg));
275 /// Returns the result of the specified operation and whether it overflowed.
280 left: ImmTy<'tcx, M::PointerTag>,
281 right: ImmTy<'tcx, M::PointerTag>,
282 ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
283 trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
284 bin_op, *left, left.layout.ty, *right, right.layout.ty);
286 match left.layout.ty.sty {
288 assert_eq!(left.layout.ty, right.layout.ty);
289 let left = left.to_scalar()?.to_char()?;
290 let right = right.to_scalar()?.to_char()?;
291 self.binary_char_op(bin_op, left, right)
294 assert_eq!(left.layout.ty, right.layout.ty);
295 let left = left.to_scalar()?.to_bool()?;
296 let right = right.to_scalar()?.to_bool()?;
297 self.binary_bool_op(bin_op, left, right)
300 assert_eq!(left.layout.ty, right.layout.ty);
301 let left = left.to_bits()?;
302 let right = right.to_bits()?;
303 self.binary_float_op(bin_op, fty, left, right)
306 // Must be integer(-like) types. Don't forget about == on fn pointers.
307 assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() ||
308 left.layout.ty.is_fn());
309 assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() ||
310 right.layout.ty.is_fn());
312 // Handle operations that support pointer values
313 if left.to_scalar_ptr()?.is_ptr() ||
314 right.to_scalar_ptr()?.is_ptr() ||
315 bin_op == mir::BinOp::Offset
317 return M::ptr_op(self, bin_op, left, right);
320 // Everything else only works with "proper" bits
321 let l = left.to_bits().expect("we checked is_ptr");
322 let r = right.to_bits().expect("we checked is_ptr");
323 self.binary_int_op(bin_op, l, left.layout, r, right.layout)
331 val: ImmTy<'tcx, M::PointerTag>,
332 ) -> EvalResult<'tcx, Scalar<M::PointerTag>> {
333 use rustc::mir::UnOp::*;
334 use rustc_apfloat::ieee::{Single, Double};
335 use rustc_apfloat::Float;
337 let layout = val.layout;
338 let val = val.to_scalar()?;
339 trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);
341 match layout.ty.sty {
343 let val = val.to_bool()?;
344 let res = match un_op {
346 _ => bug!("Invalid bool op {:?}", un_op)
348 Ok(Scalar::from_bool(res))
351 let val = val.to_bits(layout.size)?;
352 let res = match (un_op, fty) {
353 (Neg, FloatTy::F32) => Single::to_bits(-Single::from_bits(val)),
354 (Neg, FloatTy::F64) => Double::to_bits(-Double::from_bits(val)),
355 _ => bug!("Invalid float op {:?}", un_op)
357 Ok(Scalar::from_uint(res, layout.size))
360 assert!(layout.ty.is_integral());
361 let val = val.to_bits(layout.size)?;
362 let res = match un_op {
365 assert!(layout.abi.is_signed());
366 (-(val as i128)) as u128
369 // res needs tuncating
370 Ok(Scalar::from_uint(self.truncate(res, layout), layout.size))