@@ -581,6 +581,34 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
581
581
Some ( ( ) )
582
582
}
583
583
584
+ fn propagate_operand ( & mut self , operand : & mut Operand < ' tcx > ) {
585
+ match * operand {
586
+ Operand :: Copy ( l) | Operand :: Move ( l) => {
587
+ if let Some ( value) = self . get_const ( l) {
588
+ if self . should_const_prop ( value) {
589
+ // FIXME(felix91gr): this code only handles `Scalar` cases.
590
+ // For now, we're not handling `ScalarPair` cases because
591
+ // doing so here would require a lot of code duplication.
592
+ // We should hopefully generalize `Operand` handling into a fn,
593
+ // and use it to do const-prop here and everywhere else
594
+ // where it makes sense.
595
+ if let interpret:: Operand :: Immediate ( interpret:: Immediate :: Scalar (
596
+ ScalarMaybeUninit :: Scalar ( scalar) ,
597
+ ) ) = * value
598
+ {
599
+ * operand = self . operand_from_scalar (
600
+ scalar,
601
+ value. layout . ty ,
602
+ self . source_info . unwrap ( ) . span ,
603
+ ) ;
604
+ }
605
+ }
606
+ }
607
+ }
608
+ Operand :: Constant ( _) => ( ) ,
609
+ }
610
+ }
611
+
584
612
fn const_prop (
585
613
& mut self ,
586
614
rvalue : & Rvalue < ' tcx > ,
@@ -969,6 +997,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
969
997
}
970
998
}
971
999
1000
+ fn visit_operand ( & mut self , operand : & mut Operand < ' tcx > , location : Location ) {
1001
+ self . super_operand ( operand, location) ;
1002
+
1003
+ // Only const prop copies and moves on `mir_opt_level=3` as doing so
1004
+ // currently increases compile time.
1005
+ if self . tcx . sess . opts . debugging_opts . mir_opt_level >= 3 {
1006
+ self . propagate_operand ( operand)
1007
+ }
1008
+ }
1009
+
972
1010
fn visit_constant ( & mut self , constant : & mut Constant < ' tcx > , location : Location ) {
973
1011
trace ! ( "visit_constant: {:?}" , constant) ;
974
1012
self . super_constant ( constant, location) ;
@@ -1136,18 +1174,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
1136
1174
}
1137
1175
}
1138
1176
}
1139
- TerminatorKind :: SwitchInt { ref mut discr, switch_ty, .. } => {
1140
- if let Some ( value) = self . eval_operand ( & discr, source_info) {
1141
- if self . should_const_prop ( value) {
1142
- if let ScalarMaybeUninit :: Scalar ( scalar) =
1143
- self . ecx . read_scalar ( value) . unwrap ( )
1144
- {
1145
- * discr = self . operand_from_scalar ( scalar, switch_ty, source_info. span ) ;
1146
- }
1147
- }
1148
- }
1177
+ TerminatorKind :: SwitchInt { ref mut discr, .. } => {
1178
+ // FIXME: This is currently redundant with `visit_operand`, but sadly
1179
+ // always visiting operands currently causes a perf regression in LLVM codegen, so
1180
+ // `visit_operand` currently only runs for propagates places for `mir_opt_level=3`.
1181
+ self . propagate_operand ( discr)
1149
1182
}
1150
- // None of these have Operands to const-propagate
1183
+ // None of these have Operands to const-propagate.
1151
1184
TerminatorKind :: Goto { .. }
1152
1185
| TerminatorKind :: Resume
1153
1186
| TerminatorKind :: Abort
@@ -1160,61 +1193,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
1160
1193
| TerminatorKind :: FalseEdge { .. }
1161
1194
| TerminatorKind :: FalseUnwind { .. }
1162
1195
| TerminatorKind :: InlineAsm { .. } => { }
1163
- // Every argument in our function calls can be const propagated.
1164
- TerminatorKind :: Call { ref mut args, .. } => {
1165
- let mir_opt_level = self . tcx . sess . opts . debugging_opts . mir_opt_level ;
1166
- // Constant Propagation into function call arguments is gated
1167
- // under mir-opt-level 2, because LLVM codegen gives performance
1168
- // regressions with it.
1169
- if mir_opt_level >= 2 {
1170
- for opr in args {
1171
- /*
1172
- The following code would appear to be incomplete, because
1173
- the function `Operand::place()` returns `None` if the
1174
- `Operand` is of the variant `Operand::Constant`. In this
1175
- context however, that variant will never appear. This is why:
1176
-
1177
- When constructing the MIR, all function call arguments are
1178
- copied into `Locals` of `LocalKind::Temp`. At least, all arguments
1179
- that are not unsized (Less than 0.1% are unsized. See #71170
1180
- to learn more about those).
1181
-
1182
- This means that, conversely, all `Operands` found as function call
1183
- arguments are of the variant `Operand::Copy`. This allows us to
1184
- simplify our handling of `Operands` in this case.
1185
- */
1186
- if let Some ( l) = opr. place ( ) {
1187
- if let Some ( value) = self . get_const ( l) {
1188
- if self . should_const_prop ( value) {
1189
- // FIXME(felix91gr): this code only handles `Scalar` cases.
1190
- // For now, we're not handling `ScalarPair` cases because
1191
- // doing so here would require a lot of code duplication.
1192
- // We should hopefully generalize `Operand` handling into a fn,
1193
- // and use it to do const-prop here and everywhere else
1194
- // where it makes sense.
1195
- if let interpret:: Operand :: Immediate (
1196
- interpret:: Immediate :: Scalar ( ScalarMaybeUninit :: Scalar (
1197
- scalar,
1198
- ) ) ,
1199
- ) = * value
1200
- {
1201
- * opr = self . operand_from_scalar (
1202
- scalar,
1203
- value. layout . ty ,
1204
- source_info. span ,
1205
- ) ;
1206
- }
1207
- }
1208
- }
1209
- }
1210
- }
1211
- }
1212
- }
1196
+ // Every argument in our function calls have already been propagated in `visit_operand`.
1197
+ //
1198
+ // NOTE: because LLVM codegen gives performance regressions with it, so this is gated
1199
+ // on `mir_opt_level=3`.
1200
+ TerminatorKind :: Call { .. } => { }
1213
1201
}
1214
1202
1215
1203
// We remove all Locals which are restricted in propagation to their containing blocks and
1216
1204
// which were modified in the current block.
1217
- // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`
1205
+ // Take it out of the ecx so we can get a mutable reference to the ecx for `remove_const`.
1218
1206
let mut locals = std:: mem:: take ( & mut self . ecx . machine . written_only_inside_own_block_locals ) ;
1219
1207
for & local in locals. iter ( ) {
1220
1208
Self :: remove_const ( & mut self . ecx , local) ;
0 commit comments