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