2 use rustc
::ty
::{self, Ty, layout::{TyLayout, LayoutOf}
};
3 use syntax
::ast
::FloatTy
;
4 use rustc_apfloat
::Float
;
5 use rustc
::mir
::interpret
::{InterpResult, Scalar}
;
7 use super::{InterpCx, PlaceTy, Immediate, Machine, ImmTy}
;
10 impl<'mir
, 'tcx
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
11 /// Applies the binary operation `op` to the two operands and writes a tuple of the result
12 /// and a boolean signifying the potential overflow to the destination.
13 pub fn binop_with_overflow(
16 left
: ImmTy
<'tcx
, M
::PointerTag
>,
17 right
: ImmTy
<'tcx
, M
::PointerTag
>,
18 dest
: PlaceTy
<'tcx
, M
::PointerTag
>,
19 ) -> InterpResult
<'tcx
> {
20 let (val
, overflowed
, ty
) = self.overflowing_binary_op(op
, left
, right
)?
;
22 self.tcx
.intern_tup(&[ty
, self.tcx
.types
.bool
]),
24 "type mismatch for result of {:?}", op
,
26 let val
= Immediate
::ScalarPair(val
.into(), Scalar
::from_bool(overflowed
).into());
27 self.write_immediate(val
, dest
)
30 /// Applies the binary operation `op` to the arguments and writes the result to the
32 pub fn binop_ignore_overflow(
35 left
: ImmTy
<'tcx
, M
::PointerTag
>,
36 right
: ImmTy
<'tcx
, M
::PointerTag
>,
37 dest
: PlaceTy
<'tcx
, M
::PointerTag
>,
38 ) -> InterpResult
<'tcx
> {
39 let (val
, _overflowed
, ty
) = self.overflowing_binary_op(op
, left
, right
)?
;
40 assert_eq
!(ty
, dest
.layout
.ty
, "type mismatch for result of {:?}", op
);
41 self.write_scalar(val
, dest
)
45 impl<'mir
, 'tcx
, M
: Machine
<'mir
, 'tcx
>> InterpCx
<'mir
, 'tcx
, M
> {
51 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
52 use rustc
::mir
::BinOp
::*;
54 let res
= match bin_op
{
61 _
=> bug
!("Invalid operation on char: {:?}", bin_op
),
63 return (Scalar
::from_bool(res
), false, self.tcx
.types
.bool
);
71 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
72 use rustc
::mir
::BinOp
::*;
74 let res
= match bin_op
{
84 _
=> bug
!("Invalid operation on bool: {:?}", bin_op
),
86 return (Scalar
::from_bool(res
), false, self.tcx
.types
.bool
);
89 fn binary_float_op
<F
: Float
+ Into
<Scalar
<M
::PointerTag
>>>(
95 ) -> (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>) {
96 use rustc
::mir
::BinOp
::*;
98 let (val
, ty
) = match bin_op
{
99 Eq
=> (Scalar
::from_bool(l
== r
), self.tcx
.types
.bool
),
100 Ne
=> (Scalar
::from_bool(l
!= r
), self.tcx
.types
.bool
),
101 Lt
=> (Scalar
::from_bool(l
< r
), self.tcx
.types
.bool
),
102 Le
=> (Scalar
::from_bool(l
<= r
), self.tcx
.types
.bool
),
103 Gt
=> (Scalar
::from_bool(l
> r
), self.tcx
.types
.bool
),
104 Ge
=> (Scalar
::from_bool(l
>= r
), self.tcx
.types
.bool
),
105 Add
=> ((l
+ r
).value
.into(), ty
),
106 Sub
=> ((l
- r
).value
.into(), ty
),
107 Mul
=> ((l
* r
).value
.into(), ty
),
108 Div
=> ((l
/ r
).value
.into(), ty
),
109 Rem
=> ((l
% r
).value
.into(), ty
),
110 _
=> bug
!("invalid float op: `{:?}`", bin_op
),
112 return (val
, false, ty
);
118 // passing in raw bits
120 left_layout
: TyLayout
<'tcx
>,
122 right_layout
: TyLayout
<'tcx
>,
123 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
124 use rustc
::mir
::BinOp
::*;
126 // Shift ops can have an RHS with a different numeric type.
127 if bin_op
== Shl
|| bin_op
== Shr
{
128 let signed
= left_layout
.abi
.is_signed();
129 let mut oflo
= (r
as u32 as u128
) != r
;
130 let mut r
= r
as u32;
131 let size
= left_layout
.size
;
132 oflo
|= r
>= size
.bits() as u32;
134 r
%= size
.bits() as u32;
136 let result
= if signed
{
137 let l
= self.sign_extend(l
, left_layout
) as i128
;
138 let result
= match bin_op
{
141 _
=> bug
!("it has already been checked that this is a shift op"),
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
, size
), oflo
, 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
{
158 "invalid asymmetric binary op {:?}: {:?} ({:?}), {:?} ({:?})",
165 // Operations that need special treatment for signed integers
166 if left_layout
.abi
.is_signed() {
167 let op
: Option
<fn(&i128
, &i128
) -> bool
> = match bin_op
{
168 Lt
=> Some(i128
::lt
),
169 Le
=> Some(i128
::le
),
170 Gt
=> Some(i128
::gt
),
171 Ge
=> Some(i128
::ge
),
174 if let Some(op
) = op
{
175 let l
= self.sign_extend(l
, left_layout
) as i128
;
176 let r
= self.sign_extend(r
, right_layout
) as i128
;
177 return Ok((Scalar
::from_bool(op(&l
, &r
)), false, self.tcx
.types
.bool
));
179 let op
: Option
<fn(i128
, i128
) -> (i128
, bool
)> = match bin_op
{
180 Div
if r
== 0 => throw_panic
!(DivisionByZero
),
181 Rem
if r
== 0 => throw_panic
!(RemainderByZero
),
182 Div
=> Some(i128
::overflowing_div
),
183 Rem
=> Some(i128
::overflowing_rem
),
184 Add
=> Some(i128
::overflowing_add
),
185 Sub
=> Some(i128
::overflowing_sub
),
186 Mul
=> Some(i128
::overflowing_mul
),
189 if let Some(op
) = op
{
190 let l128
= self.sign_extend(l
, left_layout
) as i128
;
191 let r
= self.sign_extend(r
, right_layout
) as i128
;
192 let size
= left_layout
.size
;
196 if r
== -1 && l
== (1 << (size
.bits() - 1)) {
197 return Ok((Scalar
::from_uint(l
, size
), true, left_layout
.ty
));
202 trace
!("{}, {}, {}", l
, l128
, r
);
203 let (result
, mut oflo
) = op(l128
, r
);
204 trace
!("{}, {}", result
, oflo
);
205 if !oflo
&& size
.bits() != 128 {
206 let max
= 1 << (size
.bits() - 1);
207 oflo
= result
>= max
|| result
< -max
;
209 // this may be out-of-bounds for the result type, so we have to truncate ourselves
210 let result
= result
as u128
;
211 let truncated
= self.truncate(result
, left_layout
);
212 return Ok((Scalar
::from_uint(truncated
, size
), oflo
, left_layout
.ty
));
216 let size
= left_layout
.size
;
218 let (val
, ty
) = match bin_op
{
219 Eq
=> (Scalar
::from_bool(l
== r
), self.tcx
.types
.bool
),
220 Ne
=> (Scalar
::from_bool(l
!= r
), self.tcx
.types
.bool
),
222 Lt
=> (Scalar
::from_bool(l
< r
), self.tcx
.types
.bool
),
223 Le
=> (Scalar
::from_bool(l
<= r
), self.tcx
.types
.bool
),
224 Gt
=> (Scalar
::from_bool(l
> r
), self.tcx
.types
.bool
),
225 Ge
=> (Scalar
::from_bool(l
>= r
), self.tcx
.types
.bool
),
227 BitOr
=> (Scalar
::from_uint(l
| r
, size
), left_layout
.ty
),
228 BitAnd
=> (Scalar
::from_uint(l
& r
, size
), left_layout
.ty
),
229 BitXor
=> (Scalar
::from_uint(l ^ r
, size
), left_layout
.ty
),
231 Add
| Sub
| Mul
| Rem
| Div
=> {
232 debug_assert
!(!left_layout
.abi
.is_signed());
233 let op
: fn(u128
, u128
) -> (u128
, bool
) = match bin_op
{
234 Add
=> u128
::overflowing_add
,
235 Sub
=> u128
::overflowing_sub
,
236 Mul
=> u128
::overflowing_mul
,
237 Div
if r
== 0 => throw_panic
!(DivisionByZero
),
238 Rem
if r
== 0 => throw_panic
!(RemainderByZero
),
239 Div
=> u128
::overflowing_div
,
240 Rem
=> u128
::overflowing_rem
,
243 let (result
, oflo
) = op(l
, r
);
244 let truncated
= self.truncate(result
, left_layout
);
246 Scalar
::from_uint(truncated
, size
),
247 oflo
|| truncated
!= result
,
254 "invalid binary op {:?}: {:?}, {:?} (both {:?})",
266 /// Returns the result of the specified operation, whether it overflowed, and
268 pub fn overflowing_binary_op(
271 left
: ImmTy
<'tcx
, M
::PointerTag
>,
272 right
: ImmTy
<'tcx
, M
::PointerTag
>,
273 ) -> InterpResult
<'tcx
, (Scalar
<M
::PointerTag
>, bool
, Ty
<'tcx
>)> {
274 trace
!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})",
275 bin_op
, *left
, left
.layout
.ty
, *right
, right
.layout
.ty
);
277 match left
.layout
.ty
.sty
{
279 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
280 let left
= left
.to_scalar()?
;
281 let right
= right
.to_scalar()?
;
282 Ok(self.binary_char_op(bin_op
, left
.to_char()?
, right
.to_char()?
))
285 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
286 let left
= left
.to_scalar()?
;
287 let right
= right
.to_scalar()?
;
288 Ok(self.binary_bool_op(bin_op
, left
.to_bool()?
, right
.to_bool()?
))
291 assert_eq
!(left
.layout
.ty
, right
.layout
.ty
);
292 let ty
= left
.layout
.ty
;
293 let left
= left
.to_scalar()?
;
294 let right
= right
.to_scalar()?
;
297 self.binary_float_op(bin_op
, ty
, left
.to_f32()?
, right
.to_f32()?
),
299 self.binary_float_op(bin_op
, ty
, left
.to_f64()?
, right
.to_f64()?
),
302 _
if left
.layout
.ty
.is_integral() => {
303 // the RHS type can be different, e.g. for shifts -- but it has to be integral, too
305 right
.layout
.ty
.is_integral(),
306 "Unexpected types for BinOp: {:?} {:?} {:?}",
307 left
.layout
.ty
, bin_op
, right
.layout
.ty
310 let l
= self.force_bits(left
.to_scalar()?
, left
.layout
.size
)?
;
311 let r
= self.force_bits(right
.to_scalar()?
, right
.layout
.size
)?
;
312 self.binary_int_op(bin_op
, l
, left
.layout
, r
, right
.layout
)
314 _
if left
.layout
.ty
.is_any_ptr() => {
315 // The RHS type must be the same *or an integer type* (for `Offset`).
317 right
.layout
.ty
== left
.layout
.ty
|| right
.layout
.ty
.is_integral(),
318 "Unexpected types for BinOp: {:?} {:?} {:?}",
319 left
.layout
.ty
, bin_op
, right
.layout
.ty
322 M
::binary_ptr_op(self, bin_op
, left
, right
)
324 _
=> bug
!("Invalid MIR: bad LHS type for binop: {:?}", left
.layout
.ty
),
328 /// Typed version of `checked_binary_op`, returning an `ImmTy`. Also ignores overflows.
333 left
: ImmTy
<'tcx
, M
::PointerTag
>,
334 right
: ImmTy
<'tcx
, M
::PointerTag
>,
335 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::PointerTag
>> {
336 let (val
, _overflow
, ty
) = self.overflowing_binary_op(bin_op
, left
, right
)?
;
337 Ok(ImmTy
::from_scalar(val
, self.layout_of(ty
)?
))
343 val
: ImmTy
<'tcx
, M
::PointerTag
>,
344 ) -> InterpResult
<'tcx
, ImmTy
<'tcx
, M
::PointerTag
>> {
345 use rustc
::mir
::UnOp
::*;
347 let layout
= val
.layout
;
348 let val
= val
.to_scalar()?
;
349 trace
!("Running unary op {:?}: {:?} ({:?})", un_op
, val
, layout
.ty
);
351 match layout
.ty
.sty
{
353 let val
= val
.to_bool()?
;
354 let res
= match un_op
{
356 _
=> bug
!("Invalid bool op {:?}", un_op
)
358 Ok(ImmTy
::from_scalar(Scalar
::from_bool(res
), self.layout_of(self.tcx
.types
.bool
)?
))
361 let res
= match (un_op
, fty
) {
362 (Neg
, FloatTy
::F32
) => Scalar
::from_f32(-val
.to_f32()?
),
363 (Neg
, FloatTy
::F64
) => Scalar
::from_f64(-val
.to_f64()?
),
364 _
=> bug
!("Invalid float op {:?}", un_op
)
366 Ok(ImmTy
::from_scalar(res
, layout
))
369 assert
!(layout
.ty
.is_integral());
370 let val
= self.force_bits(val
, layout
.size
)?
;
371 let res
= match un_op
{
374 assert
!(layout
.abi
.is_signed());
375 (-(val
as i128
)) as u128
378 // res needs tuncating
379 Ok(ImmTy
::from_uint(self.truncate(res
, layout
), layout
))