Skip to content

Commit b5d6de2

Browse files
committed
Skip those blocks in codegen too, to avoid the linker errors
And hey, maybe even be faster because it means less IR emitted.
1 parent 10acbf8 commit b5d6de2

File tree

13 files changed

+132
-127
lines changed

13 files changed

+132
-127
lines changed

compiler/rustc_codegen_ssa/src/mir/block.rs

+6
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
956956
self.codegen_terminator(bx, bb, data.terminator());
957957
}
958958

959+
pub fn codegen_block_as_unreachable(&mut self, bb: mir::BasicBlock) {
960+
let mut bx = self.build_block(bb);
961+
debug!("codegen_block_as_unreachable({:?})", bb);
962+
bx.unreachable();
963+
}
964+
959965
fn codegen_terminator(
960966
&mut self,
961967
mut bx: Bx,

compiler/rustc_codegen_ssa/src/mir/mod.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -240,10 +240,19 @@ pub fn codegen_mir<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
240240
// Apply debuginfo to the newly allocated locals.
241241
fx.debug_introduce_locals(&mut bx);
242242

243+
let reachable_blocks = mir.reachable_blocks_in_mono(cx.tcx(), instance);
244+
243245
// Codegen the body of each block using reverse postorder
244246
// FIXME(eddyb) reuse RPO iterator between `analysis` and this.
245247
for (bb, _) in traversal::reverse_postorder(&mir) {
246-
fx.codegen_block(bb);
248+
if reachable_blocks.contains(bb) {
249+
fx.codegen_block(bb);
250+
} else {
251+
// This may have references to things we didn't monomorphize, so we
252+
// don't actually codegen the body. We still create the block so
253+
// terminators in other blocks can reference it without worry.
254+
fx.codegen_block_as_unreachable(bb);
255+
}
247256
}
248257

249258
// For backends that support CFI using type membership (i.e., testing whether a given pointer

compiler/rustc_middle/src/mir/mod.rs

+69-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor};
1111
use crate::ty::print::{FmtPrinter, Printer};
1212
use crate::ty::subst::{Subst, SubstsRef};
1313
use crate::ty::{self, List, Ty, TyCtxt};
14-
use crate::ty::{AdtDef, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
14+
use crate::ty::{AdtDef, Instance, InstanceDef, Region, ScalarInt, UserTypeAnnotationIndex};
1515
use rustc_hir::def::{CtorKind, Namespace};
1616
use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
1717
use rustc_hir::{self, GeneratorKind};
@@ -23,7 +23,7 @@ pub use rustc_ast::Mutability;
2323
use rustc_data_structures::fx::FxHashSet;
2424
use rustc_data_structures::graph::dominators::{dominators, Dominators};
2525
use rustc_data_structures::graph::{self, GraphSuccessors};
26-
use rustc_index::bit_set::BitMatrix;
26+
use rustc_index::bit_set::{BitMatrix, BitSet};
2727
use rustc_index::vec::{Idx, IndexVec};
2828
use rustc_serialize::{Decodable, Encodable};
2929
use rustc_span::symbol::Symbol;
@@ -517,6 +517,73 @@ impl<'tcx> Body<'tcx> {
517517
pub fn generator_kind(&self) -> Option<GeneratorKind> {
518518
self.generator.as_ref().map(|generator| generator.generator_kind)
519519
}
520+
521+
/// Finds which basic blocks are actually reachable for a specific
522+
/// monomorphization of this body.
523+
///
524+
/// This is allowed to have false positives; just because this says a block
525+
/// is reachable doesn't mean that's necessarily true. It's thus always
526+
/// legal for this to return a filled set.
527+
///
528+
/// Regardless, the [`BitSet::domain_size`] of the returned set will always
529+
/// exactly match the number of blocks in the body so that `contains`
530+
/// checks can be done without worrying about panicking.
531+
///
532+
/// The main case this supports is filtering out `if <T as Trait>::CONST`
533+
/// bodies that can't be removed in generic MIR, but *can* be removed once
534+
/// the specific `T` is known.
535+
///
536+
/// This is used in the monomorphization collector as well as in codegen.
537+
pub fn reachable_blocks_in_mono(
538+
&self,
539+
tcx: TyCtxt<'tcx>,
540+
instance: Instance<'tcx>,
541+
) -> BitSet<BasicBlock> {
542+
if instance.substs.is_noop() {
543+
// If it's non-generic, then mir-opt const prop has already run, meaning it's
544+
// probably not worth doing any further filtering. So call everything reachable.
545+
return BitSet::new_filled(self.basic_blocks().len());
546+
}
547+
548+
let mut set = BitSet::new_empty(self.basic_blocks().len());
549+
self.reachable_blocks_in_mono_from(tcx, instance, &mut set, START_BLOCK);
550+
set
551+
}
552+
553+
fn reachable_blocks_in_mono_from(
554+
&self,
555+
tcx: TyCtxt<'tcx>,
556+
instance: Instance<'tcx>,
557+
set: &mut BitSet<BasicBlock>,
558+
bb: BasicBlock,
559+
) {
560+
if !set.insert(bb) {
561+
return;
562+
}
563+
564+
let data = &self.basic_blocks()[bb];
565+
566+
if let TerminatorKind::SwitchInt {
567+
discr: Operand::Constant(constant),
568+
switch_ty,
569+
targets,
570+
} = &data.terminator().kind
571+
{
572+
let env = ty::ParamEnv::reveal_all();
573+
let mono_literal =
574+
instance.subst_mir_and_normalize_erasing_regions(tcx, env, constant.literal);
575+
if let Some(bits) = mono_literal.try_eval_bits(tcx, env, switch_ty) {
576+
let target = targets.target_for_value(bits);
577+
return self.reachable_blocks_in_mono_from(tcx, instance, set, target);
578+
} else {
579+
bug!("Couldn't evaluate constant {:?} in mono {:?}", constant, instance);
580+
}
581+
}
582+
583+
for &target in data.terminator().successors() {
584+
self.reachable_blocks_in_mono_from(tcx, instance, set, target);
585+
}
586+
}
520587
}
521588

522589
#[derive(Copy, Clone, PartialEq, Eq, Debug, TyEncodable, TyDecodable, HashStable)]

compiler/rustc_middle/src/mir/terminator.rs

+7
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ impl SwitchTargets {
7878
pub fn all_targets_mut(&mut self) -> &mut [BasicBlock] {
7979
&mut self.targets
8080
}
81+
82+
/// Finds the `BasicBlock` to which this `SwitchInt` will branch given the
83+
/// specific value. This cannot fail, as it'll return the `otherwise`
84+
/// branch if there's not a specific match for the value.
85+
pub fn target_for_value(&self, value: u128) -> BasicBlock {
86+
self.iter().find_map(|(v, t)| (v == value).then_some(t)).unwrap_or_else(|| self.otherwise())
87+
}
8188
}
8289

8390
pub struct SwitchTargetsIter<'a> {

compiler/rustc_mir_transform/src/simplify_branches.rs

+2-9
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,8 @@ impl<'tcx> MirPass<'tcx> for SimplifyBranches {
3434
} => {
3535
let constant = c.literal.try_eval_bits(tcx, param_env, switch_ty);
3636
if let Some(constant) = constant {
37-
let otherwise = targets.otherwise();
38-
let mut ret = TerminatorKind::Goto { target: otherwise };
39-
for (v, t) in targets.iter() {
40-
if v == constant {
41-
ret = TerminatorKind::Goto { target: t };
42-
break;
43-
}
44-
}
45-
ret
37+
let target = targets.target_for_value(constant);
38+
TerminatorKind::Goto { target }
4639
} else {
4740
continue;
4841
}

compiler/rustc_monomorphize/src/collector.rs

+4-88
Original file line numberDiff line numberDiff line change
@@ -623,92 +623,14 @@ impl<'a, 'tcx> MirNeighborCollector<'a, 'tcx> {
623623
value,
624624
)
625625
}
626-
627-
fn find_reachable_blocks(&mut self, bb: mir::BasicBlock) {
628-
if !self.reachable_blocks.insert(bb) {
629-
return;
630-
}
631-
632-
use mir::TerminatorKind::*;
633-
let data = &self.body.basic_blocks()[bb];
634-
match data.terminator().kind {
635-
Goto { target } => self.find_reachable_blocks(target),
636-
Resume | Abort | Return | Unreachable | GeneratorDrop => (),
637-
Drop { place: _, target, unwind }
638-
| DropAndReplace { place: _, value: _, target, unwind }
639-
| Assert { cond: _, expected: _, msg: _, target, cleanup: unwind }
640-
| Yield { value: _, resume: target, resume_arg: _, drop: unwind } => {
641-
self.find_reachable_blocks(target);
642-
unwind.map(|b| self.find_reachable_blocks(b));
643-
}
644-
Call { func: _, args: _, destination, cleanup, from_hir_call: _, fn_span: _} => {
645-
destination.map(|(_, b)| self.find_reachable_blocks(b));
646-
cleanup.map(|b| self.find_reachable_blocks(b));
647-
}
648-
FalseEdge { .. } | FalseUnwind { .. } => {
649-
bug!("Expected false edges to already be gone when collecting neighbours for {:?}", self.instance);
650-
}
651-
InlineAsm { template: _, operands: _, options: _, line_spans: _, destination} => {
652-
destination.map(|b| self.find_reachable_blocks(b));
653-
}
654-
SwitchInt { ref discr, switch_ty, ref targets } => {
655-
if let mir::Operand::Constant(constant) = discr {
656-
if let Some(raw_value) = self.try_eval_constant_to_bits(constant, switch_ty) {
657-
// We know what this is going to be,
658-
// so we can ignore all the other blocks.
659-
for (test_value, target) in targets.iter() {
660-
if test_value == raw_value {
661-
return self.find_reachable_blocks(target);
662-
}
663-
}
664-
665-
return self.find_reachable_blocks(targets.otherwise());
666-
}
667-
}
668-
669-
// If it's not a constant or we can't evaluate it,
670-
// then we have to consider them all as reachable.
671-
for &b in targets.all_targets() {
672-
self.find_reachable_blocks(b)
673-
}
674-
}
675-
}
676-
}
677-
678-
fn try_eval_constant_to_bits(&self, constant: &mir::Constant<'tcx>, ty: Ty<'tcx>) -> Option<u128> {
679-
let env = ty::ParamEnv::reveal_all();
680-
let ct = self.monomorphize(constant.literal);
681-
let value = match ct {
682-
mir::ConstantKind::Val(value, _) => value,
683-
mir::ConstantKind::Ty(ct) => {
684-
match ct.val {
685-
ty::ConstKind::Unevaluated(ct) => self
686-
.tcx
687-
.const_eval_resolve(env, ct, None).ok()?,
688-
ty::ConstKind::Value(value) => value,
689-
other => span_bug!(
690-
constant.span,
691-
"encountered bad ConstKind after monomorphizing: {:?}",
692-
other
693-
),
694-
}
695-
}
696-
};
697-
value.try_to_bits_for_ty(self.tcx, env, ty)
698-
}
699626
}
700627

