Skip to content

Commit 908604a

Browse files
committed
Add post-mono passes to make mono-reachable analysis more accurate
1 parent 1e0df74 commit 908604a

File tree

95 files changed

+520
-743
lines changed

Some content is hidden

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

95 files changed

+520
-743
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
@@ -1252,16 +1252,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12521252
}
12531253
}
12541254

1255-
pub(crate) fn codegen_block_as_unreachable(&mut self, bb: mir::BasicBlock) {
1256-
let llbb = match self.try_llbb(bb) {
1257-
Some(llbb) => llbb,
1258-
None => return,
1259-
};
1260-
let bx = &mut Bx::build(self.cx, llbb);
1261-
debug!("codegen_block_as_unreachable({:?})", bb);
1262-
bx.unreachable();
1263-
}
1264-
12651255
fn codegen_terminator(
12661256
&mut self,
12671257
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
@@ -159,15 +159,15 @@ impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> {
159159
///////////////////////////////////////////////////////////////////////////
160160

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

168168
let llfn = cx.get_fn(instance);
169169

170-
let mir = cx.tcx().instance_mir(instance.def);
170+
let mir = cx.tcx().codegen_mir(instance);
171171

172172
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
173173
debug!("fn_abi: {:?}", fn_abi);
@@ -222,7 +222,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
222222
fx.compute_per_local_var_debug_info(&mut start_bx).unzip();
223223
fx.per_local_var_debug_info = per_local_var_debug_info;
224224

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

228229
// Allocate variable and temp allocas
@@ -282,20 +283,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
282283
// So drop the builder of `start_llbb` to avoid having two at the same time.
283284
drop(start_bx);
284285

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

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 & 81 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, TypingMode,
42+
self, AdtDef, GenericArg, GenericArgsRef, InstanceKind, List, Ty, TyCtxt, TypingMode,
4343
UserTypeAnnotationIndex,
4444
};
4545

@@ -617,73 +617,6 @@ impl<'tcx> Body<'tcx> {
617617
self.injection_phase.is_some()
618618
}
619619

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

14511371
///////////////////////////////////////////////////////////////////////////

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)