@@ -787,6 +787,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
787787 | NonMutatingUse ( NonMutatingUseContext :: Inspect )
788788 | NonMutatingUse ( NonMutatingUseContext :: Projection )
789789 | NonUse ( _) => { }
790+ // FIXME(felix91gr): explain the reasoning behind this
790791 MutatingUse ( MutatingUseContext :: Projection ) => {
791792 if self . local_kinds [ local] != LocalKind :: Temp {
792793 self . can_const_prop [ local] = ConstPropMode :: NoPropagation ;
@@ -969,13 +970,58 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
969970 | TerminatorKind :: GeneratorDrop
970971 | TerminatorKind :: FalseEdges { .. }
971972 | 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+ }
9741023 }
9751024 // 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;
9791025 for local in self . locals_of_current_block . iter ( ) {
9801026 Self :: remove_const ( & mut self . ecx , local) ;
9811027 }
0 commit comments