]> git.proxmox.com Git - rustc.git/blobdiff - src/librustc_mir/transform/inline.rs
New upstream version 1.23.0+dfsg1
[rustc.git] / src / librustc_mir / transform / inline.rs
index 9d32861aedade2e9609c67f08a7c0ec34023897e..628a8161615e3da28bdfda8a2c0d24f875916303 100644 (file)
 
 //! Inlining pass for MIR functions
 
+use rustc::hir;
 use rustc::hir::def_id::DefId;
 
 use rustc_data_structures::bitvec::BitVector;
 use rustc_data_structures::indexed_vec::{Idx, IndexVec};
 
 use rustc::mir::*;
-use rustc::mir::transform::{MirPass, MirSource};
 use rustc::mir::visit::*;
-use rustc::ty::{self, Ty, TyCtxt, Instance};
+use rustc::ty::{self, Instance, Ty, TyCtxt, TypeFoldable};
 use rustc::ty::subst::{Subst,Substs};
 
 use std::collections::VecDeque;
+use transform::{MirPass, MirSource};
 use super::simplify::{remove_dead_blocks, CfgSimplifier};
 
 use syntax::{attr};
@@ -37,7 +38,7 @@ const UNKNOWN_SIZE_COST: usize = 10;
 
 pub struct Inline;
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 struct CallSite<'tcx> {
     callee: DefId,
     substs: &'tcx Substs<'tcx>,
@@ -77,8 +78,13 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
 
         let mut callsites = VecDeque::new();
 
+        let param_env = self.tcx.param_env(self.source.def_id);
+
         // Only do inlining into fn bodies.
