]>
Commit | Line | Data |
---|---|---|
ba9703b0 XL |
1 | use std::convert::TryFrom; |
2 | ||
dfeec247 | 3 | use rustc_apfloat::Float; |
ba9703b0 XL |
4 | use rustc_middle::mir; |
5 | use rustc_middle::mir::interpret::{InterpResult, Scalar}; | |
5869c6ff | 6 | use rustc_middle::ty::{self, layout::TyAndLayout, FloatTy, Ty}; |
ba9703b0 | 7 | use rustc_target::abi::LayoutOf; |
ea8adc8c | 8 | |
dfeec247 | 9 | use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}; |
ea8adc8c | 10 | |
ba9703b0 | 11 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
ea8adc8c XL |
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. | |
b7449926 | 14 | pub fn binop_with_overflow( |
ea8adc8c XL |
15 | &mut self, |
16 | op: mir::BinOp, | |
a1dfa0c6 XL |
17 | left: ImmTy<'tcx, M::PointerTag>, |
18 | right: ImmTy<'tcx, M::PointerTag>, | |
0bf4aa26 | 19 | dest: PlaceTy<'tcx, M::PointerTag>, |
dc9dc135 | 20 | ) -> InterpResult<'tcx> { |
e1599b0c XL |
21 | let (val, overflowed, ty) = self.overflowing_binary_op(op, left, right)?; |
22 | debug_assert_eq!( | |
23 | self.tcx.intern_tup(&[ty, self.tcx.types.bool]), | |
24 | dest.layout.ty, | |
dfeec247 XL |
25 | "type mismatch for result of {:?}", |
26 | op, | |
e1599b0c | 27 | ); |
a1dfa0c6 XL |
28 | let val = Immediate::ScalarPair(val.into(), Scalar::from_bool(overflowed).into()); |
29 | self.write_immediate(val, dest) | |
ea8adc8c XL |
30 | } |
31 | ||
32 | /// Applies the binary operation `op` to the arguments and writes the result to the | |
b7449926 XL |
33 | /// destination. |
34 | pub fn binop_ignore_overflow( | |
ea8adc8c XL |
35 | &mut self, |
36 | op: mir::BinOp, | |
a1dfa0c6 XL |
37 | left: ImmTy<'tcx, M::PointerTag>, |
38 | right: ImmTy<'tcx, M::PointerTag>, | |
0bf4aa26 | 39 | dest: PlaceTy<'tcx, M::PointerTag>, |
dc9dc135 | 40 | ) -> InterpResult<'tcx> { |
e1599b0c XL |
41 | let (val, _overflowed, ty) = self.overflowing_binary_op(op, left, right)?; |
42 | assert_eq!(ty, dest.layout.ty, "type mismatch for result of {:?}", op); | |
b7449926 | 43 | self.write_scalar(val, dest) |
ea8adc8c XL |
44 | } |
45 | } | |
46 | ||
ba9703b0 | 47 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
b7449926 | 48 | fn binary_char_op( |
ea8adc8c XL |
49 | &self, |
50 | bin_op: mir::BinOp, | |
b7449926 XL |
51 | l: char, |
52 | r: char, | |
e1599b0c | 53 | ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { |
ba9703b0 | 54 | use rustc_middle::mir::BinOp::*; |
ea8adc8c | 55 | |
b7449926 XL |
56 | let res = match bin_op { |
57 | Eq => l == r, | |
58 | Ne => l != r, | |
59 | Lt => l < r, | |
60 | Le => l <= r, | |
61 | Gt => l > r, | |
62 | Ge => l >= r, | |
f035d41b | 63 | _ => span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op), |
94b46f34 | 64 | }; |
ba9703b0 | 65 | (Scalar::from_bool(res), false, self.tcx.types.bool) |
b7449926 XL |
66 | } |
67 | ||
68 | fn binary_bool_op( | |
69 | &self, | |
70 | bin_op: mir::BinOp, | |
71 | l: bool, | |
72 | r: bool, | |
e1599b0c | 73 | ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { |
ba9703b0 | 74 | use rustc_middle::mir::BinOp::*; |
b7449926 XL |
75 | |
76 | let res = match bin_op { | |
77 | Eq => l == r, | |
78 | Ne => l != r, | |
79 | Lt => l < r, | |
80 | Le => l <= r, | |
81 | Gt => l > r, | |
82 | Ge => l >= r, | |
83 | BitAnd => l & r, | |
84 | BitOr => l | r, | |
85 | BitXor => l ^ r, | |
f035d41b | 86 | _ => span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op), |
94b46f34 | 87 | }; |
ba9703b0 | 88 | (Scalar::from_bool(res), false, self.tcx.types.bool) |
b7449926 | 89 | } |
ea8adc8c | 90 | |
dc9dc135 | 91 | fn binary_float_op<F: Float + Into<Scalar<M::PointerTag>>>( |
b7449926 XL |
92 | &self, |
93 | bin_op: mir::BinOp, | |
e1599b0c | 94 | ty: Ty<'tcx>, |
dc9dc135 XL |
95 | l: F, |
96 | r: F, | |
e1599b0c | 97 | ) -> (Scalar<M::PointerTag>, bool, Ty<'tcx>) { |
ba9703b0 | 98 | use rustc_middle::mir::BinOp::*; |
b7449926 | 99 | |
e1599b0c XL |
100 | let (val, ty) = match bin_op { |
101 | Eq => (Scalar::from_bool(l == r), self.tcx.types.bool), | |
102 | Ne => (Scalar::from_bool(l != r), self.tcx.types.bool), | |
103 | Lt => (Scalar::from_bool(l < r), self.tcx.types.bool), | |
104 | Le => (Scalar::from_bool(l <= r), self.tcx.types.bool), | |
105 | Gt => (Scalar::from_bool(l > r), self.tcx.types.bool), | |
106 | Ge => (Scalar::from_bool(l >= r), self.tcx.types.bool), | |
107 | Add => ((l + r).value.into(), ty), | |
108 | Sub => ((l - r).value.into(), ty), | |
109 | Mul => ((l * r).value.into(), ty), | |
110 | Div => ((l / r).value.into(), ty), | |
111 | Rem => ((l % r).value.into(), ty), | |
f035d41b | 112 | _ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op), |
dc9dc135 | 113 | }; |
ba9703b0 | 114 | (val, false, ty) |
b7449926 | 115 | } |
ea8adc8c | 116 | |
b7449926 XL |
117 | fn binary_int_op( |
118 | &self, | |
119 | bin_op: mir::BinOp, | |
120 | // passing in raw bits | |
121 | l: u128, | |
ba9703b0 | 122 | left_layout: TyAndLayout<'tcx>, |
b7449926 | 123 | r: u128, |
ba9703b0 | 124 | right_layout: TyAndLayout<'tcx>, |
e1599b0c | 125 | ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { |
ba9703b0 | 126 | use rustc_middle::mir::BinOp::*; |
0531ce1d | 127 | |
b7449926 XL |
128 | // Shift ops can have an RHS with a different numeric type. |
129 | if bin_op == Shl || bin_op == Shr { | |
0531ce1d | 130 | let signed = left_layout.abi.is_signed(); |
ba9703b0 XL |
131 | let size = u128::from(left_layout.size.bits()); |
132 | let overflow = r >= size; | |
133 | let r = r % size; // mask to type size | |
134 | let r = u32::try_from(r).unwrap(); // we masked so this will always fit | |
0531ce1d | 135 | let result = if signed { |
b7449926 | 136 | let l = self.sign_extend(l, left_layout) as i128; |
0531ce1d | 137 | let result = match bin_op { |
ba9703b0 XL |
138 | Shl => l.checked_shl(r).unwrap(), |
139 | Shr => l.checked_shr(r).unwrap(), | |
0531ce1d XL |
140 | _ => bug!("it has already been checked that this is a shift op"), |
141 | }; | |
142 | result as u128 | |
143 | } else { | |
144 | match bin_op { | |
ba9703b0 XL |
145 | Shl => l.checked_shl(r).unwrap(), |
146 | Shr => l.checked_shr(r).unwrap(), | |
0531ce1d XL |
147 | _ => bug!("it has already been checked that this is a shift op"), |
148 | } | |
ea8adc8c | 149 | }; |
b7449926 | 150 | let truncated = self.truncate(result, left_layout); |
ba9703b0 | 151 | return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty)); |
ea8adc8c XL |
152 | } |
153 | ||
b7449926 XL |
154 | // For the remaining ops, the types must be the same on both sides |
155 | if left_layout.ty != right_layout.ty { | |
f035d41b XL |
156 | span_bug!( |
157 | self.cur_span(), | |
416331ca | 158 | "invalid asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})", |
ea8adc8c | 159 | bin_op, |
dfeec247 XL |
160 | l, |
161 | left_layout.ty, | |
162 | r, | |
163 | right_layout.ty, | |
416331ca | 164 | ) |
ea8adc8c XL |
165 | } |
166 | ||
74b04a01 XL |
167 | let size = left_layout.size; |
168 | ||
b7449926 | 169 | // Operations that need special treatment for signed integers |
0531ce1d XL |
170 | if left_layout.abi.is_signed() { |
171 | let op: Option<fn(&i128, &i128) -> bool> = match bin_op { | |
172 | Lt => Some(i128::lt), | |
173 | Le => Some(i128::le), | |
174 | Gt => Some(i128::gt), | |
175 | Ge => Some(i128::ge), | |
176 | _ => None, | |
177 | }; | |
178 | if let Some(op) = op { | |
b7449926 XL |
179 | let l = self.sign_extend(l, left_layout) as i128; |
180 | let r = self.sign_extend(r, right_layout) as i128; | |
e1599b0c | 181 | return Ok((Scalar::from_bool(op(&l, &r)), false, self.tcx.types.bool)); |
0531ce1d XL |
182 | } |
183 | let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op { | |
60c5eb7d XL |
184 | Div if r == 0 => throw_ub!(DivisionByZero), |
185 | Rem if r == 0 => throw_ub!(RemainderByZero), | |
0531ce1d XL |
186 | Div => Some(i128::overflowing_div), |
187 | Rem => Some(i128::overflowing_rem), | |
188 | Add => Some(i128::overflowing_add), | |
189 | Sub => Some(i128::overflowing_sub), | |
190 | Mul => Some(i128::overflowing_mul), | |
191 | _ => None, | |
192 | }; | |
193 | if let Some(op) = op { | |
b7449926 | 194 | let r = self.sign_extend(r, right_layout) as i128; |
74b04a01 XL |
195 | // We need a special check for overflowing remainder: |
196 | // "int_min % -1" overflows and returns 0, but after casting things to a larger int | |
197 | // type it does *not* overflow nor give an unrepresentable result! | |
ba9703b0 XL |
198 | if bin_op == Rem { |
199 | if r == -1 && l == (1 << (size.bits() - 1)) { | |
200 | return Ok((Scalar::from_int(0, size), true, left_layout.ty)); | |
dfeec247 | 201 | } |
0531ce1d | 202 | } |
ba9703b0 | 203 | let l = self.sign_extend(l, left_layout) as i128; |
74b04a01 | 204 | |
ba9703b0 | 205 | let (result, oflo) = op(l, r); |
74b04a01 XL |
206 | // This may be out-of-bounds for the result type, so we have to truncate ourselves. |
207 | // If that truncation loses any information, we have an overflow. | |
0531ce1d | 208 | let result = result as u128; |
b7449926 | 209 | let truncated = self.truncate(result, left_layout); |
74b04a01 XL |
210 | return Ok(( |
211 | Scalar::from_uint(truncated, size), | |
212 | oflo || self.sign_extend(truncated, left_layout) != result, | |
213 | left_layout.ty, | |
214 | )); | |
83c7162d | 215 | } |
0531ce1d | 216 | } |
ea8adc8c | 217 | |
e1599b0c XL |
218 | let (val, ty) = match bin_op { |
219 | Eq => (Scalar::from_bool(l == r), self.tcx.types.bool), | |
220 | Ne => (Scalar::from_bool(l != r), self.tcx.types.bool), | |
0531ce1d | 221 | |
e1599b0c XL |
222 | Lt => (Scalar::from_bool(l < r), self.tcx.types.bool), |
223 | Le => (Scalar::from_bool(l <= r), self.tcx.types.bool), | |
224 | Gt => (Scalar::from_bool(l > r), self.tcx.types.bool), | |
225 | Ge => (Scalar::from_bool(l >= r), self.tcx.types.bool), | |
0531ce1d | 226 | |
e1599b0c XL |
227 | BitOr => (Scalar::from_uint(l | r, size), left_layout.ty), |
228 | BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty), | |
229 | BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty), | |
0531ce1d XL |
230 | |
231 | Add | Sub | Mul | Rem | Div => { | |
74b04a01 | 232 | assert!(!left_layout.abi.is_signed()); |
0531ce1d XL |
233 | let op: fn(u128, u128) -> (u128, bool) = match bin_op { |
234 | Add => u128::overflowing_add, | |
235 | Sub => u128::overflowing_sub, | |
236 | Mul => u128::overflowing_mul, | |
60c5eb7d XL |
237 | Div if r == 0 => throw_ub!(DivisionByZero), |
238 | Rem if r == 0 => throw_ub!(RemainderByZero), | |
0531ce1d XL |
239 | Div => u128::overflowing_div, |
240 | Rem => u128::overflowing_rem, | |
241 | _ => bug!(), | |
242 | }; | |
243 | let (result, oflo) = op(l, r); | |
74b04a01 XL |
244 | // Truncate to target type. |
245 | // If that truncation loses any information, we have an overflow. | |
b7449926 | 246 | let truncated = self.truncate(result, left_layout); |
e1599b0c XL |
247 | return Ok(( |
248 | Scalar::from_uint(truncated, size), | |
249 | oflo || truncated != result, | |
250 | left_layout.ty, | |
251 | )); | |
0531ce1d | 252 | } |
ea8adc8c | 253 | |
f035d41b XL |
254 | _ => span_bug!( |
255 | self.cur_span(), | |
dfeec247 XL |
256 | "invalid binary op {:?}: {:?}, {:?} (both {:?})", |
257 | bin_op, | |
258 | l, | |
259 | r, | |
260 | right_layout.ty, | |
261 | ), | |
ea8adc8c XL |
262 | }; |
263 | ||
e1599b0c | 264 | Ok((val, false, ty)) |
ea8adc8c | 265 | } |
ea8adc8c | 266 | |
e1599b0c XL |
267 | /// Returns the result of the specified operation, whether it overflowed, and |
268 | /// the result type. | |
269 | pub fn overflowing_binary_op( | |
b7449926 XL |
270 | &self, |
271 | bin_op: mir::BinOp, | |
a1dfa0c6 XL |
272 | left: ImmTy<'tcx, M::PointerTag>, |
273 | right: ImmTy<'tcx, M::PointerTag>, | |
e1599b0c | 274 | ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { |
dfeec247 XL |
275 | trace!( |
276 | "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", | |
277 | bin_op, | |
278 | *left, | |
279 | left.layout.ty, | |
280 | *right, | |
281 | right.layout.ty | |
282 | ); | |
b7449926 | 283 | |
1b1a35ee | 284 | match left.layout.ty.kind() { |
b7449926 | 285 | ty::Char => { |
9fa01778 | 286 | assert_eq!(left.layout.ty, right.layout.ty); |
dc9dc135 XL |
287 | let left = left.to_scalar()?; |
288 | let right = right.to_scalar()?; | |
289 | Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) | |
b7449926 XL |
290 | } |
291 | ty::Bool => { | |
9fa01778 | 292 | assert_eq!(left.layout.ty, right.layout.ty); |
dc9dc135 XL |
293 | let left = left.to_scalar()?; |
294 | let right = right.to_scalar()?; | |
295 | Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) | |
b7449926 XL |
296 | } |
297 | ty::Float(fty) => { | |
9fa01778 | 298 | assert_eq!(left.layout.ty, right.layout.ty); |
e1599b0c | 299 | let ty = left.layout.ty; |
dc9dc135 XL |
300 | let left = left.to_scalar()?; |
301 | let right = right.to_scalar()?; | |
302 | Ok(match fty { | |
dfeec247 XL |
303 | FloatTy::F32 => { |
304 | self.binary_float_op(bin_op, ty, left.to_f32()?, right.to_f32()?) | |
305 | } | |
306 | FloatTy::F64 => { | |
307 | self.binary_float_op(bin_op, ty, left.to_f64()?, right.to_f64()?) | |
308 | } | |
dc9dc135 | 309 | }) |
b7449926 | 310 | } |
416331ca XL |
311 | _ if left.layout.ty.is_integral() => { |
312 | // the RHS type can be different, e.g. for shifts -- but it has to be integral, too | |
dc9dc135 | 313 | assert!( |
416331ca XL |
314 | right.layout.ty.is_integral(), |
315 | "Unexpected types for BinOp: {:?} {:?} {:?}", | |
dfeec247 XL |
316 | left.layout.ty, |
317 | bin_op, | |
318 | right.layout.ty | |
416331ca | 319 | ); |
b7449926 | 320 | |
416331ca XL |
321 | let l = self.force_bits(left.to_scalar()?, left.layout.size)?; |
322 | let r = self.force_bits(right.to_scalar()?, right.layout.size)?; | |
9fa01778 | 323 | self.binary_int_op(bin_op, l, left.layout, r, right.layout) |
b7449926 | 324 | } |
416331ca XL |
325 | _ if left.layout.ty.is_any_ptr() => { |
326 | // The RHS type must be the same *or an integer type* (for `Offset`). | |
327 | assert!( | |
328 | right.layout.ty == left.layout.ty || right.layout.ty.is_integral(), | |
329 | "Unexpected types for BinOp: {:?} {:?} {:?}", | |
dfeec247 XL |
330 | left.layout.ty, |
331 | bin_op, | |
332 | right.layout.ty | |
416331ca XL |
333 | ); |
334 | ||
335 | M::binary_ptr_op(self, bin_op, left, right) | |
336 | } | |
f035d41b XL |
337 | _ => span_bug!( |
338 | self.cur_span(), | |
339 | "Invalid MIR: bad LHS type for binop: {:?}", | |
340 | left.layout.ty | |
341 | ), | |
b7449926 XL |
342 | } |
343 | } | |
344 | ||
74b04a01 | 345 | /// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows. |
e1599b0c XL |
346 | #[inline] |
347 | pub fn binary_op( | |
348 | &self, | |
349 | bin_op: mir::BinOp, | |
350 | left: ImmTy<'tcx, M::PointerTag>, | |
351 | right: ImmTy<'tcx, M::PointerTag>, | |
352 | ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { | |
353 | let (val, _overflow, ty) = self.overflowing_binary_op(bin_op, left, right)?; | |
354 | Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) | |
355 | } | |
356 | ||
74b04a01 XL |
357 | /// Returns the result of the specified operation, whether it overflowed, and |
358 | /// the result type. | |
359 | pub fn overflowing_unary_op( | |
0531ce1d XL |
360 | &self, |
361 | un_op: mir::UnOp, | |
9fa01778 | 362 | val: ImmTy<'tcx, M::PointerTag>, |
74b04a01 | 363 | ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { |
ba9703b0 | 364 | use rustc_middle::mir::UnOp::*; |
ea8adc8c | 365 | |
9fa01778 XL |
366 | let layout = val.layout; |
367 | let val = val.to_scalar()?; | |
532ac7d7 | 368 | trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty); |
ea8adc8c | 369 | |
1b1a35ee | 370 | match layout.ty.kind() { |
b7449926 XL |
371 | ty::Bool => { |
372 | let val = val.to_bool()?; | |
373 | let res = match un_op { | |
374 | Not => !val, | |
f035d41b | 375 | _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op), |
b7449926 | 376 | }; |
74b04a01 | 377 | Ok((Scalar::from_bool(res), false, self.tcx.types.bool)) |
b7449926 XL |
378 | } |
379 | ty::Float(fty) => { | |
b7449926 | 380 | let res = match (un_op, fty) { |
dc9dc135 XL |
381 | (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), |
382 | (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), | |
f035d41b | 383 | _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op), |
b7449926 | 384 | }; |
74b04a01 | 385 | Ok((res, false, layout.ty)) |
b7449926 XL |
386 | } |
387 | _ => { | |
388 | assert!(layout.ty.is_integral()); | |
dc9dc135 | 389 | let val = self.force_bits(val, layout.size)?; |
74b04a01 XL |
390 | let (res, overflow) = match un_op { |
391 | Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate | |
b7449926 | 392 | Neg => { |
74b04a01 | 393 | // arithmetic negation |
b7449926 | 394 | assert!(layout.abi.is_signed()); |
74b04a01 XL |
395 | let val = self.sign_extend(val, layout) as i128; |
396 | let (res, overflow) = val.overflowing_neg(); | |
397 | let res = res as u128; | |
398 | // Truncate to target type. | |
399 | // If that truncation loses any information, we have an overflow. | |
400 | let truncated = self.truncate(res, layout); | |
401 | (truncated, overflow || self.sign_extend(truncated, layout) != res) | |
b7449926 XL |
402 | } |
403 | }; | |
74b04a01 | 404 | Ok((Scalar::from_uint(res, layout.size), overflow, layout.ty)) |
b7449926 XL |
405 | } |
406 | } | |
0531ce1d | 407 | } |
74b04a01 XL |
408 | |
409 | pub fn unary_op( | |
410 | &self, | |
411 | un_op: mir::UnOp, | |
412 | val: ImmTy<'tcx, M::PointerTag>, | |
413 | ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { | |
414 | let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?; | |
415 | Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) | |
416 | } | |
ea8adc8c | 417 | } |