701628
impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> {
702-
fn visit_body(&mut self, body: &mir::Body<'tcx>) {
703-
self.find_reachable_blocks(mir::START_BLOCK);
704-
self.super_body(body);
705-
}
706-
707629
fn visit_basic_block_data(&mut self, block: mir::BasicBlock, data: &mir::BasicBlockData<'tcx>) {
708630
if self.reachable_blocks.contains(block) {
709631
self.super_basic_block_data(block, data);
710632
} else {
711-
debug!("skipping basic block {:?}", block);
633+
debug!("skipping mono-unreachable basic block {:?}", block);
712634
}
713635
}
714636

@@ -1482,15 +1404,9 @@ fn collect_neighbours<'tcx>(
14821404
debug!("collect_neighbours: {:?}", instance.def_id());
14831405
let body = tcx.instance_mir(instance.def);
14841406

1485-
let reachable_blocks =
1486-
if instance.substs.is_noop() {
1487-
// If it's non-generic, then it's already filtered out
1488-
// any blocks that are unreachable, so don't re-do the work.
1489-
BitSet::new_filled(body.basic_blocks().len())
1490-
} else {
1491-
BitSet::new_empty(body.basic_blocks().len())
1492-
};
1493-
let mut collector = MirNeighborCollector { tcx, body: &body, output, instance, reachable_blocks };
1407+
let reachable_blocks = body.reachable_blocks_in_mono(tcx, instance);
1408+
let mut collector =
1409+
MirNeighborCollector { tcx, body: &body, output, instance, reachable_blocks };
14941410
collector.visit_body(&body);
14951411
}
14961412

