2016-11-03 04:38:08 -05:00
use rustc ::mir ;
2017-06-04 19:26:19 -05:00
use rustc ::ty ::{ self , Ty } ;
2016-03-13 01:43:28 -06:00
2016-03-19 00:03:46 -05:00
use error ::{ EvalError , EvalResult } ;
2016-12-10 19:03:12 -06:00
use eval_context ::EvalContext ;
use lvalue ::Lvalue ;
2016-12-10 18:23:07 -06:00
use memory ::Pointer ;
use value ::{
PrimVal ,
PrimValKind ,
2016-12-10 19:03:12 -06:00
Value ,
2016-12-17 05:19:24 -06:00
bytes_to_f32 ,
bytes_to_f64 ,
f32_to_bytes ,
f64_to_bytes ,
bytes_to_bool ,
2016-12-10 18:23:07 -06:00
} ;
2016-10-20 05:42:19 -05:00
2016-12-10 19:03:12 -06:00
impl < ' a , ' tcx > EvalContext < ' a , ' tcx > {
fn binop_with_overflow (
& mut self ,
op : mir ::BinOp ,
left : & mir ::Operand < ' tcx > ,
right : & mir ::Operand < ' tcx > ,
) -> EvalResult < ' tcx , ( PrimVal , bool ) > {
let left_ty = self . operand_ty ( left ) ;
let right_ty = self . operand_ty ( right ) ;
let left_val = self . eval_operand_to_primval ( left ) ? ;
let right_val = self . eval_operand_to_primval ( right ) ? ;
2017-06-04 19:26:19 -05:00
self . binary_op ( op , left_val , left_ty , right_val , right_ty )
2016-12-10 19:03:12 -06:00
}
/// Applies the binary operation `op` to the two operands and writes a tuple of the result
/// and a boolean signifying the potential overflow to the destination.
pub ( super ) fn intrinsic_with_overflow (
& mut self ,
op : mir ::BinOp ,
left : & mir ::Operand < ' tcx > ,
right : & mir ::Operand < ' tcx > ,
dest : Lvalue < ' tcx > ,
dest_ty : Ty < ' tcx > ,
2017-02-04 15:09:10 -06:00
) -> EvalResult < ' tcx > {
2016-12-10 19:03:12 -06:00
let ( val , overflowed ) = self . binop_with_overflow ( op , left , right ) ? ;
let val = Value ::ByValPair ( val , PrimVal ::from_bool ( overflowed ) ) ;
self . write_value ( val , dest , dest_ty )
}
/// Applies the binary operation `op` to the arguments and writes the result to the
/// destination. Returns `true` if the operation overflowed.
pub ( super ) fn intrinsic_overflowing (
& mut self ,
op : mir ::BinOp ,
left : & mir ::Operand < ' tcx > ,
right : & mir ::Operand < ' tcx > ,
dest : Lvalue < ' tcx > ,
dest_ty : Ty < ' tcx > ,
) -> EvalResult < ' tcx , bool > {
let ( val , overflowed ) = self . binop_with_overflow ( op , left , right ) ? ;
self . write_primval ( dest , val , dest_ty ) ? ;
Ok ( overflowed )
}
}
2016-10-20 05:42:19 -05:00
macro_rules ! overflow {
2016-11-27 00:58:01 -06:00
( $op :ident , $l :expr , $r :expr ) = > ( {
2016-10-20 05:42:19 -05:00
let ( val , overflowed ) = $l . $op ( $r ) ;
2017-01-12 01:28:42 -06:00
let primval = PrimVal ::Bytes ( val as u128 ) ;
2016-10-20 05:42:19 -05:00
Ok ( ( primval , overflowed ) )
} )
}
2016-06-17 06:09:20 -05:00
2016-10-20 05:42:19 -05:00
macro_rules ! int_arithmetic {
( $kind :expr , $int_op :ident , $l :expr , $r :expr ) = > ( {
let l = $l ;
let r = $r ;
match $kind {
2016-11-27 00:58:01 -06:00
I8 = > overflow! ( $int_op , l as i8 , r as i8 ) ,
I16 = > overflow! ( $int_op , l as i16 , r as i16 ) ,
I32 = > overflow! ( $int_op , l as i32 , r as i32 ) ,
I64 = > overflow! ( $int_op , l as i64 , r as i64 ) ,
2017-02-07 10:52:32 -06:00
I128 = > overflow! ( $int_op , l as i128 , r as i128 ) ,
2016-11-27 00:58:01 -06:00
U8 = > overflow! ( $int_op , l as u8 , r as u8 ) ,
U16 = > overflow! ( $int_op , l as u16 , r as u16 ) ,
U32 = > overflow! ( $int_op , l as u32 , r as u32 ) ,
U64 = > overflow! ( $int_op , l as u64 , r as u64 ) ,
2017-02-07 10:52:32 -06:00
U128 = > overflow! ( $int_op , l as u128 , r as u128 ) ,
2016-10-20 05:42:19 -05:00
_ = > bug! ( " int_arithmetic should only be called on int primvals " ) ,
2016-04-23 01:39:38 -05:00
}
2016-10-20 05:42:19 -05:00
} )
}
macro_rules ! int_shift {
( $kind :expr , $int_op :ident , $l :expr , $r :expr ) = > ( {
let l = $l ;
let r = $r ;
match $kind {
2016-11-27 00:58:01 -06:00
I8 = > overflow! ( $int_op , l as i8 , r ) ,
I16 = > overflow! ( $int_op , l as i16 , r ) ,
I32 = > overflow! ( $int_op , l as i32 , r ) ,
I64 = > overflow! ( $int_op , l as i64 , r ) ,
2017-02-07 10:52:32 -06:00
I128 = > overflow! ( $int_op , l as i128 , r ) ,
2016-11-27 00:58:01 -06:00
U8 = > overflow! ( $int_op , l as u8 , r ) ,
U16 = > overflow! ( $int_op , l as u16 , r ) ,
U32 = > overflow! ( $int_op , l as u32 , r ) ,
U64 = > overflow! ( $int_op , l as u64 , r ) ,
2017-02-07 10:52:32 -06:00
U128 = > overflow! ( $int_op , l as u128 , r ) ,
2016-10-20 05:42:19 -05:00
_ = > bug! ( " int_shift should only be called on int primvals " ) ,
}
} )
}
macro_rules ! float_arithmetic {
2016-12-17 05:19:24 -06:00
( $from_bytes :ident , $to_bytes :ident , $float_op :tt , $l :expr , $r :expr ) = > ( {
let l = $from_bytes ( $l ) ;
let r = $from_bytes ( $r ) ;
let bytes = $to_bytes ( l $float_op r ) ;
PrimVal ::Bytes ( bytes )
2016-10-20 05:42:19 -05:00
} )
}
macro_rules ! f32_arithmetic {
( $float_op :tt , $l :expr , $r :expr ) = > (
2016-12-17 05:19:24 -06:00
float_arithmetic! ( bytes_to_f32 , f32_to_bytes , $float_op , $l , $r )
2016-10-20 05:42:19 -05:00
)
}
macro_rules ! f64_arithmetic {
( $float_op :tt , $l :expr , $r :expr ) = > (
2016-12-17 05:19:24 -06:00
float_arithmetic! ( bytes_to_f64 , f64_to_bytes , $float_op , $l , $r )
2016-10-20 05:42:19 -05:00
)
}
2016-04-23 01:39:38 -05:00
2017-06-04 19:26:19 -05:00
impl < ' a , ' tcx > EvalContext < ' a , ' tcx > {
/// Returns the result of the specified operation and whether it overflowed.
pub fn binary_op (
& self ,
bin_op : mir ::BinOp ,
left : PrimVal ,
left_ty : Ty < ' tcx > ,
right : PrimVal ,
right_ty : Ty < ' tcx > ,
) -> EvalResult < ' tcx , ( PrimVal , bool ) > {
use rustc ::mir ::BinOp ::* ;
use value ::PrimValKind ::* ;
2016-10-20 05:42:19 -05:00
2017-06-04 19:26:19 -05:00
// FIXME(solson): Temporary hack. It will go away when we get rid of Pointer's ability to store
// plain bytes, and leave that to PrimVal::Bytes.
fn normalize ( val : PrimVal ) -> PrimVal {
if let PrimVal ::Ptr ( ptr ) = val {
if let Ok ( bytes ) = ptr . to_int ( ) {
return PrimVal ::Bytes ( bytes as u128 ) ;
}
2016-12-17 05:09:57 -06:00
}
2017-06-04 19:26:19 -05:00
val
2016-12-17 05:09:57 -06:00
}
2017-06-04 19:26:19 -05:00
let ( left , right ) = ( normalize ( left ) , normalize ( right ) ) ;
2016-12-17 05:09:57 -06:00
2017-06-04 19:26:19 -05:00
// Offset is handled early, before we dispatch to unrelated_ptr_ops. We have to also catch the case where both arguments *are* convertible to integers.
if bin_op = = Offset {
let pointee_ty = left_ty . builtin_deref ( true , ty ::LvaluePreference ::NoPreference ) . expect ( " Offset called on non-ptr type " ) . ty ;
let ptr = self . pointer_offset ( left . to_ptr ( ) ? , pointee_ty , right . to_bytes ( ) ? as i64 ) ? ;
return Ok ( ( PrimVal ::Ptr ( ptr ) , false ) ) ;
2016-12-17 05:09:57 -06:00
}
2017-06-04 19:26:19 -05:00
let ( l , r ) = match ( left , right ) {
( PrimVal ::Bytes ( left_bytes ) , PrimVal ::Bytes ( right_bytes ) ) = > ( left_bytes , right_bytes ) ,
( PrimVal ::Ptr ( left_ptr ) , PrimVal ::Ptr ( right_ptr ) ) = > {
if left_ptr . alloc_id = = right_ptr . alloc_id {
// If the pointers are into the same allocation, fall through to the more general
// match later, which will do comparisons on the pointer offsets.
( left_ptr . offset as u128 , right_ptr . offset as u128 )
} else {
return Ok ( ( unrelated_ptr_ops ( bin_op , left_ptr , right_ptr ) ? , false ) ) ;
}
}
2016-10-20 05:42:19 -05:00
2017-06-04 19:26:19 -05:00
( PrimVal ::Ptr ( ptr ) , PrimVal ::Bytes ( bytes ) ) |
( PrimVal ::Bytes ( bytes ) , PrimVal ::Ptr ( ptr ) ) = > {
return Ok ( ( unrelated_ptr_ops ( bin_op , ptr , Pointer ::from_int ( bytes as u64 ) ) ? , false ) ) ;
}
2016-10-20 05:42:19 -05:00
2017-06-04 19:26:19 -05:00
( PrimVal ::Undef , _ ) | ( _ , PrimVal ::Undef ) = > return Err ( EvalError ::ReadUndefBytes ) ,
2016-10-20 05:42:19 -05:00
} ;
2017-06-04 19:26:19 -05:00
let left_kind = self . ty_to_primval_kind ( left_ty ) ? ;
let right_kind = self . ty_to_primval_kind ( right_ty ) ? ;
2016-10-20 05:42:19 -05:00
2017-06-04 19:26:19 -05:00
// These ops can have an RHS with a different numeric type.
if bin_op = = Shl | | bin_op = = Shr {
return match bin_op {
Shl = > int_shift! ( left_kind , overflowing_shl , l , r as u32 ) ,
Shr = > int_shift! ( left_kind , overflowing_shr , l , r as u32 ) ,
_ = > bug! ( " it has already been checked that this is a shift op " ) ,
} ;
}
if bin_op = = Offset {
// We permit offset-by-0 in any case. Drop glue actually does this, and it's probably (TM) fine for LLVM.
if left_kind = = PrimValKind ::Ptr & & right_kind . is_int ( ) & & r = = 0 {
return Ok ( ( PrimVal ::Bytes ( l ) , false ) ) ;
} else {
let msg = format! ( " unimplemented Offset: {:?} , {:?} " , left , right ) ;
return Err ( EvalError ::Unimplemented ( msg ) ) ;
}
}
if left_kind ! = right_kind {
2016-10-20 05:42:19 -05:00
let msg = format! ( " unimplemented binary op: {:?} , {:?} , {:?} " , left , right , bin_op ) ;
return Err ( EvalError ::Unimplemented ( msg ) ) ;
}
2016-03-19 00:03:46 -05:00
2017-06-04 19:26:19 -05:00
let val = match ( bin_op , left_kind ) {
( Eq , F32 ) = > PrimVal ::from_bool ( bytes_to_f32 ( l ) = = bytes_to_f32 ( r ) ) ,
( Ne , F32 ) = > PrimVal ::from_bool ( bytes_to_f32 ( l ) ! = bytes_to_f32 ( r ) ) ,
( Lt , F32 ) = > PrimVal ::from_bool ( bytes_to_f32 ( l ) < bytes_to_f32 ( r ) ) ,
( Le , F32 ) = > PrimVal ::from_bool ( bytes_to_f32 ( l ) < = bytes_to_f32 ( r ) ) ,
( Gt , F32 ) = > PrimVal ::from_bool ( bytes_to_f32 ( l ) > bytes_to_f32 ( r ) ) ,
( Ge , F32 ) = > PrimVal ::from_bool ( bytes_to_f32 ( l ) > = bytes_to_f32 ( r ) ) ,
( Eq , F64 ) = > PrimVal ::from_bool ( bytes_to_f64 ( l ) = = bytes_to_f64 ( r ) ) ,
( Ne , F64 ) = > PrimVal ::from_bool ( bytes_to_f64 ( l ) ! = bytes_to_f64 ( r ) ) ,
( Lt , F64 ) = > PrimVal ::from_bool ( bytes_to_f64 ( l ) < bytes_to_f64 ( r ) ) ,
( Le , F64 ) = > PrimVal ::from_bool ( bytes_to_f64 ( l ) < = bytes_to_f64 ( r ) ) ,
( Gt , F64 ) = > PrimVal ::from_bool ( bytes_to_f64 ( l ) > bytes_to_f64 ( r ) ) ,
( Ge , F64 ) = > PrimVal ::from_bool ( bytes_to_f64 ( l ) > = bytes_to_f64 ( r ) ) ,
( Add , F32 ) = > f32_arithmetic! ( + , l , r ) ,
( Sub , F32 ) = > f32_arithmetic! ( - , l , r ) ,
( Mul , F32 ) = > f32_arithmetic! ( * , l , r ) ,
( Div , F32 ) = > f32_arithmetic! ( / , l , r ) ,
( Rem , F32 ) = > f32_arithmetic! ( % , l , r ) ,
( Add , F64 ) = > f64_arithmetic! ( + , l , r ) ,
( Sub , F64 ) = > f64_arithmetic! ( - , l , r ) ,
( Mul , F64 ) = > f64_arithmetic! ( * , l , r ) ,
( Div , F64 ) = > f64_arithmetic! ( / , l , r ) ,
( Rem , F64 ) = > f64_arithmetic! ( % , l , r ) ,
( Eq , _ ) = > PrimVal ::from_bool ( l = = r ) ,
( Ne , _ ) = > PrimVal ::from_bool ( l ! = r ) ,
( Lt , k ) if k . is_signed_int ( ) = > PrimVal ::from_bool ( ( l as i128 ) < ( r as i128 ) ) ,
( Lt , _ ) = > PrimVal ::from_bool ( l < r ) ,
( Le , k ) if k . is_signed_int ( ) = > PrimVal ::from_bool ( ( l as i128 ) < = ( r as i128 ) ) ,
( Le , _ ) = > PrimVal ::from_bool ( l < = r ) ,
( Gt , k ) if k . is_signed_int ( ) = > PrimVal ::from_bool ( ( l as i128 ) > ( r as i128 ) ) ,
( Gt , _ ) = > PrimVal ::from_bool ( l > r ) ,
( Ge , k ) if k . is_signed_int ( ) = > PrimVal ::from_bool ( ( l as i128 ) > = ( r as i128 ) ) ,
( Ge , _ ) = > PrimVal ::from_bool ( l > = r ) ,
( BitOr , _ ) = > PrimVal ::Bytes ( l | r ) ,
( BitAnd , _ ) = > PrimVal ::Bytes ( l & r ) ,
( BitXor , _ ) = > PrimVal ::Bytes ( l ^ r ) ,
( Add , k ) if k . is_int ( ) = > return int_arithmetic! ( k , overflowing_add , l , r ) ,
( Sub , k ) if k . is_int ( ) = > return int_arithmetic! ( k , overflowing_sub , l , r ) ,
( Mul , k ) if k . is_int ( ) = > return int_arithmetic! ( k , overflowing_mul , l , r ) ,
( Div , k ) if k . is_int ( ) = > return int_arithmetic! ( k , overflowing_div , l , r ) ,
( Rem , k ) if k . is_int ( ) = > return int_arithmetic! ( k , overflowing_rem , l , r ) ,
_ = > {
let msg = format! ( " unimplemented binary op: {:?} , {:?} , {:?} " , left , right , bin_op ) ;
return Err ( EvalError ::Unimplemented ( msg ) ) ;
}
} ;
Ok ( ( val , false ) )
}
2016-03-13 01:43:28 -06:00
}
2016-11-15 07:12:49 -06:00
fn unrelated_ptr_ops < ' tcx > ( bin_op : mir ::BinOp , left : Pointer , right : Pointer ) -> EvalResult < ' tcx , PrimVal > {
2016-11-03 04:38:08 -05:00
use rustc ::mir ::BinOp ::* ;
2016-10-20 05:42:19 -05:00
match bin_op {
Eq = > Ok ( PrimVal ::from_bool ( false ) ) ,
Ne = > Ok ( PrimVal ::from_bool ( true ) ) ,
Lt | Le | Gt | Ge = > Err ( EvalError ::InvalidPointerMath ) ,
2016-11-15 07:12:49 -06:00
_ if left . to_int ( ) . is_ok ( ) ^ right . to_int ( ) . is_ok ( ) = > {
Err ( EvalError ::ReadPointerAsBytes )
} ,
2016-10-20 05:42:19 -05:00
_ = > bug! ( ) ,
}
}
2016-11-27 00:58:01 -06:00
pub fn unary_op < ' tcx > (
un_op : mir ::UnOp ,
val : PrimVal ,
val_kind : PrimValKind ,
) -> EvalResult < ' tcx , PrimVal > {
2016-11-03 04:38:08 -05:00
use rustc ::mir ::UnOp ::* ;
2016-12-10 18:23:07 -06:00
use value ::PrimValKind ::* ;
2016-10-20 05:42:19 -05:00
2016-12-17 05:09:57 -06:00
let bytes = val . to_bytes ( ) ? ;
let result_bytes = match ( un_op , val_kind ) {
2017-01-12 01:28:42 -06:00
( Not , Bool ) = > ! bytes_to_bool ( bytes ) as u128 ,
( Not , U8 ) = > ! ( bytes as u8 ) as u128 ,
( Not , U16 ) = > ! ( bytes as u16 ) as u128 ,
( Not , U32 ) = > ! ( bytes as u32 ) as u128 ,
( Not , U64 ) = > ! ( bytes as u64 ) as u128 ,
( Not , U128 ) = > ! bytes ,
( Not , I8 ) = > ! ( bytes as i8 ) as u128 ,
( Not , I16 ) = > ! ( bytes as i16 ) as u128 ,
( Not , I32 ) = > ! ( bytes as i32 ) as u128 ,
( Not , I64 ) = > ! ( bytes as i64 ) as u128 ,
( Not , I128 ) = > ! ( bytes as i128 ) as u128 ,
( Neg , I8 ) = > - ( bytes as i8 ) as u128 ,
( Neg , I16 ) = > - ( bytes as i16 ) as u128 ,
( Neg , I32 ) = > - ( bytes as i32 ) as u128 ,
( Neg , I64 ) = > - ( bytes as i64 ) as u128 ,
( Neg , I128 ) = > - ( bytes as i128 ) as u128 ,
2016-10-20 05:42:19 -05:00
2016-12-17 05:19:24 -06:00
( Neg , F32 ) = > f32_to_bytes ( - bytes_to_f32 ( bytes ) ) ,
( Neg , F64 ) = > f64_to_bytes ( - bytes_to_f64 ( bytes ) ) ,
2016-10-20 05:42:19 -05:00
_ = > {
let msg = format! ( " unimplemented unary op: {:?} , {:?} " , un_op , val ) ;
return Err ( EvalError ::Unimplemented ( msg ) ) ;
}
} ;
2016-12-17 05:09:57 -06:00
Ok ( PrimVal ::Bytes ( result_bytes ) )
2016-03-13 01:43:28 -06:00
}