1 use std
::convert
::TryFrom
;
3 use rustc_apfloat
::Float
;
5 use rustc_middle
::mir
::interpret
::{InterpResult, Scalar}
;
6 use rustc_middle
::ty
::layout
::{LayoutOf, TyAndLayout}
;
7 use rustc_middle
::ty
::{self, FloatTy, Ty}
;
9 use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}
;
11 impl<'mir
, 'tcx
: 'mir
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
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.
14 pub fn binop_with_overflow(
17 left
: &ImmTy
<'tcx
, M
::PointerTag
>,
18 right
: &ImmTy
<'tcx
, M
::PointerTag
>,
19 dest
: &PlaceTy
<'tcx
, M
::PointerTag
>,
20 ) -> InterpResult
<'tcx
> {
21 let (val
, overflowed
, ty
) = self.overflowing_binary_op(op
, &left
, &right
)?
;
23 self.tcx
.intern_tup(&[ty
, self.tcx
.types
.bool
]),
25 "type mismatch for result of {:?}",
28 let val
= Immediate
::ScalarPair(val
.into(), Scalar
::from_bool(overflowed
).into());
29 self.write_immediate(val
, dest
)
32 /// Applies the binary operation `op` to the arguments and writes the result to the
34 pub fn binop_ignore_overflow(
37 left
: &ImmTy
<'tcx
, M
::PointerTag
>,
38 right
: &ImmTy
<'tcx
, M
::PointerTag
>,
39 dest
: &PlaceTy
<'tcx
, M
::PointerTag
>,
40 ) -> InterpResult
<'tcx
> {
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
);
43 self.write_scalar(val
, dest
)
47 impl<'mir
, 'tcx
: 'mir
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
53 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
54 use rustc_middle
::mir
::BinOp
::*;
56 let res
= match bin_op
{
63 _
=> span_bug
!(self.cur_span(), "Invalid operation on char: {:?}", bin_op
),
65 (Scalar
::from_bool(res
), false, self.tcx
.types
.bool
)
73 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
74 use rustc_middle
::mir
::BinOp
::*;
76 let res
= match bin_op
{
86 _
=> span_bug
!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op
),
88 (Scalar
::from_bool(res
), false, self.tcx
.types
.bool
)
91 fn binary_float_op
<F
: Float
+ Into
<Scalar
<M
::PointerTag
>>>(
97 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
98 use rustc_middle
::mir
::BinOp
::*;
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
),
112 _
=> span_bug
!(self.cur_span(), "invalid float op: `{:?}`", bin_op
),
120 // passing in raw bits
122 left_layout
: TyAndLayout
<'tcx
>,
124 right_layout
: TyAndLayout
<'tcx
>,
125 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
126 use rustc_middle
::mir
::BinOp
::*;
128 // Shift ops can have an RHS with a different numeric type.
129 if bin_op
== Shl
|| bin_op
== Shr
{
130 let size
= u128
::from(left_layout
.size
.bits());
131 // Even if `r` is signed, we treat it as if it was unsigned (i.e., we use its
132 // zero-extended form). This matches the codegen backend:
133 // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/base.rs#L315-L317>.
134 // The overflow check is also ignorant to the sign:
135 // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/mir/rvalue.rs#L728>.
136 // This would behave rather strangely if we had integer types of size 256: a shift by
137 // -1i8 would actually shift by 255, but that would *not* be considered overflowing. A
138 // shift by -1i16 though would be considered overflowing. If we had integers of size
139 // 512, then a shift by -1i8 would even produce a different result than one by -1i16:
140 // the first shifts by 255, the latter by u16::MAX % 512 = 511. Lucky enough, our
141 // integers are maximally 128bits wide, so negative shifts *always* overflow and we have
142 // consistent results for the same value represented at different bit widths.
143 assert
!(size
<= 128);
144 let overflow
= r
>= size
;
145 // The shift offset is implicitly masked to the type size, to make sure this operation
146 // is always defined. This is the one MIR operator that does *not* directly map to a
147 // single LLVM operation. See
148 // <https://github.com/rust-lang/rust/blob/c274e4969f058b1c644243181ece9f829efa7594/compiler/rustc_codegen_ssa/src/common.rs#L131-L158>
149 // for the corresponding truncation in our codegen backends.
151 let r
= u32::try_from(r
).unwrap(); // we masked so this will always fit
152 let result
= if left_layout
.abi
.is_signed() {
153 let l
= self.sign_extend(l
, left_layout
) as i128
;
154 let result
= match bin_op
{
155 Shl
=> l
.checked_shl(r
).unwrap(),
156 Shr
=> l
.checked_shr(r
).unwrap(),
162 Shl
=> l
.checked_shl(r
).unwrap(),
163 Shr
=> l
.checked_shr(r
).unwrap(),
167 let truncated
= self.truncate(result
, left_layout
);
168 return Ok((Scalar
::from_uint(truncated
, left_layout
.size
), overflow
, left_layout
.ty
));
171 // For the remaining ops, the types must be the same on both sides
172 if left_layout
.ty
!= right_layout
.ty
{
175 "invalid asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
184 let size
= left_layout
.size
;
186 // Operations that need special treatment for signed integers
187 if left_layout
.abi
.is_signed() {
188 let op
: Option
<fn(&i128
, &i128
) -> bool
> = match bin_op
{
189 Lt
=> Some(i128
::lt
),
190 Le
=> Some(i128
::le
),
191 Gt
=> Some(i128
::gt
),
192 Ge
=> Some(i128
::ge
),
195 if let Some(op
) = op
{
196 let l
= self.sign_extend(l
, left_layout
) as i128
;
197 let r
= self.sign_extend(r
, right_layout
) as i128
;
198 return Ok((Scalar
::from_bool(op(&l
, &r
)), false, self.tcx
.types
.bool
));
200 let op
: Option
<fn(i128
, i128
) -> (i128
, bool
)> = match bin_op
{
201 Div
if r
== 0 => throw_ub
!(DivisionByZero
),
202 Rem
if r
== 0 => throw_ub
!(RemainderByZero
),
203 Div
=> Some(i128
::overflowing_div
),
204 Rem
=> Some(i128
::overflowing_rem
),
205 Add
=> Some(i128
::overflowing_add
),
206 Sub
=> Some(i128
::overflowing_sub
),
207 Mul
=> Some(i128
::overflowing_mul
),
210 if let Some(op
) = op
{
211 let l
= self.sign_extend(l
, left_layout
) as i128
;
212 let r
= self.sign_extend(r
, right_layout
) as i128
;
214 // We need a special check for overflowing Rem and Div since they are *UB*
215 // on overflow, which can happen with "int_min $OP -1".
216 if matches
!(bin_op
, Rem
| Div
) {
217 if l
== size
.signed_int_min() && r
== -1 {
219 throw_ub
!(RemainderOverflow
)
221 throw_ub
!(DivisionOverflow
)
226 let (result
, oflo
) = op(l
, r
);
227 // This may be out-of-bounds for the result type, so we have to truncate ourselves.
228 // If that truncation loses any information, we have an overflow.
229 let result
= result
as u128
;
230 let truncated
= self.truncate(result
, left_layout
);
232 Scalar
::from_uint(truncated
, size
),
233 oflo
|| self.sign_extend(truncated
, left_layout
) != result
,
239 let (val
, ty
) = match bin_op
{
240 Eq
=> (Scalar
::from_bool(l
== r
), self.tcx
.types
.bool
),
241 Ne
=> (Scalar
::from_bool(l
!= r
), self.tcx
.types
.bool
),
243 Lt
=> (Scalar
::from_bool(l
< r
), self.tcx
.types
.bool
),
244 Le
=> (Scalar
::from_bool(l
<= r
), self.tcx
.types
.bool
),
245 Gt
=> (Scalar
::from_bool(l
> r
), self.tcx
.types
.bool
),
246 Ge
=> (Scalar
::from_bool(l
>= r
), self.tcx
.types
.bool
),
248 BitOr
=> (Scalar
::from_uint(l
| r
, size
), left_layout
.ty
),
249 BitAnd
=> (Scalar
::from_uint(l
& r
, size
), left_layout
.ty
),
250 BitXor
=> (Scalar
::from_uint(l ^ r
, size
), left_layout
.ty
),
252 Add
| Sub
| Mul
| Rem
| Div
=> {
253 assert
!(!left_layout
.abi
.is_signed());
254 let op
: fn(u128
, u128
) -> (u128
, bool
) = match bin_op
{
255 Add
=> u128
::overflowing_add
,
256 Sub
=> u128
::overflowing_sub
,
257 Mul
=> u128
::overflowing_mul
,
258 Div
if r
== 0 => throw_ub
!(DivisionByZero
),
259 Rem
if r
== 0 => throw_ub
!(RemainderByZero
),
260 Div
=> u128
::overflowing_div
,
261 Rem
=> u128
::overflowing_rem
,
264 let (result
, oflo
) = op(l
, r
);
265 // Truncate to target type.
266 // If that truncation loses any information, we have an overflow.
267 let truncated
= self.truncate(result
, left_layout
);
269 Scalar
::from_uint(truncated
, size
),
270 oflo
|| truncated
!= result
,
277 "invalid binary op {:?}: {:?}, {:?} (both {:?})",
288 /// Returns the result of the specified operation, whether it overflowed, and
290 pub fn overflowing_binary_op(
293 left
: &ImmTy
<'tcx
, M
::PointerTag
>,
294 right
: &ImmTy
<'tcx
, M
::PointerTag
>,
295 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
297 "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
305 match left
.layout
.ty
.kind() {
307 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
308 let left
= left
.to_scalar()?
;
309 let right
= right
.to_scalar()?
;
310 Ok(self.binary_char_op(bin_op
, left
.to_char()?
, right
.to_char()?
))
313 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
314 let left
= left
.to_scalar()?
;
315 let right
= right
.to_scalar()?
;
316 Ok(self.binary_bool_op(bin_op
, left
.to_bool()?
, right
.to_bool()?
))
319 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
320 let ty
= left
.layout
.ty
;
321 let left
= left
.to_scalar()?
;
322 let right
= right
.to_scalar()?
;
325 self.binary_float_op(bin_op
, ty
, left
.to_f32()?
, right
.to_f32()?
)
328 self.binary_float_op(bin_op
, ty
, left
.to_f64()?
, right
.to_f64()?
)
332 _
if left
.layout
.ty
.is_integral() => {
333 // the RHS type can be different, e.g. for shifts -- but it has to be integral, too
335 right
.layout
.ty
.is_integral(),
336 "Unexpected types for BinOp: {:?} {:?} {:?}",
342 let l
= left
.to_scalar()?
.to_bits(left
.layout
.size
)?
;
343 let r
= right
.to_scalar()?
.to_bits(right
.layout
.size
)?
;
344 self.binary_int_op(bin_op
, l
, left
.layout
, r
, right
.layout
)
346 _
if left
.layout
.ty
.is_any_ptr() => {
347 // The RHS type must be a `pointer` *or an integer type* (for `Offset`).
348 // (Even when both sides are pointers, their type might differ, see issue #91636)
350 right
.layout
.ty
.is_any_ptr() || right
.layout
.ty
.is_integral(),
351 "Unexpected types for BinOp: {:?} {:?} {:?}",
357 M
::binary_ptr_op(self, bin_op
, left
, right
)
361 "Invalid MIR: bad LHS type for binop: {:?}",
367 /// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows.
372 left
: &ImmTy
<'tcx
, M
::PointerTag
>,
373 right
: &ImmTy
<'tcx
, M
::PointerTag
>,
374 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::PointerTag
>> {
375 let (val
, _overflow
, ty
) = self.overflowing_binary_op(bin_op
, left
, right
)?
;
376 Ok(ImmTy
::from_scalar(val
, self.layout_of(ty
)?
))
379 /// Returns the result of the specified operation, whether it overflowed, and
381 pub fn overflowing_unary_op(
384 val
: &ImmTy
<'tcx
, M
::PointerTag
>,
385 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
386 use rustc_middle
::mir
::UnOp
::*;
388 let layout
= val
.layout
;
389 let val
= val
.to_scalar()?
;
390 trace
!("Running unary op {:?}: {:?} ({:?})", un_op
, val
, layout
.ty
);
392 match layout
.ty
.kind() {
394 let val
= val
.to_bool()?
;
395 let res
= match un_op
{
397 _
=> span_bug
!(self.cur_span(), "Invalid bool op {:?}", un_op
),
399 Ok((Scalar
::from_bool(res
), false, self.tcx
.types
.bool
))
402 let res
= match (un_op
, fty
) {
403 (Neg
, FloatTy
::F32
) => Scalar
::from_f32(-val
.to_f32()?
),
404 (Neg
, FloatTy
::F64
) => Scalar
::from_f64(-val
.to_f64()?
),
405 _
=> span_bug
!(self.cur_span(), "Invalid float op {:?}", un_op
),
407 Ok((res
, false, layout
.ty
))
410 assert
!(layout
.ty
.is_integral());
411 let val
= val
.to_bits(layout
.size
)?
;
412 let (res
, overflow
) = match un_op
{
413 Not
=> (self.truncate(!val
, layout
), false), // bitwise negation, then truncate
415 // arithmetic negation
416 assert
!(layout
.abi
.is_signed());
417 let val
= self.sign_extend(val
, layout
) as i128
;
418 let (res
, overflow
) = val
.overflowing_neg();
419 let res
= res
as u128
;
420 // Truncate to target type.
421 // If that truncation loses any information, we have an overflow.
422 let truncated
= self.truncate(res
, layout
);
423 (truncated
, overflow
|| self.sign_extend(truncated
, layout
) != res
)
426 Ok((Scalar
::from_uint(res
, layout
.size
), overflow
, layout
.ty
))
434 val
: &ImmTy
<'tcx
, M
::PointerTag
>,
435 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::PointerTag
>> {
436 let (val
, _overflow
, ty
) = self.overflowing_unary_op(un_op
, val
)?
;
437 Ok(ImmTy
::from_scalar(val
, self.layout_of(ty
)?
))