Introduce ConstValue and use it instead of miri's Value for constant values

This commit is contained in:
John Kåre Alsaker 2018-04-26 09:18:19 +02:00
parent 41707d8df9
commit fdd9787777
55 changed files with 999 additions and 751 deletions

View file

@ -19,15 +19,17 @@ use rustc::mir::{TerminatorKind, ClearCrossCrate, SourceInfo, BinOp, ProjectionE
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 rustc::mir::interpret::{Value, PrimVal, GlobalId, EvalResult};
use interpret::EvalContext;
use interpret::CompileTimeEvaluator;
use interpret::{eval_promoted, mk_borrowck_eval_cx, ValTy};
use transform::{MirPass, MirSource};
use syntax::codemap::Span;
use syntax::codemap::{Span, DUMMY_SP};
use rustc::ty::subst::Substs;
use rustc_data_structures::indexed_vec::IndexVec;
use rustc::ty::ParamEnv;
use rustc::ty::layout::{
LayoutOf, TyLayout, LayoutError,
LayoutOf, TyLayout, LayoutError, LayoutCx,
HasTyCtxt, TargetDataLayout, HasDataLayout,
};
@ -64,6 +66,7 @@ type Const<'tcx> = (Value, ty::Ty<'tcx>, Span);
/// Finds optimization opportunities on the MIR.
struct ConstPropagator<'b, 'a, 'tcx:'a+'b> {
ecx: EvalContext<'a, 'b, 'tcx, CompileTimeEvaluator>,
mir: &'b Mir<'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
source: MirSource,
@ -102,7 +105,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
source: MirSource,
) -> ConstPropagator<'b, 'a, 'tcx> {
let param_env = tcx.param_env(source.def_id);
let substs = Substs::identity_for_item(tcx, source.def_id);
let instance = Instance::new(source.def_id, substs);
let ecx = mk_borrowck_eval_cx(tcx, instance, mir, DUMMY_SP).unwrap();
ConstPropagator {
ecx,
mir,
tcx,
source,
@ -112,7 +119,27 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
}
}
fn const_eval(&self, cid: GlobalId<'tcx>, span: Span) -> Option<Const<'tcx>> {
fn use_ecx<F, T>(
&mut self,
span: Span,
f: F
) -> Option<T>
where
F: FnOnce(&mut Self) -> EvalResult<'tcx, T>,
{
self.ecx.tcx.span = span;
let r = match f(self) {
Ok(val) => Some(val),
Err(mut err) => {
self.ecx.report(&mut err, false, Some(span));
None
},
};
self.ecx.tcx.span = DUMMY_SP;
r
}
fn const_eval(&mut self, cid: GlobalId<'tcx>, span: Span) -> Option<Const<'tcx>> {
let value = match self.tcx.const_eval(self.param_env.and(cid)) {
Ok(val) => val,
Err(err) => {
@ -121,7 +148,9 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
},
};
let val = match value.val {
ConstVal::Value(v) => v,
ConstVal::Value(v) => {
self.use_ecx(span, |this| this.ecx.const_value_to_value(v, value.ty))?
},
_ => bug!("eval produced: {:?}", value),
};
let val = (val, value.ty, span);
@ -132,7 +161,12 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
fn eval_constant(&mut self, c: &Constant<'tcx>) -> Option<Const<'tcx>> {
match c.literal {
Literal::Value { value } => match value.val {
ConstVal::Value(v) => Some((v, value.ty, c.span)),
ConstVal::Value(v) => {
let v = self.use_ecx(c.span, |this| {
this.ecx.const_value_to_value(v, value.ty)
})?;
Some((v, value.ty, c.span))
},
ConstVal::Unevaluated(did, substs) => {
let instance = Instance::resolve(
self.tcx,
@ -162,7 +196,10 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
};
// cannot use `const_eval` here, because that would require having the MIR
// for the current function available, but we're producing said MIR right now
let (value, _, ty) = eval_promoted(self.tcx, cid, self.mir, self.param_env)?;
let span = self.mir.span;
let (value, _, ty) = self.use_ecx(span, |this| {
Ok(eval_promoted(&mut this.ecx, cid, this.mir, this.param_env))
})??;
let val = (value, ty, c.span);
trace!("evaluated {:?} to {:?}", c, val);
Some(val)
@ -185,7 +222,11 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
use rustc_data_structures::indexed_vec::Idx;
let field_index = field.index();
let val = [a, b][field_index];
let field = base_layout.field(&*self, field_index).ok()?;
let cx = LayoutCx {
tcx: self.tcx,
param_env: self.param_env,
};
let field = base_layout.field(cx, field_index).ok()?;
trace!("projection resulted in: {:?}", val);
Some((Value::ByVal(val), field.ty, span))
},
@ -258,19 +299,13 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
// FIXME: can't handle code with generics
return None;
}
let substs = Substs::identity_for_item(self.tcx, self.source.def_id);
let instance = Instance::new(self.source.def_id, substs);
let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap();
let val = self.eval_operand(arg)?;
let prim = ecx.value_to_primval(ValTy { value: val.0, ty: val.1 }).ok()?;
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));
None
},
}
let prim = self.use_ecx(span, |this| {
this.ecx.value_to_primval(ValTy { value: val.0, ty: val.1 })
})?;
let val = self.use_ecx(span, |this| this.ecx.unary_op(op, prim, val.1))?;
Some((Value::ByVal(val), place_ty, span))
}
Rvalue::CheckedBinaryOp(op, ref left, ref right) |
Rvalue::BinaryOp(op, ref left, ref right) => {
@ -287,11 +322,10 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
// FIXME: can't handle code with generics
return None;
}
let substs = Substs::identity_for_item(self.tcx, self.source.def_id);
let instance = Instance::new(self.source.def_id, substs);
let ecx = mk_borrowck_eval_cx(self.tcx, instance, self.mir, span).unwrap();
let r = ecx.value_to_primval(ValTy { value: right.0, ty: right.1 }).ok()?;
let r = self.use_ecx(span, |this| {
this.ecx.value_to_primval(ValTy { value: right.0, ty: right.1 })
})?;
if op == BinOp::Shr || op == BinOp::Shl {
let param_env = self.tcx.param_env(self.source.def_id);
let left_ty = left.ty(self.mir, self.tcx);
@ -316,31 +350,31 @@ impl<'b, 'a, 'tcx:'b> ConstPropagator<'b, 'a, 'tcx> {
}
}
let left = self.eval_operand(left)?;
let l = ecx.value_to_primval(ValTy { value: left.0, ty: left.1 }).ok()?;
let l = self.use_ecx(span, |this| {
this.ecx.value_to_primval(ValTy { value: left.0, ty: left.1 })
})?;
trace!("const evaluating {:?} for {:?} and {:?}", op, left, right);
match ecx.binary_op(op, l, left.1, r, right.1) {
Ok((val, overflow)) => {
let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
Value::ByValPair(
val,
PrimVal::from_bool(overflow),
)
} else {
if overflow {
use rustc::mir::interpret::EvalErrorKind;
let mut err = EvalErrorKind::Overflow(op).into();
ecx.report(&mut err, false, Some(span));
return None;
}
Value::ByVal(val)
};
Some((val, place_ty, span))
},
Err(mut err) => {
ecx.report(&mut err, false, Some(span));
None
},
}
let (val, overflow) = self.use_ecx(span, |this| {
this.ecx.binary_op(op, l, left.1, r, right.1)
})?;
let val = if let Rvalue::CheckedBinaryOp(..) = *rvalue {
Value::ByValPair(
val,
PrimVal::from_bool(overflow),
)
} else {
if overflow {
use rustc::mir::interpret::EvalErrorKind;
let mut err = EvalErrorKind::Overflow(op).into();
self.use_ecx(span, |this| {
this.ecx.report(&mut err, false, Some(span));
Ok(())
});
return None;
}
Value::ByVal(val)
};
Some((val, place_ty, span))
},
}
}

