@@ -787,6 +787,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
787
787
| NonMutatingUse ( NonMutatingUseContext :: Inspect )
788
788
| NonMutatingUse ( NonMutatingUseContext :: Projection )
789
789
| NonUse ( _) => { }
790
+ // FIXME(felix91gr): explain the reasoning behind this
790
791
MutatingUse ( MutatingUseContext :: Projection ) => {
791
792
if self . local_kinds [ local] != LocalKind :: Temp {
792
793
self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
@@ -969,13 +970,58 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
969
970
| TerminatorKind :: GeneratorDrop
970
971
| TerminatorKind :: FalseEdges { .. }
971
972
| TerminatorKind :: FalseUnwind { .. } => { }
972
- //FIXME(wesleywiser) Call does have Operands that could be const-propagated
973
- TerminatorKind :: Call { .. } => { }
973
+ // Every argument in our function calls can be const propagated.
974
+ TerminatorKind :: Call { ref mut args, .. } => {
975
+ let mir_opt_level = self . tcx . sess . opts . debugging_opts . mir_opt_level ;
976
+ // Constant Propagation into function call arguments is gated
977
+ // under mir-opt-level 2, because LLVM codegen gives performance
978
+ // regressions with it.
979
+ if mir_opt_level >= 2 {
980
+ for opr in args {
981
+ /*
982
+ The following code would appear to be incomplete, because
983
+ the function `Operand::place()` returns `None` if the
984
+ `Operand` is of the variant `Operand::Constant`. In this
985
+ context however, that variant will never appear. This is why:
986
+
987
+ When constructing the MIR, all function call arguments are
988
+ copied into `Locals` of `LocalKind::Temp`. At least, all arguments
989
+ that are not unsized (Less than 0.1% are unsized. See #71170
990
+ to learn more about those).
991
+
992
+ This means that, conversely, all `Operands` found as function call
993
+ arguments are of the variant `Operand::Copy`. This allows us to
994
+ simplify our handling of `Operands` in this case.
995
+ */
996
+ if let Some ( l) = opr. place ( ) . and_then ( |p| p. as_local ( ) ) {
997
+ if let Some ( value) = self . get_const ( l) {
998
+ if self . should_const_prop ( value) {
999
+ // FIXME(felix91gr): this code only handles `Scalar` cases.
1000
+ // For now, we're not handling `ScalarPair` cases because
1001
+ // doing so here would require a lot of code duplication.
1002
+ // We should hopefully generalize `Operand` handling into a fn,
1003
+ // and use it to do const-prop here and everywhere else
1004
+ // where it makes sense.
1005
+ if let interpret:: Operand :: Immediate (
1006
+ interpret:: Immediate :: Scalar (
1007
+ interpret:: ScalarMaybeUndef :: Scalar ( scalar) ,
1008
+ ) ,
1009
+ ) = * value
1010
+ {
1011
+ * opr = self . operand_from_scalar (
1012
+ scalar,
1013
+ value. layout . ty ,
1014
+ source_info. span ,
1015
+ ) ;
1016
+ }
1017
+ }
1018
+ }
1019
+ }
1020
+ }
1021
+ }
1022
+ }
974
1023
}
975
1024
// We remove all Locals which are restricted in propagation to their containing blocks.
976
- // We wouldn't need to clone, but the borrow checker can't see that we're not aliasing
977
- // the locals_of_current_block field, so we need to clone it first.
978
- // let ecx = &mut self.ecx;
979
1025
for local in self . locals_of_current_block . iter ( ) {
980
1026
Self :: remove_const ( & mut self . ecx , local) ;
981
1027
}
0 commit comments