Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

rustc_mir_transform: Add a local value numbering pass, off by default. #92051

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/rustc_mir_transform/src/const_goto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
use rustc_middle::{mir::visit::Visitor, ty::ParamEnv};

use super::simplify::{simplify_cfg, simplify_locals};
use super::simplify::{remove_unused_locals, simplify_cfg};

pub struct ConstGoto;

Expand Down Expand Up @@ -51,7 +51,7 @@ impl<'tcx> MirPass<'tcx> for ConstGoto {
// make it easier for further passes
if should_simplify {
simplify_cfg(tcx, body);
simplify_locals(body, tcx);
remove_unused_locals(body, tcx);
}
}
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_mir_transform/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ mod simplify_comparison_integral;
mod simplify_try;
mod uninhabited_enum_branching;
mod unreachable_prop;
mod value_numbering;

use rustc_const_eval::transform::check_consts::{self, ConstCx};
use rustc_const_eval::transform::promote_consts;
Expand Down Expand Up @@ -480,9 +481,11 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
&separate_const_switch::SeparateConstSwitch,
//
// FIXME(#70073): This pass is responsible for both optimization as well as some lints.
&const_prop::ConstProp,
//
// Const-prop runs unconditionally, but doesn't mutate the MIR at mir-opt-level=0.
&const_prop::ConstProp,
// Experimental: only runs if `-Z number-values` is on.
&value_numbering::ValueNumbering,
&o1(simplify_branches::SimplifyConstCondition::new("after-const-prop")),
&early_otherwise_branch::EarlyOtherwiseBranch,
&simplify_comparison_integral::SimplifyComparisonIntegral,
Expand Down
46 changes: 44 additions & 2 deletions compiler/rustc_mir_transform/src/simplify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,20 @@ impl<'tcx> MirPass<'tcx> for SimplifyLocals {

fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
trace!("running SimplifyLocals on {:?}", body.source);
simplify_locals(body, tcx);
// FIXME(pcwalton): Maybe we should call `promote_mutable_locals_to_immutable()` here.
// Right now, the only pass that really benefits from this is ValueNumbering, which calls it
// itself.
remove_unused_locals(body, tcx);
}
}

pub fn simplify_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) {
pub fn promote_mutable_locals_to_immutable<'tcx>(body: &mut Body<'tcx>, _: TyCtxt<'tcx>) {
let mut mutable_locals = PromoteMutableLocals::new(body);
mutable_locals.visit_body(body);
mutable_locals.update_locals(body);
}

pub fn remove_unused_locals<'tcx>(body: &mut Body<'tcx>, tcx: TyCtxt<'tcx>) {
// First, we're going to get a count of *actual* uses for every `Local`.
let mut used_locals = UsedLocals::new(body);

Expand Down Expand Up @@ -424,6 +433,39 @@ fn make_local_map<V>(
map
}

/// Promotes mutable locals to immutable.
struct PromoteMutableLocals {
mutating_use_count: IndexVec<Local, u32>,
}

impl PromoteMutableLocals {
fn new<'tcx>(body: &Body<'tcx>) -> PromoteMutableLocals {
PromoteMutableLocals { mutating_use_count: IndexVec::from_elem(0, &body.local_decls) }
}

fn update_locals<'tcx>(&self, body: &mut Body<'tcx>) {
for (local, local_decl) in body.local_decls.iter_mut().enumerate() {
let local = Local::from_usize(local);
if local.as_usize() == 0 {
// Return value must always be mutable.
continue;
}
if local_decl.mutability == Mutability::Mut && self.mutating_use_count[local] <= 1 {
local_decl.mutability = Mutability::Not;
}
}
}
}

impl<'tcx> Visitor<'tcx> for PromoteMutableLocals {
fn visit_local(&mut self, local: &Local, place_context: PlaceContext, _: Location) {
match place_context {
PlaceContext::MutatingUse(_) => self.mutating_use_count[*local] += 1,
PlaceContext::NonMutatingUse(_) | PlaceContext::NonUse(_) => {}
}
}
}

/// Keeps track of used & unused locals.
struct UsedLocals {
increment: bool,
Expand Down
Loading