@@ -38,7 +38,19 @@ impl MirPass<'_> for UnreachablePropagation {
38
38
}
39
39
}
40
40
41
+ // We do want do keep some unreachable blocks, but make them empty.
42
+ for bb in unreachable_blocks {
43
+ if !tcx. consider_optimizing ( || {
44
+ format ! ( "UnreachablePropagation {:?} " , body. source. def_id( ) )
45
+ } ) {
46
+ break ;
47
+ }
48
+
49
+ body. basic_blocks_mut ( ) [ bb] . statements . clear ( ) ;
50
+ }
51
+
41
52
let replaced = !replacements. is_empty ( ) ;
53
+
42
54
for ( bb, terminator_kind) in replacements {
43
55
if !tcx. consider_optimizing ( || {
44
56
format ! ( "UnreachablePropagation {:?} " , body. source. def_id( ) )
@@ -57,42 +69,55 @@ impl MirPass<'_> for UnreachablePropagation {
57
69
58
70
fn remove_successors < ' tcx , F > (
59
71
terminator_kind : & TerminatorKind < ' tcx > ,
60
- predicate : F ,
72
+ is_unreachable : F ,
61
73
) -> Option < TerminatorKind < ' tcx > >
62
74
where
63
75
F : Fn ( BasicBlock ) -> bool ,
64
76
{
65
- let terminator = match * terminator_kind {
66
- TerminatorKind :: Goto { target } if predicate ( target) => TerminatorKind :: Unreachable ,
67
- TerminatorKind :: SwitchInt { ref discr, switch_ty, ref targets } => {
77
+ let terminator = match terminator_kind {
78
+ // This will unconditionally run into an unreachable and is therefore unreachable as well.
79
+ TerminatorKind :: Goto { target } if is_unreachable ( * target) => TerminatorKind :: Unreachable ,
80
+ TerminatorKind :: SwitchInt { targets, discr, switch_ty } => {
68
81
let otherwise = targets. otherwise ( ) ;
69
82
70
- let original_targets_len = targets. iter ( ) . len ( ) + 1 ;
71
- let ( mut values, mut targets) : ( Vec < _ > , Vec < _ > ) =
72
- targets. iter ( ) . filter ( |( _, bb) | !predicate ( * bb) ) . unzip ( ) ;
83
+ // If all targets are unreachable, we can be unreachable as well.
84
+ if targets. all_targets ( ) . iter ( ) . all ( |bb| is_unreachable ( * bb) ) {
85
+ TerminatorKind :: Unreachable
86
+ } else if is_unreachable ( otherwise) {
87
+ // If there are multiple targets, don't delete unreachable branches (like an unreachable otherwise)
88
+ // unless otherwise is unrachable, in which case deleting a normal branch causes it to be merged with
89
+ // the otherwise, keeping its unreachable.
90
+ // This looses information about reachability causing worse codegen.
91
+ // For example (see src/test/codegen/match-optimizes-away.rs)
92
+ //
93
+ // pub enum Two { A, B }
94
+ // pub fn identity(x: Two) -> Two {
95
+ // match x {
96
+ // Two::A => Two::A,
97
+ // Two::B => Two::B,
98
+ // }
99
+ // }
100
+ //
101
+ // This generates a `switchInt() -> [0: 0, 1: 1, otherwise: unreachable]`, which allows us or LLVM to
102
+ // turn it into just `x` later. Without the unreachable, such a transformation would be illegal.
103
+ // If the otherwise branch is unreachable, we can delete all other unreacahble targets, as they will
104
+ // still point to the unreachable and therefore not lose reachability information.
105
+ let reachable_iter = targets. iter ( ) . filter ( |( _, bb) | !is_unreachable ( * bb) ) ;
73
106
74
- if !predicate ( otherwise) {
75
- targets. push ( otherwise) ;
76
- } else {
77
- values. pop ( ) ;
78
- }
107
+ let new_targets = SwitchTargets :: new ( reachable_iter, otherwise) ;
79
108
80
- let retained_targets_len = targets. len ( ) ;
109
+ // No unreachable branches were removed.
110
+ if new_targets. all_targets ( ) . len ( ) == targets. all_targets ( ) . len ( ) {
111
+ return None ;
112
+ }
81
113
82
- if targets. is_empty ( ) {
83
- TerminatorKind :: Unreachable
84
- } else if targets. len ( ) == 1 {
85
- TerminatorKind :: Goto { target : targets[ 0 ] }
86
- } else if original_targets_len != retained_targets_len {
87
114
TerminatorKind :: SwitchInt {
88
115
discr : discr. clone ( ) ,
89
- switch_ty,
90
- targets : SwitchTargets :: new (
91
- values. iter ( ) . copied ( ) . zip ( targets. iter ( ) . copied ( ) ) ,
92
- * targets. last ( ) . unwrap ( ) ,
93
- ) ,
116
+ switch_ty : * switch_ty,
117
+ targets : new_targets,
94
118
}
95
119
} else {
120
+ // If the otherwise branch is reachable, we don't want to delete any unreachable branches.
96
121
return None ;
97
122
}
98
123
}
0 commit comments