]> git.lizzy.rs Git - rust.git/commitdiff
Merge Call and DivergingCall diffs into CallKind
authorSimonas Kazlauskas <git@kazlauskas.me>
Mon, 21 Dec 2015 23:46:56 +0000 (01:46 +0200)
committerSimonas Kazlauskas <git@kazlauskas.me>
Wed, 6 Jan 2016 11:57:52 +0000 (13:57 +0200)
This merges two separate Call terminators and uses a separate CallKind sub-enum instead.

A little bit unrelatedly, copying into destination value for a certain kind of invoke, is also
implemented here. See the associated comment in code for various details that arise with this
implementation.

src/librustc/mir/repr.rs
src/librustc/mir/visit.rs
src/librustc_mir/build/expr/into.rs
src/librustc_mir/build/scope.rs
src/librustc_mir/transform/erase_regions.rs
src/librustc_trans/trans/mir/block.rs

index fa3573b383b685909ff89585f5cef3a74f3b2021..b97d5939cf3b05857fd39c9c660cd4f6aac4fdb4 100644 (file)
@@ -250,51 +250,58 @@ pub enum Terminator<'tcx> {
         func: Operand<'tcx>,
         /// Arguments the function is called with
         args: Vec<Operand<'tcx>>,
-        /// Location to write the return value into
-        destination: Lvalue<'tcx>,
-        targets: CallTargets,
+        /// The kind of call with associated information
+        kind: CallKind<'tcx>,
     },
-
-    /// Block ends with a call of a diverging function.
-    DivergingCall {
-        /// The function that’s being called
-        func: Operand<'tcx>,
-        /// Arguments the function is called with
-        args: Vec<Operand<'tcx>>,
-        /// Some, if there’s any cleanup to be done when function unwinds
-        cleanup: Option<BasicBlock>,
-    }
 }
 
-#[derive(Clone, Copy, RustcEncodable, RustcDecodable)]
-pub enum CallTargets {
-    /// The only target that should be entered when function returns normally.
-    Return(BasicBlock),
-    /// In addition to the normal-return block, function has associated cleanup that should be done
-    /// when function unwinds.
-    WithCleanup((BasicBlock, BasicBlock))
+#[derive(Clone, RustcEncodable, RustcDecodable)]
+pub enum CallKind<'tcx> {
+    /// Diverging function without associated cleanup
+    Diverging,
+    /// Diverging function with associated cleanup
+    DivergingCleanup(BasicBlock),
+    /// Converging function without associated cleanup
+    Converging {
+        /// Destination where the call result is written
+        destination: Lvalue<'tcx>,
+        /// Block to branch into on successful return
+        target: BasicBlock,
+    },
+    ConvergingCleanup {
+        /// Destination where the call result is written
+        destination: Lvalue<'tcx>,
+        /// First target is branched to on successful return.
+        /// Second block contains the cleanups to do on unwind.
+        targets: (BasicBlock, BasicBlock)
+    }
 }
 
