]>
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}; | |
c295e0f8 XL |
6 | use rustc_middle::ty::layout::{LayoutOf, TyAndLayout}; |
7 | use rustc_middle::ty::{self, FloatTy, Ty}; | |
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, | |
6a06907d XL |
17 | left: &ImmTy<'tcx, M::PointerTag>, |
18 | right: &ImmTy<'tcx, M::PointerTag>, | |
19 | dest: &PlaceTy<'tcx, M::PointerTag>, | |
dc9dc135 | 20 | ) -> InterpResult<'tcx> { |
6a06907d | 21 | let (val, overflowed, ty) = self.overflowing_binary_op(op, &left, &right)?; |
e1599b0c XL |
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, | |
6a06907d XL |
37 | left: &ImmTy<'tcx, M::PointerTag>, |
38 | right: &ImmTy<'tcx, M::PointerTag>, | |
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; | |
3c0e092e XL |
133 | // The shift offset is implicitly masked to the type size, to make sure this operation |
134 | // is always defined. This is the one MIR operator that does *not* directly map to a | |
135 | // single LLVM operation. See | |
136 | // <https://github.com/rust-lang/rust/blob/a3b9405ae7bb6ab4e8103b414e75c44598a10fd2/compiler/rustc_codegen_ssa/src/common.rs#L131-L158> | |
137 | // for the corresponding truncation in our codegen backends. | |
138 | let r = r % size; | |
ba9703b0 | 139 | let r = u32::try_from(r).unwrap(); // we masked so this will always fit |
0531ce1d | 140 | let result = if signed { |
b7449926 | 141 | let l = self.sign_extend(l, left_layout) as i128; |
0531ce1d | 142 | let result = match bin_op { |
ba9703b0 XL |
143 | Shl => l.checked_shl(r).unwrap(), |
144 | Shr => l.checked_shr(r).unwrap(), | |
0531ce1d XL |
145 | _ => bug!("it has already been checked that this is a shift op"), |
146 | }; | |
147 | result as u128 | |
148 | } else { | |
149 | match bin_op { | |
ba9703b0 XL |
150 | Shl => l.checked_shl(r).unwrap(), |
151 | Shr => l.checked_shr(r).unwrap(), | |
0531ce1d XL |
152 | _ => bug!("it has already been checked that this is a shift op"), |
153 | } | |
ea8adc8c | 154 | }; |
b7449926 | 155 | let truncated = self.truncate(result, left_layout); |
ba9703b0 | 156 | return Ok((Scalar::from_uint(truncated, left_layout.size), overflow, left_layout.ty)); |
ea8adc8c XL |
157 | } |
158 | ||
b7449926 XL |
159 | // For the remaining ops, the types must be the same on both sides |
160 | if left_layout.ty != right_layout.ty { | |
f035d41b XL |
161 | span_bug!( |
162 | self.cur_span(), | |
416331ca | 163 | "invalid asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})", |
ea8adc8c | 164 | bin_op, |
dfeec247 XL |
165 | l, |
166 | left_layout.ty, | |
167 | r, | |
168 | right_layout.ty, | |
416331ca | 169 | ) |
ea8adc8c XL |
170 | } |
171 | ||
74b04a01 XL |
172 | let size = left_layout.size; |
173 | ||
b7449926 | 174 | // Operations that need special treatment for signed integers |
0531ce1d XL |
175 | if left_layout.abi.is_signed() { |
176 | let op: Option<fn(&i128, &i128) -> bool> = match bin_op { | |
177 | Lt => Some(i128::lt), | |
178 | Le => Some(i128::le), | |
179 | Gt => Some(i128::gt), | |
180 | Ge => Some(i128::ge), | |
181 | _ => None, | |
182 | }; | |
183 | if let Some(op) = op { | |
b7449926 XL |
184 | let l = self.sign_extend(l, left_layout) as i128; |
185 | let r = self.sign_extend(r, right_layout) as i128; | |
e1599b0c | 186 | return Ok((Scalar::from_bool(op(&l, &r)), false, self.tcx.types.bool)); |
0531ce1d XL |
187 | } |
188 | let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op { | |
60c5eb7d XL |
189 | Div if r == 0 => throw_ub!(DivisionByZero), |
190 | Rem if r == 0 => throw_ub!(RemainderByZero), | |
0531ce1d XL |
191 | Div => Some(i128::overflowing_div), |
192 | Rem => Some(i128::overflowing_rem), | |
193 | Add => Some(i128::overflowing_add), | |
194 | Sub => Some(i128::overflowing_sub), | |
195 | Mul => Some(i128::overflowing_mul), | |
196 | _ => None, | |
197 | }; | |
198 | if let Some(op) = op { | |
b7449926 | 199 | let r = self.sign_extend(r, right_layout) as i128; |
74b04a01 XL |
200 | // We need a special check for overflowing remainder: |
201 | // "int_min % -1" overflows and returns 0, but after casting things to a larger int | |
202 | // type it does *not* overflow nor give an unrepresentable result! | |
ba9703b0 XL |
203 | if bin_op == Rem { |
204 | if r == -1 && l == (1 << (size.bits() - 1)) { | |
205 | return Ok((Scalar::from_int(0, size), true, left_layout.ty)); | |
dfeec247 | 206 | } |
0531ce1d | 207 | } |
ba9703b0 | 208 | let l = self.sign_extend(l, left_layout) as i128; |
74b04a01 | 209 | |
ba9703b0 | 210 | let (result, oflo) = op(l, r); |
74b04a01 XL |
211 | // This may be out-of-bounds for the result type, so we have to truncate ourselves. |
212 | // If that truncation loses any information, we have an overflow. | |
0531ce1d | 213 | let result = result as u128; |
b7449926 | 214 | let truncated = self.truncate(result, left_layout); |
74b04a01 XL |
215 | return Ok(( |
216 | Scalar::from_uint(truncated, size), | |
217 | oflo || self.sign_extend(truncated, left_layout) != result, | |
218 | left_layout.ty, | |
219 | )); | |
83c7162d | 220 | } |
0531ce1d | 221 | } |
ea8adc8c | 222 | |
e1599b0c XL |
223 | let (val, ty) = match bin_op { |
224 | Eq => (Scalar::from_bool(l == r), self.tcx.types.bool), | |
225 | Ne => (Scalar::from_bool(l != r), self.tcx.types.bool), | |
0531ce1d | 226 | |
e1599b0c XL |
227 | Lt => (Scalar::from_bool(l < r), self.tcx.types.bool), |
228 | Le => (Scalar::from_bool(l <= r), self.tcx.types.bool), | |
229 | Gt => (Scalar::from_bool(l > r), self.tcx.types.bool), | |
230 | Ge => (Scalar::from_bool(l >= r), self.tcx.types.bool), | |
0531ce1d | 231 | |
e1599b0c XL |
232 | BitOr => (Scalar::from_uint(l | r, size), left_layout.ty), |
233 | BitAnd => (Scalar::from_uint(l & r, size), left_layout.ty), | |
234 | BitXor => (Scalar::from_uint(l ^ r, size), left_layout.ty), | |
0531ce1d XL |
235 | |
236 | Add | Sub | Mul | Rem | Div => { | |
74b04a01 | 237 | assert!(!left_layout.abi.is_signed()); |
0531ce1d XL |
238 | let op: fn(u128, u128) -> (u128, bool) = match bin_op { |
239 | Add => u128::overflowing_add, | |
240 | Sub => u128::overflowing_sub, | |
241 | Mul => u128::overflowing_mul, | |
60c5eb7d XL |
242 | Div if r == 0 => throw_ub!(DivisionByZero), |
243 | Rem if r == 0 => throw_ub!(RemainderByZero), | |
0531ce1d XL |
244 | Div => u128::overflowing_div, |
245 | Rem => u128::overflowing_rem, | |
246 | _ => bug!(), | |
247 | }; | |
248 | let (result, oflo) = op(l, r); | |
74b04a01 XL |
249 | // Truncate to target type. |
250 | // If that truncation loses any information, we have an overflow. | |
b7449926 | 251 | let truncated = self.truncate(result, left_layout); |
e1599b0c XL |
252 | return Ok(( |
253 | Scalar::from_uint(truncated, size), | |
254 | oflo || truncated != result, | |
255 | left_layout.ty, | |
256 | )); | |
0531ce1d | 257 | } |
ea8adc8c | 258 | |
f035d41b XL |
259 | _ => span_bug!( |
260 | self.cur_span(), | |
dfeec247 XL |
261 | "invalid binary op {:?}: {:?}, {:?} (both {:?})", |
262 | bin_op, | |
263 | l, | |
264 | r, | |
265 | right_layout.ty, | |
266 | ), | |
ea8adc8c XL |
267 | }; |
268 | ||
e1599b0c | 269 | Ok((val, false, ty)) |
ea8adc8c | 270 | } |
ea8adc8c | 271 | |
e1599b0c XL |
272 | /// Returns the result of the specified operation, whether it overflowed, and |
273 | /// the result type. | |
274 | pub fn overflowing_binary_op( | |
b7449926 XL |
275 | &self, |
276 | bin_op: mir::BinOp, | |
6a06907d XL |
277 | left: &ImmTy<'tcx, M::PointerTag>, |
278 | right: &ImmTy<'tcx, M::PointerTag>, | |
e1599b0c | 279 | ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { |
dfeec247 XL |
280 | trace!( |
281 | "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", | |
282 | bin_op, | |
283 | *left, | |
284 | left.layout.ty, | |
285 | *right, | |
286 | right.layout.ty | |
287 | ); | |
b7449926 | 288 | |
1b1a35ee | 289 | match left.layout.ty.kind() { |
b7449926 | 290 | ty::Char => { |
9fa01778 | 291 | assert_eq!(left.layout.ty, right.layout.ty); |
dc9dc135 XL |
292 | let left = left.to_scalar()?; |
293 | let right = right.to_scalar()?; | |
294 | Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) | |
b7449926 XL |
295 | } |
296 | ty::Bool => { | |
9fa01778 | 297 | assert_eq!(left.layout.ty, right.layout.ty); |
dc9dc135 XL |
298 | let left = left.to_scalar()?; |
299 | let right = right.to_scalar()?; | |
300 | Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) | |
b7449926 XL |
301 | } |
302 | ty::Float(fty) => { | |
9fa01778 | 303 | assert_eq!(left.layout.ty, right.layout.ty); |
e1599b0c | 304 | let ty = left.layout.ty; |
dc9dc135 XL |
305 | let left = left.to_scalar()?; |
306 | let right = right.to_scalar()?; | |
307 | Ok(match fty { | |
dfeec247 XL |
308 | FloatTy::F32 => { |
309 | self.binary_float_op(bin_op, ty, left.to_f32()?, right.to_f32()?) | |
310 | } | |
311 | FloatTy::F64 => { | |
312 | self.binary_float_op(bin_op, ty, left.to_f64()?, right.to_f64()?) | |
313 | } | |
dc9dc135 | 314 | }) |
b7449926 | 315 | } |
416331ca XL |
316 | _ if left.layout.ty.is_integral() => { |
317 | // the RHS type can be different, e.g. for shifts -- but it has to be integral, too | |
dc9dc135 | 318 | assert!( |
416331ca XL |
319 | right.layout.ty.is_integral(), |
320 | "Unexpected types for BinOp: {:?} {:?} {:?}", | |
dfeec247 XL |
321 | left.layout.ty, |
322 | bin_op, | |
323 | right.layout.ty | |
416331ca | 324 | ); |
b7449926 | 325 | |
136023e0 XL |
326 | let l = left.to_scalar()?.to_bits(left.layout.size)?; |
327 | let r = right.to_scalar()?.to_bits(right.layout.size)?; | |
9fa01778 | 328 | self.binary_int_op(bin_op, l, left.layout, r, right.layout) |
b7449926 | 329 | } |
416331ca | 330 | _ if left.layout.ty.is_any_ptr() => { |
a2a8927a XL |
331 | // The RHS type must be a `pointer` *or an integer type* (for `Offset`). |
332 | // (Even when both sides are pointers, their type might differ, see issue #91636) | |
416331ca | 333 | assert!( |
a2a8927a | 334 | right.layout.ty.is_any_ptr() || right.layout.ty.is_integral(), |
416331ca | 335 | "Unexpected types for BinOp: {:?} {:?} {:?}", |
dfeec247 XL |
336 | left.layout.ty, |
337 | bin_op, | |
338 | right.layout.ty | |
416331ca XL |
339 | ); |
340 | ||
341 | M::binary_ptr_op(self, bin_op, left, right) | |
342 | } | |
f035d41b XL |
343 | _ => span_bug!( |
344 | self.cur_span(), | |
345 | "Invalid MIR: bad LHS type for binop: {:?}", | |
346 | left.layout.ty | |
347 | ), | |
b7449926 XL |
348 | } |
349 | } | |
350 | ||
74b04a01 | 351 | /// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows. |
e1599b0c XL |
352 | #[inline] |
353 | pub fn binary_op( | |
354 | &self, | |
355 | bin_op: mir::BinOp, | |
6a06907d XL |
356 | left: &ImmTy<'tcx, M::PointerTag>, |
357 | right: &ImmTy<'tcx, M::PointerTag>, | |
e1599b0c XL |
358 | ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { |
359 | let (val, _overflow, ty) = self.overflowing_binary_op(bin_op, left, right)?; | |
360 | Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) | |
361 | } | |
362 | ||
74b04a01 XL |
363 | /// Returns the result of the specified operation, whether it overflowed, and |
364 | /// the result type. | |
365 | pub fn overflowing_unary_op( | |
0531ce1d XL |
366 | &self, |
367 | un_op: mir::UnOp, | |
6a06907d | 368 | val: &ImmTy<'tcx, M::PointerTag>, |
74b04a01 | 369 | ) -> InterpResult<'tcx, (Scalar<M::PointerTag>, bool, Ty<'tcx>)> { |
ba9703b0 | 370 | use rustc_middle::mir::UnOp::*; |
ea8adc8c | 371 | |
9fa01778 XL |
372 | let layout = val.layout; |
373 | let val = val.to_scalar()?; | |
532ac7d7 | 374 | trace!("Running unary op {:?}: {:?} ({:?})", un_op, val, layout.ty); |
ea8adc8c | 375 | |
1b1a35ee | 376 | match layout.ty.kind() { |
b7449926 XL |
377 | ty::Bool => { |
378 | let val = val.to_bool()?; | |
379 | let res = match un_op { | |
380 | Not => !val, | |
f035d41b | 381 | _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op), |
b7449926 | 382 | }; |
74b04a01 | 383 | Ok((Scalar::from_bool(res), false, self.tcx.types.bool)) |
b7449926 XL |
384 | } |
385 | ty::Float(fty) => { | |
b7449926 | 386 | let res = match (un_op, fty) { |
dc9dc135 XL |
387 | (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), |
388 | (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), | |
f035d41b | 389 | _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op), |
b7449926 | 390 | }; |
74b04a01 | 391 | Ok((res, false, layout.ty)) |
b7449926 XL |
392 | } |
393 | _ => { | |
394 | assert!(layout.ty.is_integral()); | |
136023e0 | 395 | let val = val.to_bits(layout.size)?; |
74b04a01 XL |
396 | let (res, overflow) = match un_op { |
397 | Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate | |
b7449926 | 398 | Neg => { |
74b04a01 | 399 | // arithmetic negation |
b7449926 | 400 | assert!(layout.abi.is_signed()); |
74b04a01 XL |
401 | let val = self.sign_extend(val, layout) as i128; |
402 | let (res, overflow) = val.overflowing_neg(); | |
403 | let res = res as u128; | |
404 | // Truncate to target type. | |
405 | // If that truncation loses any information, we have an overflow. | |
406 | let truncated = self.truncate(res, layout); | |
407 | (truncated, overflow || self.sign_extend(truncated, layout) != res) | |
b7449926 XL |
408 | } |
409 | }; | |
74b04a01 | 410 | Ok((Scalar::from_uint(res, layout.size), overflow, layout.ty)) |
b7449926 XL |
411 | } |
412 | } | |
0531ce1d | 413 | } |
74b04a01 XL |
414 | |
415 | pub fn unary_op( | |
416 | &self, | |
417 | un_op: mir::UnOp, | |
6a06907d | 418 | val: &ImmTy<'tcx, M::PointerTag>, |
74b04a01 XL |
419 | ) -> InterpResult<'tcx, ImmTy<'tcx, M::PointerTag>> { |
420 | let (val, _overflow, ty) = self.overflowing_unary_op(un_op, val)?; | |
421 | Ok(ImmTy::from_scalar(val, self.layout_of(ty)?)) | |
422 | } | |
ea8adc8c | 423 | } |