Skip to content

Commit 20e8d54

Browse files
committed
Reverts #117206
cc @cjgillot @tmiasko This revert is based on the following report of a regression caused by this PR: #121094 In accordance with the compiler team [revert policy], PRs that cause meaningful regressions should be reverted and re-landed once the regression has been fixed (and a regression test has been added, where appropriate). [revert policy]: https://forge.rust-lang.org/compiler/reviews.html#reverts Fear not! Regressions happen. Please rest assured that this does not represent a negative judgment of your contribution or ability to contribute positively to Rust in the future. We simply want to prioritize keeping existing use cases working, and keep the compiler more stable for everyone. r? compiler
1 parent eaff1af commit 20e8d54

27 files changed

+1286
-466
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
//! This pass optimizes the following sequence
2+
//! ```rust,ignore (example)
3+
//! bb2: {
4+
//! _2 = const true;
5+
//! goto -> bb3;
6+
//! }
7+
//!
8+
//! bb3: {
9+
//! switchInt(_2) -> [false: bb4, otherwise: bb5];
10+
//! }
11+
//! ```
12+
//! into
13+
//! ```rust,ignore (example)
14+
//! bb2: {
15+
//! _2 = const true;
16+
//! goto -> bb5;
17+
//! }
18+
//! ```
19+
20+
use rustc_middle::mir::*;
21+
use rustc_middle::ty::TyCtxt;
22+
use rustc_middle::{mir::visit::Visitor, ty::ParamEnv};
23+
24+
use super::simplify::{simplify_cfg, simplify_locals};
25+
26+
pub struct ConstGoto;
27+
28+
impl<'tcx> MirPass<'tcx> for ConstGoto {
29+
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
30+
// This pass participates in some as-of-yet untested unsoundness found
31+
// in https://github.com/rust-lang/rust/issues/112460
32+
sess.mir_opt_level() >= 2 && sess.opts.unstable_opts.unsound_mir_opts
33+
}
34+
35+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
36+
trace!("Running ConstGoto on {:?}", body.source);
37+
let param_env = tcx.param_env_reveal_all_normalized(body.source.def_id());
38+
let mut opt_finder =
39+
ConstGotoOptimizationFinder { tcx, body, optimizations: vec![], param_env };
40+
opt_finder.visit_body(body);
41+
let should_simplify = !opt_finder.optimizations.is_empty();
42+
for opt in opt_finder.optimizations {
43+
let block = &mut body.basic_blocks_mut()[opt.bb_with_goto];
44+
block.statements.extend(opt.stmts_move_up);
45+
let terminator = block.terminator_mut();
46+
let new_goto = TerminatorKind::Goto { target: opt.target_to_use_in_goto };
47+
debug!("SUCCESS: replacing `{:?}` with `{:?}`", terminator.kind, new_goto);
48+
terminator.kind = new_goto;
49+
}
50+
51+
// if we applied optimizations, we potentially have some cfg to cleanup to
52+
// make it easier for further passes
53+
if should_simplify {
54+
simplify_cfg(body);
55+
simplify_locals(body, tcx);
56+
}
57+
}
58+
}
59+
60+
impl<'tcx> Visitor<'tcx> for ConstGotoOptimizationFinder<'_, 'tcx> {
61+
fn visit_basic_block_data(&mut self, block: BasicBlock, data: &BasicBlockData<'tcx>) {
62+
if data.is_cleanup {
63+
// Because of the restrictions around control flow in cleanup blocks, we don't perform
64+
// this optimization at all in such blocks.
65+
return;
66+
}
67+
self.super_basic_block_data(block, data);
68+
}
69+
70+
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
71+
let _: Option<_> = try {
72+
let target = terminator.kind.as_goto()?;
73+
// We only apply this optimization if the last statement is a const assignment
74+
let last_statement = self.body.basic_blocks[location.block].statements.last()?;
75+
76+
if let (place, Rvalue::Use(Operand::Constant(_const))) =
77+
last_statement.kind.as_assign()?
78+
{
79+
// We found a constant being assigned to `place`.
80+
// Now check that the target of this Goto switches on this place.
81+
let target_bb = &self.body.basic_blocks[target];
82+
83+
// The `StorageDead(..)` statement does not affect the functionality of mir.
84+
// We can move this part of the statement up to the predecessor.
85+
let mut stmts_move_up = Vec::new();
86+
for stmt in &target_bb.statements {
87+
if let StatementKind::StorageDead(..) = stmt.kind {
88+
stmts_move_up.push(stmt.clone())
89+
} else {
90+
None?;
91+
}
92+
}
93+
94+
let target_bb_terminator = target_bb.terminator();
95+
let (discr, targets) = target_bb_terminator.kind.as_switch()?;
96+
if discr.place() == Some(*place) {
97+
let switch_ty = place.ty(self.body.local_decls(), self.tcx).ty;
98+
debug_assert_eq!(switch_ty, _const.ty());
99+
// We now know that the Switch matches on the const place, and it is statementless
100+
// Now find which value in the Switch matches the const value.
101+
let const_value = _const.const_.try_eval_bits(self.tcx, self.param_env)?;
102+
let target_to_use_in_goto = targets.target_for_value(const_value);
103+
self.optimizations.push(OptimizationToApply {
104+
bb_with_goto: location.block,
105+
target_to_use_in_goto,
106+
stmts_move_up,
107+
});
108+
}
109+
}
110+
Some(())
111+
};
112+
113+
self.super_terminator(terminator, location);
114+
}
115+
}
116+
117+
struct OptimizationToApply<'tcx> {
118+
bb_with_goto: BasicBlock,
119+
target_to_use_in_goto: BasicBlock,
120+
stmts_move_up: Vec<Statement<'tcx>>,
121+
}
122+
123+
pub struct ConstGotoOptimizationFinder<'a, 'tcx> {
124+
tcx: TyCtxt<'tcx>,
125+
body: &'a Body<'tcx>,
126+
param_env: ParamEnv<'tcx>,
127+
optimizations: Vec<OptimizationToApply<'tcx>>,
128+
}