View file

@ -17,8 +17,6 @@ use dataflow::MoveDataParamEnv;
use dataflow::{self, do_dataflow, DebugFormatted};
use rustc::ty::{self, TyCtxt};
use rustc::mir::*;
use rustc::middle::const_val::ConstVal;
use rustc::mir::interpret::{Value, PrimVal};
use rustc::util::nodemap::FxHashMap;
use rustc_data_structures::indexed_set::IdxSetBuf;
use rustc_data_structures::indexed_vec::Idx;
@ -533,10 +531,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> {
span,
ty: self.tcx.types.bool,
literal: Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(val as u128))),
ty: self.tcx.types.bool
})
value: ty::Const::from_bool(self.tcx, val)
}
})))
}

View file

@ -61,7 +61,6 @@
use rustc::hir;
use rustc::hir::def_id::DefId;
use rustc::middle::const_val::ConstVal;
use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, Visitor, MutVisitor};
use rustc::ty::{self, TyCtxt, AdtDef, Ty};
@ -79,7 +78,6 @@ use transform::simplify;
use transform::no_landing_pads::no_landing_pads;
use dataflow::{do_dataflow, DebugFormatted, state_for_location};
use dataflow::{MaybeStorageLive, HaveBeenBorrowedLocals};
use rustc::mir::interpret::{Value, PrimVal};
pub struct StateTransform;
@ -180,10 +178,10 @@ impl<'a, 'tcx> TransformVisitor<'a, 'tcx> {
span: source_info.span,
ty: self.tcx.types.u32,
literal: Literal::Value {
value: self.tcx.mk_const(ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(state_disc.into()))),
ty: self.tcx.types.u32
}),
value: ty::Const::from_bits(
self.tcx,
state_disc.into(),
self.tcx.types.u32),
},
});
Statement {
@ -698,10 +696,7 @@ fn insert_panic_block<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
span: mir.span,
ty: tcx.types.bool,
literal: Literal::Value {
value: tcx.mk_const(ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(0))),
ty: tcx.types.bool
}),
value: ty::Const::from_bool(tcx, false),
},
}),
expected: true,