-impl CallTargets {
-    pub fn new(ret: BasicBlock, cleanup: Option<BasicBlock>) -> CallTargets {
-        if let Some(c) = cleanup {
-            CallTargets::WithCleanup((ret, c))
-        } else {
-            CallTargets::Return(ret)
+impl<'tcx> CallKind<'tcx> {
+    pub fn successors(&self) -> &[BasicBlock] {
+        match *self {
+            CallKind::Diverging => &[],
+            CallKind::DivergingCleanup(ref b) |
+            CallKind::Converging { target: ref b, .. } => slice::ref_slice(b),
+            CallKind::ConvergingCleanup { ref targets, .. } => targets.as_slice(),
         }
     }
 
-    pub fn as_slice(&self) -> &[BasicBlock] {
+    pub fn successors_mut(&mut self) -> &mut [BasicBlock] {
         match *self {
-            CallTargets::Return(ref b) => slice::ref_slice(b),
-            CallTargets::WithCleanup(ref bs) => bs.as_slice()
+            CallKind::Diverging => &mut [],
+            CallKind::DivergingCleanup(ref mut b) |
+            CallKind::Converging { target: ref mut b, .. } => slice::mut_ref_slice(b),
+            CallKind::ConvergingCleanup { ref mut targets, .. } => targets.as_mut_slice(),
         }
     }
 
-    pub fn as_mut_slice(&mut self) -> &mut [BasicBlock] {
+    pub fn destination(&self) -> Option<Lvalue<'tcx>> {
         match *self {
-            CallTargets::Return(ref mut b) => slice::mut_ref_slice(b),
-            CallTargets::WithCleanup(ref mut bs) => bs.as_mut_slice()
+            CallKind::Converging { ref destination, .. } |
+            CallKind::ConvergingCleanup { ref destination, .. } => Some(destination.clone()),
+            CallKind::Diverging |
+            CallKind::DivergingCleanup(_) => None
         }
     }
 }
@@ -309,12 +316,7 @@ pub fn successors(&self) -> &[BasicBlock] {
             SwitchInt { targets: ref b, .. } => b,
             Resume => &[],
             Return => &[],
-            Call { targets: ref b, .. } => b.as_slice(),
-            DivergingCall { cleanup: ref b, .. } => if let Some(b) = b.as_ref() {
-                slice::ref_slice(b)
-            } else {
-                &mut []
-            },
+            Call { ref kind, .. } => kind.successors(),
         }
     }
 
@@ -327,12 +329,7 @@ pub fn successors_mut(&mut self) -> &mut [BasicBlock] {
             SwitchInt { targets: ref mut b, .. } => b,
             Resume => &mut [],
             Return => &mut [],
-            Call { targets: ref mut b, .. } => b.as_mut_slice(),
-            DivergingCall { cleanup: ref mut b, .. } => if let Some(b) = b.as_mut() {
-                slice::mut_ref_slice(b)
-            } else {
-                &mut []
-            },
+            Call { ref mut kind, .. } => kind.successors_mut(),
         }
     }
 }
@@ -399,13 +396,18 @@ pub fn fmt_head<W: Write>(&self, fmt: &mut W) -> fmt::Result {
             SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv),
             Return => write!(fmt, "return"),
             Resume => write!(fmt, "resume"),
-            Call { .. } => {
-                // the author didn’t bother rebasing this
-                unimplemented!()
-            },
-            DivergingCall { .. } => {
-                // the author didn’t bother rebasing this
-                unimplemented!()
+            Call { ref kind, ref func, ref args } => {
+                if let Some(destination) = kind.destination() {
+                    try!(write!(fmt, "{:?} = ", destination));
+                }
+                try!(write!(fmt, "{:?}(", func));
+                for (index, arg) in args.iter().enumerate() {
+                    if index > 0 {
+                        try!(write!(fmt, ", "));
+                    }
+                    try!(write!(fmt, "{:?}", arg));
+                }
+                write!(fmt, ")")
             }
         }
     }
@@ -417,8 +419,6 @@ pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
             Return | Resume => vec![],
             Goto { .. } => vec!["".into_cow()],
             If { .. } => vec!["true".into_cow(), "false".into_cow()],
-            Call { .. } => vec!["return".into_cow(), "unwind".into_cow()],
-            DivergingCall { .. } => vec!["unwind".into_cow()],
             Switch { ref adt_def, .. } => {
                 adt_def.variants
                        .iter()
@@ -435,6 +435,16 @@ pub fn fmt_successor_labels(&self) -> Vec<Cow<'static, str>> {
                       .chain(iter::once(String::from("otherwise").into_cow()))
                       .collect()
             }
+            Call { ref kind, .. } => match *kind {
+                CallKind::Diverging =>
+                    vec![],
+                CallKind::DivergingCleanup(..) =>
+                    vec!["unwind".into_cow()],
+                CallKind::Converging { .. } =>
+                    vec!["return".into_cow()],
+                CallKind::ConvergingCleanup { .. } =>
+                    vec!["return".into_cow(), "unwind".into_cow()],
+            },
         }
     }
 }
