Skip to content

Commit 43d63b7

Browse files
committed
Add post-mono passes to make mono-reachable analysis more accurate
1 parent d8a6409 commit 43d63b7

Some content is hidden

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

47 files changed

+320
-639
lines changed

compiler/rustc_codegen_cranelift/src/base.rs

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

46-
let mir = tcx.instance_mir(instance.def);
46+
let mir = tcx.codegen_mir(instance);
4747
let _mir_guard = crate::PrintOnPanic(|| {
4848
let mut buf = Vec::new();
4949
with_no_trimmed_paths!({
@@ -305,19 +305,10 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
305305
.generic_activity("codegen prelude")
306306
.run(|| crate::abi::codegen_fn_prelude(fx, start_block));
307307

308-
let reachable_blocks = traversal::mono_reachable_as_bitset(fx.mir, fx.tcx, fx.instance);
309-
310308
for (bb, bb_data) in fx.mir.basic_blocks.iter_enumerated() {
311309
let block = fx.get_block(bb);
312310
fx.bcx.switch_to_block(block);
313311

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

compiler/rustc_codegen_ssa/src/base.rs

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

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

423423
/// 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
@@ -1261,16 +1261,6 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
12611261
}
12621262
}
12631263

1264-
pub(crate) fn codegen_block_as_unreachable(&mut self, bb: mir::BasicBlock) {
1265-
let llbb = match self.try_llbb(bb) {
1266-
Some(llbb) => llbb,
1267-
None => return,
1268-
};
1269-
let bx = &mut Bx::build(self.cx, llbb);
1270-
debug!("codegen_block_as_unreachable({:?})", bb);
1271-
bx.unreachable();
1272-
}
1273-
12741264
fn codegen_terminator(
12751265
&mut self,
12761266
bx: &mut Bx,

compiler/rustc_codegen_ssa/src/mir/mod.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use rustc_index::IndexVec;
44
use rustc_index::bit_set::DenseBitSet;
55
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
66
use rustc_middle::mir::{UnwindTerminateReason, traversal};
7-
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout};
7+
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, TyAndLayout};
88
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt};
99
use rustc_middle::{bug, mir, span_bug};
1010
use rustc_target::callconv::{FnAbi, PassMode};
@@ -126,12 +126,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
126126
where
127127
T: Copy + TypeFoldable<TyCtxt<'tcx>>,
128128
{
129-
debug!("monomorphize: self.instance={:?}", self.instance);
130-
self.instance.instantiate_mir_and_normalize_erasing_regions(
131-
self.cx.tcx(),
132-
self.cx.typing_env(),
133-
ty::EarlyBinder::bind(value),
134-
)
129+
value
135130
}
136131
}
137132

@@ -164,15 +159,15 @@ impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> {
164159
///////////////////////////////////////////////////////////////////////////
165160

166161
#[instrument(level = "debug", skip(cx))]
167-
pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
162+
pub fn lower_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
168163
cx: &'a Bx::CodegenCx,
169164
instance: Instance<'tcx>,
170165
) {
171166
assert!(!instance.args.has_infer());
172167

173168
let llfn = cx.get_fn(instance);
174169

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

177172
let fn_abi = cx.fn_abi_of_instance(instance, ty::List::empty());
178173
debug!("fn_abi: {:?}", fn_abi);
@@ -233,7 +228,8 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
233228
fx.compute_per_local_var_debug_info(&mut start_bx).unzip();
234229
fx.per_local_var_debug_info = per_local_var_debug_info;
235230

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

239235
// Allocate variable and temp allocas
@@ -293,20 +289,9 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
293289
// So drop the builder of `start_llbb` to avoid having two at the same time.
294290
drop(start_bx);
295291

296-
let mut unreached_blocks = DenseBitSet::new_filled(mir.basic_blocks.len());
297292
// Codegen the body of each reachable block using our reverse postorder list.
298293
for bb in traversal_order {
299294
fx.codegen_block(bb);
300-
unreached_blocks.remove(bb);
301-
}
302-
303-
// FIXME: These empty unreachable blocks are *mostly* a waste. They are occasionally
304-
// targets for a SwitchInt terminator, but the reimplementation of the mono-reachable
305-
// simplification in SwitchInt lowering sometimes misses cases that
306-
// mono_reachable_reverse_postorder manages to figure out.
307-
// The solution is to do something like post-mono GVN. But for now we have this hack.
308-
for bb in unreached_blocks.iter() {
309-
fx.codegen_block_as_unreachable(bb);
310295
}
311296
}
312297

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 & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ use crate::ty::fold::{FallibleTypeFolder, TypeFoldable};
3838
use crate::ty::print::{FmtPrinter, Printer, pretty_print_const, with_no_trimmed_paths};
3939
use crate::ty::visit::TypeVisitableExt;
4040
use crate::ty::{
41-
self, AdtDef, GenericArg, GenericArgsRef, Instance, InstanceKind, List, Ty, TyCtxt, TypingEnv,
41+
self, AdtDef, GenericArg, GenericArgsRef, InstanceKind, List, Ty, TyCtxt, TypingEnv,
4242
UserTypeAnnotationIndex,
4343
};
4444

@@ -618,74 +618,6 @@ impl<'tcx> Body<'tcx> {
618618
self.injection_phase.is_some()
619619
}
620620

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

14531372
///////////////////////////////////////////////////////////////////////////

0 commit comments

Comments
 (0)