-        if let MirSource::Fn(caller_id) = self.source {
+        let id = self.tcx.hir.as_local_node_id(self.source.def_id).unwrap();
+        let body_owner_kind = self.tcx.hir.body_owner_kind(id);
+        if let (hir::BodyOwnerKind::Fn, None) = (body_owner_kind, self.source.promoted) {
+
             for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated() {
                 // Don't inline calls that are in cleanup blocks.
                 if bb_data.is_cleanup { continue; }
@@ -88,9 +94,6 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                 if let TerminatorKind::Call {
                     func: Operand::Constant(ref f), .. } = terminator.kind {
                         if let ty::TyFnDef(callee_def_id, substs) = f.ty.sty {
-                            let caller_def_id = self.tcx.hir.local_def_id(caller_id);
-                            let param_env = self.tcx.param_env(caller_def_id);
-
                             if let Some(instance) = Instance::resolve(self.tcx,
                                                                       param_env,
                                                                       callee_def_id,
@@ -105,6 +108,8 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                         }
                     }
             }
+        } else {
+            return;
         }
 
         let mut local_change;
@@ -113,7 +118,9 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
         loop {
             local_change = false;
             while let Some(callsite) = callsites.pop_front() {
+                debug!("checking whether to inline callsite {:?}", callsite);
                 if !self.tcx.is_mir_available(callsite.callee) {
+                    debug!("checking whether to inline callsite {:?} - MIR unavailable", callsite);
                     continue;
                 }
 
@@ -121,7 +128,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                                                                            callsite.location.span,
                                                                            callsite.callee) {
                     Ok(ref callee_mir) if self.should_inline(callsite, callee_mir) => {
-                        callee_mir.subst(self.tcx, callsite.substs)
+                        subst_and_normalize(callee_mir, self.tcx, &callsite.substs, param_env)
                     }
                     Ok(_) => continue,
 
@@ -133,10 +140,12 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                 };
 
                 let start = caller_mir.basic_blocks().len();
-
+                debug!("attempting to inline callsite {:?} - mir={:?}", callsite, callee_mir);
                 if !self.inline_call(callsite, caller_mir, callee_mir) {
+                    debug!("attempting to inline callsite {:?} - failure", callsite);
                     continue;
                 }
+                debug!("attempting to inline callsite {:?} - success", callsite);
 
                 // Add callsites from inlined function
                 for (bb, bb_data) in caller_mir.basic_blocks().iter_enumerated().skip(start) {
@@ -180,16 +189,19 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
                      callee_mir: &Mir<'tcx>)
                      -> bool
     {
+        debug!("should_inline({:?})", callsite);
         let tcx = self.tcx;
 
         // Don't inline closures that have captures
         // FIXME: Handle closures better
         if callee_mir.upvar_decls.len() > 0 {
+            debug!("    upvar decls present - not inlining");
             return false;
         }
 
         // Cannot inline generators which haven't been transformed yet
         if callee_mir.yield_ty.is_some() {
+            debug!("    yield ty present - not inlining");
             return false;
         }
 
@@ -201,7 +213,10 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
             // there are cases that prevent inlining that we
             // need to check for first.
             attr::InlineAttr::Always => true,
-            attr::InlineAttr::Never => return false,
+            attr::InlineAttr::Never => {
+                debug!("#[inline(never)] present - not inlining");
+                return false
+            }
             attr::InlineAttr::Hint => true,
             attr::InlineAttr::None => false,
         };
@@ -211,6 +226,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
         // reference unexported symbols
         if callsite.callee.is_local() {
             if callsite.substs.types().count() == 0 && !hinted {
+                debug!("    callee is an exported function - not inlining");
                 return false;
             }
         }
@@ -232,11 +248,11 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
         if callee_mir.basic_blocks().len() <= 3 {
             threshold += threshold / 4;
         }
+        debug!("    final inline threshold = {}", threshold);
 
         // FIXME: Give a bonus to functions with only a single caller
 
-        let def_id = tcx.hir.local_def_id(self.source.item_id());
-        let param_env = tcx.param_env(def_id);
+        let param_env = tcx.param_env(self.source.def_id);
 
         let mut first_block = true;
         let mut cost = 0;
@@ -327,12 +343,17 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
             }
         }
 
-        debug!("Inline cost for {:?} is {}", callsite.callee, cost);
-
         if let attr::InlineAttr::Always = hint {
+            debug!("INLINING {:?} because inline(always) [cost={}]", callsite, cost);
             true
         } else {
-            cost <= threshold
+            if cost <= threshold {
+                debug!("INLINING {:?} [cost={} <= threshold={}]", callsite, cost, threshold);
+                true
+            } else {
+                debug!("NOT inlining {:?} [cost={} > threshold={}]", callsite, cost, threshold);
+                false
+            }
         }
     }
 
@@ -529,36 +550,75 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> {
         Operand::Consume(cast_tmp)
     }
 
-    fn make_call_args(&self, args: Vec<Operand<'tcx>>,
-                      callsite: &CallSite<'tcx>, caller_mir: &mut Mir<'tcx>) -> Vec<Operand<'tcx>> {
+    fn make_call_args(
+        &self,
+        args: Vec<Operand<'tcx>>,
+        callsite: &CallSite<'tcx>,
+        caller_mir: &mut Mir<'tcx>,
+    ) -> Vec<Operand<'tcx>> {
         let tcx = self.tcx;
+
+        // A closure is passed its self-type and a tuple like `(arg1, arg2, ...)`,
+        // hence mappings to tuple fields are needed.
+        if tcx.is_closure(callsite.callee) {
+            let mut args = args.into_iter();
+            let self_ = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir);
+            let tuple = self.create_temp_if_necessary(args.next().unwrap(), callsite, caller_mir);
+            assert!(args.next().is_none());
+
+            let tuple_tys = if let ty::TyTuple(s, _) = tuple.ty(caller_mir, tcx).to_ty(tcx).sty {
+                s
+            } else {
+                bug!("Closure arguments are not passed as a tuple");
+            };
+
+            let mut res = Vec::with_capacity(1 + tuple_tys.len());
+            res.push(Operand::Consume(self_));
+            res.extend(tuple_tys.iter().enumerate().map(|(i, ty)| {
+                Operand::Consume(tuple.clone().field(Field::new(i), ty))
+            }));
+            res
+        } else {
+            args.into_iter()
+                .map(|a| Operand::Consume(self.create_temp_if_necessary(a, callsite, caller_mir)))
+                .collect()
+        }
+    }
+
+    /// If `arg` is already a temporary, returns it. Otherwise, introduces a fresh
+    /// temporary `T` and an instruction `T = arg`, and returns `T`.
+    fn create_temp_if_necessary(
+        &self,
+        arg: Operand<'tcx>,
+        callsite: &CallSite<'tcx>,
+        caller_mir: &mut Mir<'tcx>,
+    ) -> Lvalue<'tcx> {
         // FIXME: Analysis of the usage of the arguments to avoid
         // unnecessary temporaries.
