]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/interpret/operator.rs
606761f371c2c4fa925633f95f75010aa2998354
[rust.git] / src / librustc_mir / interpret / operator.rs
1 use rustc::mir;
2 use rustc::ty::Ty;
3
4 use super::{
5     EvalError, EvalResult,
6     EvalContext,
7     Lvalue,
8     Machine,
9 };
10
11 use super::value::{
12     PrimVal,
13     PrimValKind,
14     Value,
15     bytes_to_f32,
16     bytes_to_f64,
17     f32_to_bytes,
18     f64_to_bytes,
19 };
20
21 impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
22     fn binop_with_overflow(
23         &mut self,
24         op: mir::BinOp,
25         left: &mir::Operand<'tcx>,
26         right: &mir::Operand<'tcx>,
27     ) -> EvalResult<'tcx, (PrimVal, bool)> {
28         let left_ty    = self.operand_ty(left);
29         let right_ty   = self.operand_ty(right);
30         let left_val   = self.eval_operand_to_primval(left)?;
31         let right_val  = self.eval_operand_to_primval(right)?;
32         self.binary_op(op, left_val, left_ty, right_val, right_ty)
33     }
34
35     /// Applies the binary operation `op` to the two operands and writes a tuple of the result
36     /// and a boolean signifying the potential overflow to the destination.
37     pub(super) fn intrinsic_with_overflow(
38         &mut self,
39         op: mir::BinOp,
40         left: &mir::Operand<'tcx>,
41         right: &mir::Operand<'tcx>,
42         dest: Lvalue<'tcx>,
43         dest_ty: Ty<'tcx>,
44     ) -> EvalResult<'tcx> {
45         let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
46         let val = Value::ByValPair(val, PrimVal::from_bool(overflowed));
47         self.write_value(val, dest, dest_ty)
48     }
49
50     /// Applies the binary operation `op` to the arguments and writes the result to the
51     /// destination. Returns `true` if the operation overflowed.
52     pub(super) fn intrinsic_overflowing(
53         &mut self,
54         op: mir::BinOp,
55         left: &mir::Operand<'tcx>,
56         right: &mir::Operand<'tcx>,
57         dest: Lvalue<'tcx>,
58         dest_ty: Ty<'tcx>,
59     ) -> EvalResult<'tcx, bool> {
60         let (val, overflowed) = self.binop_with_overflow(op, left, right)?;
61         self.write_primval(dest, val, dest_ty)?;
62         Ok(overflowed)
63     }
64 }
65
66 macro_rules! overflow {
67     ($op:ident, $l:expr, $r:expr) => ({
68         let (val, overflowed) = $l.$op($r);
69         let primval = PrimVal::Bytes(val as u128);
70         Ok((primval, overflowed))
71     })
72 }
73
74 macro_rules! int_arithmetic {
75     ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
76         let l = $l;
77         let r = $r;
78         use super::PrimValKind::*;
79         match $kind {
80             I8  => overflow!($int_op, l as i8,  r as i8),
81             I16 => overflow!($int_op, l as i16, r as i16),
82             I32 => overflow!($int_op, l as i32, r as i32),
83             I64 => overflow!($int_op, l as i64, r as i64),
84             I128 => overflow!($int_op, l as i128, r as i128),
85             U8  => overflow!($int_op, l as u8,  r as u8),
86             U16 => overflow!($int_op, l as u16, r as u16),
87             U32 => overflow!($int_op, l as u32, r as u32),
88             U64 => overflow!($int_op, l as u64, r as u64),
89             U128 => overflow!($int_op, l as u128, r as u128),
90             _ => bug!("int_arithmetic should only be called on int primvals"),
91         }
92     })
93 }
94
95 macro_rules! int_shift {
96     ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({
97         let l = $l;
98         let r = $r;
99         let r_wrapped = r as u32;
100         match $kind {
101             I8  => overflow!($int_op, l as i8,  r_wrapped),
102             I16 => overflow!($int_op, l as i16, r_wrapped),
103             I32 => overflow!($int_op, l as i32, r_wrapped),
104             I64 => overflow!($int_op, l as i64, r_wrapped),
105             I128 => overflow!($int_op, l as i128, r_wrapped),
106             U8  => overflow!($int_op, l as u8,  r_wrapped),
107             U16 => overflow!($int_op, l as u16, r_wrapped),
108             U32 => overflow!($int_op, l as u32, r_wrapped),
109             U64 => overflow!($int_op, l as u64, r_wrapped),
110             U128 => overflow!($int_op, l as u128, r_wrapped),
111             _ => bug!("int_shift should only be called on int primvals"),
112         }.map(|(val, over)| (val, over || r != r_wrapped as u128))
113     })
114 }
115
116 macro_rules! float_arithmetic {
117     ($from_bytes:ident, $to_bytes:ident, $float_op:tt, $l:expr, $r:expr) => ({
118         let l = $from_bytes($l);
119         let r = $from_bytes($r);
120         let bytes = $to_bytes(l $float_op r);
121         PrimVal::Bytes(bytes)
122     })
123 }
124
125 macro_rules! f32_arithmetic {
126     ($float_op:tt, $l:expr, $r:expr) => (
127         float_arithmetic!(bytes_to_f32, f32_to_bytes, $float_op, $l, $r)
128     )
129 }
130
131 macro_rules! f64_arithmetic {
132     ($float_op:tt, $l:expr, $r:expr) => (
133         float_arithmetic!(bytes_to_f64, f64_to_bytes, $float_op, $l, $r)
134     )
135 }
136
137 impl<'a, 'tcx, M: Machine<'tcx>> EvalContext<'a, 'tcx, M> {
138     /// Returns the result of the specified operation and whether it overflowed.
139     pub fn binary_op(
140         &self,
141         bin_op: mir::BinOp,
142         left: PrimVal,
143         left_ty: Ty<'tcx>,
144         right: PrimVal,
145         right_ty: Ty<'tcx>,
146     ) -> EvalResult<'tcx, (PrimVal, bool)> {
147         use rustc::mir::BinOp::*;
148         use super::PrimValKind::*;
149
150         let left_kind  = self.ty_to_primval_kind(left_ty)?;
151         let right_kind = self.ty_to_primval_kind(right_ty)?;
152         //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
153
154         // I: Handle operations that support pointers
155         if !left_kind.is_float() && !right_kind.is_float() {
156             if let Some(handled) = M::ptr_op(self, bin_op, left, left_ty, right, right_ty)? {
157                 return Ok(handled);
158             }
159         }
160
161         // II: From now on, everything must be bytes, no pointers
162         let l = left.to_bytes()?;
163         let r = right.to_bytes()?;
164
165         // These ops can have an RHS with a different numeric type.
166         if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) {
167             return match bin_op {
168                 Shl => int_shift!(left_kind, overflowing_shl, l, r),
169                 Shr => int_shift!(left_kind, overflowing_shr, l, r),
170                 _ => bug!("it has already been checked that this is a shift op"),
171             };
172         }
173
174         if left_kind != right_kind {
175             let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
176             return Err(EvalError::Unimplemented(msg));
177         }
178
179         let val = match (bin_op, left_kind) {
180             (Eq, F32) => PrimVal::from_bool(bytes_to_f32(l) == bytes_to_f32(r)),
181             (Ne, F32) => PrimVal::from_bool(bytes_to_f32(l) != bytes_to_f32(r)),
182             (Lt, F32) => PrimVal::from_bool(bytes_to_f32(l) <  bytes_to_f32(r)),
183             (Le, F32) => PrimVal::from_bool(bytes_to_f32(l) <= bytes_to_f32(r)),
184             (Gt, F32) => PrimVal::from_bool(bytes_to_f32(l) >  bytes_to_f32(r)),
185             (Ge, F32) => PrimVal::from_bool(bytes_to_f32(l) >= bytes_to_f32(r)),
186
187             (Eq, F64) => PrimVal::from_bool(bytes_to_f64(l) == bytes_to_f64(r)),
188             (Ne, F64) => PrimVal::from_bool(bytes_to_f64(l) != bytes_to_f64(r)),
189             (Lt, F64) => PrimVal::from_bool(bytes_to_f64(l) <  bytes_to_f64(r)),
190             (Le, F64) => PrimVal::from_bool(bytes_to_f64(l) <= bytes_to_f64(r)),
191             (Gt, F64) => PrimVal::from_bool(bytes_to_f64(l) >  bytes_to_f64(r)),
192             (Ge, F64) => PrimVal::from_bool(bytes_to_f64(l) >= bytes_to_f64(r)),
193
194             (Add, F32) => f32_arithmetic!(+, l, r),
195             (Sub, F32) => f32_arithmetic!(-, l, r),
196             (Mul, F32) => f32_arithmetic!(*, l, r),
197             (Div, F32) => f32_arithmetic!(/, l, r),
198             (Rem, F32) => f32_arithmetic!(%, l, r),
199
200             (Add, F64) => f64_arithmetic!(+, l, r),
201             (Sub, F64) => f64_arithmetic!(-, l, r),
202             (Mul, F64) => f64_arithmetic!(*, l, r),
203             (Div, F64) => f64_arithmetic!(/, l, r),
204             (Rem, F64) => f64_arithmetic!(%, l, r),
205
206             (Eq, _) => PrimVal::from_bool(l == r),
207             (Ne, _) => PrimVal::from_bool(l != r),
208
209             (Lt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) < (r as i128)),
210             (Lt, _) => PrimVal::from_bool(l <  r),
211             (Le, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) <= (r as i128)),
212             (Le, _) => PrimVal::from_bool(l <= r),
213             (Gt, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) > (r as i128)),
214             (Gt, _) => PrimVal::from_bool(l >  r),
215             (Ge, k) if k.is_signed_int() => PrimVal::from_bool((l as i128) >= (r as i128)),
216             (Ge, _) => PrimVal::from_bool(l >= r),
217
218             (BitOr,  _) => PrimVal::Bytes(l | r),
219             (BitAnd, _) => PrimVal::Bytes(l & r),
220             (BitXor, _) => PrimVal::Bytes(l ^ r),
221
222             (Add, k) if k.is_int() => return int_arithmetic!(k, overflowing_add, l, r),
223             (Sub, k) if k.is_int() => return int_arithmetic!(k, overflowing_sub, l, r),
224             (Mul, k) if k.is_int() => return int_arithmetic!(k, overflowing_mul, l, r),
225             (Div, k) if k.is_int() => return int_arithmetic!(k, overflowing_div, l, r),
226             (Rem, k) if k.is_int() => return int_arithmetic!(k, overflowing_rem, l, r),
227
228             _ => {
229                 let msg = format!("unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind);
230                 return Err(EvalError::Unimplemented(msg));
231             }
232         };
233
234         Ok((val, false))
235     }
236 }
237
238 pub fn unary_op<'tcx>(
239     un_op: mir::UnOp,
240     val: PrimVal,
241     val_kind: PrimValKind,
242 ) -> EvalResult<'tcx, PrimVal> {
243     use rustc::mir::UnOp::*;
244     use super::PrimValKind::*;
245
246     let bytes = val.to_bytes()?;
247
248     let result_bytes = match (un_op, val_kind) {
249         (Not, Bool) => !val.to_bool()? as u128,
250
251         (Not, U8)  => !(bytes as u8) as u128,
252         (Not, U16) => !(bytes as u16) as u128,
253         (Not, U32) => !(bytes as u32) as u128,
254         (Not, U64) => !(bytes as u64) as u128,
255         (Not, U128) => !bytes,
256
257         (Not, I8)  => !(bytes as i8) as u128,
258         (Not, I16) => !(bytes as i16) as u128,
259         (Not, I32) => !(bytes as i32) as u128,
260         (Not, I64) => !(bytes as i64) as u128,
261         (Not, I128) => !(bytes as i128) as u128,
262
263         (Neg, I8)  => -(bytes as i8) as u128,
264         (Neg, I16) => -(bytes as i16) as u128,
265         (Neg, I32) => -(bytes as i32) as u128,
266         (Neg, I64) => -(bytes as i64) as u128,
267         (Neg, I128) => -(bytes as i128) as u128,
268
269         (Neg, F32) => f32_to_bytes(-bytes_to_f32(bytes)),
270         (Neg, F64) => f64_to_bytes(-bytes_to_f64(bytes)),
271
272         _ => {
273             let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val);
274             return Err(EvalError::Unimplemented(msg));
275         }
276     };
277
278     Ok(PrimVal::Bytes(result_bytes))
279 }