@@ -582,6 +582,34 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
582
582
Some ( ( ) )
583
583
}
584
584
585
+ fn propagate_operand ( & mut self , operand : & mut Operand < ' tcx > , location : Location ) {
586
+ match * operand {
587
+ Operand :: Copy ( l) | Operand :: Move ( l) => {
588
+ if let Some ( value) = self . get_const ( l) {
589
+ if self . should_const_prop ( value) {
590
+ // FIXME(felix91gr): this code only handles `Scalar` cases.
591
+ // For now, we're not handling `ScalarPair` cases because
592
+ // doing so here would require a lot of code duplication.
593
+ // We should hopefully generalize `Operand` handling into a fn,
594
+ // and use it to do const-prop here and everywhere else
595
+ // where it makes sense.
596
+ if let interpret:: Operand :: Immediate ( interpret:: Immediate :: Scalar (
597
+ ScalarMaybeUninit :: Scalar ( scalar) ,
598
+ ) ) = * value
599
+ {
600
+ * operand = self . operand_from_scalar (
601
+ scalar,
602
+ value. layout . ty ,
603
+ self . source_info . unwrap ( ) . span ,
604
+ ) ;
605
+ }
606
+ }
607
+ }
608
+ }
609
+ Operand :: Constant ( ref mut ct) => self . visit_constant ( ct, location) ,
610
+ }
611
+ }
612
+
585
613
fn const_prop (
586
614
& mut self ,
587
615
rvalue : & Rvalue < ' tcx > ,
@@ -905,6 +933,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
905
933
}
906
934
}
907
935
936
+ fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , location : Location ) {
937
+ // Only const prop copies and moves on `mir_opt_level=3` as doing so
938
+ // currently increases compile time.
939
+ if self . tcx . sess . opts . debugging_opts . mir_opt_level < 3 {
940
+ self . super_operand ( operand, location)
941
+ } else {
942
+ self . propagate_operand ( operand, location)
943
+ }
944
+ }
945
+
908
946
fn visit_constant ( & mut self , constant : & mut Constant < ' tcx > , location : Location ) {
909
947
trace ! ( "visit_constant: {:?}" , constant) ;
910
948
self . super_constant ( constant, location) ;
@@ -1072,18 +1110,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
1072
1110
}
1073
1111
}
1074
1112
}
1075
- TerminatorKind :: SwitchInt { ref mut discr, switch_ty, .. } => {
1076
- if let Some ( value) = self . eval_operand ( & discr, source_info) {
1077
- if self . should_const_prop ( value) {
1078
- if let ScalarMaybeUninit :: Scalar ( scalar) =
1079
- self . ecx . read_scalar ( value) . unwrap ( )
1080
- {
1081
- * discr = self . operand_from_scalar ( scalar, switch_ty, source_info. span ) ;
1082
- }
1083
- }
1084
- }
1113
+ TerminatorKind :: SwitchInt { ref mut discr, .. } => {
1114
+ // FIXME: This is currently redundant with `visit_operand`, but sadly
1115
+ // always visiting operands currently causes a perf regression in LLVM codegen, so
1116
+ // `visit_operand` currently only runs for propagates places for `mir_opt_level=3`.
1117
+ self . propagate_operand ( discr, location)
1085
1118
}
1086
- // None of these have Operands to const-propagate
1119
+ // None of these have Operands to const-propagate.
1087
1120
TerminatorKind :: Goto { .. }
1088
1121
| TerminatorKind :: Resume
1089
1122
| TerminatorKind :: Abort
@@ -1096,61 +1129,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
1096
1129
| TerminatorKind :: FalseEdge { .. }
1097
1130
| TerminatorKind :: FalseUnwind { .. }
1098
1131
| TerminatorKind :: InlineAsm { .. } => { }
1099
- // Every argument in our function calls can be const propagated.
1100
- TerminatorKind :: Call { ref mut args, .. } => {
1101
- let mir_opt_level = self . tcx . sess . opts . debugging_opts . mir_opt_level ;
1102
- // Constant Propagation into function call arguments is gated
1103
- // under mir-opt-level 2, because LLVM codegen gives performance
1104
- // regressions with it.
1105
- if mir_opt_level >= 2 {
1106
- for opr in args {
1107
- /*
1108
- The following code would appear to be incomplete, because
1109
- the function `Operand::place()` returns `None` if the
1110
- `Operand` is of the variant `Operand::Constant`. In this
1111
- context however, that variant will never appear. This is why:
1112
-
1113
- When constructing the MIR, all function call arguments are
1114
- copied into `Locals` of `LocalKind::Temp`. At least, all arguments
1115
- that are not unsized (Less than 0.1% are unsized. See #71170
1116
- to learn more about those).
1117
-
1118
- This means that, conversely, all `Operands` found as function call
1119
- arguments are of the variant `Operand::Copy`. This allows us to
1120
- simplify our handling of `Operands` in this case.
1121
- */
1122
- if let Some ( l) = opr. place ( ) {
1123
- if let Some ( value) = self . get_const ( l) {
1124
- if self . should_const_prop ( value) {
1125
- // FIXME(felix91gr): this code only handles `Scalar` cases.
1126
- // For now, we're not handling `ScalarPair` cases because
1127
- // doing so here would require a lot of code duplication.
1128
- // We should hopefully generalize `Operand` handling into a fn,
1129
- // and use it to do const-prop here and everywhere else
1130
- // where it makes sense.
1131
- if let interpret:: Operand :: Immediate (
1132
- interpret:: Immediate :: Scalar ( ScalarMaybeUninit :: Scalar (
1133
- scalar,
1134
- ) ) ,
1135
- ) = * value
1136
- {
1137
- * opr = self . operand_from_scalar (
1138
- scalar,
1139
- value. layout . ty ,
1140
- source_info. span ,
1141
- ) ;
1142
- }
1143
- }
1144
- }
1145
- }
1146
- }
1147
- }
1148
- }
1132
+ // Every argument in our function calls have already been propagated in `visit_operand`.
1133
+ //
1134
+ // NOTE: because LLVM codegen gives performance regressions with it, so this is gated
1135
+ // on `mir_opt_level=3`.
1136
+ TerminatorKind :: Call { .. } => { }
1149
1137
}
1150
1138
1151
1139
// We remove all Locals which are restricted in propagation to their containing blocks and
1152
1140
// which were modified in the current block.
1153
- // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`
1141
+ // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`.
1154
1142
let mut locals = std:: mem:: take ( & mut self . ecx . machine . written_only_inside_own_block_locals ) ;
1155
1143
for & local in locals. iter ( ) {
1156
1144
Self :: remove_const ( & mut self . ecx , local) ;
0 commit comments