2 use rustc::ty::{self, layout::TyLayout};
3 use syntax::ast::FloatTy;
4 use rustc_apfloat::Float;
5 use rustc::mir::interpret::{InterpResult, Scalar};
7 use super::{InterpretCx, PlaceTy, Immediate, Machine, ImmTy};
10 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
11 /// Applies the binary operation `op` to the two operands and writes a tuple of the result
12 /// and a boolean signifying the potential overflow to the destination.
13 pub fn binop_with_overflow(
16 left: ImmTy<'tcx, M::PointerTag>,
17 right: ImmTy<'tcx, M::PointerTag>,
18 dest: PlaceTy<'tcx, M::PointerTag>,
19 ) -> InterpResult<'tcx> {
20 let (val, overflowed) = self.binary_op(op, left, right)?;
21 let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into());
22 self.write_immediate(val, dest)
25 /// Applies the binary operation `op` to the arguments and writes the result to the
27 pub fn binop_ignore_overflow(
30 left: ImmTy<'tcx, M::PointerTag>,
31 right: ImmTy<'tcx, M::PointerTag>,
32 dest: PlaceTy<'tcx, M::PointerTag>,
33 ) -> InterpResult<'tcx> {
34 let (val, _overflowed) = self.binary_op(op, left, right)?;
35 self.write_scalar(val, dest)
39 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
45 ) -> (Scalar<M::PointerTag>, bool) {
46 use rustc::mir::BinOp::*;
48 let res = match bin_op {
55 _ => bug!("Invalid operation on char: {:?}", bin_op),
57 return (Scalar::from_bool(res), false);
65 ) -> (Scalar<M::PointerTag>, bool) {
66 use rustc::mir::BinOp::*;
68 let res = match bin_op {
78 _ => bug!("Invalid operation on bool: {:?}", bin_op),
80 return (Scalar::from_bool(res), false);
83 fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
88 ) -> (Scalar<M::PointerTag>, bool) {
89 use rustc::mir::BinOp::*;
91 let val = match bin_op {
92 Eq => Scalar::from_bool(l == r),
93 Ne => Scalar::from_bool(l != r),
94 Lt => Scalar::from_bool(l < r),
95 Le => Scalar::from_bool(l <= r),
96 Gt => Scalar::from_bool(l > r),
97 Ge => Scalar::from_bool(l >= r),
98 Add => (l + r).value.into(),
99 Sub => (l - r).value.into(),
100 Mul => (l * r).value.into(),
101 Div => (l / r).value.into(),
102 Rem => (l % r).value.into(),
103 _ => bug!("invalid float op: `{:?}`", bin_op),
111 // passing in raw bits
113 left_layout: TyLayout<'tcx>,
115 right_layout: TyLayout<'tcx>,
116 ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
117 use rustc::mir::BinOp::*;
119 // Shift ops can have an RHS with a different numeric type.
120 if bin_op == Shl || bin_op == Shr {
121 let signed = left_layout.abi.is_signed();
122 let mut oflo = (r as u32 as u128) != r;
123 let mut r = r as u32;
124 let size = left_layout.size;
125 oflo |= r >= size.bits() as u32;
127 r %= size.bits() as u32;
129 let result = if signed {
130 let l = self.sign_extend(l, left_layout) as i128;
131 let result = match bin_op {
134 _ => bug!("it has already been checked that this is a shift op"),
141 _ => bug!("it has already been checked that this is a shift op"),
144 let truncated = self.truncate(result, left_layout);
145 return Ok((Scalar::from_uint(truncated, size), oflo));
148 // For the remaining ops, the types must be the same on both sides
149 if left_layout.ty != right_layout.ty {
151 "unimplemented asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
158 return err!(Unimplemented(msg));
161 // Operations that need special treatment for signed integers
162 if left_layout.abi.is_signed() {
163 let op: Option<fn(&i128, &i128) -> bool> = match bin_op {
164 Lt => Some(i128::lt),
165 Le => Some(i128::le),
166 Gt => Some(i128::gt),
167 Ge => Some(i128::ge),
170 if let Some(op) = op {
171 let l = self.sign_extend(l, left_layout) as i128;
172 let r = self.sign_extend(r, right_layout) as i128;
173 return Ok((Scalar::from_bool(op(&l, &r)), false));
175 let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op {
176 Div if r == 0 => return err!(DivisionByZero),
177 Rem if r == 0 => return err!(RemainderByZero),
178 Div => Some(i128::overflowing_div),
179 Rem => Some(i128::overflowing_rem),
180 Add => Some(i128::overflowing_add),
181 Sub => Some(i128::overflowing_sub),
182 Mul => Some(i128::overflowing_mul),
185 if let Some(op) = op {
186 let l128 = self.sign_extend(l, left_layout) as i128;
187 let r = self.sign_extend(r, right_layout) as i128;
188 let size = left_layout.size;
192 if r == -1 && l == (1 << (size.bits() - 1)) {
193 return Ok((Scalar::from_uint(l, size), true));
198 trace!("{}, {}, {}", l, l128, r);
199 let (result, mut oflo) = op(l128, r);
200 trace!("{}, {}", result, oflo);
201 if !oflo && size.bits() != 128 {
202 let max = 1 << (size.bits() - 1);
203 oflo = result >= max || result < -max;
205 // this may be out-of-bounds for the result type, so we have to truncate ourselves
206 let result = result as u128;
207 let truncated = self.truncate(result, left_layout);
208 return Ok((Scalar::from_uint(truncated, size), oflo));
212 let size = left_layout.size;
215 let val = match bin_op {
216 Eq => Scalar::from_bool(l == r),
217 Ne => Scalar::from_bool(l != r),
219 Lt => Scalar::from_bool(l < r),
220 Le => Scalar::from_bool(l <= r),
221 Gt => Scalar::from_bool(l > r),
222 Ge => Scalar::from_bool(l >= r),
224 BitOr => Scalar::from_uint(l | r, size),
225 BitAnd => Scalar::from_uint(l & r, size),
226 BitXor => Scalar::from_uint(l ^ r, size),
228 Add | Sub | Mul | Rem | Div => {
229 debug_assert!(!left_layout.abi.is_signed());
230 let op: fn(u128, u128) -> (u128, bool) = match bin_op {
231 Add => u128::overflowing_add,
232 Sub => u128::overflowing_sub,
233 Mul => u128::overflowing_mul,
234 Div if r == 0 => return err!(DivisionByZero),
235 Rem if r == 0 => return err!(RemainderByZero),
236 Div => u128::overflowing_div,
237 Rem => u128::overflowing_rem,
240 let (result, oflo) = op(l, r);
241 let truncated = self.truncate(result, left_layout);
242 return Ok((Scalar::from_uint(truncated, size), oflo || truncated != result));
247 "unimplemented binary op {:?}: {:?}, {:?} (both {:?})",
253 return err!(Unimplemented(msg));
260 /// Returns the result of the specified operation and whether it overflowed.
265 left: ImmTy<'tcx, M::PointerTag>,
266 right: ImmTy<'tcx, M::PointerTag>,
267 ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
268 trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
269 bin_op, *left, left.layout.ty, *right, right.layout.ty);
271 match left.layout.ty.sty {
273 assert_eq!(left.layout.ty, right.layout.ty);
274 let left = left.to_scalar()?;
275 let right = right.to_scalar()?;
276 Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?))
279 assert_eq!(left.layout.ty, right.layout.ty);
280 let left = left.to_scalar()?;
281 let right = right.to_scalar()?;
282 Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?))
285 assert_eq!(left.layout.ty, right.layout.ty);
286 let left = left.to_scalar()?;
287 let right = right.to_scalar()?;
289 FloatTy::F32 => self.binary_float_op(bin_op, left.to_f32()?, right.to_f32()?),
290 FloatTy::F64 => self.binary_float_op(bin_op, left.to_f64()?, right.to_f64()?),
294 // Must be integer(-like) types. Don't forget about == on fn pointers.
295 assert!(left.layout.ty.is_integral() || left.layout.ty.is_unsafe_ptr() ||
296 left.layout.ty.is_fn());
297 assert!(right.layout.ty.is_integral() || right.layout.ty.is_unsafe_ptr() ||
298 right.layout.ty.is_fn());
300 // Handle operations that support pointer values
301 if left.to_scalar_ptr()?.is_ptr() ||
302 right.to_scalar_ptr()?.is_ptr() ||
303 bin_op == mir::BinOp::Offset
305 return M::ptr_op(self, bin_op, left, right);
308 // Everything else only works with "proper" bits
309 let l = left.to_bits().expect("we checked is_ptr");
310 let r = right.to_bits().expect("we checked is_ptr");
311 self.binary_int_op(bin_op, l, left.layout, r, right.layout)
319 val: ImmTy<'tcx, M::PointerTag>,
320 ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
321 use rustc::mir::UnOp::*;
323 let layout = val.layout;
324 let val = val.to_scalar()?;
325 trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);
327 match layout.ty.sty {
329 let val = val.to_bool()?;
330 let res = match un_op {
332 _ => bug!("Invalid bool op {:?}", un_op)
334 Ok(Scalar::from_bool(res))
337 let res = match (un_op, fty) {
338 (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?),
339 (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?),
340 _ => bug!("Invalid float op {:?}", un_op)
345 assert!(layout.ty.is_integral());
346 let val = val.to_bits(layout.size)?;
347 let res = match un_op {
350 assert!(layout.abi.is_signed());
351 (-(val as i128)) as u128
354 // res needs tuncating
355 Ok(Scalar::from_uint(self.truncate(res, layout), layout.size))