about summary refs log tree commit diff
path: root/src/librustc_mir/transform
diff options
context:
space:
mode:
authorFélix Fischer <felix91gr@gmail.com>2020-04-29 18:59:13 -0400
committerFélix Fischer <felix91gr@gmail.com>2020-05-02 00:40:03 -0400
commitd0dea9f5887c8371279b0b07ce0380f060ca1e99 (patch)
tree8430908727076b9c07bc05d20aadd975e76a6330 /src/librustc_mir/transform
parente91aebc1a3835b9b420da0c021e211175a724b8d (diff)
downloadrust-d0dea9f5887c8371279b0b07ce0380f060ca1e99.tar.gz
rust-d0dea9f5887c8371279b0b07ce0380f060ca1e99.zip
Added MIR constant propagation of Scalars into function call arguments
- Documented rationale of current solution
- Polished documentation
Diffstat (limited to 'src/librustc_mir/transform')
-rw-r--r--src/librustc_mir/transform/const_prop.rs56
1 files changed, 51 insertions, 5 deletions
diff --git a/src/librustc_mir/transform/const_prop.rs b/src/librustc_mir/transform/const_prop.rs
index 09d8f89676a..a34b9dac428 100644
--- a/src/librustc_mir/transform/const_prop.rs
+++ b/src/librustc_mir/transform/const_prop.rs
@@ -787,6 +787,7 @@ impl<'tcx> Visitor<'tcx> for CanConstProp {
             | NonMutatingUse(NonMutatingUseContext::Inspect)
             | NonMutatingUse(NonMutatingUseContext::Projection)
             | NonUse(_) => {}
+            // FIXME(felix91gr): explain the reasoning behind this
             MutatingUse(MutatingUseContext::Projection) => {
                 if self.local_kinds[local] != LocalKind::Temp {
                     self.can_const_prop[local] = ConstPropMode::NoPropagation;
@@ -969,13 +970,58 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
             | TerminatorKind::GeneratorDrop
             | TerminatorKind::FalseEdges { .. }
             | TerminatorKind::FalseUnwind { .. } => {}
-            //FIXME(wesleywiser) Call does have Operands that could be const-propagated
-            TerminatorKind::Call { .. } => {}
+            // Every argument in our function calls can be const propagated.
+            TerminatorKind::Call { ref mut args, .. } => {
+                let mir_opt_level = self.tcx.sess.opts.debugging_opts.mir_opt_level;
+                // Constant Propagation into function call arguments is gated
+                // under mir-opt-level 2, because LLVM codegen gives performance
+                // regressions with it.
+                if mir_opt_level >= 2 {
+                    for opr in args {
+                        /*
+                          The following code would appear to be incomplete, because
+                          the function `Operand::place()` returns `None` if the
+                          `Operand` is of the variant `Operand::Constant`. In this
+                          context however, that variant will never appear. This is why:
+
+                          When constructing the MIR, all function call arguments are
+                          copied into `Locals` of `LocalKind::Temp`. At least, all arguments
+                          that are not unsized (Less than 0.1% are unsized. See #71170
+                          to learn more about those).
+
+                          This means that, conversely, all `Operands` found as function call
+                          arguments are of the variant `Operand::Copy`. This allows us to
+                          simplify our handling of `Operands` in this case.
+                        */
+                        if let Some(l) = opr.place().and_then(|p| p.as_local()) {
+                            if let Some(value) = self.get_const(l) {
+                                if self.should_const_prop(value) {
+                                    // FIXME(felix91gr): this code only handles `Scalar` cases.
+                                    // For now, we're not handling `ScalarPair` cases because
+                                    // doing so here would require a lot of code duplication.
+                                    // We should hopefully generalize `Operand` handling into a fn,
+                                    // and use it to do const-prop here and everywhere else
+                                    // where it makes sense.
+                                    if let interpret::Operand::Immediate(
+                                        interpret::Immediate::Scalar(
+                                            interpret::ScalarMaybeUndef::Scalar(scalar),
+                                        ),
+                                    ) = *value
+                                    {
+                                        *opr = self.operand_from_scalar(
+                                            scalar,
+                                            value.layout.ty,
+                                            source_info.span,
+                                        );
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+            }
         }
         // We remove all Locals which are restricted in propagation to their containing blocks.
-        // We wouldn't need to clone, but the borrow checker can't see that we're not aliasing
-        // the locals_of_current_block field, so we need to clone it first.
-        // let ecx = &mut self.ecx;
         for local in self.locals_of_current_block.iter() {
             Self::remove_const(&mut self.ecx, local);
         }