-        args.into_iter().map(|a| {
-            if let Operand::Consume(Lvalue::Local(local)) = a {
-                if caller_mir.local_kind(local) == LocalKind::Temp {
-                    // Reuse the operand if it's a temporary already
-                    return a;
-                }
+
+        if let Operand::Consume(Lvalue::Local(local)) = arg {
+            if caller_mir.local_kind(local) == LocalKind::Temp {
+                // Reuse the operand if it's a temporary already
+                return Lvalue::Local(local);
             }
+        }
 
-            debug!("Creating temp for argument");
-            // Otherwise, create a temporary for the arg
-            let arg = Rvalue::Use(a);
+        debug!("Creating temp for argument {:?}", arg);
+        // Otherwise, create a temporary for the arg
+        let arg = Rvalue::Use(arg);
 
-            let ty = arg.ty(caller_mir, tcx);
+        let ty = arg.ty(caller_mir, self.tcx);
 
-            let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span);
-            let arg_tmp = caller_mir.local_decls.push(arg_tmp);
-            let arg_tmp = Lvalue::Local(arg_tmp);
+        let arg_tmp = LocalDecl::new_temp(ty, callsite.location.span);
+        let arg_tmp = caller_mir.local_decls.push(arg_tmp);
+        let arg_tmp = Lvalue::Local(arg_tmp);
 
-            let stmt = Statement {
-                source_info: callsite.location,
-                kind: StatementKind::Assign(arg_tmp.clone(), arg)
-            };
-            caller_mir[callsite.bb].statements.push(stmt);
-            Operand::Consume(arg_tmp)
-        }).collect()
+        let stmt = Statement {
+            source_info: callsite.location,
+            kind: StatementKind::Assign(arg_tmp.clone(), arg),
+        };
+        caller_mir[callsite.bb].statements.push(stmt);
+        arg_tmp
     }
 }
 
@@ -570,6 +630,30 @@ fn type_size_of<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
     })
 }
 
+fn subst_and_normalize<'a, 'tcx: 'a>(
+    mir: &Mir<'tcx>,
+    tcx: TyCtxt<'a, 'tcx, 'tcx>,
+    substs: &'tcx ty::subst::Substs<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+) -> Mir<'tcx> {
+    struct Folder<'a, 'tcx: 'a> {
+        tcx: TyCtxt<'a, 'tcx, 'tcx>,
+        param_env: ty::ParamEnv<'tcx>,
+        substs: &'tcx ty::subst::Substs<'tcx>,
+    }
+    impl<'a, 'tcx: 'a> ty::fold::TypeFolder<'tcx, 'tcx> for Folder<'a, 'tcx> {
+        fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> {
+            self.tcx
+        }
+
+        fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+            self.tcx.trans_apply_param_substs_env(&self.substs, self.param_env, &t)
+        }
+    }
+    let mut f = Folder { tcx, param_env, substs };
+    mir.fold_with(&mut f)
+}
+
 /**
  * Integrator.
  *
@@ -720,6 +804,12 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Integrator<'a, 'tcx> {
                 }
             }
             TerminatorKind::Unreachable => { }
+            TerminatorKind::FalseEdges { ref mut real_target, ref mut imaginary_targets } => {
+                *real_target = self.update_target(*real_target);
+                for target in imaginary_targets {
+                    *target = self.update_target(*target);
+                }
+            }
         }
     }