]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/operator.rs
eb45bb0e2771b76f6ba05ff87009b6df1e547096
[rust.git] / src / librustc_mir / interpret / operator.rs
1 use rustc::mir;
2 use rustc::ty::{self, layout::TyLayout};
3 use syntax::ast::FloatTy;
4 use rustc_apfloat::Float;
5 use rustc::mir::interpret::{InterpResult, Scalar};
6
7 use super::{InterpretCx, PlaceTy, Immediate, Machine, ImmTy};
8
9
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(
14         &mut self,
15         op: mir::BinOp,
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)
23     }
24
25     /// Applies the binary operation `op` to the arguments and writes the result to the
26     /// destination.
27     pub fn binop_ignore_overflow(
28         &mut self,
29         op: mir::BinOp,
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)
36     }
37 }
38
39 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
40     fn binary_char_op(
41         &self,
42         bin_op: mir::BinOp,
43         l: char,
44         r: char,
45     ) -> (Scalar<M::PointerTag>, bool) {
46         use rustc::mir::BinOp::*;
47
48         let res = match bin_op {
49             Eq => l == r,
50             Ne => l != r,
51             Lt => l < r,
52             Le => l <= r,
53             Gt => l > r,
54             Ge => l >= r,
55             _ => bug!("Invalid operation on char: {:?}", bin_op),
56         };
57         return (Scalar::from_bool(res), false);
58     }
59
60     fn binary_bool_op(
61         &self,
62         bin_op: mir::BinOp,
63         l: bool,
64         r: bool,
65     ) -> (Scalar<M::PointerTag>, bool) {
66         use rustc::mir::BinOp::*;
67
68         let res = match bin_op {
69             Eq => l == r,
70             Ne => l != r,
71             Lt => l < r,
72             Le => l <= r,
73             Gt => l > r,
74             Ge => l >= r,
75             BitAnd => l & r,
76             BitOr => l | r,
77             BitXor => l ^ r,
78             _ => bug!("Invalid operation on bool: {:?}", bin_op),
79         };
80         return (Scalar::from_bool(res), false);
81     }
82
83     fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>(
84         &self,
85         bin_op: mir::BinOp,
86         l: F,
87         r: F,
88     ) -> (Scalar<M::PointerTag>, bool) {
89         use rustc::mir::BinOp::*;
90
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),
104         };
105         return (val, false);
106     }
107
108     fn binary_int_op(
109         &self,
110         bin_op: mir::BinOp,
111         // passing in raw bits
112         l: u128,
113         left_layout: TyLayout<'tcx>,
114         r: u128,
115         right_layout: TyLayout<'tcx>,
116     ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool)> {
117         use rustc::mir::BinOp::*;
118
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;
126             if oflo {
127                 r %= size.bits() as u32;
128             }
129             let result = if signed {
130                 let l = self.sign_extend(l, left_layout) as i128;
131                 let result = match bin_op {
132                     Shl => l << r,
133                     Shr => l >> r,
134                     _ => bug!("it has already been checked that this is a shift op"),
135                 };
136                 result as u128
137             } else {
138                 match bin_op {
139                     Shl => l << r,
140                     Shr => l >> r,
141                     _ => bug!("it has already been checked that this is a shift op"),
142                 }
143             };
144             let truncated = self.truncate(result, left_layout);
145             return Ok((Scalar::from_uint(truncated, size), oflo));
146         }
147
148         // For the remaining ops, the types must be the same on both sides
149         if left_layout.ty != right_layout.ty {
150             let msg = format!(
151                 "unimplemented asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
152                 bin_op,
153                 l,
154                 left_layout.ty,
155                 r,
156                 right_layout.ty
157             );
158             return err!(Unimplemented(msg));
159         }
160
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),
168                 _ => None,
169             };
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));
174             }
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),
183                 _ => None,
184             };
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;
189                 match bin_op {
190                     Rem | Div => {
191                         // int_min / -1
192                         if r == -1 && l == (1 << (size.bits() - 1)) {
193                             return Ok((Scalar::from_uint(l, size), true));
194                         }
195                     },
196                     _ => {},
197                 }
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;
204                 }
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));
209             }
210         }
211
212         let size = left_layout.size;
213
214         // only ints left
215         let val = match bin_op {
216             Eq => Scalar::from_bool(l == r),
217             Ne => Scalar::from_bool(l != r),
218
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),
223
224             BitOr => Scalar::from_uint(l | r, size),
225             BitAnd => Scalar::from_uint(l & r, size),
226             BitXor => Scalar::from_uint(l ^ r, size),
227
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,
238                     _ => bug!(),
239                 };
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));
243             }
244
245             _ => {
246                 let msg = format!(
247                     "unimplemented binary op {:?}: {:?}, {:?} (both {:?})",
248                     bin_op,
249                     l,
250                     r,
251                     right_layout.ty,
252                 );
253                 return err!(Unimplemented(msg));
254             }
255         };
256
257         Ok((val, false))
258     }
259
260     /// Returns the result of the specified operation and whether it overflowed.
261     #[inline]
262     pub fn binary_op(
263         &self,
264         bin_op: mir::BinOp,
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);
270
271         match left.layout.ty.sty {
272             ty::Char => {
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()?))
277             }
278             ty::Bool => {
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()?))
283             }
284             ty::Float(fty) => {
285                 assert_eq!(left.layout.ty, right.layout.ty);
286                 let left = left.to_scalar()?;
287                 let right = right.to_scalar()?;
288                 Ok(match fty {
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()?),
291                 })
292             }
293             _ => {
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());
299
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
304                 {
305                     return M::ptr_op(self, bin_op, left, right);
306                 }
307
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)
312             }
313         }
314     }
315
316     pub fn unary_op(
317         &self,
318         un_op: mir::UnOp,
319         val: ImmTy<'tcx, M::PointerTag>,
320     ) -> InterpResult<'tcx, Scalar<M::PointerTag>> {
321         use rustc::mir::UnOp::*;
322
323         let layout = val.layout;
324         let val = val.to_scalar()?;
325         trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);
326
327         match layout.ty.sty {
328             ty::Bool => {
329                 let val = val.to_bool()?;
330                 let res = match un_op {
331                     Not => !val,
332                     _ => bug!("Invalid bool op {:?}", un_op)
333                 };
334                 Ok(Scalar::from_bool(res))
335             }
336             ty::Float(fty) => {
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)
341                 };
342                 Ok(res)
343             }
344             _ => {
345                 assert!(layout.ty.is_integral());
346                 let val = val.to_bits(layout.size)?;
347                 let res = match un_op {
348                     Not => !val,
349                     Neg => {
350                         assert!(layout.abi.is_signed());
351                         (-(val as i128)) as u128
352                     }
353                 };
354                 // res needs tuncating
355                 Ok(Scalar::from_uint(self.truncate(res, layout), layout.size))
356             }
357         }
358     }
359 }