]>
Commit | Line | Data |
---|---|---|
dfeec247 | 1 | use rustc_apfloat::Float; |
ba9703b0 XL |
2 | use rustc_middle::mir; |
3 | use rustc_middle::mir::interpret::{InterpResult, Scalar}; | |
781aab86 | 4 | use rustc_middle::ty::layout::TyAndLayout; |
c295e0f8 | 5 | use rustc_middle::ty::{self, FloatTy, Ty}; |
fe692bf9 | 6 | use rustc_span::symbol::sym; |
064997fb | 7 | use rustc_target::abi::Abi; |
ea8adc8c | 8 | |
dfeec247 | 9 | use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}; |
ea8adc8c | 10 | |
fe692bf9 FG |
11 | use crate::fluent_generated as fluent; |
12 | ||
ba9703b0 | 13 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
ea8adc8c XL |
14 | /// Applies the binary operation `op` to the two operands and writes a tuple of the result |
15 | /// and a boolean signifying the potential overflow to the destination. | |
b7449926 | 16 | pub fn binop_with_overflow( |
ea8adc8c XL |
17 | &mut self, |
18 | op: mir::BinOp, | |
064997fb FG |
19 | left: &ImmTy<'tcx, M::Provenance>, |
20 | right: &ImmTy<'tcx, M::Provenance>, | |
21 | dest: &PlaceTy<'tcx, M::Provenance>, | |
dc9dc135 | 22 | ) -> InterpResult<'tcx> { |
781aab86 | 23 | let (val, overflowed) = self.overflowing_binary_op(op, &left, &right)?; |
e1599b0c | 24 | debug_assert_eq!( |
781aab86 | 25 | Ty::new_tup(self.tcx.tcx, &[val.layout.ty, self.tcx.types.bool]), |
e1599b0c | 26 | dest.layout.ty, |
add651ee | 27 | "type mismatch for result of {op:?}", |
e1599b0c | 28 | ); |
064997fb FG |
29 | // Write the result to `dest`. |
30 | if let Abi::ScalarPair(..) = dest.layout.abi { | |
31 | // We can use the optimized path and avoid `place_field` (which might do | |
32 | // `force_allocation`). | |
781aab86 | 33 | let pair = Immediate::ScalarPair(val.to_scalar(), Scalar::from_bool(overflowed)); |
064997fb FG |
34 | self.write_immediate(pair, dest)?; |
35 | } else { | |
36 | assert!(self.tcx.sess.opts.unstable_opts.randomize_layout); | |
37 | // With randomized layout, `(int, bool)` might cease to be a `ScalarPair`, so we have to | |
38 | // do a component-wise write here. This code path is slower than the above because | |
39 | // `place_field` will have to `force_allocate` locals here. | |
add651ee | 40 | let val_field = self.project_field(dest, 0)?; |
781aab86 | 41 | self.write_scalar(val.to_scalar(), &val_field)?; |
add651ee | 42 | let overflowed_field = self.project_field(dest, 1)?; |
064997fb FG |
43 | self.write_scalar(Scalar::from_bool(overflowed), &overflowed_field)?; |
44 | } | |
45 | Ok(()) | |
ea8adc8c XL |
46 | } |
47 | ||
48 | /// Applies the binary operation `op` to the arguments and writes the result to the | |
b7449926 XL |
49 | /// destination. |
50 | pub fn binop_ignore_overflow( | |
ea8adc8c XL |
51 | &mut self, |
52 | op: mir::BinOp, | |
064997fb FG |
53 | left: &ImmTy<'tcx, M::Provenance>, |
54 | right: &ImmTy<'tcx, M::Provenance>, | |
55 | dest: &PlaceTy<'tcx, M::Provenance>, | |
dc9dc135 | 56 | ) -> InterpResult<'tcx> { |
781aab86 FG |
57 | let val = self.wrapping_binary_op(op, left, right)?; |
58 | assert_eq!(val.layout.ty, dest.layout.ty, "type mismatch for result of {op:?}"); | |
59 | self.write_immediate(*val, dest) | |
ea8adc8c XL |
60 | } |
61 | } | |
62 | ||
ba9703b0 | 63 | impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { |
b7449926 | 64 | fn binary_char_op( |
ea8adc8c XL |
65 | &self, |
66 | bin_op: mir::BinOp, | |
b7449926 XL |
67 | l: char, |
68 | r: char, | |
781aab86 | 69 | ) -> (ImmTy<'tcx, M::Provenance>, bool) { |
ba9703b0 | 70 | use rustc_middle::mir::BinOp::*; |
ea8adc8c | 71 | |
b7449926 XL |
72 | let res = match bin_op { |
73 | Eq => l == r, | |
74 | Ne => l != r, | |
75 | Lt => l < r, | |
76 | Le => l <= r, | |
77 | Gt => l > r, | |
78 | Ge => l >= r, | |
f035d41b | 79 | _ => span_bug!(self.cur_span(), "Invalid operation on char: {:?}", bin_op), |
94b46f34 | 80 | }; |
781aab86 | 81 | (ImmTy::from_bool(res, *self.tcx), false) |
b7449926 XL |
82 | } |
83 | ||
84 | fn binary_bool_op( | |
85 | &self, | |
86 | bin_op: mir::BinOp, | |
87 | l: bool, | |
88 | r: bool, | |
781aab86 | 89 | ) -> (ImmTy<'tcx, M::Provenance>, bool) { |
ba9703b0 | 90 | use rustc_middle::mir::BinOp::*; |
b7449926 XL |
91 | |
92 | let res = match bin_op { | |
93 | Eq => l == r, | |
94 | Ne => l != r, | |
95 | Lt => l < r, | |
96 | Le => l <= r, | |
97 | Gt => l > r, | |
98 | Ge => l >= r, | |
99 | BitAnd => l & r, | |
100 | BitOr => l | r, | |
101 | BitXor => l ^ r, | |
f035d41b | 102 | _ => span_bug!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op), |
94b46f34 | 103 | }; |
781aab86 | 104 | (ImmTy::from_bool(res, *self.tcx), false) |
b7449926 | 105 | } |
ea8adc8c | 106 | |
064997fb | 107 | fn binary_float_op<F: Float + Into<Scalar<M::Provenance>>>( |
b7449926 XL |
108 | &self, |
109 | bin_op: mir::BinOp, | |
781aab86 | 110 | layout: TyAndLayout<'tcx>, |
dc9dc135 XL |
111 | l: F, |
112 | r: F, | |
781aab86 | 113 | ) -> (ImmTy<'tcx, M::Provenance>, bool) { |
ba9703b0 | 114 | use rustc_middle::mir::BinOp::*; |
b7449926 | 115 | |
781aab86 FG |
116 | let val = match bin_op { |
117 | Eq => ImmTy::from_bool(l == r, *self.tcx), | |
118 | Ne => ImmTy::from_bool(l != r, *self.tcx), | |
119 | Lt => ImmTy::from_bool(l < r, *self.tcx), | |
120 | Le => ImmTy::from_bool(l <= r, *self.tcx), | |
121 | Gt => ImmTy::from_bool(l > r, *self.tcx), | |
122 | Ge => ImmTy::from_bool(l >= r, *self.tcx), | |
123 | Add => ImmTy::from_scalar((l + r).value.into(), layout), | |
124 | Sub => ImmTy::from_scalar((l - r).value.into(), layout), | |
125 | Mul => ImmTy::from_scalar((l * r).value.into(), layout), | |
126 | Div => ImmTy::from_scalar((l / r).value.into(), layout), | |
127 | Rem => ImmTy::from_scalar((l % r).value.into(), layout), | |
f035d41b | 128 | _ => span_bug!(self.cur_span(), "invalid float op: `{:?}`", bin_op), |
dc9dc135 | 129 | }; |
781aab86 | 130 | (val, false) |
b7449926 | 131 | } |
ea8adc8c | 132 | |
b7449926 XL |
133 | fn binary_int_op( |
134 | &self, | |
135 | bin_op: mir::BinOp, | |
136 | // passing in raw bits | |
137 | l: u128, | |
ba9703b0 | 138 | left_layout: TyAndLayout<'tcx>, |
b7449926 | 139 | r: u128, |
ba9703b0 | 140 | right_layout: TyAndLayout<'tcx>, |
781aab86 | 141 | ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { |
ba9703b0 | 142 | use rustc_middle::mir::BinOp::*; |
0531ce1d | 143 | |
fe692bf9 FG |
144 | let throw_ub_on_overflow = match bin_op { |
145 | AddUnchecked => Some(sym::unchecked_add), | |
146 | SubUnchecked => Some(sym::unchecked_sub), | |
147 | MulUnchecked => Some(sym::unchecked_mul), | |
148 | ShlUnchecked => Some(sym::unchecked_shl), | |
149 | ShrUnchecked => Some(sym::unchecked_shr), | |
150 | _ => None, | |
151 | }; | |
152 | ||
b7449926 | 153 | // Shift ops can have an RHS with a different numeric type. |
fe692bf9 | 154 | if matches!(bin_op, Shl | ShlUnchecked | Shr | ShrUnchecked) { |
ba9703b0 | 155 | let size = u128::from(left_layout.size.bits()); |
5e7ed085 FG |
156 | // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its |
157 | // zero-extended form). This matches the codegen backend: | |
158 | // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/base.rs#L315-L317>. | |
159 | // The overflow check is also ignorant to the sign: | |
160 | // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/mir/rvalue.rs#L728>. | |
161 | // This would behave rather strangely if we had integer types of size 256: a shift by | |
162 | // -1i8 would actually shift by 255, but that would *not* be considered overflowing. A | |
163 | // shift by -1i16 though would be considered overflowing. If we had integers of size | |
164 | // 512, then a shift by -1i8 would even produce a different result than one by -1i16: | |
165 | // the first shifts by 255, the latter by u16::MAX % 512 = 511. Lucky enough, our | |
166 | // integers are maximally 128bits wide, so negative shifts *always* overflow and we have | |
167 | // consistent results for the same value represented at different bit widths. | |
168 | assert!(size <= 128); | |
fe692bf9 | 169 | let original_r = r; |
ba9703b0 | 170 | let overflow = r >= size; |
3c0e092e XL |
171 | // The shift offset is implicitly masked to the type size, to make sure this operation |
172 | // is always defined. This is the one MIR operator that does *not* directly map to a | |
173 | // single LLVM operation. See | |
5e7ed085 | 174 | // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/common.rs#L131-L158> |
3c0e092e XL |
175 | // for the corresponding truncation in our codegen backends. |
176 | let r = r % size; | |
ba9703b0 | 177 | let r = u32::try_from(r).unwrap(); // we masked so this will always fit |
5e7ed085 | 178 | let result = if left_layout.abi.is_signed() { |
b7449926 | 179 | let l = self.sign_extend(l, left_layout) as i128; |
0531ce1d | 180 | let result = match bin_op { |
fe692bf9 FG |
181 | Shl | ShlUnchecked => l.checked_shl(r).unwrap(), |
182 | Shr | ShrUnchecked => l.checked_shr(r).unwrap(), | |
923072b8 | 183 | _ => bug!(), |
0531ce1d XL |
184 | }; |
185 | result as u128 | |
186 | } else { | |
187 | match bin_op { | |
fe692bf9 FG |
188 | Shl | ShlUnchecked => l.checked_shl(r).unwrap(), |
189 | Shr | ShrUnchecked => l.checked_shr(r).unwrap(), | |
923072b8 | 190 | _ => bug!(), |
0531ce1d | 191 | } |
ea8adc8c | 192 | }; |
b7449926 | 193 | let truncated = self.truncate(result, left_layout); |
fe692bf9 FG |
194 | |
195 | if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { | |
196 | throw_ub_custom!( | |
197 | fluent::const_eval_overflow_shift, | |
198 | val = original_r, | |
199 | name = intrinsic_name | |
200 | ); | |
201 | } | |
202 | ||
781aab86 | 203 | return Ok((ImmTy::from_uint(truncated, left_layout), overflow)); |
ea8adc8c XL |
204 | } |
205 | ||
b7449926 XL |
206 | // For the remaining ops, the types must be the same on both sides |
207 | if left_layout.ty != right_layout.ty { | |
f035d41b XL |
208 | span_bug!( |
209 | self.cur_span(), | |
781aab86 FG |
210 | "invalid asymmetric binary op {bin_op:?}: {l:?} ({l_ty}), {r:?} ({r_ty})", |
211 | l_ty = left_layout.ty, | |
212 | r_ty = right_layout.ty, | |
416331ca | 213 | ) |
ea8adc8c XL |
214 | } |
215 | ||
74b04a01 XL |
216 | let size = left_layout.size; |
217 | ||
b7449926 | 218 | // Operations that need special treatment for signed integers |
0531ce1d XL |
219 | if left_layout.abi.is_signed() { |
220 | let op: Option<fn(&i128, &i128) -> bool> = match bin_op { | |
221 | Lt => Some(i128::lt), | |
222 | Le => Some(i128::le), | |
223 | Gt => Some(i128::gt), | |
224 | Ge => Some(i128::ge), | |
225 | _ => None, | |
226 | }; | |
227 | if let Some(op) = op { | |
b7449926 XL |
228 | let l = self.sign_extend(l, left_layout) as i128; |
229 | let r = self.sign_extend(r, right_layout) as i128; | |
781aab86 | 230 | return Ok((ImmTy::from_bool(op(&l, &r), *self.tcx), false)); |
0531ce1d XL |
231 | } |
232 | let op: Option<fn(i128, i128) -> (i128, bool)> = match bin_op { | |
60c5eb7d XL |
233 | Div if r == 0 => throw_ub!(DivisionByZero), |
234 | Rem if r == 0 => throw_ub!(RemainderByZero), | |
0531ce1d XL |
235 | Div => Some(i128::overflowing_div), |
236 | Rem => Some(i128::overflowing_rem), | |
fe692bf9 FG |
237 | Add | AddUnchecked => Some(i128::overflowing_add), |
238 | Sub | SubUnchecked => Some(i128::overflowing_sub), | |
239 | Mul | MulUnchecked => Some(i128::overflowing_mul), | |
0531ce1d XL |
240 | _ => None, |
241 | }; | |
242 | if let Some(op) = op { | |
5e7ed085 | 243 | let l = self.sign_extend(l, left_layout) as i128; |
b7449926 | 244 | let r = self.sign_extend(r, right_layout) as i128; |
5e7ed085 FG |
245 | |
246 | // We need a special check for overflowing Rem and Div since they are *UB* | |
247 | // on overflow, which can happen with "int_min $OP -1". | |
248 | if matches!(bin_op, Rem | Div) { | |
249 | if l == size.signed_int_min() && r == -1 { | |
250 | if bin_op == Rem { | |
251 | throw_ub!(RemainderOverflow) | |
252 | } else { | |
253 | throw_ub!(DivisionOverflow) | |
254 | } | |
dfeec247 | 255 | } |
0531ce1d | 256 | } |
74b04a01 | 257 | |
ba9703b0 | 258 | let (result, oflo) = op(l, r); |
74b04a01 XL |
259 | // This may be out-of-bounds for the result type, so we have to truncate ourselves. |
260 | // If that truncation loses any information, we have an overflow. | |
0531ce1d | 261 | let result = result as u128; |
b7449926 | 262 | let truncated = self.truncate(result, left_layout); |
fe692bf9 FG |
263 | let overflow = oflo || self.sign_extend(truncated, left_layout) != result; |
264 | if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { | |
265 | throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name); | |
266 | } | |
781aab86 | 267 | return Ok((ImmTy::from_uint(truncated, left_layout), overflow)); |
83c7162d | 268 | } |
0531ce1d | 269 | } |
ea8adc8c | 270 | |
781aab86 FG |
271 | let val = match bin_op { |
272 | Eq => ImmTy::from_bool(l == r, *self.tcx), | |
273 | Ne => ImmTy::from_bool(l != r, *self.tcx), | |
0531ce1d | 274 | |
781aab86 FG |
275 | Lt => ImmTy::from_bool(l < r, *self.tcx), |
276 | Le => ImmTy::from_bool(l <= r, *self.tcx), | |
277 | Gt => ImmTy::from_bool(l > r, *self.tcx), | |
278 | Ge => ImmTy::from_bool(l >= r, *self.tcx), | |
0531ce1d | 279 | |
781aab86 FG |
280 | BitOr => ImmTy::from_uint(l | r, left_layout), |
281 | BitAnd => ImmTy::from_uint(l & r, left_layout), | |
282 | BitXor => ImmTy::from_uint(l ^ r, left_layout), | |
0531ce1d | 283 | |
fe692bf9 | 284 | Add | AddUnchecked | Sub | SubUnchecked | Mul | MulUnchecked | Rem | Div => { |
74b04a01 | 285 | assert!(!left_layout.abi.is_signed()); |
0531ce1d | 286 | let op: fn(u128, u128) -> (u128, bool) = match bin_op { |
fe692bf9 FG |
287 | Add | AddUnchecked => u128::overflowing_add, |
288 | Sub | SubUnchecked => u128::overflowing_sub, | |
289 | Mul | MulUnchecked => u128::overflowing_mul, | |
60c5eb7d XL |
290 | Div if r == 0 => throw_ub!(DivisionByZero), |
291 | Rem if r == 0 => throw_ub!(RemainderByZero), | |
0531ce1d XL |
292 | Div => u128::overflowing_div, |
293 | Rem => u128::overflowing_rem, | |
294 | _ => bug!(), | |
295 | }; | |
296 | let (result, oflo) = op(l, r); | |
74b04a01 XL |
297 | // Truncate to target type. |
298 | // If that truncation loses any information, we have an overflow. | |
b7449926 | 299 | let truncated = self.truncate(result, left_layout); |
fe692bf9 FG |
300 | let overflow = oflo || truncated != result; |
301 | if overflow && let Some(intrinsic_name) = throw_ub_on_overflow { | |
302 | throw_ub_custom!(fluent::const_eval_overflow, name = intrinsic_name); | |
303 | } | |
781aab86 | 304 | return Ok((ImmTy::from_uint(truncated, left_layout), overflow)); |
0531ce1d | 305 | } |
ea8adc8c | 306 | |
f035d41b XL |
307 | _ => span_bug!( |
308 | self.cur_span(), | |
781aab86 | 309 | "invalid binary op {:?}: {:?}, {:?} (both {})", |
dfeec247 XL |
310 | bin_op, |
311 | l, | |
312 | r, | |
313 | right_layout.ty, | |
314 | ), | |
ea8adc8c XL |
315 | }; |
316 | ||
781aab86 | 317 | Ok((val, false)) |
ea8adc8c | 318 | } |
ea8adc8c | 319 | |
49aad941 FG |
320 | fn binary_ptr_op( |
321 | &self, | |
322 | bin_op: mir::BinOp, | |
323 | left: &ImmTy<'tcx, M::Provenance>, | |
324 | right: &ImmTy<'tcx, M::Provenance>, | |
781aab86 | 325 | ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { |
49aad941 FG |
326 | use rustc_middle::mir::BinOp::*; |
327 | ||
328 | match bin_op { | |
329 | // Pointer ops that are always supported. | |
330 | Offset => { | |
331 | let ptr = left.to_scalar().to_pointer(self)?; | |
332 | let offset_count = right.to_scalar().to_target_isize(self)?; | |
333 | let pointee_ty = left.layout.ty.builtin_deref(true).unwrap().ty; | |
334 | ||
335 | let offset_ptr = self.ptr_offset_inbounds(ptr, pointee_ty, offset_count)?; | |
781aab86 FG |
336 | Ok(( |
337 | ImmTy::from_scalar(Scalar::from_maybe_pointer(offset_ptr, self), left.layout), | |
338 | false, | |
339 | )) | |
49aad941 FG |
340 | } |
341 | ||
342 | // Fall back to machine hook so Miri can support more pointer ops. | |
343 | _ => M::binary_ptr_op(self, bin_op, left, right), | |
344 | } | |
345 | } | |
346 | ||
781aab86 | 347 | /// Returns the result of the specified operation, and whether it overflowed. |
e1599b0c | 348 | pub fn overflowing_binary_op( |
b7449926 XL |
349 | &self, |
350 | bin_op: mir::BinOp, | |
064997fb FG |
351 | left: &ImmTy<'tcx, M::Provenance>, |
352 | right: &ImmTy<'tcx, M::Provenance>, | |
781aab86 | 353 | ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { |
dfeec247 | 354 | trace!( |
781aab86 | 355 | "Running binary op {:?}: {:?} ({}), {:?} ({})", |
dfeec247 XL |
356 | bin_op, |
357 | *left, | |
358 | left.layout.ty, | |
359 | *right, | |
360 | right.layout.ty | |
361 | ); | |
b7449926 | 362 | |
1b1a35ee | 363 | match left.layout.ty.kind() { |
b7449926 | 364 | ty::Char => { |
9fa01778 | 365 | assert_eq!(left.layout.ty, right.layout.ty); |
f2b60f7d FG |
366 | let left = left.to_scalar(); |
367 | let right = right.to_scalar(); | |
dc9dc135 | 368 | Ok(self.binary_char_op(bin_op, left.to_char()?, right.to_char()?)) |
b7449926 XL |
369 | } |
370 | ty::Bool => { | |
9fa01778 | 371 | assert_eq!(left.layout.ty, right.layout.ty); |
f2b60f7d FG |
372 | let left = left.to_scalar(); |
373 | let right = right.to_scalar(); | |
dc9dc135 | 374 | Ok(self.binary_bool_op(bin_op, left.to_bool()?, right.to_bool()?)) |
b7449926 XL |
375 | } |
376 | ty::Float(fty) => { | |
9fa01778 | 377 | assert_eq!(left.layout.ty, right.layout.ty); |
781aab86 | 378 | let layout = left.layout; |
f2b60f7d FG |
379 | let left = left.to_scalar(); |
380 | let right = right.to_scalar(); | |
dc9dc135 | 381 | Ok(match fty { |
dfeec247 | 382 | FloatTy::F32 => { |
781aab86 | 383 | self.binary_float_op(bin_op, layout, left.to_f32()?, right.to_f32()?) |
dfeec247 XL |
384 | } |
385 | FloatTy::F64 => { | |
781aab86 | 386 | self.binary_float_op(bin_op, layout, left.to_f64()?, right.to_f64()?) |
dfeec247 | 387 | } |
dc9dc135 | 388 | }) |
b7449926 | 389 | } |
416331ca XL |
390 | _ if left.layout.ty.is_integral() => { |
391 | // the RHS type can be different, e.g. for shifts -- but it has to be integral, too | |
dc9dc135 | 392 | assert!( |
416331ca | 393 | right.layout.ty.is_integral(), |
781aab86 | 394 | "Unexpected types for BinOp: {} {:?} {}", |
dfeec247 XL |
395 | left.layout.ty, |
396 | bin_op, | |
397 | right.layout.ty | |
416331ca | 398 | ); |
b7449926 | 399 | |
f2b60f7d FG |
400 | let l = left.to_scalar().to_bits(left.layout.size)?; |
401 | let r = right.to_scalar().to_bits(right.layout.size)?; | |
9fa01778 | 402 | self.binary_int_op(bin_op, l, left.layout, r, right.layout) |
b7449926 | 403 | } |
416331ca | 404 | _ if left.layout.ty.is_any_ptr() => { |
a2a8927a XL |
405 | // The RHS type must be a `pointer` *or an integer type* (for `Offset`). |
406 | // (Even when both sides are pointers, their type might differ, see issue #91636) | |
416331ca | 407 | assert!( |
a2a8927a | 408 | right.layout.ty.is_any_ptr() || right.layout.ty.is_integral(), |
781aab86 | 409 | "Unexpected types for BinOp: {} {:?} {}", |
dfeec247 XL |
410 | left.layout.ty, |
411 | bin_op, | |
412 | right.layout.ty | |
416331ca XL |
413 | ); |
414 | ||
49aad941 | 415 | self.binary_ptr_op(bin_op, left, right) |
416331ca | 416 | } |
f035d41b XL |
417 | _ => span_bug!( |
418 | self.cur_span(), | |
781aab86 | 419 | "Invalid MIR: bad LHS type for binop: {}", |
f035d41b XL |
420 | left.layout.ty |
421 | ), | |
b7449926 XL |
422 | } |
423 | } | |
424 | ||
e1599b0c | 425 | #[inline] |
781aab86 | 426 | pub fn wrapping_binary_op( |
e1599b0c XL |
427 | &self, |
428 | bin_op: mir::BinOp, | |
064997fb FG |
429 | left: &ImmTy<'tcx, M::Provenance>, |
430 | right: &ImmTy<'tcx, M::Provenance>, | |
431 | ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { | |
781aab86 FG |
432 | let (val, _overflow) = self.overflowing_binary_op(bin_op, left, right)?; |
433 | Ok(val) | |
e1599b0c XL |
434 | } |
435 | ||
74b04a01 XL |
436 | /// Returns the result of the specified operation, whether it overflowed, and |
437 | /// the result type. | |
438 | pub fn overflowing_unary_op( | |
0531ce1d XL |
439 | &self, |
440 | un_op: mir::UnOp, | |
064997fb | 441 | val: &ImmTy<'tcx, M::Provenance>, |
781aab86 | 442 | ) -> InterpResult<'tcx, (ImmTy<'tcx, M::Provenance>, bool)> { |
ba9703b0 | 443 | use rustc_middle::mir::UnOp::*; |
ea8adc8c | 444 | |
9fa01778 | 445 | let layout = val.layout; |
f2b60f7d | 446 | let val = val.to_scalar(); |
781aab86 | 447 | trace!("Running unary op {:?}: {:?} ({})", un_op, val, layout.ty); |
ea8adc8c | 448 | |
1b1a35ee | 449 | match layout.ty.kind() { |
b7449926 XL |
450 | ty::Bool => { |
451 | let val = val.to_bool()?; | |
452 | let res = match un_op { | |
453 | Not => !val, | |
f035d41b | 454 | _ => span_bug!(self.cur_span(), "Invalid bool op {:?}", un_op), |
b7449926 | 455 | }; |
781aab86 | 456 | Ok((ImmTy::from_bool(res, *self.tcx), false)) |
b7449926 XL |
457 | } |
458 | ty::Float(fty) => { | |
b7449926 | 459 | let res = match (un_op, fty) { |
dc9dc135 XL |
460 | (Neg, FloatTy::F32) => Scalar::from_f32(-val.to_f32()?), |
461 | (Neg, FloatTy::F64) => Scalar::from_f64(-val.to_f64()?), | |
f035d41b | 462 | _ => span_bug!(self.cur_span(), "Invalid float op {:?}", un_op), |
b7449926 | 463 | }; |
781aab86 | 464 | Ok((ImmTy::from_scalar(res, layout), false)) |
b7449926 XL |
465 | } |
466 | _ => { | |
467 | assert!(layout.ty.is_integral()); | |
136023e0 | 468 | let val = val.to_bits(layout.size)?; |
74b04a01 XL |
469 | let (res, overflow) = match un_op { |
470 | Not => (self.truncate(!val, layout), false), // bitwise negation, then truncate | |
b7449926 | 471 | Neg => { |
74b04a01 | 472 | // arithmetic negation |
b7449926 | 473 | assert!(layout.abi.is_signed()); |
74b04a01 XL |
474 | let val = self.sign_extend(val, layout) as i128; |
475 | let (res, overflow) = val.overflowing_neg(); | |
476 | let res = res as u128; | |
477 | // Truncate to target type. | |
478 | // If that truncation loses any information, we have an overflow. | |
479 | let truncated = self.truncate(res, layout); | |
480 | (truncated, overflow || self.sign_extend(truncated, layout) != res) | |
b7449926 XL |
481 | } |
482 | }; | |
781aab86 | 483 | Ok((ImmTy::from_uint(res, layout), overflow)) |
b7449926 XL |
484 | } |
485 | } | |
0531ce1d | 486 | } |
74b04a01 | 487 | |
781aab86 FG |
488 | #[inline] |
489 | pub fn wrapping_unary_op( | |
74b04a01 XL |
490 | &self, |
491 | un_op: mir::UnOp, | |
064997fb FG |
492 | val: &ImmTy<'tcx, M::Provenance>, |
493 | ) -> InterpResult<'tcx, ImmTy<'tcx, M::Provenance>> { | |
781aab86 FG |
494 | let (val, _overflow) = self.overflowing_unary_op(un_op, val)?; |
495 | Ok(val) | |
74b04a01 | 496 | } |
ea8adc8c | 497 | } |