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 signed
= left_layout
.abi
.is_signed();
131 let size
= u128
::from(left_layout
.size
.bits());
132 let overflow
= r
>= size
;
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.
139 let r
= u32::try_from(r
).unwrap(); // we masked so this will always fit
140 let result
= if signed
{
141 let l
= self.sign_extend(l
, left_layout
) as i128
;
142 let result
= match bin_op
{
143 Shl
=> l
.checked_shl(r
).unwrap(),
144 Shr
=> l
.checked_shr(r
).unwrap(),
145 _
=> bug
!("it has already been checked that this is a shift op"),
150 Shl
=> l
.checked_shl(r
).unwrap(),
151 Shr
=> l
.checked_shr(r
).unwrap(),
152 _
=> bug
!("it has already been checked that this is a shift op"),
155 let truncated
= self.truncate(result
, left_layout
);
156 return Ok((Scalar
::from_uint(truncated
, left_layout
.size
), overflow
, left_layout
.ty
));
159 // For the remaining ops, the types must be the same on both sides
160 if left_layout
.ty
!= right_layout
.ty
{
163 "invalid asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
172 let size
= left_layout
.size
;
174 // Operations that need special treatment for signed integers
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
),
183 if let Some(op
) = op
{
184 let l
= self.sign_extend(l
, left_layout
) as i128
;
185 let r
= self.sign_extend(r
, right_layout
) as i128
;
186 return Ok((Scalar
::from_bool(op(&l
, &r
)), false, self.tcx
.types
.bool
));
188 let op
: Option
<fn(i128
, i128
) -> (i128
, bool
)> = match bin_op
{
189 Div
if r
== 0 => throw_ub
!(DivisionByZero
),
190 Rem
if r
== 0 => throw_ub
!(RemainderByZero
),
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
),
198 if let Some(op
) = op
{
199 let r
= self.sign_extend(r
, right_layout
) as i128
;
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!
204 if r
== -1 && l
== (1 << (size
.bits() - 1)) {
205 return Ok((Scalar
::from_int(0, size
), true, left_layout
.ty
));
208 let l
= self.sign_extend(l
, left_layout
) as i128
;
210 let (result
, oflo
) = op(l
, r
);
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.
213 let result
= result
as u128
;
214 let truncated
= self.truncate(result
, left_layout
);
216 Scalar
::from_uint(truncated
, size
),
217 oflo
|| self.sign_extend(truncated
, left_layout
) != result
,
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
),
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
),
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
),
236 Add
| Sub
| Mul
| Rem
| Div
=> {
237 assert
!(!left_layout
.abi
.is_signed());
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
,
242 Div
if r
== 0 => throw_ub
!(DivisionByZero
),
243 Rem
if r
== 0 => throw_ub
!(RemainderByZero
),
244 Div
=> u128
::overflowing_div
,
245 Rem
=> u128
::overflowing_rem
,
248 let (result
, oflo
) = op(l
, r
);
249 // Truncate to target type.
250 // If that truncation loses any information, we have an overflow.
251 let truncated
= self.truncate(result
, left_layout
);
253 Scalar
::from_uint(truncated
, size
),
254 oflo
|| truncated
!= result
,
261 "invalid binary op {:?}: {:?}, {:?} (both {:?})",
272 /// Returns the result of the specified operation, whether it overflowed, and
274 pub fn overflowing_binary_op(
277 left
: &ImmTy
<'tcx
, M
::PointerTag
>,
278 right
: &ImmTy
<'tcx
, M
::PointerTag
>,
279 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
281 "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
289 match left
.layout
.ty
.kind() {
291 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
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()?
))
297 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
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()?
))
303 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
304 let ty
= left
.layout
.ty
;
305 let left
= left
.to_scalar()?
;
306 let right
= right
.to_scalar()?
;
309 self.binary_float_op(bin_op
, ty
, left
.to_f32()?
, right
.to_f32()?
)
312 self.binary_float_op(bin_op
, ty
, left
.to_f64()?
, right
.to_f64()?
)
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
319 right
.layout
.ty
.is_integral(),
320 "Unexpected types for BinOp: {:?} {:?} {:?}",
326 let l
= left
.to_scalar()?
.to_bits(left
.layout
.size
)?
;
327 let r
= right
.to_scalar()?
.to_bits(right
.layout
.size
)?
;
328 self.binary_int_op(bin_op
, l
, left
.layout
, r
, right
.layout
)
330 _
if left
.layout
.ty
.is_any_ptr() => {
331 // The RHS type must be the same *or an integer type* (for `Offset`).
333 right
.layout
.ty
== left
.layout
.ty
|| right
.layout
.ty
.is_integral(),
334 "Unexpected types for BinOp: {:?} {:?} {:?}",
340 M
::binary_ptr_op(self, bin_op
, left
, right
)
344 "Invalid MIR: bad LHS type for binop: {:?}",
350 /// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows.
355 left
: &ImmTy
<'tcx
, M
::PointerTag
>,
356 right
: &ImmTy
<'tcx
, M
::PointerTag
>,
357 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::PointerTag
>> {
358 let (val
, _overflow
, ty
) = self.overflowing_binary_op(bin_op
, left
, right
)?
;
359 Ok(ImmTy
::from_scalar(val
, self.layout_of(ty
)?
))
362 /// Returns the result of the specified operation, whether it overflowed, and
364 pub fn overflowing_unary_op(
367 val
: &ImmTy
<'tcx
, M
::PointerTag
>,
368 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
369 use rustc_middle
::mir
::UnOp
::*;
371 let layout
= val
.layout
;
372 let val
= val
.to_scalar()?
;
373 trace
!("Running unary op {:?}: {:?} ({:?})", un_op
, val
, layout
.ty
);
375 match layout
.ty
.kind() {
377 let val
= val
.to_bool()?
;
378 let res
= match un_op
{
380 _
=> span_bug
!(self.cur_span(), "Invalid bool op {:?}", un_op
),
382 Ok((Scalar
::from_bool(res
), false, self.tcx
.types
.bool
))
385 let res
= match (un_op
, fty
) {
386 (Neg
, FloatTy
::F32
) => Scalar
::from_f32(-val
.to_f32()?
),
387 (Neg
, FloatTy
::F64
) => Scalar
::from_f64(-val
.to_f64()?
),
388 _
=> span_bug
!(self.cur_span(), "Invalid float op {:?}", un_op
),
390 Ok((res
, false, layout
.ty
))
393 assert
!(layout
.ty
.is_integral());
394 let val
= val
.to_bits(layout
.size
)?
;
395 let (res
, overflow
) = match un_op
{
396 Not
=> (self.truncate(!val
, layout
), false), // bitwise negation, then truncate
398 // arithmetic negation
399 assert
!(layout
.abi
.is_signed());
400 let val
= self.sign_extend(val
, layout
) as i128
;
401 let (res
, overflow
) = val
.overflowing_neg();
402 let res
= res
as u128
;
403 // Truncate to target type.
404 // If that truncation loses any information, we have an overflow.
405 let truncated
= self.truncate(res
, layout
);
406 (truncated
, overflow
|| self.sign_extend(truncated
, layout
) != res
)
409 Ok((Scalar
::from_uint(res
, layout
.size
), overflow
, layout
.ty
))
417 val
: &ImmTy
<'tcx
, M
::PointerTag
>,
418 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::PointerTag
>> {
419 let (val
, _overflow
, ty
) = self.overflowing_unary_op(un_op
, val
)?
;
420 Ok(ImmTy
::from_scalar(val
, self.layout_of(ty
)?
))