remove optimizations from const_prop_lint
This commit is contained in:
parent
5e4ff26618
commit
c2f9278b40
|
@ -9,9 +9,7 @@ use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::HirId;
|
use rustc_hir::HirId;
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use rustc_index::vec::IndexVec;
|
use rustc_index::vec::IndexVec;
|
||||||
use rustc_middle::mir::visit::{
|
use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
|
||||||
MutVisitor, MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor,
|
|
||||||
};
|
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
|
AssertKind, BasicBlock, BinOp, Body, Constant, ConstantKind, Local, LocalDecl, LocalKind,
|
||||||
Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
|
Location, Operand, Place, Rvalue, SourceInfo, SourceScope, SourceScopeData, Statement,
|
||||||
|
@ -28,12 +26,12 @@ use rustc_target::abi::{HasDataLayout, Size, TargetDataLayout};
|
||||||
use rustc_target::spec::abi::Abi;
|
use rustc_target::spec::abi::Abi;
|
||||||
use rustc_trait_selection::traits;
|
use rustc_trait_selection::traits;
|
||||||
|
|
||||||
use crate::MirPass;
|
use crate::MirLint;
|
||||||
use rustc_const_eval::const_eval::ConstEvalErr;
|
use rustc_const_eval::const_eval::ConstEvalErr;
|
||||||
use rustc_const_eval::interpret::{
|
use rustc_const_eval::interpret::{
|
||||||
self, compile_time_machine, AllocId, ConstAllocation, ConstValue, CtfeValidationMode, Frame,
|
self, compile_time_machine, AllocId, ConstAllocation, Frame, ImmTy, InterpCx, InterpResult,
|
||||||
ImmTy, Immediate, InterpCx, InterpResult, LocalState, LocalValue, MemPlace, MemoryKind, OpTy,
|
LocalState, LocalValue, MemPlace, MemoryKind, OpTy, Operand as InterpOperand, PlaceTy, Scalar,
|
||||||
Operand as InterpOperand, PlaceTy, Scalar, ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
|
ScalarMaybeUninit, StackPopCleanup, StackPopUnwind,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The maximum number of bytes that we'll allocate space for a local or the return value.
|
/// The maximum number of bytes that we'll allocate space for a local or the return value.
|
||||||
|
@ -61,15 +59,8 @@ macro_rules! throw_machine_stop_str {
|
||||||
|
|
||||||
pub struct ConstProp;
|
pub struct ConstProp;
|
||||||
|
|
||||||
impl<'tcx> MirPass<'tcx> for ConstProp {
|
impl<'tcx> MirLint<'tcx> for ConstProp {
|
||||||
fn is_enabled(&self, _sess: &rustc_session::Session) -> bool {
|
fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
||||||
// FIXME(#70073): Unlike the other passes in "optimizations", this one emits errors, so it
|
|
||||||
// runs even when MIR optimizations are disabled. We should separate the lint out from the
|
|
||||||
// transform and move the lint as early in the pipeline as possible.
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|
||||||
// will be evaluated by miri and produce its errors there
|
// will be evaluated by miri and produce its errors there
|
||||||
if body.source.promoted.is_some() {
|
if body.source.promoted.is_some() {
|
||||||
return;
|
return;
|
||||||
|
@ -630,32 +621,6 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
Some(())
|
Some(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn propagate_operand(&mut self, operand: &mut Operand<'tcx>) {
|
|
||||||
match *operand {
|
|
||||||
Operand::Copy(l) | Operand::Move(l) => {
|
|
||||||
if let Some(value) = self.get_const(l) && self.should_const_prop(&value) {
|
|
||||||
// FIXME(felix91gr): this code only handles `Scalar` cases.
|
|
||||||
// For now, we're not handling `ScalarPair` cases because
|
|
||||||
// doing so here would require a lot of code duplication.
|
|
||||||
// We should hopefully generalize `Operand` handling into a fn,
|
|
||||||
// and use it to do const-prop here and everywhere else
|
|
||||||
// where it makes sense.
|
|
||||||
if let interpret::Operand::Immediate(interpret::Immediate::Scalar(
|
|
||||||
ScalarMaybeUninit::Scalar(scalar),
|
|
||||||
)) = *value
|
|
||||||
{
|
|
||||||
*operand = self.operand_from_scalar(
|
|
||||||
scalar,
|
|
||||||
value.layout.ty,
|
|
||||||
self.source_info.unwrap().span,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Operand::Constant(_) => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn const_prop(
|
fn const_prop(
|
||||||
&mut self,
|
&mut self,
|
||||||
rvalue: &Rvalue<'tcx>,
|
rvalue: &Rvalue<'tcx>,
|
||||||
|
@ -728,191 +693,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.tcx.sess.mir_opt_level() >= 4 {
|
self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place))
|
||||||
self.eval_rvalue_with_identities(rvalue, place)
|
|
||||||
} else {
|
|
||||||
self.use_ecx(|this| this.ecx.eval_rvalue_into_place(rvalue, place))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attempt to use albegraic identities to eliminate constant expressions
|
|
||||||
fn eval_rvalue_with_identities(
|
|
||||||
&mut self,
|
|
||||||
rvalue: &Rvalue<'tcx>,
|
|
||||||
place: Place<'tcx>,
|
|
||||||
) -> Option<()> {
|
|
||||||
self.use_ecx(|this| match rvalue {
|
|
||||||
Rvalue::BinaryOp(op, box (left, right))
|
|
||||||
| Rvalue::CheckedBinaryOp(op, box (left, right)) => {
|
|
||||||
let l = this.ecx.eval_operand(left, None);
|
|
||||||
let r = this.ecx.eval_operand(right, None);
|
|
||||||
|
|
||||||
let const_arg = match (l, r) {
|
|
||||||
(Ok(ref x), Err(_)) | (Err(_), Ok(ref x)) => this.ecx.read_immediate(x)?,
|
|
||||||
(Err(e), Err(_)) => return Err(e),
|
|
||||||
(Ok(_), Ok(_)) => return this.ecx.eval_rvalue_into_place(rvalue, place),
|
|
||||||
};
|
|
||||||
|
|
||||||
let arg_value = const_arg.to_scalar()?.to_bits(const_arg.layout.size)?;
|
|
||||||
let dest = this.ecx.eval_place(place)?;
|
|
||||||
|
|
||||||
match op {
|
|
||||||
BinOp::BitAnd if arg_value == 0 => this.ecx.write_immediate(*const_arg, &dest),
|
|
||||||
BinOp::BitOr
|
|
||||||
if arg_value == const_arg.layout.size.truncate(u128::MAX)
|
|
||||||
|| (const_arg.layout.ty.is_bool() && arg_value == 1) =>
|
|
||||||
{
|
|
||||||
this.ecx.write_immediate(*const_arg, &dest)
|
|
||||||
}
|
|
||||||
BinOp::Mul if const_arg.layout.ty.is_integral() && arg_value == 0 => {
|
|
||||||
if let Rvalue::CheckedBinaryOp(_, _) = rvalue {
|
|
||||||
let val = Immediate::ScalarPair(
|
|
||||||
const_arg.to_scalar()?.into(),
|
|
||||||
Scalar::from_bool(false).into(),
|
|
||||||
);
|
|
||||||
this.ecx.write_immediate(val, &dest)
|
|
||||||
} else {
|
|
||||||
this.ecx.write_immediate(*const_arg, &dest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => this.ecx.eval_rvalue_into_place(rvalue, place),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => this.ecx.eval_rvalue_into_place(rvalue, place),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new `Operand::Constant` from a `Scalar` value
|
|
||||||
fn operand_from_scalar(&self, scalar: Scalar, ty: Ty<'tcx>, span: Span) -> Operand<'tcx> {
|
|
||||||
Operand::Constant(Box::new(Constant {
|
|
||||||
span,
|
|
||||||
user_ty: None,
|
|
||||||
literal: ty::Const::from_scalar(self.tcx, scalar, ty).into(),
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn replace_with_const(
|
|
||||||
&mut self,
|
|
||||||
rval: &mut Rvalue<'tcx>,
|
|
||||||
value: &OpTy<'tcx>,
|
|
||||||
source_info: SourceInfo,
|
|
||||||
) {
|
|
||||||
if let Rvalue::Use(Operand::Constant(c)) = rval {
|
|
||||||
match c.literal {
|
|
||||||
ConstantKind::Ty(c) if matches!(c.val(), ConstKind::Unevaluated(..)) => {}
|
|
||||||
_ => {
|
|
||||||
trace!("skipping replace of Rvalue::Use({:?} because it is already a const", c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trace!("attempting to replace {:?} with {:?}", rval, value);
|
|
||||||
if let Err(e) = self.ecx.const_validate_operand(
|
|
||||||
value,
|
|
||||||
vec![],
|
|
||||||
// FIXME: is ref tracking too expensive?
|
|
||||||
// FIXME: what is the point of ref tracking if we do not even check the tracked refs?
|
|
||||||
&mut interpret::RefTracking::empty(),
|
|
||||||
CtfeValidationMode::Regular,
|
|
||||||
) {
|
|
||||||
trace!("validation error, attempt failed: {:?}", e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME> figure out what to do when try_read_immediate fails
|
|
||||||
let imm = self.use_ecx(|this| this.ecx.try_read_immediate(value));
|
|
||||||
|
|
||||||
if let Some(Ok(imm)) = imm {
|
|
||||||
match *imm {
|
|
||||||
interpret::Immediate::Scalar(ScalarMaybeUninit::Scalar(scalar)) => {
|
|
||||||
*rval = Rvalue::Use(self.operand_from_scalar(
|
|
||||||
scalar,
|
|
||||||
value.layout.ty,
|
|
||||||
source_info.span,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Immediate::ScalarPair(
|
|
||||||
ScalarMaybeUninit::Scalar(_),
|
|
||||||
ScalarMaybeUninit::Scalar(_),
|
|
||||||
) => {
|
|
||||||
// Found a value represented as a pair. For now only do const-prop if the type
|
|
||||||
// of `rvalue` is also a tuple with two scalars.
|
|
||||||
// FIXME: enable the general case stated above ^.
|
|
||||||
let ty = value.layout.ty;
|
|
||||||
// Only do it for tuples
|
|
||||||
if let ty::Tuple(types) = ty.kind() {
|
|
||||||
// Only do it if tuple is also a pair with two scalars
|
|
||||||
if let [ty1, ty2] = types[..] {
|
|
||||||
let alloc = self.use_ecx(|this| {
|
|
||||||
let ty_is_scalar = |ty| {
|
|
||||||
this.ecx.layout_of(ty).ok().map(|layout| layout.abi.is_scalar())
|
|
||||||
== Some(true)
|
|
||||||
};
|
|
||||||
if ty_is_scalar(ty1) && ty_is_scalar(ty2) {
|
|
||||||
let alloc = this
|
|
||||||
.ecx
|
|
||||||
.intern_with_temp_alloc(value.layout, |ecx, dest| {
|
|
||||||
ecx.write_immediate(*imm, dest)
|
|
||||||
})
|
|
||||||
.unwrap();
|
|
||||||
Ok(Some(alloc))
|
|
||||||
} else {
|
|
||||||
Ok(None)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(Some(alloc)) = alloc {
|
|
||||||
// Assign entire constant in a single statement.
|
|
||||||
// We can't use aggregates, as we run after the aggregate-lowering `MirPhase`.
|
|
||||||
*rval = Rvalue::Use(Operand::Constant(Box::new(Constant {
|
|
||||||
span: source_info.span,
|
|
||||||
user_ty: None,
|
|
||||||
literal: self
|
|
||||||
.ecx
|
|
||||||
.tcx
|
|
||||||
.mk_const(ty::ConstS {
|
|
||||||
ty,
|
|
||||||
val: ty::ConstKind::Value(ConstValue::ByRef {
|
|
||||||
alloc,
|
|
||||||
offset: Size::ZERO,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.into(),
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Scalars or scalar pairs that contain undef values are assumed to not have
|
|
||||||
// successfully evaluated and are thus not propagated.
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns `true` if and only if this `op` should be const-propagated into.
|
|
||||||
fn should_const_prop(&mut self, op: &OpTy<'tcx>) -> bool {
|
|
||||||
let mir_opt_level = self.tcx.sess.mir_opt_level();
|
|
||||||
|
|
||||||
if mir_opt_level == 0 {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !self.tcx.consider_optimizing(|| format!("ConstantPropagation - OpTy: {:?}", op)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
match **op {
|
|
||||||
interpret::Operand::Immediate(Immediate::Scalar(ScalarMaybeUninit::Scalar(s))) => {
|
|
||||||
s.try_to_int().is_ok()
|
|
||||||
}
|
|
||||||
interpret::Operand::Immediate(Immediate::ScalarPair(
|
|
||||||
ScalarMaybeUninit::Scalar(l),
|
|
||||||
ScalarMaybeUninit::Scalar(r),
|
|
||||||
)) => l.try_to_int().is_ok() && r.try_to_int().is_ok(),
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1047,52 +828,30 @@ impl Visitor<'_> for CanConstProp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
impl<'tcx> Visitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
fn visit_body(&mut self, body: &Body<'tcx>) {
|
||||||
self.tcx
|
for (bb, data) in body.basic_blocks().iter_enumerated() {
|
||||||
}
|
|
||||||
|
|
||||||
fn visit_body(&mut self, body: &mut Body<'tcx>) {
|
|
||||||
for (bb, data) in body.basic_blocks_mut().iter_enumerated_mut() {
|
|
||||||
self.visit_basic_block_data(bb, data);
|
self.visit_basic_block_data(bb, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_operand(&mut self, operand: &mut Operand<'tcx>, location: Location) {
|
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
|
||||||
self.super_operand(operand, location);
|
self.super_operand(operand, location);
|
||||||
|
|
||||||
// Only const prop copies and moves on `mir_opt_level=3` as doing so
|
|
||||||
// currently slightly increases compile time in some cases.
|
|
||||||
if self.tcx.sess.mir_opt_level() >= 3 {
|
|
||||||
self.propagate_operand(operand)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_constant(&mut self, constant: &mut Constant<'tcx>, location: Location) {
|
fn visit_constant(&mut self, constant: &Constant<'tcx>, location: Location) {
|
||||||
trace!("visit_constant: {:?}", constant);
|
trace!("visit_constant: {:?}", constant);
|
||||||
self.super_constant(constant, location);
|
self.super_constant(constant, location);
|
||||||
self.eval_constant(constant, self.source_info.unwrap());
|
self.eval_constant(constant, self.source_info.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
|
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||||
trace!("visit_statement: {:?}", statement);
|
trace!("visit_statement: {:?}", statement);
|
||||||
let source_info = statement.source_info;
|
let source_info = statement.source_info;
|
||||||
self.source_info = Some(source_info);
|
self.source_info = Some(source_info);
|
||||||
if let StatementKind::Assign(box (place, ref mut rval)) = statement.kind {
|
if let StatementKind::Assign(box (place, ref rval)) = statement.kind {
|
||||||
let can_const_prop = self.ecx.machine.can_const_prop[place.local];
|
let can_const_prop = self.ecx.machine.can_const_prop[place.local];
|
||||||
if let Some(()) = self.const_prop(rval, source_info, place) {
|
if let Some(()) = self.const_prop(rval, source_info, place) {
|
||||||
// This will return None if the above `const_prop` invocation only "wrote" a
|
|
||||||
// type whose creation requires no write. E.g. a generator whose initial state
|
|
||||||
// consists solely of uninitialized memory (so it doesn't capture any locals).
|
|
||||||
if let Some(ref value) = self.get_const(place) && self.should_const_prop(value) {
|
|
||||||
trace!("replacing {:?} with {:?}", rval, value);
|
|
||||||
self.replace_with_const(rval, value, source_info);
|
|
||||||
if can_const_prop == ConstPropMode::FullConstProp
|
|
||||||
|| can_const_prop == ConstPropMode::OnlyInsideOwnBlock
|
|
||||||
{
|
|
||||||
trace!("propagated into {:?}", place);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
match can_const_prop {
|
match can_const_prop {
|
||||||
ConstPropMode::OnlyInsideOwnBlock => {
|
ConstPropMode::OnlyInsideOwnBlock => {
|
||||||
trace!(
|
trace!(
|
||||||
|
@ -1159,12 +918,12 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
self.super_statement(statement, location);
|
self.super_statement(statement, location);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, location: Location) {
|
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||||
let source_info = terminator.source_info;
|
let source_info = terminator.source_info;
|
||||||
self.source_info = Some(source_info);
|
self.source_info = Some(source_info);
|
||||||
self.super_terminator(terminator, location);
|
self.super_terminator(terminator, location);
|
||||||
match &mut terminator.kind {
|
match &terminator.kind {
|
||||||
TerminatorKind::Assert { expected, ref msg, ref mut cond, .. } => {
|
TerminatorKind::Assert { expected, ref msg, ref cond, .. } => {
|
||||||
if let Some(ref value) = self.eval_operand(&cond, source_info) {
|
if let Some(ref value) = self.eval_operand(&cond, source_info) {
|
||||||
trace!("assertion on {:?} should be {:?}", value, expected);
|
trace!("assertion on {:?} should be {:?}", value, expected);
|
||||||
let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected));
|
let expected = ScalarMaybeUninit::from(Scalar::from_bool(*expected));
|
||||||
|
@ -1231,25 +990,9 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
msg,
|
msg,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
if self.should_const_prop(value) {
|
|
||||||
if let ScalarMaybeUninit::Scalar(scalar) = value_const {
|
|
||||||
*cond = self.operand_from_scalar(
|
|
||||||
scalar,
|
|
||||||
self.tcx.types.bool,
|
|
||||||
source_info.span,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TerminatorKind::SwitchInt { ref mut discr, .. } => {
|
|
||||||
// FIXME: This is currently redundant with `visit_operand`, but sadly
|
|
||||||
// always visiting operands currently causes a perf regression in LLVM codegen, so
|
|
||||||
// `visit_operand` currently only runs for propagates places for `mir_opt_level=4`.
|
|
||||||
self.propagate_operand(discr)
|
|
||||||
}
|
|
||||||
// None of these have Operands to const-propagate.
|
// None of these have Operands to const-propagate.
|
||||||
TerminatorKind::Goto { .. }
|
TerminatorKind::Goto { .. }
|
||||||
| TerminatorKind::Resume
|
| TerminatorKind::Resume
|
||||||
|
@ -1262,12 +1005,9 @@ impl<'tcx> MutVisitor<'tcx> for ConstPropagator<'_, 'tcx> {
|
||||||
| TerminatorKind::GeneratorDrop
|
| TerminatorKind::GeneratorDrop
|
||||||
| TerminatorKind::FalseEdge { .. }
|
| TerminatorKind::FalseEdge { .. }
|
||||||
| TerminatorKind::FalseUnwind { .. }
|
| TerminatorKind::FalseUnwind { .. }
|
||||||
|
| TerminatorKind::SwitchInt { .. }
|
||||||
|
| TerminatorKind::Call { .. }
|
||||||
| TerminatorKind::InlineAsm { .. } => {}
|
| TerminatorKind::InlineAsm { .. } => {}
|
||||||
// Every argument in our function calls have already been propagated in `visit_operand`.
|
|
||||||
//
|
|
||||||
// NOTE: because LLVM codegen gives slight performance regressions with it, so this is
|
|
||||||
// gated on `mir_opt_level=3`.
|
|
||||||
TerminatorKind::Call { .. } => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// We remove all Locals which are restricted in propagation to their containing blocks and
|
// We remove all Locals which are restricted in propagation to their containing blocks and
|
||||||
|
|
|
@ -431,7 +431,7 @@ fn run_post_borrowck_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tc
|
||||||
// `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
|
// `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
|
||||||
// and it can help optimizations.
|
// and it can help optimizations.
|
||||||
&deaggregator::Deaggregator,
|
&deaggregator::Deaggregator,
|
||||||
&const_prop_lint::ConstProp,
|
&Lint(const_prop_lint::ConstProp),
|
||||||
];
|
];
|
||||||
|
|
||||||
pm::run_passes(tcx, body, post_borrowck_cleanup);
|
pm::run_passes(tcx, body, post_borrowck_cleanup);
|
||||||
|
|
Loading…
Reference in a new issue