From df283df887431eec0f6da0f4aa85db9c88c9449b Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Wed, 21 Feb 2018 22:02:52 +0100 Subject: [PATCH] Don't use the undefined bytes of PrimVal::Bytes --- src/librustc/mir/interpret/mod.rs | 2 +- src/librustc/mir/interpret/value.rs | 28 --- src/librustc/ty/mod.rs | 32 ++- src/librustc_mir/build/expr/as_rvalue.rs | 38 +-- src/librustc_mir/hair/cx/mod.rs | 35 ++- src/librustc_mir/interpret/cast.rs | 105 +++----- src/librustc_mir/interpret/eval_context.rs | 22 +- src/librustc_mir/interpret/memory.rs | 5 +- src/librustc_mir/interpret/mod.rs | 1 - src/librustc_mir/interpret/operator.rs | 267 ++++++++++----------- src/librustc_mir/transform/const_prop.rs | 5 +- src/test/run-pass/const-negation.rs | 2 + 12 files changed, 260 insertions(+), 282 deletions(-) diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index 91f7d79108b..67f30f53a68 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -10,7 +10,7 @@ mod value; pub use self::error::{EvalError, EvalResult, EvalErrorKind}; -pub use self::value::{PrimVal, PrimValKind, Value, Pointer, bytes_to_f32, bytes_to_f64}; +pub use self::value::{PrimVal, PrimValKind, Value, Pointer}; use std::collections::BTreeMap; use std::fmt; diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index c00956c0a85..7289d74bfbb 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -4,22 +4,6 @@ use ty::layout::{Align, HasDataLayout}; use ty; use super::{EvalResult, MemoryPointer, PointerArithmetic}; -use syntax::ast::FloatTy; -use rustc_const_math::ConstFloat; - -pub fn bytes_to_f32(bits: u128) -> ConstFloat { - ConstFloat { - bits, - ty: FloatTy::F32, - } -} - -pub fn bytes_to_f64(bits: u128) -> ConstFloat { - ConstFloat { - bits, - ty: FloatTy::F64, - } -} /// A `Value` represents a single self-contained Rust value. /// @@ -182,10 +166,6 @@ impl<'tcx> PrimVal { PrimVal::Bytes(n as u128) } - pub fn from_float(f: ConstFloat) -> Self { - PrimVal::Bytes(f.bits) - } - pub fn from_bool(b: bool) -> Self { PrimVal::Bytes(b as u128) } @@ -260,14 +240,6 @@ impl<'tcx> PrimVal { }) } - pub fn to_f32(self) -> EvalResult<'tcx, ConstFloat> { - self.to_bytes().map(bytes_to_f32) - } - - pub fn to_f64(self) -> EvalResult<'tcx, ConstFloat> { - self.to_bytes().map(bytes_to_f64) - } - pub fn to_bool(self) -> EvalResult<'tcx, bool> { match self.to_bytes()? { 0 => Ok(false), diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index cb09687a657..d730c95f4dd 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -1845,10 +1845,34 @@ impl<'a, 'gcx, 'tcx> AdtDef { .. }) => { trace!("discriminants: {} ({:?})", b, repr_type); - discr = Discr { - val: b, - ty: repr_type.to_ty(tcx), - }; + let ty = repr_type.to_ty(tcx); + if ty.is_signed() { + let (ty, param_env) = tcx + .lift_to_global(&(ty, param_env)) + .unwrap_or_else(|| { + bug!("MIR: discriminants({:?}, {:?}) got \ + type with inference types/regions", + ty, param_env); + }); + let size = tcx.global_tcx() + .layout_of(param_env.and(ty)) + .expect("int layout") + .size + .bits(); + let val = b as i128; + // sign extend to i128 + let amt = 128 - size; + let val = (val << amt) >> amt; + discr = Discr { + val: val as u128, + ty, + }; + } else { + discr = Discr { + val: b, + ty, + }; + } } _ => { if !expr_did.is_local() { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index e2cc58b1fb0..59f44fa9229 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -22,7 +22,6 @@ use rustc::middle::region; use rustc::ty::{self, Ty}; use rustc::mir::*; use rustc::mir::interpret::{Value, PrimVal}; -use syntax::ast; use syntax_pos::Span; impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { @@ -382,9 +381,11 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get a `-1` value of the appropriate type fn neg_1_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { + let bits = self.hir.type_bit_size(ty); + let n = (!0u128) >> (128 - bits); let literal = Literal::Value { value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(-1i128 as u128))), + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))), ty }) }; @@ -394,31 +395,14 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { // Helper to get the minimum value of the appropriate type fn minval_literal(&mut self, span: Span, ty: Ty<'tcx>) -> Operand<'tcx> { - let literal = match ty.sty { - ty::TyInt(ity) => { - let ity = match ity { - ast::IntTy::Isize => self.hir.tcx().sess.target.isize_ty, - other => other, - }; - let val = match ity { - ast::IntTy::I8 => i8::min_value() as i128, - ast::IntTy::I16 => i16::min_value() as i128, - ast::IntTy::I32 => i32::min_value() as i128, - ast::IntTy::I64 => i64::min_value() as i128, - ast::IntTy::I128 => i128::min_value() as i128, - ast::IntTy::Isize => unreachable!(), - }; - - Literal::Value { - value: self.hir.tcx().mk_const(ty::Const { - val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))), - ty - }) - } - } - _ => { - span_bug!(span, "Invalid type for minval_literal: `{:?}`", ty) - } + assert!(ty.is_signed()); + let bits = self.hir.type_bit_size(ty); + let n = 1 << (bits - 1); + let literal = Literal::Value { + value: self.hir.tcx().mk_const(ty::Const { + val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(n))), + ty + }) }; self.literal_operand(span, ty, literal) diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 0b59ce9e117..ec484785ca2 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -149,6 +149,26 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { } } + pub fn type_bit_size( + &self, + ty: Ty<'tcx>, + ) -> u64 { + let tcx = self.tcx.global_tcx(); + let (ty, param_env) = self + .tcx + .lift_to_global(&(ty, self.param_env)) + .unwrap_or_else(|| { + bug!("MIR: Cx::const_eval_literal({:?}, {:?}) got \ + type with inference types/regions", + ty, self.param_env); + }); + tcx + .layout_of(param_env.and(ty)) + .expect("int layout") + .size + .bits() + } + pub fn const_eval_literal( &mut self, lit: &'tcx ast::LitKind, @@ -156,6 +176,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { sp: Span, neg: bool, ) -> Literal<'tcx> { + trace!("const_eval_literal: {:#?}, {:?}, {:?}, {:?}", lit, ty, sp, neg); let tcx = self.tcx.global_tcx(); let parse_float = |num: &str, fty| -> ConstFloat { @@ -165,6 +186,15 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { }) }; + let clamp = |n| { + let size = self.type_bit_size(ty); + trace!("clamp {} with size {} and amt {}", n, size, 128 - size); + let amt = 128 - size; + let result = (n << amt) >> amt; + trace!("clamp result: {}", result); + result + }; + use rustc::mir::interpret::*; let lit = match *lit { LitKind::Str(ref s, _) => { @@ -185,9 +215,10 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { LitKind::Int(n, _) if neg => { let n = n as i128; let n = n.overflowing_neg().0; - Value::ByVal(PrimVal::Bytes(n as u128)) + let n = clamp(n as u128); + Value::ByVal(PrimVal::Bytes(n)) }, - LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(n)), + LitKind::Int(n, _) => Value::ByVal(PrimVal::Bytes(clamp(n))), LitKind::Float(n, fty) => { let n = n.as_str(); let mut f = parse_float(&n, fty); diff --git a/src/librustc_mir/interpret/cast.rs b/src/librustc_mir/interpret/cast.rs index 7d7e6ec9451..9b118b7fb78 100644 --- a/src/librustc_mir/interpret/cast.rs +++ b/src/librustc_mir/interpret/cast.rs @@ -14,75 +14,37 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { src_ty: Ty<'tcx>, dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, PrimVal> { + use rustc::ty::TypeVariants::*; trace!("Casting {:?}: {:?} to {:?}", val, src_ty, dest_ty); - let src_kind = self.ty_to_primval_kind(src_ty)?; match val { PrimVal::Undef => Ok(PrimVal::Undef), PrimVal::Ptr(ptr) => self.cast_from_ptr(ptr, dest_ty), - val @ PrimVal::Bytes(_) => { - use rustc::mir::interpret::PrimValKind::*; - match src_kind { - F32 => self.cast_from_float(val.to_f32()?, dest_ty), - F64 => self.cast_from_float(val.to_f64()?, dest_ty), - - I8 | I16 | I32 | I64 | I128 => { - self.cast_from_signed_int(val.to_i128()?, dest_ty) - } - - Bool | Char | U8 | U16 | U32 | U64 | U128 | FnPtr | Ptr => { - self.cast_from_int(val.to_u128()?, dest_ty, false) - } + PrimVal::Bytes(b) => { + match src_ty.sty { + TyFloat(fty) => self.cast_from_float(b, fty, dest_ty), + _ => self.cast_from_int(b, src_ty, dest_ty), } } } } - fn cast_from_signed_int(&self, val: i128, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { - self.cast_from_int(val as u128, ty, val < 0) - } - - fn int_to_int(&self, v: i128, ty: IntTy) -> u128 { - match ty { - IntTy::I8 => v as i8 as u128, - IntTy::I16 => v as i16 as u128, - IntTy::I32 => v as i32 as u128, - IntTy::I64 => v as i64 as u128, - IntTy::I128 => v as u128, - IntTy::Isize => { - let ty = self.tcx.sess.target.isize_ty; - self.int_to_int(v, ty) - } - } - } - fn int_to_uint(&self, v: u128, ty: UintTy) -> u128 { - match ty { - UintTy::U8 => v as u8 as u128, - UintTy::U16 => v as u16 as u128, - UintTy::U32 => v as u32 as u128, - UintTy::U64 => v as u64 as u128, - UintTy::U128 => v, - UintTy::Usize => { - let ty = self.tcx.sess.target.usize_ty; - self.int_to_uint(v, ty) - } - } - } - fn cast_from_int( &self, v: u128, - ty: Ty<'tcx>, - negative: bool, + src_ty: Ty<'tcx>, + dest_ty: Ty<'tcx>, ) -> EvalResult<'tcx, PrimVal> { - trace!("cast_from_int: {}, {}, {}", v, ty, negative); + trace!("cast_from_int: {}, {}, {}", v, src_ty, dest_ty); use rustc::ty::TypeVariants::*; - match ty.sty { - // Casts to bool are not permitted by rustc, no need to handle them here. - TyInt(ty) => Ok(PrimVal::Bytes(self.int_to_int(v as i128, ty))), - TyUint(ty) => Ok(PrimVal::Bytes(self.int_to_uint(v, ty))), + match dest_ty.sty { + TyInt(_) | TyUint(_) => { + let v = self.sign_extend(v, src_ty)?; + let v = self.truncate(v, dest_ty)?; + Ok(PrimVal::Bytes(v)) + } - TyFloat(fty) if negative => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), + TyFloat(fty) if src_ty.is_signed() => Ok(PrimVal::Bytes(ConstFloat::from_i128(v as i128, fty).bits)), TyFloat(fty) => Ok(PrimVal::Bytes(ConstFloat::from_u128(v, fty).bits)), TyChar if v as u8 as u128 == v => Ok(PrimVal::Bytes(v)), @@ -91,31 +53,42 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // No alignment check needed for raw pointers. But we have to truncate to target ptr size. TyRawPtr(_) => Ok(PrimVal::Bytes(self.memory.truncate_to_ptr(v).0 as u128)), - _ => err!(Unimplemented(format!("int to {:?} cast", ty))), + // Casts to bool are not permitted by rustc, no need to handle them here. + _ => err!(Unimplemented(format!("int to {:?} cast", dest_ty))), } } - fn cast_from_float(&self, val: ConstFloat, ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { + fn cast_from_float(&self, bits: u128, fty: FloatTy, dest_ty: Ty<'tcx>) -> EvalResult<'tcx, PrimVal> { use rustc::ty::TypeVariants::*; - match ty.sty { + use rustc_apfloat::FloatConvert; + match dest_ty.sty { + // float -> uint TyUint(t) => { let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); - match val.ty { - FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(val.bits).to_u128(width).value)), - FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(val.bits).to_u128(width).value)), + match fty { + FloatTy::F32 => Ok(PrimVal::Bytes(Single::from_bits(bits).to_u128(width).value)), + FloatTy::F64 => Ok(PrimVal::Bytes(Double::from_bits(bits).to_u128(width).value)), } }, - + // float -> int TyInt(t) => { let width = t.bit_width().unwrap_or(self.memory.pointer_size() as usize * 8); - match val.ty { - FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(val.bits).to_i128(width).value)), - FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(val.bits).to_i128(width).value)), + match fty { + FloatTy::F32 => Ok(PrimVal::from_i128(Single::from_bits(bits).to_i128(width).value)), + FloatTy::F64 => Ok(PrimVal::from_i128(Double::from_bits(bits).to_i128(width).value)), } }, - - TyFloat(fty) => Ok(PrimVal::from_float(val.convert(fty))), - _ => err!(Unimplemented(format!("float to {:?} cast", ty))), + // f64 -> f32 + TyFloat(FloatTy::F32) if fty == FloatTy::F64 => { + Ok(PrimVal::Bytes(Single::to_bits(Double::from_bits(bits).convert(&mut false).value))) + }, + // f32 -> f64 + TyFloat(FloatTy::F64) if fty == FloatTy::F32 => { + Ok(PrimVal::Bytes(Double::to_bits(Single::from_bits(bits).convert(&mut false).value))) + }, + // identity cast + TyFloat(_) => Ok(PrimVal::Bytes(bits)), + _ => err!(Unimplemented(format!("float to {:?} cast", dest_ty))), } } diff --git a/src/librustc_mir/interpret/eval_context.rs b/src/librustc_mir/interpret/eval_context.rs index e0ad306571d..e38969e45d2 100644 --- a/src/librustc_mir/interpret/eval_context.rs +++ b/src/librustc_mir/interpret/eval_context.rs @@ -19,7 +19,7 @@ use rustc::mir::interpret::{ }; use super::{Place, PlaceExtra, Memory, - HasMemory, MemoryKind, operator, + HasMemory, MemoryKind, Machine}; pub struct EvalContext<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> { @@ -536,10 +536,10 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M UnaryOp(un_op, ref operand) => { let val = self.eval_operand_to_primval(operand)?; - let kind = self.ty_to_primval_kind(dest_ty)?; + let val = self.unary_op(un_op, val, dest_ty)?; self.write_primval( dest, - operator::unary_op(un_op, val, kind)?, + val, dest_ty, )?; } @@ -1677,6 +1677,22 @@ impl<'a, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M self.tcx.sess.err(&e.to_string()); } } + + pub fn sign_extend(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + let size = self.layout_of(ty)?.size.bits(); + // sign extend + let amt = 128 - size; + // shift the unsigned value to the left + // and back to the right as signed (essentially fills with FF on the left) + Ok((((value << amt) as i128) >> amt) as u128) + } + + pub fn truncate(&self, value: u128, ty: Ty<'tcx>) -> EvalResult<'tcx, u128> { + let size = self.layout_of(ty)?.size.bits(); + let amt = 128 - size; + // truncate (shift left to drop out leftover values, shift right to fill with zeroes) + Ok((value << amt) >> amt) + } } impl<'mir, 'tcx> Frame<'mir, 'tcx> { diff --git a/src/librustc_mir/interpret/memory.rs b/src/librustc_mir/interpret/memory.rs index 72a966ab38d..010ec8b9bc0 100644 --- a/src/librustc_mir/interpret/memory.rs +++ b/src/librustc_mir/interpret/memory.rs @@ -666,7 +666,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> { } // Now we do the actual reading let bytes = if signed { - read_target_int(endianness, bytes).unwrap() as u128 + let bytes = read_target_int(endianness, bytes).unwrap() as u128; + let amt = 128 - (size * 8); + // truncate (shift left to drop out leftover values, shift right to fill with zeroes) + (bytes << amt) >> amt } else { read_target_uint(endianness, bytes).unwrap() }; diff --git a/src/librustc_mir/interpret/mod.rs b/src/librustc_mir/interpret/mod.rs index 8e0158569a8..b1ee3d568fd 100644 --- a/src/librustc_mir/interpret/mod.rs +++ b/src/librustc_mir/interpret/mod.rs @@ -30,5 +30,4 @@ pub use self::const_eval::{ pub use self::machine::Machine; -pub use self::operator::unary_op; pub use self::memory::{write_target_uint, write_target_int, read_target_uint, read_target_int}; diff --git a/src/librustc_mir/interpret/operator.rs b/src/librustc_mir/interpret/operator.rs index bad744194d5..13087cfd473 100644 --- a/src/librustc_mir/interpret/operator.rs +++ b/src/librustc_mir/interpret/operator.rs @@ -1,12 +1,13 @@ use rustc::mir; -use rustc::ty::Ty; +use rustc::ty::{self, Ty}; use rustc_const_math::ConstFloat; use syntax::ast::FloatTy; use std::cmp::Ordering; +use rustc::ty::layout::LayoutOf; use super::{EvalContext, Place, Machine, ValTy}; -use rustc::mir::interpret::{EvalResult, PrimVal, PrimValKind, Value, bytes_to_f32, bytes_to_f64}; +use rustc::mir::interpret::{EvalResult, PrimVal, Value}; impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { fn binop_with_overflow( @@ -55,74 +56,6 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } } -macro_rules! overflow { - (overflowing_div, $l:expr, $r:expr) => ({ - let (val, overflowed) = if $r == 0 { - ($l, true) - } else { - $l.overflowing_div($r) - }; - let primval = PrimVal::Bytes(val as u128); - Ok((primval, overflowed)) - }); - (overflowing_rem, $l:expr, $r:expr) => ({ - let (val, overflowed) = if $r == 0 { - ($l, true) - } else { - $l.overflowing_rem($r) - }; - let primval = PrimVal::Bytes(val as u128); - Ok((primval, overflowed)) - }); - ($op:ident, $l:expr, $r:expr) => ({ - let (val, overflowed) = $l.$op($r); - let primval = PrimVal::Bytes(val as u128); - Ok((primval, overflowed)) - }) -} - -macro_rules! int_arithmetic { - ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ - let l = $l; - let r = $r; - use rustc::mir::interpret::PrimValKind::*; - match $kind { - 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), - I128 => overflow!($int_op, l as i128, r as i128), - 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), - U128 => overflow!($int_op, l as u128, r as u128), - _ => bug!("int_arithmetic should only be called on int primvals"), - } - }) -} - -macro_rules! int_shift { - ($kind:expr, $int_op:ident, $l:expr, $r:expr) => ({ - let l = $l; - let r = $r; - let r_wrapped = r as u32; - match $kind { - I8 => overflow!($int_op, l as i8, r_wrapped), - I16 => overflow!($int_op, l as i16, r_wrapped), - I32 => overflow!($int_op, l as i32, r_wrapped), - I64 => overflow!($int_op, l as i64, r_wrapped), - I128 => overflow!($int_op, l as i128, r_wrapped), - U8 => overflow!($int_op, l as u8, r_wrapped), - U16 => overflow!($int_op, l as u16, r_wrapped), - U32 => overflow!($int_op, l as u32, r_wrapped), - U64 => overflow!($int_op, l as u64, r_wrapped), - U128 => overflow!($int_op, l as u128, r_wrapped), - _ => bug!("int_shift should only be called on int primvals"), - }.map(|(val, over)| (val, over || r != r_wrapped as u128)) - }) -} - impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { /// Returns the result of the specified operation and whether it overflowed. pub fn binary_op( @@ -134,11 +67,10 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { right_ty: Ty<'tcx>, ) -> EvalResult<'tcx, (PrimVal, bool)> { use rustc::mir::BinOp::*; - use rustc::mir::interpret::PrimValKind::*; let left_kind = self.ty_to_primval_kind(left_ty)?; let right_kind = self.ty_to_primval_kind(right_ty)?; - //trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); + trace!("Running binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, left_kind, right, right_kind); // I: Handle operations that support pointers if !left_kind.is_float() && !right_kind.is_float() { @@ -153,11 +85,19 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { // These ops can have an RHS with a different numeric type. if right_kind.is_int() && (bin_op == Shl || bin_op == Shr) { - return match bin_op { - Shl => int_shift!(left_kind, overflowing_shl, l, r), - Shr => int_shift!(left_kind, overflowing_shr, l, r), + let op: fn(u128, u32) -> (u128, bool) = match bin_op { + Shl => u128::overflowing_shl, + Shr => u128::overflowing_shr, _ => bug!("it has already been checked that this is a shift op"), }; + let l = if left_ty.is_signed() { + self.sign_extend(l, left_ty)? + } else { + l + }; + let (result, oflo) = op(l, r as u32); + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo || truncated != result)); } if left_kind != right_kind { @@ -197,41 +137,95 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { } }; - let val = match (bin_op, left_kind) { - (_, F32) => float_op(bin_op, l, r, FloatTy::F32), - (_, F64) => float_op(bin_op, l, r, FloatTy::F64), + if left_ty.is_signed() { + let op: Option bool> = match bin_op { + Lt => Some(i128::lt), + Le => Some(i128::le), + Gt => Some(i128::gt), + Ge => Some(i128::ge), + _ => None, + }; + if let Some(op) = op { + let l = self.sign_extend(l, left_ty)? as i128; + let r = self.sign_extend(r, right_ty)? as i128; + return Ok((PrimVal::from_bool(op(&l, &r)), false)); + } + let op: Option (i128, bool)> = match bin_op { + Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)), + Div => Some(i128::overflowing_div), + Rem => Some(i128::overflowing_rem), + Add => Some(i128::overflowing_add), + Sub => Some(i128::overflowing_sub), + Mul => Some(i128::overflowing_mul), + _ => None, + }; + if let Some(op) = op { + let l128 = self.sign_extend(l, left_ty)? as i128; + let r = self.sign_extend(r, right_ty)? as i128; + let size = self.layout_of(left_ty)?.size.bits(); + match bin_op { + Rem | Div => { + // int_min / -1 + if r == -1 && l == (1 << (size - 1)) { + return Ok((PrimVal::Bytes(l), true)); + } + }, + _ => {}, + } + trace!("{}, {}, {}", l, l128, r); + let (result, mut oflo) = op(l128, r); + trace!("{}, {}", result, oflo); + if !oflo && size != 128 { + let max = 1 << (size - 1); + oflo = result >= max || result < -max; + } + let result = result as u128; + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo)); + } + } + if let ty::TyFloat(fty) = left_ty.sty { + return Ok((float_op(bin_op, l, r, fty), false)); + } - (Eq, _) => PrimVal::from_bool(l == r), - (Ne, _) => PrimVal::from_bool(l != r), + // only ints left + let val = match bin_op { + 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), + Lt => PrimVal::from_bool(l < r), + Le => PrimVal::from_bool(l <= r), + Gt => PrimVal::from_bool(l > r), + Ge => PrimVal::from_bool(l >= r), - (BitOr, _) => PrimVal::Bytes(l | r), - (BitAnd, _) => PrimVal::Bytes(l & r), - (BitXor, _) => PrimVal::Bytes(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), + Add | Sub | Mul | Rem | Div => { + let op: fn(u128, u128) -> (u128, bool) = match bin_op { + Add => u128::overflowing_add, + Sub => u128::overflowing_sub, + Mul => u128::overflowing_mul, + Rem | Div if r == 0 => return Ok((PrimVal::Bytes(l), true)), + Div => u128::overflowing_div, + Rem => u128::overflowing_rem, + _ => bug!(), + }; + let (result, oflo) = op(l, r); + let truncated = self.truncate(result, left_ty)?; + return Ok((PrimVal::Bytes(truncated), oflo || truncated != result)); + } _ => { let msg = format!( "unimplemented binary op {:?}: {:?} ({:?}), {:?} ({:?})", bin_op, left, - left_kind, + left_ty, right, - right_kind + right_ty, ); return err!(Unimplemented(msg)); } @@ -239,52 +233,33 @@ impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> EvalContext<'a, 'mir, 'tcx, M> { Ok((val, false)) } -} - -pub fn unary_op<'tcx>( - un_op: mir::UnOp, - val: PrimVal, - val_kind: PrimValKind, -) -> EvalResult<'tcx, PrimVal> { - use rustc::mir::UnOp::*; - use rustc::mir::interpret::PrimValKind::*; - - let bytes = val.to_bytes()?; - - let result_bytes = match (un_op, val_kind) { - (Not, Bool) => !val.to_bool()? 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) if bytes == i8::min_value() as u128 => return err!(OverflowingMath), - (Neg, I8) => -(bytes as i8) as u128, - (Neg, I16) if bytes == i16::min_value() as u128 => return err!(OverflowingMath), - (Neg, I16) => -(bytes as i16) as u128, - (Neg, I32) if bytes == i32::min_value() as u128 => return err!(OverflowingMath), - (Neg, I32) => -(bytes as i32) as u128, - (Neg, I64) if bytes == i64::min_value() as u128 => return err!(OverflowingMath), - (Neg, I64) => -(bytes as i64) as u128, - (Neg, I128) if bytes == i128::min_value() as u128 => return err!(OverflowingMath), - (Neg, I128) => -(bytes as i128) as u128, - - (Neg, F32) => (-bytes_to_f32(bytes)).bits, - (Neg, F64) => (-bytes_to_f64(bytes)).bits, - - _ => { - let msg = format!("unimplemented unary op: {:?}, {:?}", un_op, val); - return err!(Unimplemented(msg)); - } - }; - - Ok(PrimVal::Bytes(result_bytes)) + + pub fn unary_op( + &self, + un_op: mir::UnOp, + val: PrimVal, + ty: Ty<'tcx>, + ) -> EvalResult<'tcx, PrimVal> { + use rustc::mir::UnOp::*; + use rustc_apfloat::ieee::{Single, Double}; + use rustc_apfloat::Float; + + let bytes = val.to_bytes()?; + let size = self.layout_of(ty)?.size.bits(); + + let result_bytes = match (un_op, &ty.sty) { + + (Not, ty::TyBool) => !val.to_bool()? as u128, + + (Not, _) => !bytes, + + (Neg, ty::TyFloat(FloatTy::F32)) => Single::to_bits(-Single::from_bits(bytes)), + (Neg, ty::TyFloat(FloatTy::F64)) => Double::to_bits(-Double::from_bits(bytes)), + + (Neg, _) if bytes == (1 << (size - 1)) => return err!(OverflowingMath), + (Neg, _) => (-(bytes as i128)) as u128, + }; + + Ok(PrimVal::Bytes(self.truncate(result_bytes, ty)?)) + } } diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index de17872e96f..1b64178477e 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -20,7 +20,7 @@ use rustc::mir::visit::{Visitor, PlaceContext}; use rustc::middle::const_val::ConstVal; use rustc::ty::{TyCtxt, self, Instance}; use rustc::mir::interpret::{Value, PrimVal, GlobalId}; -use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, unary_op, ValTy}; +use interpret::{eval_body_with_mir, mk_borrowck_eval_cx, ValTy}; use transform::{MirPass, MirSource}; use syntax::codemap::Span; use rustc::ty::subst::Substs; @@ -205,8 +205,7 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> { let val = self.eval_operand(arg)?; let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?; - let kind = ecx.ty_to_primval_kind(val.1).ok()?; - match unary_op(op, prim, kind) { + match ecx.unary_op(op, prim, val.1) { Ok(val) => Some((Value::ByVal(val), place_ty, span)), Err(mut err) => { ecx.report(&mut err, false, Some(span)); diff --git a/src/test/run-pass/const-negation.rs b/src/test/run-pass/const-negation.rs index 012fe0d95ec..5c633eb6112 100644 --- a/src/test/run-pass/const-negation.rs +++ b/src/test/run-pass/const-negation.rs @@ -17,11 +17,13 @@ fn main() { const I: isize = -9223372036854775808isize; assert_eq!(::std::i32::MIN as u64, 0xffffffff80000000); assert_eq!(-2147483648isize as u64, 0xffffffff80000000); + assert_eq!(-2147483648i32 as u64, 0xffffffff80000000); assert_eq!(::std::i64::MIN as u64, 0x8000000000000000); #[cfg(target_pointer_width = "64")] assert_eq!(-9223372036854775808isize as u64, 0x8000000000000000); #[cfg(target_pointer_width = "32")] assert_eq!(-9223372036854775808isize as u64, 0); + assert_eq!(-9223372036854775808i32 as u64, 0); const J: usize = ::std::i32::MAX as usize; const K: usize = -1i32 as u32 as usize; const L: usize = ::std::i32::MIN as usize;