Skip to content

Commit 977ac5b

Browse files
committed
MIR: opt-in normalization of BasicBlock and Local numbering
1 parent 57c5ac7 commit 977ac5b

File tree

2 files changed

+139
-0
lines changed

2 files changed

+139
-0
lines changed

compiler/rustc_mir_transform/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#![deny(rustc::diagnostic_outside_of_impl)]
44
#![feature(box_patterns)]
55
#![feature(drain_filter)]
6+
#![feature(is_sorted)]
67
#![feature(let_chains)]
78
#![feature(map_try_insert)]
89
#![feature(min_specialization)]
@@ -84,6 +85,7 @@ mod match_branches;
8485
mod multiple_return_terminators;
8586
mod normalize_array_len;
8687
mod nrvo;
88+
mod prettify;
8789
mod ref_prop;
8890
mod remove_noop_landing_pads;
8991
mod remove_storage_markers;
@@ -581,6 +583,9 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
581583
&large_enums::EnumSizeOpt { discrepancy: 128 },
582584
// Some cleanup necessary at least for LLVM and potentially other codegen backends.
583585
&add_call_guards::CriticalCallEdges,
586+
// Cleanup for human readability, off by default.
587+
&prettify::ReorderBasicBlocks,
588+
&prettify::ReorderLocals,
584589
// Dump the end result for testing and debugging purposes.
585590
&dump_mir::Marker("PreCodegen"),
586591
],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
//! These two passes provide no value to the compiler, so are off at every level.
2+
//!
3+
//! However, they can be enabled on the command line
4+
//! (`-Zmir-enable-passes=+ReorderBasicBlocks,+ReorderLocals`)
5+
//! to make the MIR easier to read for humans.
6+
7+
use crate::MirPass;
8+
use rustc_index::{bit_set::BitSet, IndexSlice, IndexVec};
9+
use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor};
10+
use rustc_middle::mir::*;
11+
use rustc_middle::ty::TyCtxt;
12+
use rustc_session::Session;
13+
14+
pub struct ReorderBasicBlocks;
15+
16+
impl<'tcx> MirPass<'tcx> for ReorderBasicBlocks {
17+
fn is_enabled(&self, _session: &Session) -> bool {
18+
false
19+
}
20+
21+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
22+
let rpo: IndexVec<BasicBlock, BasicBlock> =
23+
body.basic_blocks.postorder().iter().copied().rev().collect();
24+
if rpo.iter().is_sorted() {
25+
return;
26+
}
27+
28+
let mut updater = BasicBlockUpdater { map: rpo.invert_bijective_mapping(), tcx };
29+
debug_assert_eq!(updater.map[START_BLOCK], START_BLOCK);
30+
updater.visit_body(body);
31+
32+
permute(body.basic_blocks.as_mut(), &updater.map);
33+
}
34+
}
35+
36+
pub struct ReorderLocals;
37+
38+
impl<'tcx> MirPass<'tcx> for ReorderLocals {
39+
fn is_enabled(&self, _session: &Session) -> bool {
40+
false
41+
}
42+
43+
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
44+
let mut finder =
45+
LocalFinder { map: IndexVec::new(), seen: BitSet::new_empty(body.local_decls.len()) };
46+
47+
// We can't reorder the return place or the arguments
48+
for i in 0..=body.arg_count {
49+
finder.track(Local::from_usize(i));
50+
}
51+
52+
for (bb, bbd) in body.basic_blocks.iter_enumerated() {
53+
finder.visit_basic_block_data(bb, bbd);
54+
}
55+
56+
// track everything in case there are some locals that we never saw,
57+
// such as in non-block things like debug info or in non-uses.
58+
for local in body.local_decls.indices() {
59+
finder.track(local);
60+
}
61+
62+
if finder.map.iter().is_sorted() {
63+
return;
64+
}
65+
66+
let mut updater = LocalUpdater { map: finder.map.invert_bijective_mapping(), tcx };
67+
debug_assert_eq!(updater.map[RETURN_PLACE], RETURN_PLACE);
68+
updater.visit_body_preserves_cfg(body);
69+
70+
permute(&mut body.local_decls, &updater.map);
71+
}
72+
}
73+
74+
fn permute<I: rustc_index::Idx + Ord, T>(data: &mut IndexVec<I, T>, map: &IndexSlice<I, I>) {
75+
// FIXME: It would be nice to have a less-awkward way to apply permutations,
76+
// but I don't know one that exists. `sort_by_cached_key` has logic for it
77+
// internally, but not in a way that we're allowed to use here.
78+
let mut enumerated: Vec<_> = std::mem::take(data).into_iter_enumerated().collect();
79+
enumerated.sort_by_key(|p| map[p.0]);
80+
*data = enumerated.into_iter().map(|p| p.1).collect();
81+
}
82+
83+
struct BasicBlockUpdater<'tcx> {
84+
map: IndexVec<BasicBlock, BasicBlock>,
85+
tcx: TyCtxt<'tcx>,
86+
}
87+
88+
impl<'tcx> MutVisitor<'tcx> for BasicBlockUpdater<'tcx> {
89+
fn tcx(&self) -> TyCtxt<'tcx> {
90+
self.tcx
91+
}
92+
93+
fn visit_terminator(&mut self, terminator: &mut Terminator<'tcx>, _location: Location) {
94+
for succ in terminator.successors_mut() {
95+
*succ = self.map[*succ];
96+
}
97+
}
98+
}
99+
100+
struct LocalFinder {
101+
map: IndexVec<Local, Local>,
102+
seen: BitSet<Local>,
103+
}
104+
105+
impl LocalFinder {
106+
fn track(&mut self, l: Local) {
107+
if self.seen.insert(l) {
108+
self.map.push(l);
109+
}
110+
}
111+
}
112+
113+
impl<'tcx> Visitor<'tcx> for LocalFinder {
114+
fn visit_local(&mut self, l: Local, context: PlaceContext, _location: Location) {
115+
if context.is_use() {
116+
self.track(l);
117+
}
118+
}
119+
}
120+
121+
struct LocalUpdater<'tcx> {
122+
pub map: IndexVec<Local, Local>,
123+
pub tcx: TyCtxt<'tcx>,
124+
}
125+
126+
impl<'tcx> MutVisitor<'tcx> for LocalUpdater<'tcx> {
127+
fn tcx(&self) -> TyCtxt<'tcx> {
128+
self.tcx
129+
}
130+
131+
fn visit_local(&mut self, l: &mut Local, _: PlaceContext, _: Location) {
132+
*l = self.map[*l];
133+
}
134+
}

0 commit comments

Comments
 (0)