src/test/codegen/skip-mono-inside-if-false.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@ pub fn demo_for_i32() {
77
generic_impl::<i32>();
88
}
99

10+
// Two important things here:
11+
// - We replace the "then" block with `unreachable` to avoid linking problems
12+
// - We neither declare nor define the `big_impl` that said block "calls".
13+
1014
// CHECK-LABEL: ; skip_mono_inside_if_false::generic_impl
1115
// CHECK: start:
1216
// CHECK-NEXT: br i1 false, label %[[THEN_BRANCH:bb[0-9]+]], label %[[ELSE_BRANCH:bb[0-9]+]]
1317
// CHECK: [[ELSE_BRANCH]]:
1418
// CHECK-NEXT: call skip_mono_inside_if_false::small_impl
1519
// CHECK: [[THEN_BRANCH]]:
16-
// CHECK-NEXT: call skip_mono_inside_if_false::big_impl
17-
18-
// Then despite there being calls to both of them, only the ones that's used has a definition.
19-
// The other is only forward-declared, and its use will disappear with LLVM's simplifycfg.
20+
// CHECK-NEXT: unreachable
2021

22+
// CHECK-NOT: @_ZN25skip_mono_inside_if_false8big_impl
2123
// CHECK: define internal void @_ZN25skip_mono_inside_if_false10small_impl
22-
// CHECK: declare hidden void @_ZN25skip_mono_inside_if_false8big_impl
24+
// CHECK-NOT: @_ZN25skip_mono_inside_if_false8big_impl
2325