compiler/rustc_mir_transform/src/jump_threading.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ const MAX_PLACES: usize = 100;
6060

6161
impl<'tcx> MirPass<'tcx> for JumpThreading {
6262
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
63-
sess.mir_opt_level() >= 2
63+
sess.mir_opt_level() >= 4
6464
}
6565

6666
#[instrument(skip_all level = "debug")]

compiler/rustc_mir_transform/src/lib.rs

+7
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ mod remove_place_mention;
5959
mod add_subtyping_projections;
6060
pub mod cleanup_post_borrowck;
6161
mod const_debuginfo;
62+
mod const_goto;
6263
mod const_prop;
6364
mod const_prop_lint;
6465
mod copy_prop;
@@ -102,6 +103,7 @@ mod remove_unneeded_drops;
102103
mod remove_zsts;
103104
mod required_consts;
104105
mod reveal_all;
106+
mod separate_const_switch;
105107
mod shim;
106108
mod ssa;
107109
// This pass is public to allow external drivers to perform MIR cleanup
@@ -588,6 +590,7 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
588590

589591
// Has to run after `slice::len` lowering
590592
&normalize_array_len::NormalizeArrayLen,
593+
&const_goto::ConstGoto,
591594
&ref_prop::ReferencePropagation,
592595
&sroa::ScalarReplacementOfAggregates,
593596
&match_branches::MatchBranchSimplification,
@@ -598,6 +601,10 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
598601
&dead_store_elimination::DeadStoreElimination::Initial,
599602
&gvn::GVN,
600603
&simplify::SimplifyLocals::AfterGVN,
604+
// Perform `SeparateConstSwitch` after SSA-based analyses, as cloning blocks may
605+
// destroy the SSA property. It should still happen before const-propagation, so the
606+
// latter pass will leverage the created opportunities.
607+
&separate_const_switch::SeparateConstSwitch,
601608
&dataflow_const_prop::DataflowConstProp,
602609
&const_debuginfo::ConstDebugInfo,
603610
&o1(simplify_branches::SimplifyConstCondition::AfterConstProp),

0 commit comments

Comments
 (0)