diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs index 72af027820106..4e214c3c7253e 100644 --- a/src/librustc_mir/transform/const_prop.rs +++ b/src/librustc_mir/transform/const_prop.rs @@ -3,15 +3,17 @@ use rustc::hir::def::DefKind; use rustc::mir::{ - Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local, + AggregateKind, Constant, Location, Place, PlaceBase, Mir, Operand, Rvalue, Local, NullOp, UnOp, StatementKind, Statement, LocalKind, Static, StaticKind, TerminatorKind, Terminator, ClearCrossCrate, SourceInfo, BinOp, ProjectionElem, SourceScope, SourceScopeLocalData, LocalDecl, Promoted, }; -use rustc::mir::visit::{Visitor, PlaceContext, MutatingUseContext, NonMutatingUseContext}; +use rustc::mir::visit::{ + Visitor, PlaceContext, MutatingUseContext, MutVisitor, NonMutatingUseContext, +}; use rustc::mir::interpret::{InterpError, Scalar, GlobalId, EvalResult}; use rustc::ty::{self, Instance, ParamEnv, Ty, TyCtxt}; -use syntax::source_map::DUMMY_SP; +use syntax_pos::{Span, DUMMY_SP}; use rustc::ty::subst::InternalSubsts; use rustc_data_structures::indexed_vec::IndexVec; use rustc::ty::layout::{ @@ -19,7 +21,7 @@ use rustc::ty::layout::{ HasTyCtxt, TargetDataLayout, HasDataLayout, }; -use crate::interpret::{InterpretCx, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind}; +use crate::interpret::{self, InterpretCx, ScalarMaybeUndef, Immediate, OpTy, ImmTy, MemoryKind}; use crate::const_eval::{ CompileTimeInterpreter, error_to_const_error, eval_promoted, mk_eval_cx, }; @@ -497,6 +499,53 @@ impl<'a, 'mir, 'tcx> ConstPropagator<'a, 'mir, 'tcx> { }, } } + + fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> { + Operand::Constant(Box::new( + Constant { + span, + ty, + user_ty: None, + literal: self.tcx.mk_const(ty::Const::from_scalar( + scalar, + ty, + )) + } + )) + } + + fn replace_with_const(&self, rval: &mut Rvalue<'tcx>, value: Const<'tcx>, span: Span) { + self.ecx.validate_operand( + value, + vec![], + None, + true, + ).expect("value should already be a valid const"); + + if let interpret::Operand::Immediate(im) = *value { + match im { + interpret::Immediate::Scalar(ScalarMaybeUndef::Scalar(scalar)) => { + *rval = Rvalue::Use(self.operand_from_scalar(scalar, value.layout.ty, span)); + }, + Immediate::ScalarPair( + ScalarMaybeUndef::Scalar(one), + ScalarMaybeUndef::Scalar(two) + ) => { + let ty = &value.layout.ty.sty; + if let ty::Tuple(substs) = ty { + *rval = Rvalue::Aggregate( + Box::new(AggregateKind::Tuple), + vec![ + self.operand_from_scalar(one, substs[0].expect_ty(), span), + self.operand_from_scalar(two, substs[1].expect_ty(), span), + ], + ); + } + }, + _ => { } + } + } + } } fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, @@ -560,10 +609,10 @@ impl<'tcx> Visitor<'tcx> for CanConstProp { } } -impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { +impl<'b, 'a, 'tcx> MutVisitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { fn visit_constant( &mut self, - constant: &Constant<'tcx>, + constant: &mut Constant<'tcx>, location: Location, ) { trace!("visit_constant: {:?}", constant); @@ -573,11 +622,11 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { fn visit_statement( &mut self, - statement: &Statement<'tcx>, + statement: &mut Statement<'tcx>, location: Location, ) { trace!("visit_statement: {:?}", statement); - if let StatementKind::Assign(ref place, ref rval) = statement.kind { + if let StatementKind::Assign(ref place, ref mut rval) = statement.kind { let place_ty: Ty<'tcx> = place .ty(&self.local_decls, self.tcx) .ty; @@ -589,6 +638,10 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { trace!("storing {:?} to {:?}", value, local); assert!(self.places[local].is_none()); self.places[local] = Some(value); + + if self.tcx.sess.opts.debugging_opts.mir_opt_level >= 2 { + self.replace_with_const(rval, value, statement.source_info.span); + } } } } @@ -599,7 +652,7 @@ impl<'b, 'a, 'tcx> Visitor<'tcx> for ConstPropagator<'b, 'a, 'tcx> { fn visit_terminator( &mut self, - terminator: &Terminator<'tcx>, + terminator: &mut Terminator<'tcx>, location: Location, ) { self.super_terminator(terminator, location); diff --git a/src/test/mir-opt/const_prop/array_index.rs b/src/test/mir-opt/const_prop/array_index.rs new file mode 100644 index 0000000000000..4b97af68ff08a --- /dev/null +++ b/src/test/mir-opt/const_prop/array_index.rs @@ -0,0 +1,33 @@ +fn main() { + let x: u32 = [0, 1, 2, 3][2]; +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _2 = [const 0u32, const 1u32, const 2u32, const 3u32]; +// ... +// _3 = const 2usize; +// _4 = const 4usize; +// _5 = Lt(_3, _4); +// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb1; +// } +// bb1: { +// _1 = _2[_3]; +// ... +// return; +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _5 = const true; +// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb1; +// } +// bb1: { +// _1 = _2[_3]; +// ... +// return; +// } +// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/checked_add.rs b/src/test/mir-opt/const_prop/checked_add.rs new file mode 100644 index 0000000000000..0718316307c5e --- /dev/null +++ b/src/test/mir-opt/const_prop/checked_add.rs @@ -0,0 +1,21 @@ +// compile-flags: -C overflow-checks=on + +fn main() { + let x: u32 = 1 + 1; +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _2 = CheckedAdd(const 1u32, const 1u32); +// assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _2 = (const 2u32, const false); +// assert(!move (_2.1: bool), "attempt to add with overflow") -> bb1; +// } +// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs new file mode 100644 index 0000000000000..97d3abdcc6cbf --- /dev/null +++ b/src/test/mir-opt/const_prop/const_prop_fails_gracefully.rs @@ -0,0 +1,34 @@ +#[inline(never)] +fn read(_: usize) { } + +fn main() { + const FOO: &i32 = &1; + let x = FOO as *const i32 as usize; + read(x); +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb0: { +// ... +// _3 = _4; +// _2 = move _3 as *const i32 (Misc); +// ... +// _1 = move _2 as usize (Misc); +// ... +// _6 = _1; +// _5 = const read(move _6) -> bb1; +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _3 = _4; +// _2 = move _3 as *const i32 (Misc); +// ... +// _1 = move _2 as usize (Misc); +// ... +// _6 = _1; +// _5 = const read(move _6) -> bb1; +// } +// END rustc.main.ConstProp.after.mir diff --git a/src/test/mir-opt/const_prop/slice_len.rs b/src/test/mir-opt/const_prop/slice_len.rs new file mode 100644 index 0000000000000..3435ca07f4cd8 --- /dev/null +++ b/src/test/mir-opt/const_prop/slice_len.rs @@ -0,0 +1,37 @@ +fn test() -> &'static [u32] { + &[1, 2] +} + +fn main() { + let x = test()[0]; +} + +// END RUST SOURCE +// START rustc.main.ConstProp.before.mir +// bb1: { +// ... +// _3 = const 0usize; +// _4 = Len((*_2)); +// _5 = Lt(_3, _4); +// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb2; +// } +// bb2: { +// _1 = (*_2)[_3]; +// ... +// return; +// } +// END rustc.main.ConstProp.before.mir +// START rustc.main.ConstProp.after.mir +// bb0: { +// ... +// _3 = const 0usize; +// _4 = Len((*_2)); +// _5 = Lt(_3, _4); +// assert(move _5, "index out of bounds: the len is move _4 but the index is _3") -> bb2; +// } +// bb2: { +// _1 = (*_2)[_3]; +// ... +// return; +// } +// END rustc.main.ConstProp.after.mir