Auto merge of #121421 - saethlin:smarter-mono, r=oli-obk · rust-lang/rust@5a6c1aa (original) (raw)
`@@ -10,7 +10,7 @@ use crate::ty::print::{pretty_print_const, with_no_trimmed_paths};
`
10
10
`use crate::ty::print::{FmtPrinter, Printer};
`
11
11
`use crate::ty::visit::TypeVisitableExt;
`
12
12
`use crate::ty::{self, List, Ty, TyCtxt};
`
13
``
`-
use crate::ty::{AdtDef, InstanceDef, UserTypeAnnotationIndex};
`
``
13
`+
use crate::ty::{AdtDef, Instance, InstanceDef, UserTypeAnnotationIndex};
`
14
14
`use crate::ty::{GenericArg, GenericArgsRef};
`
15
15
``
16
16
`use rustc_data_structures::captures::Captures;
`
`@@ -27,6 +27,8 @@ pub use rustc_ast::Mutability;
`
27
27
`use rustc_data_structures::fx::FxHashMap;
`
28
28
`use rustc_data_structures::fx::FxHashSet;
`
29
29
`use rustc_data_structures::graph::dominators::Dominators;
`
``
30
`+
use rustc_data_structures::stack::ensure_sufficient_stack;
`
``
31
`+
use rustc_index::bit_set::BitSet;
`
30
32
`use rustc_index::{Idx, IndexSlice, IndexVec};
`
31
33
`use rustc_serialize::{Decodable, Encodable};
`
32
34
`use rustc_span::symbol::Symbol;
`
`@@ -640,6 +642,129 @@ impl<'tcx> Body<'tcx> {
`
640
642
`self.injection_phase.is_some()
`
641
643
`}
`
642
644
``
``
645
`+
/// Finds which basic blocks are actually reachable for a specific
`
``
646
`+
/// monomorphization of this body.
`
``
647
`+
///
`
``
648
`+
/// This is allowed to have false positives; just because this says a block
`
``
649
`+
/// is reachable doesn't mean that's necessarily true. It's thus always
`
``
650
`+
/// legal for this to return a filled set.
`
``
651
`+
///
`
``
652
`` +
/// Regardless, the [BitSet::domain_size
] of the returned set will always
``
``
653
`` +
/// exactly match the number of blocks in the body so that contains
``
``
654
`+
/// checks can be done without worrying about panicking.
`
``
655
`+
///
`
``
656
`` +
/// This is mostly useful because it lets us skip lowering the false
side
``
``
657
`` +
/// of if <T as Trait>::CONST
, as well as intrinsics::debug_assertions
.
``
``
658
`+
pub fn reachable_blocks_in_mono(
`
``
659
`+
&self,
`
``
660
`+
tcx: TyCtxt<'tcx>,
`
``
661
`+
instance: Instance<'tcx>,
`
``
662
`+
) -> BitSet {
`
``
663
`+
let mut set = BitSet::new_empty(self.basic_blocks.len());
`
``
664
`+
self.reachable_blocks_in_mono_from(tcx, instance, &mut set, START_BLOCK);
`
``
665
`+
set
`
``
666
`+
}
`
``
667
+
``
668
`+
fn reachable_blocks_in_mono_from(
`
``
669
`+
&self,
`
``
670
`+
tcx: TyCtxt<'tcx>,
`
``
671
`+
instance: Instance<'tcx>,
`
``
672
`+
set: &mut BitSet,
`
``
673
`+
bb: BasicBlock,
`
``
674
`+
) {
`
``
675
`+
if !set.insert(bb) {
`
``
676
`+
return;
`
``
677
`+
}
`
``
678
+
``
679
`+
let data = &self.basic_blocks[bb];
`
``
680
+
``
681
`+
if let Some((bits, targets)) = Self::try_const_mono_switchint(tcx, instance, data) {
`
``
682
`+
let target = targets.target_for_value(bits);
`
``
683
`+
ensure_sufficient_stack(|| {
`
``
684
`+
self.reachable_blocks_in_mono_from(tcx, instance, set, target)
`
``
685
`+
});
`
``
686
`+
return;
`
``
687
`+
}
`
``
688
+
``
689
`+
for target in data.terminator().successors() {
`
``
690
`+
ensure_sufficient_stack(|| {
`
``
691
`+
self.reachable_blocks_in_mono_from(tcx, instance, set, target)
`
``
692
`+
});
`
``
693
`+
}
`
``
694
`+
}
`
``
695
+
``
696
`` +
/// If this basic block ends with a [TerminatorKind::SwitchInt
] for which we can evaluate the
``
``
697
`+
/// dimscriminant in monomorphization, we return the discriminant bits and the
`
``
698
`` +
/// [SwitchTargets
], just so the caller doesn't also have to match on the terminator.
``
``
699
`+
fn try_const_mono_switchint<'a>(
`
``
700
`+
tcx: TyCtxt<'tcx>,
`
``
701
`+
instance: Instance<'tcx>,
`
``
702
`+
block: &'a BasicBlockData<'tcx>,
`
``
703
`+
) -> Option<(u128, &'a SwitchTargets)> {
`
``
704
`+
// There are two places here we need to evaluate a constant.
`
``
705
`+
let eval_mono_const = |constant: &ConstOperand<'tcx>| {
`
``
706
`+
let env = ty::ParamEnv::reveal_all();
`
``
707
`+
let mono_literal = instance.instantiate_mir_and_normalize_erasing_regions(
`
``
708
`+
tcx,
`
``
709
`+
env,
`
``
710
`+
crate::ty::EarlyBinder::bind(constant.const_),
`
``
711
`+
);
`
``
712
`+
let Some(bits) = mono_literal.try_eval_bits(tcx, env) else {
`
``
713
`+
bug!("Couldn't evaluate constant {:?} in mono {:?}", constant, instance);
`
``
714
`+
};
`
``
715
`+
bits
`
``
716
`+
};
`
``
717
+
``
718
`+
let TerminatorKind::SwitchInt { discr, targets } = &block.terminator().kind else {
`
``
719
`+
return None;
`
``
720
`+
};
`
``
721
+
``
722
`+
// If this is a SwitchInt(const _), then we can just evaluate the constant and return.
`
``
723
`+
let discr = match discr {
`
``
724
`+
Operand::Constant(constant) => {
`
``
725
`+
let bits = eval_mono_const(constant);
`
``
726
`+
return Some((bits, targets));
`
``
727
`+
}
`
``
728
`+
Operand::Move(place) | Operand::Copy(place) => place,
`
``
729
`+
};
`
``
730
+
``
731
`` +
// MIR for if false
actually looks like this:
``
``
732
`+
// _1 = const _
`
``
733
`+
// SwitchInt(_1)
`
``
734
`+
//
`
``
735
`+
// And MIR for if intrinsics::debug_assertions() looks like this:
`
``
736
`+
// _1 = cfg!(debug_assertions)
`
``
737
`+
// SwitchInt(_1)
`
``
738
`+
//
`
``
739
`+
// So we're going to try to recognize this pattern.
`
``
740
`+
//
`
``
741
`+
// If we have a SwitchInt on a non-const place, we find the most recent statement that
`
``
742
`+
// isn't a storage marker. If that statement is an assignment of a const to our
`
``
743
`+
// discriminant place, we evaluate and return the const, as if we've const-propagated it
`
``
744
`+
// into the SwitchInt.
`
``
745
+
``
746
`+
let last_stmt = block.statements.iter().rev().find(|stmt| {
`
``
747
`+
!matches!(stmt.kind, StatementKind::StorageDead() | StatementKind::StorageLive())
`
``
748
`+
})?;
`
``
749
+
``
750
`+
let (place, rvalue) = last_stmt.kind.as_assign()?;
`
``
751
+
``
752
`+
if discr != place {
`
``
753
`+
return None;
`
``
754
`+
}
`
``
755
+
``
756
`+
match rvalue {
`
``
757
`+
Rvalue::NullaryOp(NullOp::UbCheck(_), _) => {
`
``
758
`+
Some((tcx.sess.opts.debug_assertions as u128, targets))
`
``
759
`+
}
`
``
760
`+
Rvalue::Use(Operand::Constant(constant)) => {
`
``
761
`+
let bits = eval_mono_const(constant);
`
``
762
`+
Some((bits, targets))
`
``
763
`+
}
`
``
764
`+
_ => None,
`
``
765
`+
}
`
``
766
`+
}
`
``
767
+
643
768
`` /// For a Location
in this scope, determine what the "caller location" at that point is. This
``
644
769
`` /// is interesting because of inlining: the #[track_caller]
attribute of inlined functions
``
645
770
`` /// must be honored. Falls back to the tracked_caller
value for #[track_caller]
functions,
``