]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/operator.rs
Rollup merge of #59486 - varkor:dead-code-impl, r=sanxiyn
[rust.git] / src / librustc_mir / interpret / operator.rs
1 use rustc::mir;
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};
7
8 use super::{InterpretCx, PlaceTy, Immediate, Machine, ImmTy};
9
10
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(
15         &mut self,
16         op: mir::BinOp,
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)
24     }
25
26     /// Applies the binary operation `op` to the arguments and writes the result to the
27     /// destination.
28     pub fn binop_ignore_overflow(
29         &mut self,
30         op: mir::BinOp,
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)
37     }
38 }
39
40 impl<'a, 'mir, 'tcx, M: Machine<'a, 'mir, 'tcx>> InterpretCx<'a, 'mir, 'tcx, M> {
41     fn binary_char_op(
42         &self,
43         bin_op: mir::BinOp,
44         l: char,
45         r: char,
46     ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
47         use rustc::mir::BinOp::*;
48
49         let res = match bin_op {
50             Eq => l == r,
51             Ne => l != r,
52             Lt => l < r,
53             Le => l <= r,
54             Gt => l > r,
55             Ge => l >= r,
56             _ => bug!("Invalid operation on char: {:?}", bin_op),
57         };
58         return Ok((Scalar::from_bool(res), false));
59     }
60
61     fn binary_bool_op(
62         &self,
63         bin_op: mir::BinOp,
64         l: bool,
65         r: bool,
66     ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
67         use rustc::mir::BinOp::*;
68
69         let res = match bin_op {
70             Eq => l == r,
71             Ne => l != r,
72             Lt => l < r,
73             Le => l <= r,
74             Gt => l > r,
75             Ge => l >= r,
76             BitAnd => l & r,
77             BitOr => l | r,
78             BitXor => l ^ r,
79             _ => bug!("Invalid operation on bool: {:?}", bin_op),
80         };
81         return Ok((Scalar::from_bool(res), false));
82     }
83
84     fn binary_float_op(
85         &self,
86         bin_op: mir::BinOp,
87         fty: FloatTy,
88         // passing in raw bits
89         l: u128,
90         r: u128,
91     ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
92         use rustc::mir::BinOp::*;
93
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),
113                 };
114                 return Ok((val, false));
115             }};
116         }
117         match fty {
118             FloatTy::F32 => float_math!(Single, 4),
119             FloatTy::F64 => float_math!(Double, 8),
120         }
121     }
122
123     fn binary_int_op(
124         &self,
125         bin_op: mir::BinOp,
126         // passing in raw bits
127         l: u128,
128         left_layout: TyLayout<'tcx>,
129         r: u128,
130         right_layout: TyLayout<'tcx>,
131     ) -> EvalResult<'tcx, (Scalar<M::PointerTag>, bool)> {
132         use rustc::mir::BinOp::*;
133
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;
141             if oflo {
142                 r %= size.bits() as u32;
143             }
144             let result = if signed {
145                 let l = self.sign_extend(l, left_layout) as i128;
146                 let result = match bin_op {
147                     Shl => l << r,
148                     Shr => l >> r,
149                     _ => bug!("it has already been checked that this is a shift op"),
150                 };
151                 result as u128
152             } else {
153                 match bin_op {
154                     Shl => l << r,
155                     Shr => l >> r,
156                     _ => bug!("it has already been checked that this is a shift op"),
157                 }
158             };
159             let truncated = self.truncate(result, left_layout);
160             return Ok((Scalar::from_uint(truncated, size), oflo));
161         }
162
163         // For the remaining ops, the types must be the same on both sides
164         if left_layout.ty != right_layout.ty {
165             let msg = format!(
166                 "unimplemented asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
167                 bin_op,
168                 l,
169                 left_layout.ty,
170                 r,
171                 right_layout.ty
172             );
173             return err!(Unimplemented(msg));
174         }
175
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),
183                 _ => None,
184             };
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));
189             }
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),
198                 _ => None,
199             };
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;
204                 match bin_op {
205                     Rem | Div => {
206                         // int_min / -1
207                         if r == -1 && l == (1 << (size.bits() - 1)) {
208                             return Ok((Scalar::from_uint(l, size), true));
209                         }
210                     },
211                     _ => {},
212                 }
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;
219                 }
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));
224             }
225         }
226
227         let size = left_layout.size;
228
229         // only ints left
230         let val = match bin_op {
231             Eq => Scalar::from_bool(l == r),
232             Ne => Scalar::from_bool(l != r),
233
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),
238
239             BitOr => Scalar::from_uint(l | r, size),
240             BitAnd => Scalar::from_uint(l & r, size),
241             BitXor => Scalar::from_uint(l ^ r, size),
242
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,
253                     _ => bug!(),
254                 };
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));
258             }
259
260             _ => {
261                 let msg = format!(
262                     "unimplemented binary op {:?}: {:?}, {:?} (both {:?})",
263                     bin_op,
264                     l,
265                     r,
266                     right_layout.ty,
267                 );
268                 return err!(Unimplemented(msg));
269             }
270         };
271
272         Ok((val, false))
273     }
274
275     /// Returns the result of the specified operation and whether it overflowed.
276     #[inline]
277     pub fn binary_op(
278         &self,
279         bin_op: mir::BinOp,
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);
285
286         match left.layout.ty.sty {
287             ty::Char => {
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)
292             }
293             ty::Bool => {
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)
298             }
299             ty::Float(fty) => {
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)
304             }
305             _ => {
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());
311
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
316                 {
317                     return M::ptr_op(self, bin_op, left, right);
318                 }
319
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)
324             }
325         }
326     }
327
328     pub fn unary_op(
329         &self,
330         un_op: mir::UnOp,
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;
336
337         let layout = val.layout;
338         let val = val.to_scalar()?;
339         trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty);
340
341         match layout.ty.sty {
342             ty::Bool => {
343                 let val = val.to_bool()?;
344                 let res = match un_op {
345                     Not => !val,
346                     _ => bug!("Invalid bool op {:?}", un_op)
347                 };
348                 Ok(Scalar::from_bool(res))
349             }
350             ty::Float(fty) => {
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)
356                 };
357                 Ok(Scalar::from_uint(res, layout.size))
358             }
359             _ => {
360                 assert!(layout.ty.is_integral());
361                 let val = val.to_bits(layout.size)?;
362                 let res = match un_op {
363                     Not => !val,
364                     Neg => {
365                         assert!(layout.abi.is_signed());
366                         (-(val as i128)) as u128
367                     }
368                 };
369                 // res needs tuncating
370                 Ok(Scalar::from_uint(self.truncate(res, layout), layout.size))
371             }
372         }
373     }
374 }