1 use std
::convert
::TryFrom
;
3 use rustc_apfloat
::Float
;
4 use rustc_ast
::FloatTy
;
6 use rustc_middle
::mir
::interpret
::{InterpResult, Scalar}
;
7 use rustc_middle
::ty
::{self, layout::TyAndLayout, Ty}
;
8 use rustc_target
::abi
::LayoutOf
;
10 use super::{ImmTy, Immediate, InterpCx, Machine, PlaceTy}
;
12 impl<'mir
, 'tcx
: 'mir
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
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.
15 pub fn binop_with_overflow(
18 left
: ImmTy
<'tcx
, M
::PointerTag
>,
19 right
: ImmTy
<'tcx
, M
::PointerTag
>,
20 dest
: PlaceTy
<'tcx
, M
::PointerTag
>,
21 ) -> InterpResult
<'tcx
> {
22 let (val
, overflowed
, ty
) = self.overflowing_binary_op(op
, left
, right
)?
;
24 self.tcx
.intern_tup(&[ty
, self.tcx
.types
.bool
]),
26 "type mismatch for result of {:?}",
29 let val
= Immediate
::ScalarPair(val
.into(), Scalar
::from_bool(overflowed
).into());
30 self.write_immediate(val
, dest
)
33 /// Applies the binary operation `op` to the arguments and writes the result to the
35 pub fn binop_ignore_overflow(
38 left
: ImmTy
<'tcx
, M
::PointerTag
>,
39 right
: ImmTy
<'tcx
, M
::PointerTag
>,
40 dest
: PlaceTy
<'tcx
, M
::PointerTag
>,
41 ) -> InterpResult
<'tcx
> {
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
);
44 self.write_scalar(val
, dest
)
48 impl<'mir
, 'tcx
: 'mir
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
54 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
55 use rustc_middle
::mir
::BinOp
::*;
57 let res
= match bin_op
{
64 _
=> span_bug
!(self.cur_span(), "Invalid operation on char: {:?}", bin_op
),
66 (Scalar
::from_bool(res
), false, self.tcx
.types
.bool
)
74 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
75 use rustc_middle
::mir
::BinOp
::*;
77 let res
= match bin_op
{
87 _
=> span_bug
!(self.cur_span(), "Invalid operation on bool: {:?}", bin_op
),
89 (Scalar
::from_bool(res
), false, self.tcx
.types
.bool
)
92 fn binary_float_op
<F
: Float
+ Into
<Scalar
<M
::PointerTag
>>>(
98 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
99 use rustc_middle
::mir
::BinOp
::*;
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
),
113 _
=> span_bug
!(self.cur_span(), "invalid float op: `{:?}`", bin_op
),
121 // passing in raw bits
123 left_layout
: TyAndLayout
<'tcx
>,
125 right_layout
: TyAndLayout
<'tcx
>,
126 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
127 use rustc_middle
::mir
::BinOp
::*;
129 // Shift ops can have an RHS with a different numeric type.
130 if bin_op
== Shl
|| bin_op
== Shr
{
131 let signed
= left_layout
.abi
.is_signed();
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
136 let result
= if signed
{
137 let l
= self.sign_extend(l
, left_layout
) as i128
;
138 let result
= match bin_op
{
139 Shl
=> l
.checked_shl(r
).unwrap(),
140 Shr
=> l
.checked_shr(r
).unwrap(),
141 _
=> bug
!("it has already been checked that this is a shift op"),
146 Shl
=> l
.checked_shl(r
).unwrap(),
147 Shr
=> l
.checked_shr(r
).unwrap(),
148 _
=> bug
!("it has already been checked that this is a shift op"),
151 let truncated
= self.truncate(result
, left_layout
);
152 return Ok((Scalar
::from_uint(truncated
, left_layout
.size
), overflow
, left_layout
.ty
));
155 // For the remaining ops, the types must be the same on both sides
156 if left_layout
.ty
!= right_layout
.ty
{
159 "invalid asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
168 let size
= left_layout
.size
;
170 // Operations that need special treatment for signed integers
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
),
179 if let Some(op
) = op
{
180 let l
= self.sign_extend(l
, left_layout
) as i128
;
181 let r
= self.sign_extend(r
, right_layout
) as i128
;
182 return Ok((Scalar
::from_bool(op(&l
, &r
)), false, self.tcx
.types
.bool
));
184 let op
: Option
<fn(i128
, i128
) -> (i128
, bool
)> = match bin_op
{
185 Div
if r
== 0 => throw_ub
!(DivisionByZero
),
186 Rem
if r
== 0 => throw_ub
!(RemainderByZero
),
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
),
194 if let Some(op
) = op
{
195 let r
= self.sign_extend(r
, right_layout
) as i128
;
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!
200 if r
== -1 && l
== (1 << (size
.bits() - 1)) {
201 return Ok((Scalar
::from_int(0, size
), true, left_layout
.ty
));
204 let l
= self.sign_extend(l
, left_layout
) as i128
;
206 let (result
, oflo
) = op(l
, r
);
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.
209 let result
= result
as u128
;
210 let truncated
= self.truncate(result
, left_layout
);
212 Scalar
::from_uint(truncated
, size
),
213 oflo
|| self.sign_extend(truncated
, left_layout
) != result
,
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
),
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
),
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
),
232 Add
| Sub
| Mul
| Rem
| Div
=> {
233 assert
!(!left_layout
.abi
.is_signed());
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
,
238 Div
if r
== 0 => throw_ub
!(DivisionByZero
),
239 Rem
if r
== 0 => throw_ub
!(RemainderByZero
),
240 Div
=> u128
::overflowing_div
,
241 Rem
=> u128
::overflowing_rem
,
244 let (result
, oflo
) = op(l
, r
);
245 // Truncate to target type.
246 // If that truncation loses any information, we have an overflow.
247 let truncated
= self.truncate(result
, left_layout
);
249 Scalar
::from_uint(truncated
, size
),
250 oflo
|| truncated
!= result
,
257 "invalid binary op {:?}: {:?}, {:?} (both {:?})",
268 /// Returns the result of the specified operation, whether it overflowed, and
270 pub fn overflowing_binary_op(
273 left
: ImmTy
<'tcx
, M
::PointerTag
>,
274 right
: ImmTy
<'tcx
, M
::PointerTag
>,
275 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
277 "Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
285 match left
.layout
.ty
.kind
{
287 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
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()?
))
293 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
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()?
))
299 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
300 let ty
= left
.layout
.ty
;
301 let left
= left
.to_scalar()?
;
302 let right
= right
.to_scalar()?
;
305 self.binary_float_op(bin_op
, ty
, left
.to_f32()?
, right
.to_f32()?
)
308 self.binary_float_op(bin_op
, ty
, left
.to_f64()?
, right
.to_f64()?
)
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
315 right
.layout
.ty
.is_integral(),
316 "Unexpected types for BinOp: {:?} {:?} {:?}",
322 let l
= self.force_bits(left
.to_scalar()?
, left
.layout
.size
)?
;
323 let r
= self.force_bits(right
.to_scalar()?
, right
.layout
.size
)?
;
324 self.binary_int_op(bin_op
, l
, left
.layout
, r
, right
.layout
)
326 _
if left
.layout
.ty
.is_any_ptr() => {
327 // The RHS type must be the same *or an integer type* (for `Offset`).
329 right
.layout
.ty
== left
.layout
.ty
|| right
.layout
.ty
.is_integral(),
330 "Unexpected types for BinOp: {:?} {:?} {:?}",
336 M
::binary_ptr_op(self, bin_op
, left
, right
)
340 "Invalid MIR: bad LHS type for binop: {:?}",
346 /// Typed version of `overflowing_binary_op`, returning an `ImmTy`. Also ignores overflows.
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
)?
))
358 /// Returns the result of the specified operation, whether it overflowed, and
360 pub fn overflowing_unary_op(
363 val
: ImmTy
<'tcx
, M
::PointerTag
>,
364 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
365 use rustc_middle
::mir
::UnOp
::*;
367 let layout
= val
.layout
;
368 let val
= val
.to_scalar()?
;
369 trace
!("Running unary op {:?}: {:?} ({:?})", un_op
, val
, layout
.ty
);
371 match layout
.ty
.kind
{
373 let val
= val
.to_bool()?
;
374 let res
= match un_op
{
376 _
=> span_bug
!(self.cur_span(), "Invalid bool op {:?}", un_op
),
378 Ok((Scalar
::from_bool(res
), false, self.tcx
.types
.bool
))
381 let res
= match (un_op
, fty
) {
382 (Neg
, FloatTy
::F32
) => Scalar
::from_f32(-val
.to_f32()?
),
383 (Neg
, FloatTy
::F64
) => Scalar
::from_f64(-val
.to_f64()?
),
384 _
=> span_bug
!(self.cur_span(), "Invalid float op {:?}", un_op
),
386 Ok((res
, false, layout
.ty
))
389 assert
!(layout
.ty
.is_integral());
390 let val
= self.force_bits(val
, layout
.size
)?
;
391 let (res
, overflow
) = match un_op
{
392 Not
=> (self.truncate(!val
, layout
), false), // bitwise negation, then truncate
394 // arithmetic negation
395 assert
!(layout
.abi
.is_signed());
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
)
405 Ok((Scalar
::from_uint(res
, layout
.size
), overflow
, layout
.ty
))
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
)?
))