View file

@ -595,7 +595,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
}
Operand::Constant(ref constant) => {
if let Literal::Value {
value: &ty::Const { val: ConstVal::Unevaluated(def_id, _), ty }
value: &ty::Const { val: ConstVal::Unevaluated(def_id, _), ty, .. }
} = constant.literal {
// Don't peek inside trait associated constants.
if self.tcx.trait_of_item(def_id).is_some() {
@ -690,7 +690,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Qualifier<'a, 'tcx, 'tcx> {
_ => false
}
} else if let ty::TyArray(_, len) = ty.sty {
len.val.unwrap_u64() == 0 &&
len.unwrap_usize(self.tcx) == 0 &&
self.mode == Mode::Fn
} else {
false

View file

@ -10,10 +10,8 @@
//! A pass that simplifies branches when their condition is known.
use rustc::ty::{self, TyCtxt};
use rustc::middle::const_val::ConstVal;
use rustc::ty::TyCtxt;
use rustc::mir::*;
use rustc::mir::interpret::{Value, PrimVal};
use transform::{MirPass, MirSource};
use std::borrow::Cow;
@ -32,7 +30,7 @@ impl MirPass for SimplifyBranches {
}
fn run_pass<'a, 'tcx>(&self,
_tcx: TyCtxt<'a, 'tcx, 'tcx>,
tcx: TyCtxt<'a, 'tcx, 'tcx>,
_src: MirSource,
mir: &mut Mir<'tcx>) {
for block in mir.basic_blocks_mut() {
@ -40,8 +38,8 @@ impl MirPass for SimplifyBranches {
terminator.kind = match terminator.kind {
TerminatorKind::SwitchInt { discr: Operand::Constant(box Constant {
literal: Literal::Value { ref value }, ..
}), ref values, ref targets, .. } => {
if let Some(constint) = value.val.to_raw_bits() {
}), switch_ty, ref values, ref targets, .. } => {
if let Some(constint) = value.assert_bits(switch_ty) {
let (otherwise, targets) = targets.split_last().unwrap();
let mut ret = TerminatorKind::Goto { target: *otherwise };
for (&v, t) in values.iter().zip(targets.iter()) {
@ -57,12 +55,9 @@ impl MirPass for SimplifyBranches {
},
TerminatorKind::Assert { target, cond: Operand::Constant(box Constant {
literal: Literal::Value {
value: &ty::Const {
val: ConstVal::Value(Value::ByVal(PrimVal::Bytes(cond))),
.. }
value
}, ..
}), expected, .. } if (cond == 1) == expected => {
assert!(cond <= 1);
}), expected, .. } if (value.assert_bool(tcx) == Some(true)) == expected => {
TerminatorKind::Goto { target: target }
},
TerminatorKind::FalseEdges { real_target, .. } => {

View file

@ -81,9 +81,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UniformArrayMoveOutVisitor<'a, 'tcx> {
} else {
let place_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
if let ty::TyArray(item_ty, const_size) = place_ty.sty {
if let Some(size) = const_size.val.to_raw_bits() {
assert!(size <= (u32::max_value() as u128),
"unform array move out doesn't supported
if let Some(size) = const_size.assert_usize(self.tcx) {
assert!(size <= u32::max_value() as u64,
"uniform array move out doesn't supported
for array bigger then u32");
self.uniform(location, dst_place, proj, item_ty, size as u32);
}
@ -203,7 +203,7 @@ impl MirPass for RestoreSubsliceArrayMoveOut {
let opt_size = opt_src_place.and_then(|src_place| {
let src_ty = src_place.ty(mir, tcx).to_ty(tcx);
if let ty::TyArray(_, ref size_o) = src_ty.sty {
size_o.val.to_raw_bits().map(|n| n as u64)
size_o.assert_usize(tcx)
} else {
None
}