Skip to content

Commit 35e95a7

Browse files
committed
Add post-mono passes to make mono-reachable analysis more accurate
1 parent b71fb5e commit 35e95a7

File tree

49 files changed

+331
-598
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+331
-598
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub(crate) fn codegen_fn<'tcx>(
4141
let symbol_name = tcx.symbol_name(instance).name.to_string();
4242
let _timer = tcx.prof.generic_activity_with_arg("codegen fn", &*symbol_name);
4343

44-
let mir = tcx.instance_mir(instance.def);
44+
let mir = tcx.codegen_mir(instance);
4545
let _mir_guard = crate::PrintOnPanic(|| {
4646
let mut buf = Vec::new();
4747
with_no_trimmed_paths!({
@@ -302,19 +302,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
302302
.generic_activity("codegen prelude")
303303
.run(|| crate::abi::codegen_fn_prelude(fx, start_block));
304304

305-
let reachable_blocks = traversal::mono_reachable_as_bitset(fx.mir, fx.tcx, fx.instance);
306-
307305
for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() {
308306
let block = fx.get_block(bb);
309307
fx.bcx.switch_to_block(block);
310308

311-
if !reachable_blocks.contains(bb) {
312-
// We want to skip this block, because it's not reachable. But we still create
313-
// the block so terminators in other blocks can reference it.
314-
fx.bcx.ins().trap(TrapCode::user(1 /* unreachable */).unwrap());
315-
continue;
316-
}
317-
318309
if bb_data.is_cleanup {
319310
// Unwinding after panicking is not supported
320311
continue;

compiler/rustc_codegen_ssa/src/base.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -416,7 +416,7 @@ pub(crate) fn codegen_instance<'a, 'tcx: 'a, Bx: BuilderMethods<'a, 'tcx>>(
416416
// release builds.
417417
info!("codegen_instance({})", instance);
418418

419-
mir::codegen_mir::<Bx>(cx, instance);
419+
mir::lower_mir::<Bx>(cx, instance);
420420
}
421421

422422
/// Creates the `main` function which will initialize the rust runtime and call

compiler/rustc_codegen_ssa/src/mir/block.rs

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,16 +1258,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12581258
}
12591259
}
12601260

1261-
pub(crate) fn codegen_block_as_unreachable(&mut self, bb: mir::BasicBlock) {
1262-
let llbb = match self.try_llbb(bb) {
1263-
Some(llbb) => llbb,
1264-
None => return,
1265-
};
1266-
let bx = &mut Bx::build(self.cx, llbb);
1267-
debug!("codegen_block_as_unreachable({:?})", bb);
1268-
bx.unreachable();
1269-
}
1270-
12711261
fn codegen_terminator(
12721262
&mut self,
12731263
bx: &mut Bx,

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -163,15 +163,15 @@ impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> {
163163
///////////////////////////////////////////////////////////////////////////
164164

165165
#[instrument(level = "debug", skip(cx))]
166-
pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
166+
pub fn lower_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
167167
cx: &'a Bx::CodegenCx,
168168
instance: Instance<'tcx>,
169169
) {
170170
assert!(!instance.args.has_infer());
171171

172172
let llfn = cx.get_fn(instance);
173173

174-
let mir = cx.tcx().instance_mir(instance.def);
174+
let mir = cx.tcx().codegen_mir(instance);
175175

176176
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
177177
debug!("fn_abi: {:?}", fn_abi);
@@ -227,7 +227,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
227227
fx.compute_per_local_var_debug_info(&mut start_bx).unzip();
228228
fx.per_local_var_debug_info = per_local_var_debug_info;
229229

230-
let traversal_order = traversal::mono_reachable_reverse_postorder(mir, cx.tcx(), instance);
230+
let traversal_order: Vec<_> =
231+
traversal::reverse_postorder(mir).map(|(block, _data)| block).collect();
231232
let memory_locals = analyze::non_ssa_locals(&fx, &traversal_order);
232233

233234
// Allocate variable and temp allocas
@@ -287,20 +288,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
287288
// So drop the builder of `start_llbb` to avoid having two at the same time.
288289
drop(start_bx);
289290

290-
let mut unreached_blocks = BitSet::new_filled(mir.basic_blocks.len());
291291
// Codegen the body of each reachable block using our reverse postorder list.
292292
for bb in traversal_order {
293293
fx.codegen_block(bb);
294-
unreached_blocks.remove(bb);
295-
}
296-
297-
// FIXME: These empty unreachable blocks are *mostly* a waste. They are occasionally
298-
// targets for a SwitchInt terminator, but the reimplementation of the mono-reachable
299-
// simplification in SwitchInt lowering sometimes misses cases that
300-
// mono_reachable_reverse_postorder manages to figure out.
301-
// The solution is to do something like post-mono GVN. But for now we have this hack.
302-
for bb in unreached_blocks.iter() {
303-
fx.codegen_block_as_unreachable(bb);
304294
}
305295
}
306296

compiler/rustc_middle/src/mir/basic_blocks.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ impl<'tcx> BasicBlocks<'tcx> {
6464
#[inline]
6565
pub fn reverse_postorder(&self) -> &[BasicBlock] {
6666
self.cache.reverse_postorder.get_or_init(|| {
67-
let mut rpo: Vec<_> = Postorder::new(&self.basic_blocks, START_BLOCK, ()).collect();
67+
let mut rpo: Vec<_> = Postorder::new(&self.basic_blocks, START_BLOCK).collect();
6868
rpo.reverse();
6969
rpo
7070
})

compiler/rustc_middle/src/mir/mod.rs

Lines changed: 1 addition & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
3939
use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths};
4040
use crate::ty::visit::TypeVisitableExt;
4141
use crate::ty::{
42-
self, AdtDef, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingEnv,
42+
self, AdtDef, GenericArg, GenericArgsRef, InstanceKind, List, Ty, TyCtxt, TypingEnv,
4343
UserTypeAnnotationIndex,
4444
};
4545

@@ -622,75 +622,6 @@ impl<'tcx> Body<'tcx> {
622622
self.injection_phase.is_some()
623623
}
624624

625-
/// If this basic block ends with a [`TerminatorKind::SwitchInt`] for which we can evaluate the
626-
/// discriminant in monomorphization, we return the discriminant bits and the
627-
/// [`SwitchTargets`], just so the caller doesn't also have to match on the terminator.
628-
fn try_const_mono_switchint<'a>(
629-
tcx: TyCtxt<'tcx>,
630-
instance: Instance<'tcx>,
631-
block: &'a BasicBlockData<'tcx>,
632-
) -> Option<(u128, &'a SwitchTargets)> {
633-
// There are two places here we need to evaluate a constant.
634-
let eval_mono_const = |constant: &ConstOperand<'tcx>| {
635-
// FIXME(#132279): what is this, why are we using an empty environment with
636-
// `RevealAll` here.
637-
let typing_env = ty::TypingEnv::fully_monomorphized();
638-
let mono_literal = instance.instantiate_mir_and_normalize_erasing_regions(
639-
tcx,
640-
typing_env,
641-
crate::ty::EarlyBinder::bind(constant.const_),
642-
);
643-
mono_literal.try_eval_bits(tcx, typing_env)
644-
};
645-
646-
let TerminatorKind::SwitchInt { discr, targets } = &block.terminator().kind else {
647-
return None;
648-
};
649-
650-
// If this is a SwitchInt(const _), then we can just evaluate the constant and return.
651-
let discr = match discr {
652-
Operand::Constant(constant) => {
653-
let bits = eval_mono_const(constant)?;
654-
return Some((bits, targets));
655-
}
656-
Operand::Move(place) | Operand::Copy(place) => place,
657-
};
658-
659-
// MIR for `if false` actually looks like this:
660-
// _1 = const _
661-
// SwitchInt(_1)
662-
//
663-
// And MIR for if intrinsics::ub_checks() looks like this:
664-
// _1 = UbChecks()
665-
// SwitchInt(_1)
666-
//
667-
// So we're going to try to recognize this pattern.
668-
//
669-
// If we have a SwitchInt on a non-const place, we find the most recent statement that
670-
// isn't a storage marker. If that statement is an assignment of a const to our
671-
// discriminant place, we evaluate and return the const, as if we've const-propagated it
672-
// into the SwitchInt.
673-
674-
let last_stmt = block.statements.iter().rev().find(|stmt| {
675-
!matches!(stmt.kind, StatementKind::StorageDead(_) | StatementKind::StorageLive(_))
676-
})?;
677-
678-
let (place, rvalue) = last_stmt.kind.as_assign()?;
679-
680-
if discr != place {
681-
return None;
682-
}
683-
684-
match rvalue {
685-
Rvalue::NullaryOp(NullOp::UbChecks, _) => Some((tcx.sess.ub_checks() as u128, targets)),
686-
Rvalue::Use(Operand::Constant(constant)) => {
687-
let bits = eval_mono_const(constant)?;
688-
Some((bits, targets))
689-
}
690-
_ => None,
691-
}
692-
}
693-
694625
/// For a `Location` in this scope, determine what the "caller location" at that point is. This
695626
/// is interesting because of inlining: the `#[track_caller]` attribute of inlined functions
696627
/// must be honored. Falls back to the `tracked_caller` value for `#[track_caller]` functions,
@@ -1440,19 +1371,6 @@ impl<'tcx> BasicBlockData<'tcx> {
14401371
pub fn is_empty_unreachable(&self) -> bool {
14411372
self.statements.is_empty() && matches!(self.terminator().kind, TerminatorKind::Unreachable)
14421373
}
1443-
1444-
/// Like [`Terminator::successors`] but tries to use information available from the [`Instance`]
1445-
/// to skip successors like the `false` side of an `if const {`.
1446-
///
1447-
/// This is used to implement [`traversal::mono_reachable`] and
1448-
/// [`traversal::mono_reachable_reverse_postorder`].
1449-
pub fn mono_successors(&self, tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Successors<'_> {
1450-
if let Some((bits, targets)) = Body::try_const_mono_switchint(tcx, instance, self) {
1451-
targets.successors_for_value(bits)
1452-
} else {
1453-
self.terminator().successors()
1454-
}
1455-
}
14561374
}
14571375

14581376
///////////////////////////////////////////////////////////////////////////

compiler/rustc_middle/src/mir/traversal.rs

Lines changed: 5 additions & 125 deletions
Original file line numberDiff line numberDiff line change
@@ -104,29 +104,23 @@ impl<'a, 'tcx> Iterator for Preorder<'a, 'tcx> {
104104
/// ```
105105
///
106106
/// A Postorder traversal of this graph is `D B C A` or `D C B A`
107-
pub struct Postorder<'a, 'tcx, C> {
107+
pub struct Postorder<'a, 'tcx> {
108108
basic_blocks: &'a IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
109109
visited: BitSet<BasicBlock>,
110110
visit_stack: Vec<(BasicBlock, Successors<'a>)>,
111111
root_is_start_block: bool,
112-
extra: C,
113112
}
114113

115-
impl<'a, 'tcx, C> Postorder<'a, 'tcx, C>
116-
where
117-
C: Customization<'tcx>,
118-
{
114+
impl<'a, 'tcx> Postorder<'a, 'tcx> {
119115
pub fn new(
120116
basic_blocks: &'a IndexSlice<BasicBlock, BasicBlockData<'tcx>>,
121117
root: BasicBlock,
122-
extra: C,
123-
) -> Postorder<'a, 'tcx, C> {
118+
) -> Postorder<'a, 'tcx> {
124119
let mut po = Postorder {
125120
basic_blocks,
126121
visited: BitSet::new_empty(basic_blocks.len()),
127122
visit_stack: Vec::new(),
128123
root_is_start_block: root == START_BLOCK,
129-
extra,
130124
};
131125

132126
po.visit(root);
@@ -140,7 +134,7 @@ where
140134
return;
141135
}
142136
let data = &self.basic_blocks[bb];
143-
let successors = C::successors(data, self.extra);
137+
let successors = data.terminator().successors();
144138
self.visit_stack.push((bb, successors));
145139
}
146140

@@ -198,10 +192,7 @@ where
198192
}
199193
}
200194

201-
impl<'tcx, C> Iterator for Postorder<'_, 'tcx, C>
202-
where
203-
C: Customization<'tcx>,
204-
{
195+
impl<'tcx> Iterator for Postorder<'_, 'tcx> {
205196
type Item = BasicBlock;
206197

207198
fn next(&mut self) -> Option<BasicBlock> {
@@ -252,29 +243,6 @@ impl<'tcx> Customization<'tcx> for () {
252243
}
253244
}
254245

255-
impl<'tcx> Customization<'tcx> for (TyCtxt<'tcx>, Instance<'tcx>) {
256-
fn successors<'a>(
257-
data: &'a BasicBlockData<'tcx>,
258-
(tcx, instance): (TyCtxt<'tcx>, Instance<'tcx>),
259-
) -> Successors<'a> {
260-
data.mono_successors(tcx, instance)
261-
}
262-
}
263-
264-
pub fn mono_reachable_reverse_postorder<'a, 'tcx>(
265-
body: &'a Body<'tcx>,
266-
tcx: TyCtxt<'tcx>,
267-
instance: Instance<'tcx>,
268-
) -> Vec<BasicBlock> {
269-
let mut iter = Postorder::new(&body.basic_blocks, START_BLOCK, (tcx, instance));
270-
let mut items = Vec::with_capacity(body.basic_blocks.len());
271-
while let Some(block) = iter.next() {
272-
items.push(block);
273-
}
274-
items.reverse();
275-
items
276-
}
277-
278246
/// Returns an iterator over all basic blocks reachable from the `START_BLOCK` in no particular
279247
/// order.
280248
///
@@ -322,91 +290,3 @@ pub fn reverse_postorder<'a, 'tcx>(
322290
{
323291
body.basic_blocks.reverse_postorder().iter().map(|&bb| (bb, &body.basic_blocks[bb]))
324292
}
325-
326-
/// Traversal of a [`Body`] that tries to avoid unreachable blocks in a monomorphized [`Instance`].
327-
///
328-
/// This is allowed to have false positives; blocks may be visited even if they are not actually
329-
/// reachable.
330-
///
331-
/// Such a traversal is mostly useful because it lets us skip lowering the `false` side
332-
/// of `if <T as Trait>::CONST`, as well as [`NullOp::UbChecks`].
333-
///
334-
/// [`NullOp::UbChecks`]: rustc_middle::mir::NullOp::UbChecks
335-
pub fn mono_reachable<'a, 'tcx>(
336-
body: &'a Body<'tcx>,
337-
tcx: TyCtxt<'tcx>,
338-
instance: Instance<'tcx>,
339-
) -> MonoReachable<'a, 'tcx> {
340-
MonoReachable::new(body, tcx, instance)
341-
}
342-
343-
/// [`MonoReachable`] internally accumulates a [`BitSet`] of visited blocks. This is just a
344-
/// convenience function to run that traversal then extract its set of reached blocks.
345-
pub fn mono_reachable_as_bitset<'a, 'tcx>(
346-
body: &'a Body<'tcx>,
347-
tcx: TyCtxt<'tcx>,
348-
instance: Instance<'tcx>,
349-
) -> BitSet<BasicBlock> {
350-
let mut iter = mono_reachable(body, tcx, instance);
351-
while let Some(_) = iter.next() {}
352-
iter.visited
353-
}
354-
355-
pub struct MonoReachable<'a, 'tcx> {
356-
body: &'a Body<'tcx>,
357-
tcx: TyCtxt<'tcx>,
358-
instance: Instance<'tcx>,
359-
visited: BitSet<BasicBlock>,
360-
// Other traversers track their worklist in a Vec. But we don't care about order, so we can
361-
// store ours in a BitSet and thus save allocations because BitSet has a small size
362-
// optimization.
363-
worklist: BitSet<BasicBlock>,
364-
}
365-
366-
impl<'a, 'tcx> MonoReachable<'a, 'tcx> {
367-
pub fn new(
368-
body: &'a Body<'tcx>,
369-
tcx: TyCtxt<'tcx>,
370-
instance: Instance<'tcx>,
371-
) -> MonoReachable<'a, 'tcx> {
372-
let mut worklist = BitSet::new_empty(body.basic_blocks.len());
373-
worklist.insert(START_BLOCK);
374-
MonoReachable {
375-
body,
376-
tcx,
377-
instance,
378-
visited: BitSet::new_empty(body.basic_blocks.len()),
379-
worklist,
380-
}
381-
}
382-
383-
fn add_work(&mut self, blocks: impl IntoIterator<Item = BasicBlock>) {
384-
for block in blocks.into_iter() {
385-
if !self.visited.contains(block) {
386-
self.worklist.insert(block);
387-
}
388-
}
389-
}
390-
}
391-
392-
impl<'a, 'tcx> Iterator for MonoReachable<'a, 'tcx> {
393-
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
394-
395-
fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
396-
while let Some(idx) = self.worklist.iter().next() {
397-
self.worklist.remove(idx);
398-
if !self.visited.insert(idx) {
399-
continue;
400-
}
401-
402-
let data = &self.body[idx];
403-
404-
let targets = data.mono_successors(self.tcx, self.instance);
405-
self.add_work(targets);
406-
407-
return Some((idx, data));
408-
}
409-
410-
None
411-
}
412-
}

0 commit comments

Comments
 (0)