index 52bb9aa3d5c5a8d43d1e0d6fbd577968869dd14b..c05e4c83cd4f0fa42959887eaea7a921afa99077 100644 (file)
@@ -136,23 +136,15 @@ fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>)
             Terminator::Return => {
             }
 
-            Terminator::Call { ref func, ref args, ref destination, ref targets } => {
-                self.visit_lvalue(destination, LvalueContext::Store);
-                self.visit_operand(func);
-                for arg in args {
-                    self.visit_operand(arg);
-                }
-                for &target in targets.as_slice() {
-                    self.visit_branch(block, target);
+            Terminator::Call { ref func, ref args, ref kind } => {
+                if let Some(ref destination) = kind.destination() {
+                    self.visit_lvalue(destination, LvalueContext::Store);
                 }
-            }
-
-            Terminator::DivergingCall { ref func, ref args, ref cleanup } => {
                 self.visit_operand(func);
                 for arg in args {
                     self.visit_operand(arg);
                 }
-                for &target in cleanup.as_ref() {
+                for &target in kind.successors() {
                     self.visit_branch(block, target);
                 }
             }
@@ -432,26 +424,15 @@ fn super_terminator(&mut self,
             Terminator::Return => {
             }
 
-            Terminator::Call { ref mut func,
-                               ref mut args,
-                               ref mut destination,
-                               ref mut targets } => {
-                self.visit_lvalue(destination, LvalueContext::Store);
-                self.visit_operand(func);
-                for arg in args {
-                    self.visit_operand(arg);
-                }
-                for &target in targets.as_slice() {
-                    self.visit_branch(block, target);
+            Terminator::Call { ref mut func, ref mut args, ref mut kind } => {
+                if let Some(ref mut destination) = kind.destination() {
+                    self.visit_lvalue(destination, LvalueContext::Store);
                 }
-            }
-
-            Terminator::DivergingCall { ref mut func, ref mut args, ref mut cleanup } => {
                 self.visit_operand(func);
                 for arg in args {
                     self.visit_operand(arg);
                 }
-                for &target in cleanup.as_ref() {
+                for &target in kind.successors() {
                     self.visit_branch(block, target);
                 }
             }
index e23b5517cadf3a663d11de875043a54c1a15137c..44d1d52a06a3466a6dec9a0596f92899af8a9c05 100644 (file)
@@ -224,17 +224,22 @@ pub fn into_expr(&mut self,
 
                 let success = this.cfg.start_new_block();
                 let cleanup = this.diverge_cleanup();
-                let term = if diverges {
-                    Terminator::DivergingCall { func: fun, args: args, cleanup: cleanup }
-                } else {
-                    Terminator::Call {
-                        func: fun,
-                        args: args,
-                        destination: destination.clone(),
-                        targets: CallTargets::new(success, cleanup)
+                this.cfg.terminate(block, Terminator::Call {
+                    func: fun,
+                    args: args,
+                    kind: match (cleanup, diverges) {
+                        (None, true) => CallKind::Diverging,
+                        (Some(c), true) => CallKind::DivergingCleanup(c),
+                        (None, false) => CallKind::Converging {
+                            destination: destination.clone(),
+                            target: success
+                        },
+                        (Some(c), false) => CallKind::ConvergingCleanup {
+                            destination: destination.clone(),
+                            targets: (success, c)
+                        }
                     }
-                };
-                this.cfg.terminate(block, term);
+                });
                 success.unit()
             }
 
index 080b979c1ecc23c1781a82bd5b95796b1e0dd143..8167ffcff1ee91009a4cf79f23f6d2f830365359 100644 (file)
@@ -302,7 +302,6 @@ pub fn panic_bound_check(&mut self,
                              index: Operand<'tcx>,
                              len: Operand<'tcx>,
                              span: Span) {
-        let cleanup = self.diverge_cleanup();
         let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
         let str_ty = self.hir.tcx().mk_static_str();
         let tup_ty = self.hir.tcx().mk_tup(vec![str_ty, self.hir.tcx().types.u32]);
@@ -316,16 +315,19 @@ pub fn panic_bound_check(&mut self,
         // FIXME: ReStatic might be wrong here?
         self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
                              Rvalue::Ref(*ref_region, BorrowKind::Unique, tuple));
-        self.cfg.terminate(block, Terminator::DivergingCall {
+        let cleanup = self.diverge_cleanup();
+        self.cfg.terminate(block, Terminator::Call {
             func: func,
             args: vec![Operand::Consume(tuple_ref), index, len],
-            cleanup: cleanup,
+            kind: match cleanup {
+                None => CallKind::Diverging,
+                Some(c) => CallKind::DivergingCleanup(c)
+            }
         });
     }
 
     /// Create diverge cleanup and branch to it from `block`.
     pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
-        let cleanup = self.diverge_cleanup();
         let func = self.lang_function(lang_items::PanicFnLangItem);
 
         let str_ty = self.hir.tcx().mk_static_str();
@@ -348,11 +350,14 @@ pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
         // FIXME: ReStatic might be wrong here?
         self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
                              Rvalue::Ref(*ref_region, BorrowKind::Unique, tuple));
-
-        self.cfg.terminate(block, Terminator::DivergingCall {
+        let cleanup = self.diverge_cleanup();
+        self.cfg.terminate(block, Terminator::Call {
             func: func,
             args: vec![Operand::Consume(tuple_ref)],
-            cleanup: cleanup,
+            kind: match cleanup {
+                None => CallKind::Diverging,
+                Some(c) => CallKind::DivergingCleanup(c)
+            }
         });
     }
 
index 01d873abc6f92b77718f4189df7a3a7d2afdc216..20a14cf415404feab75ba62affa757bd34727e40 100644 (file)
@@ -93,14 +93,10 @@ fn erase_regions_terminator(&mut self,
                 self.erase_regions_lvalue(discr);
                 *switch_ty = self.tcx.erase_regions(switch_ty);
             },
-            Terminator::Call { ref mut destination, ref mut func, ref mut args, .. } => {
-                self.erase_regions_lvalue(destination);
-                self.erase_regions_operand(func);
-                for arg in &mut *args {
-                    self.erase_regions_operand(arg);
+            Terminator::Call { ref mut func, ref mut args, ref mut kind } => {
+                if let Some(ref mut destination) = kind.destination() {
+                    self.erase_regions_lvalue(destination);
                 }
-            }
-            Terminator::DivergingCall { ref mut func, ref mut args, .. } => {
                 self.erase_regions_operand(func);
                 for arg in &mut *args {
                     self.erase_regions_operand(arg);
index 55117a6db39578e99c05a0cf8f199dfe5c3f759e..969b9cd19d1a15598d5828cb9f3f06ef0488d3c3 100644 (file)
@@ -94,116 +94,137 @@ pub fn trans_block(&mut self, bb: mir::BasicBlock) {
                 base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None);
             }
 
-            mir::Terminator::Call { ref func, ref args, ref destination, ref targets } => {
-                // The location we'll write the result of the call into.
-                let call_dest = self.trans_lvalue(bcx, destination);
-                let ret_ty = call_dest.ty.to_ty(bcx.tcx());
-                // Create the callee. This will always be a fn
-                // ptr and hence a kind of scalar.
+            mir::Terminator::Call { ref func, ref args, ref kind } => {
+                // Create the callee. This will always be a fn ptr and hence a kind of scalar.
                 let callee = self.trans_operand(bcx, func);
+                let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
+                let debugloc = DebugLoc::None;
+                // The arguments we'll be passing. Plus one to account for outptr, if used.
+                let mut llargs = Vec::with_capacity(args.len() + 1);
 
-                // Does the fn use an outptr? If so, we have an extra first argument.
-                let return_outptr = type_of::return_uses_outptr(bcx.ccx(), ret_ty);
-                // The arguments we'll be passing.
-                let mut llargs = if return_outptr {
-                    let mut vec = Vec::with_capacity(args.len() + 1);
-                    vec.push(call_dest.llval);
-                    vec
+                // Prepare the return value destination
+                let (ret_dest_ty, must_copy_dest) = if let Some(ref d) = kind.destination() {
+                    let dest = self.trans_lvalue(bcx, d);
+                    let ret_ty = dest.ty.to_ty(bcx.tcx());
+                    if type_of::return_uses_outptr(bcx.ccx(), ret_ty) {
+                        llargs.push(dest.llval);
+                        (Some((dest, ret_ty)), false)
+                    } else {
+                        (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty))
+                    }
                 } else {
-                    Vec::with_capacity(args.len())
+                    (None, false)
                 };
 
                 // Process the rest of the args.
                 for arg in args {
-                    let arg_op = self.trans_operand(bcx, arg);
-                    match arg_op.val {
+                    match self.trans_operand(bcx, arg).val {
                         Ref(llval) | Immediate(llval) => llargs.push(llval),
-                        FatPtr(base, extra) => {
-                            // The two words in a fat ptr are passed separately
-                            llargs.push(base);
-                            llargs.push(extra);
+                        FatPtr(b, e) => {
+                            llargs.push(b);
+                            llargs.push(e);
                         }
                     }
                 }
 
-                let debugloc = DebugLoc::None;
-                let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
-                match (*targets, base::avoid_invoke(bcx)) {
-                    (mir::CallTargets::WithCleanup((ret, cleanup)), false) => {
+                // Many different ways to call a function handled here
+                match (base::avoid_invoke(bcx), kind) {
+                    // The two cases below are the only ones to use LLVM’s `invoke`.
+                    (false, &mir::CallKind::DivergingCleanup(cleanup)) => {
                         let cleanup = self.bcx(cleanup);
                         let landingpad = self.make_landing_pad(cleanup);
                         build::Invoke(bcx,
                                       callee.immediate(),
                                       &llargs[..],
-                                      self.llblock(ret),
+                                      self.unreachable_block().llbb,
                                       landingpad.llbb,
                                       Some(attrs),
                                       debugloc);
-                        if !return_outptr && !common::type_is_zero_size(bcx.ccx(), ret_ty) {
-                            // FIXME: What do we do here?
-                            unimplemented!()
+                    },
+                    (false, &mir::CallKind::ConvergingCleanup { ref targets, .. }) => {
+                        let cleanup = self.bcx(targets.1);
+                        let landingpad = self.make_landing_pad(cleanup);
+                        let (target, postinvoke) = if must_copy_dest {
+                            (bcx.fcx.new_block(false, "", None), Some(self.bcx(targets.0)))
+                        } else {
+                            (self.bcx(targets.0), None)
+                        };
+                        let invokeret = build::Invoke(bcx,
+                                                      callee.immediate(),
+                                                      &llargs[..],
+                                                      target.llbb,
+                                                      landingpad.llbb,
+                                                      Some(attrs),
+                                                      debugloc);
+                        if let Some(postinvoketarget) = postinvoke {
+                            // We translate the copy into a temoprary block. The temporary block is
+                            // necessary because the current block has already been terminated (by
+                            // `invoke`) and we cannot really translate into the target block
+                            // because:
+                            //  * The target block may have more than a single precedesor;
+                            //  * Some LLVM insns cannot have a preceeding store insn (phi,
+                            //    cleanuppad), and adding/prepending the store now may render
+                            //    those other instructions invalid.
+                            //
+                            // NB: This approach still may break some LLVM code. For example if the
+                            // target block starts with a `phi` (which may only match on immediate
+                            // precedesors), it cannot know about this temporary block thus
+                            // resulting in an invalid code:
+                            //
+                            // this:
+                            //     …
+                            //     %0 = …
+                            //     %1 = invoke to label %temp …
+                            // temp:
+                            //     store ty %1, ty* %dest
+                            //     br label %actualtargetblock
+                            // actualtargetblock:            ; preds: %temp, …
+                            //     phi … [%this, …], [%0, …] ; ERROR: phi requires to match only on
+                            //                               ; immediate precedesors
+                            let (ret_dest, ret_ty) = ret_dest_ty
+                                .expect("return destination and type not set");
+                            base::store_ty(target, invokeret, ret_dest.llval, ret_ty);
+                            build::Br(target, postinvoketarget.llbb, debugloc);
                         }
                     },
-                    (t, _) => {
-                        let ret = match t {
-                            mir::CallTargets::Return(ret) => ret,
-                            mir::CallTargets::WithCleanup((ret, _)) => {
+                    // Everything else uses the regular `Call`, but we have to be careful to
+                    // generate landing pads for later, even if we do not use it.
+                    // FIXME: maybe just change Resume to not panic in that case?
+                    (_, k@&mir::CallKind::DivergingCleanup(_)) |
+                    (_, k@&mir::CallKind::Diverging) => {
+                        if let mir::CallKind::DivergingCleanup(_) = *k {
+                            // make a landing pad regardless, so it sets the personality slot.
+                            let block = self.unreachable_block();
+                            self.make_landing_pad(block);
+                        }
+                        build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
+                        build::Unreachable(bcx);
+                    }
+                    (_, k@&mir::CallKind::ConvergingCleanup { .. }) |
+                    (_, k@&mir::CallKind::Converging { .. }) => {
+                        let ret = match *k {
+                            mir::CallKind::Converging { target, .. } => target,
+                            mir::CallKind::ConvergingCleanup { targets, .. } => {
                                 // make a landing pad regardless (so it sets the personality slot.
                                 let block = self.unreachable_block();
                                 self.make_landing_pad(block);
-                                ret
-                            }
+                                targets.0
+                            },
+                            _ => unreachable!()
                         };
                         let llret = build::Call(bcx,
                                                 callee.immediate(),
                                                 &llargs[..],
                                                 Some(attrs),
                                                 debugloc);
-                        if !return_outptr && !common::type_is_zero_size(bcx.ccx(), ret_ty) {
-                            base::store_ty(bcx, llret, call_dest.llval, ret_ty);
+                        if must_copy_dest {
+                            let (ret_dest, ret_ty) = ret_dest_ty
+                                .expect("return destination and type not set");
+                            base::store_ty(bcx, llret, ret_dest.llval, ret_ty);
                         }
                         build::Br(bcx, self.llblock(ret), debugloc)
                     }
                 }
-            },
-
-            mir::Terminator::DivergingCall { ref func, ref args, ref cleanup } => {
-                let callee = self.trans_operand(bcx, func);
-                let mut llargs = Vec::with_capacity(args.len());
-                for arg in args {
-                    match self.trans_operand(bcx, arg).val {
-                        Ref(llval) | Immediate(llval) => llargs.push(llval),
-                        FatPtr(b, e) => {
-                            llargs.push(b);
-                            llargs.push(e);
-                        }
-                    }
-                }
-                let debugloc = DebugLoc::None;
-                let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty);
-                match (*cleanup, base::avoid_invoke(bcx)) {
-                    (Some(cleanup), false) => {
-                        let cleanup = self.bcx(cleanup);
-                        let landingpad = self.make_landing_pad(cleanup);
-                        let unreachable = self.unreachable_block();
-                        build::Invoke(bcx,
-                                      callee.immediate(),
-                                      &llargs[..],
-                                      unreachable.llbb,
-                                      landingpad.llbb,
-                                      Some(attrs),
-                                      debugloc);
-                    }
-                    (t, _) => {
-                        if t.is_some() {
-                            // make a landing pad regardless, so it sets the personality slot.
-                            let block = self.unreachable_block();
-                            self.make_landing_pad(block);
-                        }
-                        build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc);
-                        build::Unreachable(bcx);
-                    }
-                }
             }
         }
     }