@@ -581,6 +581,34 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
581581 Some ( ( ) )
582582 }
583583
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+
584612 fn const_prop (
585613 & mut self ,
586614 rvalue : & Rvalue < ' tcx > ,
@@ -969,6 +997,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
969997 }
970998 }
971999
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+
9721010 fn visit_constant ( & mut self , constant : & mut Constant < ' tcx > , location : Location ) {
9731011 trace ! ( "visit_constant: {:?}" , constant) ;
9741012 self . super_constant ( constant, location) ;
@@ -1136,18 +1174,13 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
11361174 }
11371175 }
11381176 }
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)
11491182 }
1150- // None of these have Operands to const-propagate
1183+ // None of these have Operands to const-propagate.
11511184 TerminatorKind :: Goto { .. }
11521185 | TerminatorKind :: Resume
11531186 | TerminatorKind :: Abort
@@ -1160,61 +1193,16 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
11601193 | TerminatorKind :: FalseEdge { .. }
11611194 | TerminatorKind :: FalseUnwind { .. }
11621195 | 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 { .. } => { }
12131201 }
12141202
12151203 // We remove all Locals which are restricted in propagation to their containing blocks and
12161204 // 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`.
12181206 let mut locals = std:: mem:: take ( & mut self . ecx . machine . written_only_inside_own_block_locals ) ;
12191207 for & local in locals. iter ( ) {
12201208 Self :: remove_const ( & mut self . ecx , local) ;
0 commit comments