2426
fn generic_impl<T>() {
2527
trait MagicTrait {

src/test/mir-opt/const_prop/control-flow-simplification.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// compile-flags: -Zmir-opt-level=1
22

33
trait HasSize: Sized {
4-
const BYTES:usize = std::mem::size_of::<Self>();
4+
const BYTES: usize = std::mem::size_of::<Self>();
55
}
66

77
impl<This> HasSize for This{}
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
#![crate_type = "lib"]
22

33
pub trait HasBoolConst {
4-
const B: bool;
4+
const B: bool;
55
}
66

77
// EMIT_MIR simplify_if_generic_constant.use_associated_const.SimplifyIfConst.diff
88
pub fn use_associated_const<T: HasBoolConst>() -> u8 {
9-
if <T as HasBoolConst>::B {
10-
13
11-
} else {
12-
42
13-
}
9+
if <T as HasBoolConst>::B {
10+
13
11+
} else {
12+
42
13+
}
1414
}

src/test/mir-opt/simplify_if_generic_constant.use_associated_const.SimplifyIfConst.diff

+10-10
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,27 @@
33

44
fn use_associated_const() -> u8 {
55
let mut _0: u8; // return place in scope 0 at $DIR/simplify_if_generic_constant.rs:8:51: 8:53
6-
let mut _1: bool; // in scope 0 at $DIR/simplify_if_generic_constant.rs:9:5: 9:27
6+
let mut _1: bool; // in scope 0 at $DIR/simplify_if_generic_constant.rs:9:8: 9:30
77

88
bb0: {
9-
StorageLive(_1); // scope 0 at $DIR/simplify_if_generic_constant.rs:9:5: 9:27
10-
- _1 = const <T as HasBoolConst>::B; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:5: 9:27
11-
- switchInt(move _1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:5: 9:27
12-
+ switchInt(const <T as HasBoolConst>::B) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:5: 9:27
9+
StorageLive(_1); // scope 0 at $DIR/simplify_if_generic_constant.rs:9:8: 9:30
10+
- _1 = const <T as HasBoolConst>::B; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:8: 9:30
11+
- switchInt(move _1) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:8: 9:30
12+
+ switchInt(const <T as HasBoolConst>::B) -> [false: bb2, otherwise: bb1]; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:8: 9:30
1313
}
1414

1515
bb1: {
16-
_0 = const 13_u8; // scope 0 at $DIR/simplify_if_generic_constant.rs:10:3: 10:5
17-
goto -> bb3; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:2: 13:3
16+
_0 = const 13_u8; // scope 0 at $DIR/simplify_if_generic_constant.rs:10:9: 10:11
17+
goto -> bb3; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:5: 13:6
1818
}
1919

2020
bb2: {
21-
_0 = const 42_u8; // scope 0 at $DIR/simplify_if_generic_constant.rs:12:3: 12:5
22-
goto -> bb3; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:2: 13:3
21+
_0 = const 42_u8; // scope 0 at $DIR/simplify_if_generic_constant.rs:12:9: 12:11
22+
goto -> bb3; // scope 0 at $DIR/simplify_if_generic_constant.rs:9:5: 13:6
2323
}
2424

2525
bb3: {
26-
StorageDead(_1); // scope 0 at $DIR/simplify_if_generic_constant.rs:13:2: 13:3
26+
StorageDead(_1); // scope 0 at $DIR/simplify_if_generic_constant.rs:13:5: 13:6
2727
return; // scope 0 at $DIR/simplify_if_generic_constant.rs:14:2: 14:2
2828
}
2929
}

src/test/ui/consts/control-flow/drop-fail.precise.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | let x = Some(Vec::new());
55
| ^ constants cannot evaluate destructors
66

77
error[E0493]: destructors cannot be evaluated at compile-time
8-
--> $DIR/drop-fail.rs:39:9
8+
--> $DIR/drop-fail.rs:44:9
99
|
1010
LL | let mut tmp = None;
1111
| ^^^^^^^ constants cannot evaluate destructors

src/test/ui/consts/control-flow/drop-fail.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,18 @@ const _: Option<Vec<i32>> = {
88
let x = Some(Vec::new());
99
//[stock,precise]~^ ERROR destructors cannot be evaluated at compile-time
1010

11-
if true {
11+
if unknown() {
1212
x
1313
} else {
1414
y
1515
}
1616
};
1717

18+
#[inline(never)]
19+
const fn unknown() -> bool {
20+
true
21+
}
22+
1823
// We only clear `NeedsDrop` if a local is moved from in entirely. This is a shortcoming of the
1924
// existing analysis.
2025
const _: Vec<i32> = {

0 commit comments

Comments
 (0)