From 893a66d7a11150615c385a190d0140f0b8549409 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Mon, 14 Dec 2015 23:27:58 +0200 Subject: [PATCH 01/13] Split Call into Call and DivergingCall DivergingCall is different enough from the regular converging Call to warrant the split. This also inlines CallData struct and creates a new CallTargets enum in order to have a way to differentiate between calls that do not have an associated cleanup block. Note, that this patch still does not produce DivergingCall terminator anywhere. Look for that in the next patches. --- src/librustc/mir/repr.rs | 95 ++++++++++++++------- src/librustc/mir/visit.rs | 39 +++++++-- src/librustc_mir/build/expr/into.rs | 11 ++- src/librustc_mir/transform/erase_regions.rs | 21 ++--- src/librustc_trans/trans/mir/block.rs | 4 + 5 files changed, 113 insertions(+), 57 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 75a588d424ebb..42c9b7b0f39ab 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -256,13 +256,51 @@ pub enum Terminator<'tcx> { /// `END_BLOCK`. Return, - /// block ends with a call; it should have two successors. The - /// first successor indicates normal return. The second indicates - /// unwinding. + /// Block ends with a call of a converging function Call { - data: CallData<'tcx>, - targets: (BasicBlock, BasicBlock), + /// The function that’s being called + func: Operand<'tcx>, + /// Arguments the function is called with + args: Vec>, + /// Location to write the return value into + destination: Lvalue<'tcx>, + targets: CallTargets, }, + + /// 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>, + /// Some, if there’s any cleanup to be done when function unwinds + cleanup: Option, + } +} + +#[derive(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)) +} + +impl CallTargets { + pub fn as_slice(&self) -> &[BasicBlock] { + match *self { + CallTargets::Return(ref b) => slice::ref_slice(b), + CallTargets::WithCleanup(ref bs) => bs.as_slice() + } + } + + pub fn as_mut_slice(&mut self) -> &mut [BasicBlock] { + match *self { + CallTargets::Return(ref mut b) => slice::mut_ref_slice(b), + CallTargets::WithCleanup(ref mut bs) => bs.as_mut_slice() + } + } } impl<'tcx> Terminator<'tcx> { @@ -271,12 +309,17 @@ impl<'tcx> Terminator<'tcx> { match *self { Goto { target: ref b } => slice::ref_slice(b), Panic { target: ref b } => slice::ref_slice(b), - If { cond: _, targets: ref b } => b.as_slice(), + If { targets: ref b, .. } => b.as_slice(), Switch { targets: ref b, .. } => b, SwitchInt { targets: ref b, .. } => b, Diverge => &[], Return => &[], - Call { data: _, targets: ref b } => b.as_slice(), + Call { targets: ref b, .. } => b.as_slice(), + DivergingCall { cleanup: ref b, .. } => if let Some(b) = b.as_ref() { + slice::ref_slice(b) + } else { + &mut [] + }, } } @@ -285,28 +328,21 @@ impl<'tcx> Terminator<'tcx> { match *self { Goto { target: ref mut b } => slice::mut_ref_slice(b), Panic { target: ref mut b } => slice::mut_ref_slice(b), - If { cond: _, targets: ref mut b } => b.as_mut_slice(), + If { targets: ref mut b, .. } => b.as_mut_slice(), Switch { targets: ref mut b, .. } => b, SwitchInt { targets: ref mut b, .. } => b, Diverge => &mut [], Return => &mut [], - Call { data: _, targets: ref mut b } => b.as_mut_slice(), + 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 [] + }, } } } -#[derive(Debug, RustcEncodable, RustcDecodable)] -pub struct CallData<'tcx> { - /// where the return value is written to - pub destination: Lvalue<'tcx>, - - /// the fn being called - pub func: Operand<'tcx>, - - /// the arguments - pub args: Vec>, -} - impl<'tcx> BasicBlockData<'tcx> { pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> { BasicBlockData { @@ -357,15 +393,13 @@ impl<'tcx> Terminator<'tcx> { SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), Diverge => write!(fmt, "diverge"), Return => write!(fmt, "return"), - Call { data: ref c, .. } => { - try!(write!(fmt, "{:?} = {:?}(", c.destination, c.func)); - for (index, arg) in c.args.iter().enumerate() { - if index > 0 { - try!(write!(fmt, ", ")); - } - try!(write!(fmt, "{:?}", arg)); - } - write!(fmt, ")") + Call { .. } => { + // the author didn’t bother rebasing this + unimplemented!() + }, + DivergingCall { .. } => { + // the author didn’t bother rebasing this + unimplemented!() } } } @@ -378,6 +412,7 @@ impl<'tcx> Terminator<'tcx> { Goto { .. } | Panic { .. } => 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() diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 00d21d3c16e1f..c3a335fdfaac3 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -137,16 +137,26 @@ pub trait Visitor<'tcx> { Terminator::Return => { } - Terminator::Call { ref data, ref targets } => { - self.visit_lvalue(&data.destination, LvalueContext::Store); - self.visit_operand(&data.func); - for arg in &data.args { + 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::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() { + self.visit_branch(block, target); + } + } } } @@ -424,16 +434,29 @@ pub trait MutVisitor<'tcx> { Terminator::Return => { } - Terminator::Call { ref mut data, ref mut targets } => { - self.visit_lvalue(&mut data.destination, LvalueContext::Store); - self.visit_operand(&mut data.func); - for arg in &mut data.args { + 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::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() { + self.visit_branch(block, target); + } + } } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 802c55ce7647a..4a262196d3670 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -218,14 +218,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { .collect(); let success = this.cfg.start_new_block(); let panic = this.diverge_cleanup(); + let targets = CallTargets::WithCleanup((success, panic)); this.cfg.terminate(block, Terminator::Call { - data: CallData { - destination: destination.clone(), - func: fun, - args: args, - }, - targets: (success, panic), + func: fun, + args: args, + destination: destination.clone(), + targets: targets }); success.unit() } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 1eb3bfd7e0229..1e7bb305cdbd2 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -90,28 +90,23 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { Terminator::Switch { ref mut discr, .. } => { self.erase_regions_lvalue(discr); } - Terminator::SwitchInt { - ref mut discr, - ref mut switch_ty, - .. - } => { + Terminator::SwitchInt { ref mut discr, ref mut switch_ty, .. } => { self.erase_regions_lvalue(discr); *switch_ty = self.tcx.erase_regions(switch_ty); }, - Terminator::Call { - data: CallData { - ref mut destination, - ref mut func, - ref mut args - }, - .. - } => { + 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::DivergingCall { ref mut func, ref mut args, .. } => { + self.erase_regions_operand(func); + for arg in &mut *args { + self.erase_regions_operand(arg); + } + } } } diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 265969c52b39d..00508ed21e58e 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -164,6 +164,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } build::Br(bcx, self.llblock(targets.0), DebugLoc::None) + }, + + mir::Terminator::DivergingCall { .. } => { + unimplemented!() } } } From 6f18b559dfbfde2677fc2b1450fdcbc5c2a5566a Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 15 Dec 2015 12:20:49 +0200 Subject: [PATCH 02/13] Generate DivergingCall terminator This simplifies CFG greatly for some cases :) --- src/librustc_mir/build/expr/into.rs | 29 +++++++++++++++++++---------- src/librustc_mir/hair/cx/expr.rs | 10 +++++++++- src/librustc_mir/hair/mod.rs | 3 ++- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 4a262196d3670..a173018b74fea 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -15,6 +15,7 @@ use build::expr::category::{Category, RvalueFunc}; use build::scope::LoopScope; use hair::*; use rustc::middle::region::CodeExtent; +use rustc::middle::ty; use rustc::mir::repr::*; use syntax::codemap::Span; @@ -210,22 +211,30 @@ impl<'a,'tcx> Builder<'a,'tcx> { this.exit_scope(expr_span, extent, block, END_BLOCK); this.cfg.start_new_block().unit() } - ExprKind::Call { fun, args } => { + ExprKind::Call { ty, fun, args } => { + let diverges = match ty.sty { + ty::TyBareFn(_, ref f) => f.sig.0.output.diverges(), + _ => false + }; let fun = unpack!(block = this.as_operand(block, fun)); let args: Vec<_> = args.into_iter() .map(|arg| unpack!(block = this.as_operand(block, arg))) .collect(); + let success = this.cfg.start_new_block(); - let panic = this.diverge_cleanup(); - let targets = CallTargets::WithCleanup((success, panic)); - this.cfg.terminate(block, - Terminator::Call { - func: fun, - args: args, - destination: destination.clone(), - targets: targets - }); + let cleanup = this.diverge_cleanup(); + let term = if diverges { + Terminator::DivergingCall { func: fun, args: args, cleanup: Some(cleanup) } + } else { + Terminator::Call { + func: fun, + args: args, + destination: destination.clone(), + targets: CallTargets::WithCleanup((success, cleanup)) + } + }; + this.cfg.terminate(block, term); success.unit() } diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index aa6257345fe44..012bd9691be5f 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -41,6 +41,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { .map(|e| e.to_ref()) .collect(); ExprKind::Call { + ty: expr.ty, fun: expr.to_ref(), args: args, } @@ -58,11 +59,17 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { argrefs.extend(args.iter().map(|a| a.to_ref())); ExprKind::Call { + ty: method.ty, fun: method.to_ref(), args: argrefs, } } else { - ExprKind::Call { fun: fun.to_ref(), args: args.to_ref() } + ExprKind::Call { + ty: &cx.tcx.node_id_to_type(fun.id), + fun: fun.to_ref(), + args: args.to_ref(), + } + } } @@ -802,6 +809,7 @@ fn overloaded_operator<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, // now create the call itself let fun = method_callee(cx, expr, method_call); ExprKind::Call { + ty: fun.ty, fun: fun.to_ref(), args: argrefs, } diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 6363ddf1e1477..fb81cc7e6d97a 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -19,7 +19,7 @@ use rustc::middle::const_eval::ConstVal; use rustc::middle::def_id::DefId; use rustc::middle::region::CodeExtent; use rustc::middle::subst::Substs; -use rustc::middle::ty::{AdtDef, ClosureSubsts, Region, Ty}; +use rustc::middle::ty::{self, AdtDef, ClosureSubsts, Region, Ty}; use rustc_front::hir; use syntax::ast; use syntax::codemap::Span; @@ -124,6 +124,7 @@ pub enum ExprKind<'tcx> { value: ExprRef<'tcx>, }, Call { + ty: ty::Ty<'tcx>, fun: ExprRef<'tcx>, args: Vec>, }, From ecf4d0e3adbe77bd0e332645848dbd2505176a21 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 15 Dec 2015 20:46:39 +0200 Subject: [PATCH 03/13] Add Resume Terminator which corresponds to resume Diverge should eventually go away --- src/librustc/mir/repr.rs | 17 ++++- src/librustc/mir/visit.rs | 2 + src/librustc_mir/build/expr/into.rs | 4 +- src/librustc_mir/build/scope.rs | 81 +++++++++++---------- src/librustc_mir/transform/erase_regions.rs | 1 + src/librustc_trans/trans/mir/block.rs | 10 +-- 6 files changed, 70 insertions(+), 45 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 42c9b7b0f39ab..8cdb804c599ea 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -251,6 +251,10 @@ pub enum Terminator<'tcx> { /// well-known diverging block actually diverges. Diverge, + /// Indicates that the landing pad is finished and unwinding should + /// continue. Emitted by build::scope::diverge_cleanup. + Resume, + /// Indicates a normal return. The ReturnPointer lvalue should /// have been filled in by now. This should only occur in the /// `END_BLOCK`. @@ -288,6 +292,14 @@ pub enum CallTargets { } impl CallTargets { + pub fn new(ret: BasicBlock, cleanup: Option) -> CallTargets { + if let Some(c) = cleanup { + CallTargets::WithCleanup((ret, c)) + } else { + CallTargets::Return(ret) + } + } + pub fn as_slice(&self) -> &[BasicBlock] { match *self { CallTargets::Return(ref b) => slice::ref_slice(b), @@ -313,6 +325,7 @@ impl<'tcx> Terminator<'tcx> { Switch { targets: ref b, .. } => b, SwitchInt { targets: ref b, .. } => b, Diverge => &[], + Resume => &[], Return => &[], Call { targets: ref b, .. } => b.as_slice(), DivergingCall { cleanup: ref b, .. } => if let Some(b) = b.as_ref() { @@ -332,6 +345,7 @@ impl<'tcx> Terminator<'tcx> { Switch { targets: ref mut b, .. } => b, SwitchInt { targets: ref mut b, .. } => b, Diverge => &mut [], + 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() { @@ -393,6 +407,7 @@ impl<'tcx> Terminator<'tcx> { SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), Diverge => write!(fmt, "diverge"), Return => write!(fmt, "return"), + Resume => write!(fmt, "resume"), Call { .. } => { // the author didn’t bother rebasing this unimplemented!() @@ -408,7 +423,7 @@ impl<'tcx> Terminator<'tcx> { pub fn fmt_successor_labels(&self) -> Vec> { use self::Terminator::*; match *self { - Diverge | Return => vec![], + Diverge | Return | Resume => vec![], Goto { .. } | Panic { .. } => vec!["".into_cow()], If { .. } => vec!["true".into_cow(), "false".into_cow()], Call { .. } => vec!["return".into_cow(), "unwind".into_cow()], diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index c3a335fdfaac3..41fd505682378 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -134,6 +134,7 @@ pub trait Visitor<'tcx> { } Terminator::Diverge | + Terminator::Resume | Terminator::Return => { } @@ -431,6 +432,7 @@ pub trait MutVisitor<'tcx> { } Terminator::Diverge | + Terminator::Resume | Terminator::Return => { } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index a173018b74fea..e23b5517cadf3 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -225,13 +225,13 @@ impl<'a,'tcx> Builder<'a,'tcx> { let success = this.cfg.start_new_block(); let cleanup = this.diverge_cleanup(); let term = if diverges { - Terminator::DivergingCall { func: fun, args: args, cleanup: Some(cleanup) } + Terminator::DivergingCall { func: fun, args: args, cleanup: cleanup } } else { Terminator::Call { func: fun, args: args, destination: destination.clone(), - targets: CallTargets::WithCleanup((success, cleanup)) + targets: CallTargets::new(success, cleanup) } }; this.cfg.terminate(block, term); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index b5e029ed94bd1..e7f3794c043b0 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,7 +86,7 @@ should go to. */ -use build::{BlockAnd, BlockAndExtension, Builder, CFG}; +use build::{BlockAnd, BlockAndExtension, Builder}; use rustc::middle::region::CodeExtent; use rustc::middle::ty::Ty; use rustc::mir::repr::*; @@ -227,16 +227,44 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.cfg.terminate(block, Terminator::Goto { target: target }); } - /// Creates a path that performs all required cleanup for - /// unwinding. This path terminates in DIVERGE. Returns the start - /// of the path. See module comment for more details. - pub fn diverge_cleanup(&mut self) -> BasicBlock { - diverge_cleanup_helper(&mut self.cfg, &mut self.scopes) + /// Creates a path that performs all required cleanup for unwinding. + /// + /// This path terminates in Resume. Returns the start of the path. + /// See module comment for more details. None indicates there’s no + /// cleanup to do at this point. + pub fn diverge_cleanup(&mut self) -> Option { + if self.scopes.is_empty() { + return None; + } + + let mut terminator = Terminator::Resume; + // Given an array of scopes, we generate these from the outermost scope to the innermost + // one. Thus for array [S0, S1, S2] with corresponding cleanup blocks [B0, B1, B2], we will + // generate B0 <- B1 <- B2 in left-to-right order. The outermost scope (B0) will always + // terminate with a Resume terminator. + for scope in self.scopes.iter_mut().filter(|s| !s.drops.is_empty()) { + if let Some(b) = scope.cached_block { + terminator = Terminator::Goto { target: b }; + continue; + } else { + let new_block = self.cfg.start_new_block(); + self.cfg.terminate(new_block, terminator); + terminator = Terminator::Goto { target: new_block }; + for &(kind, span, ref lvalue) in scope.drops.iter().rev() { + self.cfg.push_drop(new_block, span, kind, lvalue); + } + scope.cached_block = Some(new_block); + } + } + // Return the innermost cached block, most likely the one we just generated. + // Note that if there are no cleanups in scope we return None. + self.scopes.iter().rev().flat_map(|b| b.cached_block).next() } /// Create diverge cleanup and branch to it from `block`. pub fn panic(&mut self, block: BasicBlock) { - let cleanup = self.diverge_cleanup(); + // FIXME: panic terminator should also have conditional cleanup? + let cleanup = self.diverge_cleanup().unwrap_or(DIVERGE_BLOCK); self.cfg.terminate(block, Terminator::Panic { target: cleanup }); } @@ -249,14 +277,18 @@ impl<'a,'tcx> Builder<'a,'tcx> { lvalue: &Lvalue<'tcx>, lvalue_ty: Ty<'tcx>) { if self.hir.needs_drop(lvalue_ty) { - match self.scopes.iter_mut().rev().find(|s| s.extent == extent) { - Some(scope) => { + for scope in self.scopes.iter_mut().rev() { + // We must invalidate all the cached_blocks leading up to the scope we’re looking + // for, because otherwise some/most of the blocks in the chain might become + // incorrect (i.e. they still are pointing at old cached_block). + scope.cached_block = None; + if scope.extent == extent { scope.drops.push((kind, span, lvalue.clone())); - scope.cached_block = None; + return; } - None => self.hir.span_bug(span, &format!("extent {:?} not in scope to drop {:?}", - extent, lvalue)), } + self.hir.span_bug(span, + &format!("extent {:?} not in scope to drop {:?}", extent, lvalue)); } } @@ -268,28 +300,3 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.scopes.first().map(|scope| scope.extent).unwrap() } } - -fn diverge_cleanup_helper<'tcx>(cfg: &mut CFG<'tcx>, scopes: &mut [Scope<'tcx>]) -> BasicBlock { - let len = scopes.len(); - - if len == 0 { - return DIVERGE_BLOCK; - } - - let (remaining, scope) = scopes.split_at_mut(len - 1); - let scope = &mut scope[0]; - - if let Some(b) = scope.cached_block { - return b; - } - - let block = cfg.start_new_block(); - for &(kind, span, ref lvalue) in &scope.drops { - cfg.push_drop(block, span, kind, lvalue); - } - scope.cached_block = Some(block); - - let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining); - cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block }); - block -} diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 1e7bb305cdbd2..3f5e18b7e7968 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -80,6 +80,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { match *terminator { Terminator::Goto { .. } | Terminator::Diverge | + Terminator::Resume | Terminator::Return | Terminator::Panic { .. } => { /* nothing to do */ diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 00508ed21e58e..ddd3ed34e8f93 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -87,16 +87,16 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Terminator::Diverge => { + build::Unreachable(bcx); + } + + mir::Terminator::Resume => { if let Some(llpersonalityslot) = self.llpersonalityslot { let lp = build::Load(bcx, llpersonalityslot); // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality); build::Resume(bcx, lp); } else { - // This fn never encountered anything fallible, so - // a Diverge cannot actually happen. Note that we - // do a total hack to ensure that we visit the - // DIVERGE block last. - build::Unreachable(bcx); + panic!("resume terminator without personality slot") } } From 5b34690842474019c0a1c6cd4d0a7b75108e9d2d Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Fri, 18 Dec 2015 23:19:29 +0200 Subject: [PATCH 04/13] Remove the Panic block terminator --- src/librustc/mir/repr.rs | 11 +-- src/librustc/mir/visit.rs | 6 +- src/librustc_mir/build/expr/as_lvalue.rs | 4 +- src/librustc_mir/build/matches/mod.rs | 2 +- src/librustc_mir/build/scope.rs | 104 ++++++++++++++++++-- src/librustc_mir/hair/cx/mod.rs | 5 + src/librustc_mir/transform/erase_regions.rs | 3 +- src/librustc_trans/trans/mir/block.rs | 4 - 8 files changed, 107 insertions(+), 32 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 8cdb804c599ea..8a43b8d0aaf16 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -204,12 +204,6 @@ pub enum Terminator<'tcx> { target: BasicBlock, }, - /// block should initiate unwinding; should be one successor - /// that does cleanup and branches to DIVERGE_BLOCK - Panic { - target: BasicBlock, - }, - /// jump to branch 0 if this lvalue evaluates to true If { cond: Operand<'tcx>, @@ -320,7 +314,6 @@ impl<'tcx> Terminator<'tcx> { use self::Terminator::*; match *self { Goto { target: ref b } => slice::ref_slice(b), - Panic { target: ref b } => slice::ref_slice(b), If { targets: ref b, .. } => b.as_slice(), Switch { targets: ref b, .. } => b, SwitchInt { targets: ref b, .. } => b, @@ -340,7 +333,6 @@ impl<'tcx> Terminator<'tcx> { use self::Terminator::*; match *self { Goto { target: ref mut b } => slice::mut_ref_slice(b), - Panic { target: ref mut b } => slice::mut_ref_slice(b), If { targets: ref mut b, .. } => b.as_mut_slice(), Switch { targets: ref mut b, .. } => b, SwitchInt { targets: ref mut b, .. } => b, @@ -401,7 +393,6 @@ impl<'tcx> Terminator<'tcx> { use self::Terminator::*; match *self { Goto { .. } => write!(fmt, "goto"), - Panic { .. } => write!(fmt, "panic"), If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv), Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv), SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), @@ -424,7 +415,7 @@ impl<'tcx> Terminator<'tcx> { use self::Terminator::*; match *self { Diverge | Return | Resume => vec![], - Goto { .. } | Panic { .. } => vec!["".into_cow()], + 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()], diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 41fd505682378..4d29a71c6e748 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -107,8 +107,7 @@ pub trait Visitor<'tcx> { fn super_terminator(&mut self, block: BasicBlock, terminator: &Terminator<'tcx>) { match *terminator { - Terminator::Goto { target } | - Terminator::Panic { target } => { + Terminator::Goto { target } => { self.visit_branch(block, target); } @@ -405,8 +404,7 @@ pub trait MutVisitor<'tcx> { block: BasicBlock, terminator: &mut Terminator<'tcx>) { match *terminator { - Terminator::Goto { target } | - Terminator::Panic { target } => { + Terminator::Goto { target } => { self.visit_branch(block, target); } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 1c96addcea0d9..6baf3ad2fe191 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -63,7 +63,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { this.cfg.push_assign(block, expr_span, // lt = idx < len <, Rvalue::BinaryOp(BinOp::Lt, idx.clone(), - Operand::Consume(len))); + Operand::Consume(len.clone()))); let (success, failure) = (this.cfg.start_new_block(), this.cfg.start_new_block()); this.cfg.terminate(block, @@ -71,7 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { cond: Operand::Consume(lt), targets: (success, failure), }); - this.panic(failure); + this.panic_bound_check(failure, idx.clone(), Operand::Consume(len), expr_span); success.and(slice.index(idx)) } ExprKind::SelfRef => { diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 10c6851feb3e1..c2c87fcbd20da 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -89,7 +89,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { // not entirely precise if !otherwise.is_empty() { let join_block = self.join_otherwise_blocks(otherwise); - self.panic(join_block); + self.panic(join_block, "something about matches algorithm not being precise", span); } // all the arm blocks will rejoin here diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index e7f3794c043b0..758c5781efc42 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -88,9 +88,12 @@ should go to. use build::{BlockAnd, BlockAndExtension, Builder}; use rustc::middle::region::CodeExtent; -use rustc::middle::ty::Ty; +use rustc::middle::lang_items; +use rustc::middle::subst::Substs; +use rustc::middle::ty::{Ty, Region}; use rustc::mir::repr::*; -use syntax::codemap::Span; +use syntax::codemap::{Span, DUMMY_SP}; +use syntax::parse::token::intern_and_get_ident; pub struct Scope<'tcx> { extent: CodeExtent, @@ -261,13 +264,6 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.scopes.iter().rev().flat_map(|b| b.cached_block).next() } - /// Create diverge cleanup and branch to it from `block`. - pub fn panic(&mut self, block: BasicBlock) { - // FIXME: panic terminator should also have conditional cleanup? - let cleanup = self.diverge_cleanup().unwrap_or(DIVERGE_BLOCK); - self.cfg.terminate(block, Terminator::Panic { target: cleanup }); - } - /// Indicates that `lvalue` should be dropped on exit from /// `extent`. pub fn schedule_drop(&mut self, @@ -299,4 +295,94 @@ impl<'a,'tcx> Builder<'a,'tcx> { pub fn extent_of_outermost_scope(&self) -> CodeExtent { self.scopes.first().map(|scope| scope.extent).unwrap() } + + pub fn panic_bound_check(&mut self, + block: BasicBlock, + 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]); + // FIXME: ReStatic might be wrong here? + let ref_region = self.hir.tcx().mk_region(Region::ReStatic); + let ref_ty = self.hir.tcx().mk_imm_ref(ref_region, tup_ty.clone()); + let (file_arg, line_arg) = self.span_to_fileline_args(span); + let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); + self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg); + Rvalue::Aggregate(AggregateKind::Tuple, vec![file_arg, line_arg])); + // 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 { + func: func, + args: vec![Operand::Consume(tuple_ref), index, len], + cleanup: cleanup, + }); + } + + /// 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(); + let tup_ty = self.hir.tcx().mk_tup(vec![str_ty, str_ty, self.hir.tcx().types.u32]); + // FIXME: ReStatic might be wrong here? + let ref_region = self.hir.tcx().mk_region(Region::ReStatic); + let ref_ty = self.hir.tcx().mk_imm_ref(ref_region, tup_ty.clone()); + let message_arg = Operand::Constant(Constant { + span: DUMMY_SP, + ty: str_ty, + literal: self.hir.str_literal(intern_and_get_ident(msg)) + }); + let (file_arg, line_arg) = self.span_to_fileline_args(span); + let tuple = self.temp(tup_ty); + let tuple_ref = self.temp(ref_ty); + self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg); + Rvalue::Aggregate(AggregateKind::Tuple, + vec![message_arg, file_arg, line_arg]) + ); + // 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 { + func: func, + args: vec![Operand::Consume(tuple_ref)], + cleanup: cleanup, + }); + } + + fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Operand<'tcx> { + let funcdid = match self.hir.tcx().lang_items.require(lang_item) { + Ok(d) => d, + Err(m) => { + self.hir.tcx().sess.fatal(&*m) + } + }; + Operand::Constant(Constant { + span: DUMMY_SP, + ty: self.hir.tcx().lookup_item_type(funcdid).ty, + literal: Literal::Item { + def_id: funcdid, + kind: ItemKind::Function, + substs: self.hir.tcx().mk_substs(Substs::empty()) + } + }) + } + + fn span_to_fileline_args(&mut self, span: Span) -> (Operand<'tcx>, Operand<'tcx>) { + let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo); + (Operand::Constant(Constant { + span: DUMMY_SP, + ty: self.hir.tcx().mk_static_str(), + literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name)) + }), Operand::Constant(Constant { + span: DUMMY_SP, + ty: self.hir.tcx().types.u32, + literal: self.hir.usize_literal(span_lines.line) + })) + } } diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 07d32240d4330..f9dd40defdf70 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -22,6 +22,7 @@ use rustc::middle::const_eval::{self, ConstVal}; use rustc::middle::infer::InferCtxt; use rustc::middle::ty::{self, Ty}; use syntax::codemap::Span; +use syntax::parse::token; use rustc_front::hir; #[derive(Copy, Clone)] @@ -61,6 +62,10 @@ impl<'a,'tcx:'a> Cx<'a, 'tcx> { self.tcx.types.bool } + pub fn str_literal(&mut self, value: token::InternedString) -> Literal<'tcx> { + Literal::Value { value: ConstVal::Str(value) } + } + pub fn true_literal(&mut self) -> Literal<'tcx> { Literal::Value { value: ConstVal::Bool(true) } } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 3f5e18b7e7968..90bb73dbe28d3 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -81,8 +81,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { Terminator::Goto { .. } | Terminator::Diverge | Terminator::Resume | - Terminator::Return | - Terminator::Panic { .. } => { + Terminator::Return => { /* nothing to do */ } Terminator::If { ref mut cond, .. } => { diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index ddd3ed34e8f93..4c2bec14e5e25 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -40,10 +40,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { build::Br(bcx, self.llblock(target), DebugLoc::None) } - mir::Terminator::Panic { .. } => { - unimplemented!() - } - mir::Terminator::If { ref cond, targets: (true_bb, false_bb) } => { let cond = self.trans_operand(bcx, cond); let lltrue = self.llblock(true_bb); From 4e86dcdb7295e88d3ccc28b508ba69a24fefa371 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 19 Dec 2015 00:44:32 +0200 Subject: [PATCH 05/13] Remove diverge terminator Unreachable terminator can be contained all within the trans. --- src/librustc/mir/repr.rs | 32 ++++++++++----------- src/librustc/mir/visit.rs | 6 ++-- src/librustc_mir/build/cfg.rs | 12 ++------ src/librustc_mir/build/mod.rs | 1 - src/librustc_mir/graphviz.rs | 4 +-- src/librustc_mir/transform/erase_regions.rs | 3 +- src/librustc_mir/transform/simplify_cfg.rs | 26 +++++++---------- src/librustc_mir/transform/util.rs | 2 +- src/librustc_trans/trans/mir/block.rs | 8 ++---- src/librustc_trans/trans/mir/mod.rs | 13 +++------ 10 files changed, 40 insertions(+), 67 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 8a43b8d0aaf16..2878b3d5e4e9a 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -51,9 +51,6 @@ pub const START_BLOCK: BasicBlock = BasicBlock(0); /// where execution ends, on normal return pub const END_BLOCK: BasicBlock = BasicBlock(1); -/// where execution ends, on panic -pub const DIVERGE_BLOCK: BasicBlock = BasicBlock(2); - impl<'tcx> Mir<'tcx> { pub fn all_basic_blocks(&self) -> Vec { (0..self.basic_blocks.len()) @@ -194,7 +191,7 @@ impl Debug for BasicBlock { #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { pub statements: Vec>, - pub terminator: Terminator<'tcx>, + pub terminator: Option>, } #[derive(RustcEncodable, RustcDecodable)] @@ -237,14 +234,6 @@ pub enum Terminator<'tcx> { targets: Vec, }, - /// Indicates that the last statement in the block panics, aborts, - /// etc. No successors. This terminator appears on exactly one - /// basic block which we create in advance. However, during - /// construction, we use this value as a sentinel for "terminator - /// not yet assigned", and assert at the end that only the - /// well-known diverging block actually diverges. - Diverge, - /// Indicates that the landing pad is finished and unwinding should /// continue. Emitted by build::scope::diverge_cleanup. Resume, @@ -317,7 +306,6 @@ impl<'tcx> Terminator<'tcx> { If { targets: ref b, .. } => b.as_slice(), Switch { targets: ref b, .. } => b, SwitchInt { targets: ref b, .. } => b, - Diverge => &[], Resume => &[], Return => &[], Call { targets: ref b, .. } => b.as_slice(), @@ -336,7 +324,6 @@ impl<'tcx> Terminator<'tcx> { If { targets: ref mut b, .. } => b.as_mut_slice(), Switch { targets: ref mut b, .. } => b, SwitchInt { targets: ref mut b, .. } => b, - Diverge => &mut [], Resume => &mut [], Return => &mut [], Call { targets: ref mut b, .. } => b.as_mut_slice(), @@ -350,12 +337,24 @@ impl<'tcx> Terminator<'tcx> { } impl<'tcx> BasicBlockData<'tcx> { - pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> { + pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { BasicBlockData { statements: vec![], terminator: terminator, } } + + /// Accessor for terminator. + /// + /// Terminator may not be None after construction of the basic block is complete. This accessor + /// provides a convenience way to reach the terminator. + pub fn terminator(&self) -> &Terminator<'tcx> { + self.terminator.as_ref().expect("invalid terminator state") + } + + pub fn terminator_mut(&mut self) -> &mut Terminator<'tcx> { + self.terminator.as_mut().expect("invalid terminator state") + } } impl<'tcx> Debug for Terminator<'tcx> { @@ -396,7 +395,6 @@ impl<'tcx> Terminator<'tcx> { If { cond: ref lv, .. } => write!(fmt, "if({:?})", lv), Switch { discr: ref lv, .. } => write!(fmt, "switch({:?})", lv), SwitchInt { discr: ref lv, .. } => write!(fmt, "switchInt({:?})", lv), - Diverge => write!(fmt, "diverge"), Return => write!(fmt, "return"), Resume => write!(fmt, "resume"), Call { .. } => { @@ -414,7 +412,7 @@ impl<'tcx> Terminator<'tcx> { pub fn fmt_successor_labels(&self) -> Vec> { use self::Terminator::*; match *self { - Diverge | Return | Resume => vec![], + Return | Resume => vec![], Goto { .. } => vec!["".into_cow()], If { .. } => vec!["true".into_cow(), "false".into_cow()], Call { .. } => vec!["return".into_cow(), "unwind".into_cow()], diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 4d29a71c6e748..52bb9aa3d5c5a 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -84,7 +84,7 @@ pub trait Visitor<'tcx> { for statement in &data.statements { self.visit_statement(block, statement); } - self.visit_terminator(block, &data.terminator); + data.terminator.as_ref().map(|r| self.visit_terminator(block, r)); } fn super_statement(&mut self, block: BasicBlock, statement: &Statement<'tcx>) { @@ -132,7 +132,6 @@ pub trait Visitor<'tcx> { } } - Terminator::Diverge | Terminator::Resume | Terminator::Return => { } @@ -374,7 +373,7 @@ pub trait MutVisitor<'tcx> { for statement in &mut data.statements { self.visit_statement(block, statement); } - self.visit_terminator(block, &mut data.terminator); + data.terminator.as_mut().map(|r| self.visit_terminator(block, r)); } fn super_statement(&mut self, @@ -429,7 +428,6 @@ pub trait MutVisitor<'tcx> { } } - Terminator::Diverge | Terminator::Resume | Terminator::Return => { } diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index d28724c30aaa0..2e70e6bb5ae04 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -28,7 +28,7 @@ impl<'tcx> CFG<'tcx> { pub fn start_new_block(&mut self) -> BasicBlock { let node_index = self.basic_blocks.len(); - self.basic_blocks.push(BasicBlockData::new(Terminator::Diverge)); + self.basic_blocks.push(BasicBlockData::new(None)); BasicBlock::new(node_index) } @@ -67,15 +67,9 @@ impl<'tcx> CFG<'tcx> { pub fn terminate(&mut self, block: BasicBlock, terminator: Terminator<'tcx>) { - // Check whether this block has already been terminated. For - // this, we rely on the fact that the initial state is to have - // a Diverge terminator and an empty list of targets (which - // is not a valid state). - debug_assert!(match self.block_data(block).terminator { Terminator::Diverge => true, - _ => false }, + debug_assert!(self.block_data(block).terminator.is_none(), "terminate: block {:?} already has a terminator set", block); - - self.block_data_mut(block).terminator = terminator; + self.block_data_mut(block).terminator = Some(terminator); } } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 8347a03cda6f7..e6e5b8380b241 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -107,7 +107,6 @@ pub fn construct<'a,'tcx>(mut hir: Cx<'a,'tcx>, assert_eq!(builder.cfg.start_new_block(), START_BLOCK); assert_eq!(builder.cfg.start_new_block(), END_BLOCK); - assert_eq!(builder.cfg.start_new_block(), DIVERGE_BLOCK); let mut block = START_BLOCK; let arg_decls = unpack!(block = builder.args_and_body(block, diff --git a/src/librustc_mir/graphviz.rs b/src/librustc_mir/graphviz.rs index d1d3e80e3402f..1b8fe6505583c 100644 --- a/src/librustc_mir/graphviz.rs +++ b/src/librustc_mir/graphviz.rs @@ -62,7 +62,7 @@ fn write_node(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<( // Terminator head at the bottom, not including the list of successor blocks. Those will be // displayed as labels on the edges between blocks. let mut terminator_head = String::new(); - data.terminator.fmt_head(&mut terminator_head).unwrap(); + data.terminator().fmt_head(&mut terminator_head).unwrap(); try!(write!(w, r#"{}"#, dot::escape_html(&terminator_head))); // Close the table, node label, and the node itself. @@ -71,7 +71,7 @@ fn write_node(block: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<( /// Write graphviz DOT edges with labels between the given basic block and all of its successors. fn write_edges(source: BasicBlock, mir: &Mir, w: &mut W) -> io::Result<()> { - let terminator = &mir.basic_block_data(source).terminator; + let terminator = &mir.basic_block_data(source).terminator(); let labels = terminator.fmt_successor_labels(); for (&target, label) in terminator.successors().iter().zip(labels) { diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 90bb73dbe28d3..01d873abc6f92 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -59,7 +59,7 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { self.erase_regions_statement(statement); } - self.erase_regions_terminator(&mut basic_block.terminator); + self.erase_regions_terminator(basic_block.terminator_mut()); } fn erase_regions_statement(&mut self, @@ -79,7 +79,6 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { terminator: &mut Terminator<'tcx>) { match *terminator { Terminator::Goto { .. } | - Terminator::Diverge | Terminator::Resume | Terminator::Return => { /* nothing to do */ diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index d0c0afc80a657..7a5a00a8d560b 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -10,7 +10,6 @@ use rustc::middle::const_eval::ConstVal; use rustc::mir::repr::*; -use std::mem; use transform::util; use transform::MirPass; @@ -27,11 +26,10 @@ impl SimplifyCfg { // These blocks are always required. seen[START_BLOCK.index()] = true; seen[END_BLOCK.index()] = true; - seen[DIVERGE_BLOCK.index()] = true; let mut worklist = vec![START_BLOCK]; while let Some(bb) = worklist.pop() { - for succ in mir.basic_block_data(bb).terminator.successors() { + for succ in mir.basic_block_data(bb).terminator().successors() { if !seen[succ.index()] { seen[succ.index()] = true; worklist.push(*succ); @@ -51,7 +49,7 @@ impl SimplifyCfg { while mir.basic_block_data(target).statements.is_empty() { match mir.basic_block_data(target).terminator { - Terminator::Goto { target: next } => { + Some(Terminator::Goto { target: next }) => { if seen.contains(&next) { return None; } @@ -67,9 +65,9 @@ impl SimplifyCfg { let mut changed = false; for bb in mir.all_basic_blocks() { - // Temporarily swap out the terminator we're modifying to keep borrowck happy - let mut terminator = Terminator::Diverge; - mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator); + // Temporarily take ownership of the terminator we're modifying to keep borrowck happy + let mut terminator = mir.basic_block_data_mut(bb).terminator.take() + .expect("invalid terminator state"); for target in terminator.successors_mut() { let new_target = match final_target(mir, *target) { @@ -80,10 +78,8 @@ impl SimplifyCfg { changed |= *target != new_target; *target = new_target; } - - mir.basic_block_data_mut(bb).terminator = terminator; + mir.basic_block_data_mut(bb).terminator = Some(terminator); } - changed } @@ -91,11 +87,10 @@ impl SimplifyCfg { let mut changed = false; for bb in mir.all_basic_blocks() { - // Temporarily swap out the terminator we're modifying to keep borrowck happy - let mut terminator = Terminator::Diverge; - mem::swap(&mut terminator, &mut mir.basic_block_data_mut(bb).terminator); + let basic_block = mir.basic_block_data_mut(bb); + let mut terminator = basic_block.terminator_mut(); - mir.basic_block_data_mut(bb).terminator = match terminator { + *terminator = match *terminator { Terminator::If { ref targets, .. } if targets.0 == targets.1 => { changed = true; Terminator::Goto { target: targets.0 } @@ -115,7 +110,7 @@ impl SimplifyCfg { Terminator::SwitchInt { ref targets, .. } if targets.len() == 1 => { Terminator::Goto { target: targets[0] } } - _ => terminator + _ => continue } } @@ -131,7 +126,6 @@ impl<'tcx> MirPass<'tcx> for SimplifyCfg { changed |= self.remove_goto_chains(mir); self.remove_dead_blocks(mir); } - // FIXME: Should probably be moved into some kind of pass manager mir.basic_blocks.shrink_to_fit(); } diff --git a/src/librustc_mir/transform/util.rs b/src/librustc_mir/transform/util.rs index 9510269454485..7e44beb18a2e9 100644 --- a/src/librustc_mir/transform/util.rs +++ b/src/librustc_mir/transform/util.rs @@ -15,7 +15,7 @@ use rustc::mir::repr::*; /// in a single pass pub fn update_basic_block_ids(mir: &mut Mir, replacements: &[BasicBlock]) { for bb in mir.all_basic_blocks() { - for target in mir.basic_block_data_mut(bb).terminator.successors_mut() { + for target in mir.basic_block_data_mut(bb).terminator_mut().successors_mut() { *target = replacements[target.index()]; } } diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 4c2bec14e5e25..c2645d9882b42 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -33,9 +33,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { bcx = self.trans_statement(bcx, statement); } - debug!("trans_block: terminator: {:?}", data.terminator); + debug!("trans_block: terminator: {:?}", data.terminator()); - match data.terminator { + match *data.terminator() { mir::Terminator::Goto { target } => { build::Br(bcx, self.llblock(target), DebugLoc::None) } @@ -82,10 +82,6 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Diverge => { - build::Unreachable(bcx); - } - mir::Terminator::Resume => { if let Some(llpersonalityslot) = self.llpersonalityslot { let lp = build::Load(bcx, llpersonalityslot); diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 0ed76ebeb4362..5403b53596c48 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -125,16 +125,11 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // Translate the body of each block for &bb in &mir_blocks { - if bb != mir::DIVERGE_BLOCK { - mircx.trans_block(bb); - } + // NB that we do not handle the Resume terminator specially, because a block containing + // that terminator will have a higher block number than a function call which should take + // care of filling in that information. + mircx.trans_block(bb); } - - // Total hack: translate DIVERGE_BLOCK last. This is so that any - // panics which the fn may do can initialize the - // `llpersonalityslot` cell. We don't do this up front because the - // LLVM type of it is (frankly) annoying to compute. - mircx.trans_block(mir::DIVERGE_BLOCK); } /// Produce, for each argument, a `ValueRef` pointing at the From a1e13983f7d3890c3fb984179925ed8ad2dee31c Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 19 Dec 2015 16:47:52 +0200 Subject: [PATCH 06/13] Have a cached unreachable block inside MIR state It is useful for various cases where direct unreachable cannot be translated and a separate block is necessary. --- src/librustc/mir/repr.rs | 2 +- src/librustc_trans/trans/mir/block.rs | 21 +++++++++++++++------ src/librustc_trans/trans/mir/mod.rs | 8 ++++++++ 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 2878b3d5e4e9a..f59cdb74b625d 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -265,7 +265,7 @@ pub enum Terminator<'tcx> { } } -#[derive(RustcEncodable, RustcDecodable)] +#[derive(Clone, Copy, RustcEncodable, RustcDecodable)] pub enum CallTargets { /// The only target that should be entered when function returns normally. Return(BasicBlock), diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index c2645d9882b42..72d5709e02229 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -9,15 +9,15 @@ // except according to those terms. use llvm::BasicBlockRef; -use middle::infer; -use middle::ty; use rustc::mir::repr as mir; use trans::adt; use trans::base; use trans::build; +use trans::attributes; use trans::common::{self, Block}; use trans::debuginfo::DebugLoc; use trans::type_of; +use trans::type_::Type; use super::MirContext; use super::operand::OperandValue::{FatPtr, Immediate, Ref}; @@ -56,10 +56,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // The else branch of the Switch can't be hit, so branch to an unreachable // instruction so LLVM knows that - // FIXME it might be nice to have just one such block (created lazilly), we could - // store it in the "MIR trans" state. - let unreachable_blk = bcx.fcx.new_temp_block("enum-variant-unreachable"); - build::Unreachable(unreachable_blk); + let unreachable_blk = self.unreachable_block(); let switch = build::Switch(bcx, discr, unreachable_blk.llbb, targets.len()); assert_eq!(adt_def.variants.len(), targets.len()); @@ -164,6 +161,18 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } + fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> { + match self.unreachable_block { + Some(b) => b, + None => { + let bl = self.fcx.new_block(false, "unreachable", None); + build::Unreachable(bl); + self.unreachable_block = Some(bl); + bl + } + } + } + fn bcx(&self, bb: mir::BasicBlock) -> Block<'bcx, 'tcx> { self.blocks[bb.index()] } diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 5403b53596c48..8eb06731ea3ae 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -28,6 +28,9 @@ use self::operand::OperandRef; pub struct MirContext<'bcx, 'tcx:'bcx> { mir: &'bcx mir::Mir<'tcx>, + /// Function context + fcx: &'bcx common::FunctionContext<'bcx, 'tcx>, + /// When unwinding is initiated, we have to store this personality /// value somewhere so that we can load it and re-use it in the /// resume instruction. The personality is (afaik) some kind of @@ -40,6 +43,9 @@ pub struct MirContext<'bcx, 'tcx:'bcx> { /// A `Block` for each MIR `BasicBlock` blocks: Vec>, + /// Cached unreachable block + unreachable_block: Option>, + /// An LLVM alloca for each MIR `VarDecl` vars: Vec>, @@ -116,8 +122,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { let mut mircx = MirContext { mir: mir, + fcx: fcx, llpersonalityslot: None, blocks: block_bcxs, + unreachable_block: None, vars: vars, temps: temps, args: args, From 924bb1e5ebeab4189c3c48dabae4915825b097b0 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 19 Dec 2015 16:48:49 +0200 Subject: [PATCH 07/13] Refine call terminator translation * Implement landing pads; and * Implement DivergingCall translation; and * Modernise previous implementation of Call somewhat. --- src/librustc_trans/trans/mir/block.rs | 141 ++++++++++++++++++-------- 1 file changed, 96 insertions(+), 45 deletions(-) diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 72d5709e02229..d41c4dec2b28b 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -80,12 +80,12 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Terminator::Resume => { - if let Some(llpersonalityslot) = self.llpersonalityslot { - let lp = build::Load(bcx, llpersonalityslot); - // FIXME(lifetime) base::call_lifetime_end(bcx, self.personality); + if let Some(personalityslot) = self.llpersonalityslot { + let lp = build::Load(bcx, personalityslot); + base::call_lifetime_end(bcx, personalityslot); build::Resume(bcx, lp); } else { - panic!("resume terminator without personality slot") + panic!("resume terminator without personality slot set") } } @@ -94,33 +94,27 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None); } - mir::Terminator::Call { ref data, targets } => { + 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, &data.destination); - + 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. - let callee = self.trans_operand(bcx, &data.func); - let ret_ty = if let ty::TyBareFn(_, ref f) = callee.ty.sty { - let sig = bcx.tcx().erase_late_bound_regions(&f.sig); - let sig = infer::normalize_associated_type(bcx.tcx(), &sig); - sig.output + let callee = self.trans_operand(bcx, func); + + // 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 } else { - panic!("trans_block: expected TyBareFn as callee"); + Vec::with_capacity(args.len()) }; - // The arguments we'll be passing - let mut llargs = vec![]; - - // Does the fn use an outptr? If so, that's the first arg. - if let ty::FnConverging(ret_ty) = ret_ty { - if type_of::return_uses_outptr(bcx.ccx(), ret_ty) { - llargs.push(call_dest.llval); - } - } - // Process the rest of the args. - for arg in &data.args { + for arg in args { let arg_op = self.trans_operand(bcx, arg); match arg_op.val { Ref(llval) | Immediate(llval) => llargs.push(llval), @@ -132,35 +126,92 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - // FIXME: Handle panics - //let panic_bb = self.llblock(targets.1); - //self.make_landing_pad(panic_bb); - - // Do the actual call. - let (llret, b) = base::invoke(bcx, - callee.immediate(), - &llargs[..], - callee.ty, - DebugLoc::None); - bcx = b; - - // Copy the return value into the destination. - if let ty::FnConverging(ret_ty) = ret_ty { - if !type_of::return_uses_outptr(bcx.ccx(), ret_ty) && - !common::type_is_zero_size(bcx.ccx(), ret_ty) { - base::store_ty(bcx, llret, call_dest.llval, ret_ty); + let debugloc = DebugLoc::None; + let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty); + match *targets { + mir::CallTargets::Return(ret) => { + 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); + } + build::Br(bcx, self.llblock(ret), debugloc) + } + mir::CallTargets::WithCleanup((ret, cleanup)) => { + let landingpad = self.make_landing_pad(cleanup); + build::Invoke(bcx, + callee.immediate(), + &llargs[..], + self.llblock(ret), + landingpad.llbb, + Some(attrs), + debugloc); + if !return_outptr && !common::type_is_zero_size(bcx.ccx(), ret_ty) { + // FIXME: What do we do here? + unimplemented!() + } } } - - build::Br(bcx, self.llblock(targets.0), DebugLoc::None) }, - mir::Terminator::DivergingCall { .. } => { - unimplemented!() + 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 { + None => { + build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc); + build::Unreachable(bcx); + } + Some(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); + } + } } } } + fn make_landing_pad(&mut self, cleanup: mir::BasicBlock) -> Block<'bcx, 'tcx> { + let bcx = self.bcx(cleanup).fcx.new_block(true, "cleanup", None); + let ccx = bcx.ccx(); + let llpersonality = bcx.fcx.eh_personality(); + let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); + let llretval = build::LandingPad(bcx, llretty, llpersonality, 1); + build::SetCleanup(bcx, llretval); + match self.llpersonalityslot { + Some(slot) => build::Store(bcx, llretval, slot), + None => { + let personalityslot = base::alloca(bcx, llretty, "personalityslot"); + self.llpersonalityslot = Some(personalityslot); + base::call_lifetime_start(bcx, personalityslot); + build::Store(bcx, llretval, personalityslot) + } + }; + build::Br(bcx, self.llblock(cleanup), DebugLoc::None); + bcx + } + fn unreachable_block(&mut self) -> Block<'bcx, 'tcx> { match self.unreachable_block { Some(b) => b, From 20ec53a0d38588044f32e35074c81e60b8d3a6ad Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 19 Dec 2015 16:51:52 +0200 Subject: [PATCH 08/13] Fix ReturnPointer generation for void return types Fixes #30480 --- src/librustc_trans/trans/mir/lvalue.rs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index b167633909a4a..f7245879e2dce 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -18,6 +18,8 @@ use trans::build; use trans::common::{self, Block}; use trans::debuginfo::DebugLoc; use trans::machine; +use trans::type_of; +use llvm; use std::ptr; @@ -91,10 +93,23 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { const_ty) }, mir::Lvalue::ReturnPointer => { - let return_ty = bcx.monomorphize(&self.mir.return_ty); - let llval = fcx.get_ret_slot(bcx, return_ty, "return"); - LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty.unwrap())) - } + let fn_return_ty = bcx.monomorphize(&self.mir.return_ty); + let return_ty = fn_return_ty.unwrap(); + let llval = if !common::return_type_is_void(bcx.ccx(), return_ty) { + fcx.get_ret_slot(bcx, fn_return_ty, "") + } else { + // This is a void return; that is, there’s no place to store the value and + // there cannot really be one (or storing into it doesn’t make sense, anyway). + // Ergo, we return an undef ValueRef, so we do not have to special-case every + // place using lvalues, and could use it the same way you use a regular + // ReturnPointer LValue (i.e. store into it, load from it etc). + let llty = type_of::type_of(bcx.ccx(), return_ty).ptr_to(); + unsafe { + llvm::LLVMGetUndef(llty.to_ref()) + } + }; + LvalueRef::new_sized(llval, LvalueTy::from_ty(return_ty)) + }, mir::Lvalue::Projection(ref projection) => { let tr_base = self.trans_lvalue(bcx, &projection.base); let projected_ty = tr_base.ty.projection_ty(tcx, &projection.elem); From cef6aee369b4fe79c72602711f8b535ed6327bba Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 20 Dec 2015 15:30:09 +0200 Subject: [PATCH 09/13] =?UTF-8?q?Don=E2=80=99t=20generate=20landing-pads?= =?UTF-8?q?=20if=20-Z=20no-landing-pads?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/librustc/mir/repr.rs | 2 + src/librustc_mir/build/scope.rs | 1 + src/librustc_trans/trans/base.rs | 20 +++++---- src/librustc_trans/trans/mir/block.rs | 60 +++++++++++++++++---------- src/librustc_trans/trans/mir/mod.rs | 5 ++- 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index f59cdb74b625d..fa3573b383b68 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -192,6 +192,7 @@ impl Debug for BasicBlock { pub struct BasicBlockData<'tcx> { pub statements: Vec>, pub terminator: Option>, + pub is_cleanup: bool, } #[derive(RustcEncodable, RustcDecodable)] @@ -341,6 +342,7 @@ impl<'tcx> BasicBlockData<'tcx> { BasicBlockData { statements: vec![], terminator: terminator, + is_cleanup: false, } } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 758c5781efc42..080b979c1ecc2 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -251,6 +251,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { continue; } else { let new_block = self.cfg.start_new_block(); + self.cfg.block_data_mut(new_block).is_cleanup = true; self.cfg.terminate(new_block, terminator); terminator = Terminator::Goto { target: new_block }; for &(kind, span, ref lvalue) in scope.drops.iter().rev() { diff --git a/src/librustc_trans/trans/base.rs b/src/librustc_trans/trans/base.rs index 4197f80cb5ea3..a6e6d30422050 100644 --- a/src/librustc_trans/trans/base.rs +++ b/src/librustc_trans/trans/base.rs @@ -958,22 +958,28 @@ pub fn wants_msvc_seh(sess: &Session) -> bool { sess.target.target.options.is_like_msvc && sess.target.target.arch == "x86" } -pub fn need_invoke(bcx: Block) -> bool { +pub fn avoid_invoke(bcx: Block) -> bool { // FIXME(#25869) currently SEH-based unwinding is pretty buggy in LLVM and // is being overhauled as this is being written. Until that // time such that upstream LLVM's implementation is more solid // and we start binding it we need to skip invokes for any // target which wants SEH-based unwinding. if bcx.sess().no_landing_pads() || wants_msvc_seh(bcx.sess()) { - return false; + true + } else if bcx.is_lpad { + // Avoid using invoke if we are already inside a landing pad. + true + } else { + false } +} - // Avoid using invoke if we are already inside a landing pad. - if bcx.is_lpad { - return false; +pub fn need_invoke(bcx: Block) -> bool { + if avoid_invoke(bcx) { + false + } else { + bcx.fcx.needs_invoke() } - - bcx.fcx.needs_invoke() } pub fn load_if_immediate<'blk, 'tcx>(cx: Block<'blk, 'tcx>, v: ValueRef, t: Ty<'tcx>) -> ValueRef { diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index d41c4dec2b28b..55117a6db3957 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -128,19 +128,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let debugloc = DebugLoc::None; let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty); - match *targets { - mir::CallTargets::Return(ret) => { - 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); - } - build::Br(bcx, self.llblock(ret), debugloc) - } - mir::CallTargets::WithCleanup((ret, cleanup)) => { + match (*targets, base::avoid_invoke(bcx)) { + (mir::CallTargets::WithCleanup((ret, cleanup)), false) => { + let cleanup = self.bcx(cleanup); let landingpad = self.make_landing_pad(cleanup); build::Invoke(bcx, callee.immediate(), @@ -153,6 +143,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // FIXME: What do we do here? unimplemented!() } + }, + (t, _) => { + let ret = match t { + mir::CallTargets::Return(ret) => ret, + mir::CallTargets::WithCleanup((ret, _)) => { + // make a landing pad regardless (so it sets the personality slot. + let block = self.unreachable_block(); + self.make_landing_pad(block); + ret + } + }; + 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); + } + build::Br(bcx, self.llblock(ret), debugloc) } } }, @@ -171,12 +181,9 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } let debugloc = DebugLoc::None; let attrs = attributes::from_fn_type(bcx.ccx(), callee.ty); - match *cleanup { - None => { - build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc); - build::Unreachable(bcx); - } - Some(cleanup) => { + 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, @@ -187,13 +194,22 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { 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); + } } } } } - fn make_landing_pad(&mut self, cleanup: mir::BasicBlock) -> Block<'bcx, 'tcx> { - let bcx = self.bcx(cleanup).fcx.new_block(true, "cleanup", None); + fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> { + let bcx = cleanup.fcx.new_block(true, "cleanup", None); let ccx = bcx.ccx(); let llpersonality = bcx.fcx.eh_personality(); let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); @@ -208,7 +224,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { build::Store(bcx, llretval, personalityslot) } }; - build::Br(bcx, self.llblock(cleanup), DebugLoc::None); + build::Br(bcx, cleanup.llbb, DebugLoc::None); bcx } diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 8eb06731ea3ae..1aceb67dd1210 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -113,7 +113,10 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // Allocate a `Block` for every basic block let block_bcxs: Vec> = mir_blocks.iter() - .map(|&bb| fcx.new_block(false, &format!("{:?}", bb), None)) + .map(|&bb|{ + let is_cleanup = mir.basic_block_data(bb).is_cleanup; + fcx.new_block(is_cleanup, &format!("{:?}", bb), None) + }) .collect(); // Branch to the START block From 50107034c08413898392ddf08d13ce1b29b1efac Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Mon, 21 Dec 2015 18:58:18 +0200 Subject: [PATCH 10/13] Add tests --- src/test/run-fail/mir_indexing_oob_1.rs | 23 +++++++++++ src/test/run-fail/mir_indexing_oob_2.rs | 23 +++++++++++ src/test/run-fail/mir_indexing_oob_3.rs | 23 +++++++++++ .../mir_trans_calls_converging_drops.rs | 37 +++++++++++++++++ .../mir_trans_calls_converging_drops_2.rs | 41 +++++++++++++++++++ .../run-fail/mir_trans_calls_diverging.rs | 24 +++++++++++ .../mir_trans_calls_diverging_drops.rs | 34 +++++++++++++++ .../run-fail/mir_trans_no_landing_pads.rs | 36 ++++++++++++++++ .../mir_trans_no_landing_pads_diverging.rs | 36 ++++++++++++++++ .../run-pass/mir_trans_call_converging.rs | 28 +++++++++++++ src/test/run-pass/mir_void_return.rs | 24 +++++++++++ src/test/run-pass/mir_void_return_2.rs | 22 ++++++++++ 12 files changed, 351 insertions(+) create mode 100644 src/test/run-fail/mir_indexing_oob_1.rs create mode 100644 src/test/run-fail/mir_indexing_oob_2.rs create mode 100644 src/test/run-fail/mir_indexing_oob_3.rs create mode 100644 src/test/run-fail/mir_trans_calls_converging_drops.rs create mode 100644 src/test/run-fail/mir_trans_calls_converging_drops_2.rs create mode 100644 src/test/run-fail/mir_trans_calls_diverging.rs create mode 100644 src/test/run-fail/mir_trans_calls_diverging_drops.rs create mode 100644 src/test/run-fail/mir_trans_no_landing_pads.rs create mode 100644 src/test/run-fail/mir_trans_no_landing_pads_diverging.rs create mode 100644 src/test/run-pass/mir_trans_call_converging.rs create mode 100644 src/test/run-pass/mir_void_return.rs create mode 100644 src/test/run-pass/mir_void_return_2.rs diff --git a/src/test/run-fail/mir_indexing_oob_1.rs b/src/test/run-fail/mir_indexing_oob_1.rs new file mode 100644 index 0000000000000..e0d20a20577a8 --- /dev/null +++ b/src/test/run-fail/mir_indexing_oob_1.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:index out of bounds: the len is 5 but the index is 10 +#![feature(rustc_attrs)] + +const C: [u32; 5] = [0; 5]; + +#[rustc_mir] +fn test() -> u32 { + C[10] +} + +fn main() { + test(); +} diff --git a/src/test/run-fail/mir_indexing_oob_2.rs b/src/test/run-fail/mir_indexing_oob_2.rs new file mode 100644 index 0000000000000..6c65be5769f2d --- /dev/null +++ b/src/test/run-fail/mir_indexing_oob_2.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:index out of bounds: the len is 5 but the index is 10 +#![feature(rustc_attrs)] + +const C: &'static [u8; 5] = b"hello"; + +#[rustc_mir] +fn test() -> u8 { + C[10] +} + +fn main() { + test(); +} diff --git a/src/test/run-fail/mir_indexing_oob_3.rs b/src/test/run-fail/mir_indexing_oob_3.rs new file mode 100644 index 0000000000000..5f3fc9376b0d3 --- /dev/null +++ b/src/test/run-fail/mir_indexing_oob_3.rs @@ -0,0 +1,23 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:index out of bounds: the len is 5 but the index is 10 +#![feature(rustc_attrs)] + +const C: &'static [u8; 5] = b"hello"; + +#[rustc_mir] +fn mir() -> u8 { + C[10] +} + +fn main() { + mir(); +} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops.rs b/src/test/run-fail/mir_trans_calls_converging_drops.rs new file mode 100644 index 0000000000000..754f616cfd5a0 --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_converging_drops.rs @@ -0,0 +1,37 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:converging_fn called +// error-pattern:0 dropped +// error-pattern:exit + +use std::io::{self, Write}; + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + write!(io::stderr(), "{} dropped\n", self.0); + } +} + +fn converging_fn() { + write!(io::stderr(), "converging_fn called\n"); +} + +#[rustc_mir] +fn mir(d: Droppable) { + converging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs new file mode 100644 index 0000000000000..5e870be3bc6ec --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs @@ -0,0 +1,41 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:complex called +// error-pattern:dropped +// error-pattern:exit + +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + write!(io::stderr(), "dropped\n"); + } +} + +// return value of this function is copied into the return slot +fn complex() -> u64 { + write!(io::stderr(), "complex called\n"); + 42 +} + + +#[rustc_mir] +fn mir() -> u64 { + let x = Droppable; + return complex(); + drop(x); +} + +pub fn main() { + assert_eq!(mir(), 42); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_trans_calls_diverging.rs b/src/test/run-fail/mir_trans_calls_diverging.rs new file mode 100644 index 0000000000000..fcd8ab26a0a88 --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_diverging.rs @@ -0,0 +1,24 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:diverging_fn called + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +#[rustc_mir] +fn mir() { + diverging_fn(); +} + +fn main() { + mir(); +} diff --git a/src/test/run-fail/mir_trans_calls_diverging_drops.rs b/src/test/run-fail/mir_trans_calls_diverging_drops.rs new file mode 100644 index 0000000000000..ffa1ff0827775 --- /dev/null +++ b/src/test/run-fail/mir_trans_calls_diverging_drops.rs @@ -0,0 +1,34 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// error-pattern:diverging_fn called +// error-pattern:0 dropped +use std::io::{self, Write}; + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + write!(io::stderr(), "{} dropped", self.0); + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +#[rustc_mir] +fn mir(d: Droppable) { + diverging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); +} diff --git a/src/test/run-fail/mir_trans_no_landing_pads.rs b/src/test/run-fail/mir_trans_no_landing_pads.rs new file mode 100644 index 0000000000000..bc913fdab1c07 --- /dev/null +++ b/src/test/run-fail/mir_trans_no_landing_pads.rs @@ -0,0 +1,36 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// compile-flags: -Z no-landing-pads +// error-pattern:converging_fn called +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn converging_fn() { + panic!("converging_fn called") +} + +#[rustc_mir] +fn mir(d: Droppable) { + let x = Droppable; + converging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs new file mode 100644 index 0000000000000..d97eb8c89e3e0 --- /dev/null +++ b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs @@ -0,0 +1,36 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] +// compile-flags: -Z no-landing-pads +// error-pattern:diverging_fn called +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +#[rustc_mir] +fn mir(d: Droppable) { + let x = Droppable; + diverging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-pass/mir_trans_call_converging.rs b/src/test/run-pass/mir_trans_call_converging.rs new file mode 100644 index 0000000000000..d8acfec25c4b5 --- /dev/null +++ b/src/test/run-pass/mir_trans_call_converging.rs @@ -0,0 +1,28 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(rustc_attrs)] + +fn converging_fn() -> u64 { + 43 +} + +#[rustc_mir] +fn mir() -> u64 { + let x; + loop { + x = converging_fn(); + break; + } + x +} + +fn main() { + assert_eq!(mir(), 43); +} diff --git a/src/test/run-pass/mir_void_return.rs b/src/test/run-pass/mir_void_return.rs new file mode 100644 index 0000000000000..8b07449b8fafd --- /dev/null +++ b/src/test/run-pass/mir_void_return.rs @@ -0,0 +1,24 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +#[rustc_mir] +fn mir() -> (){ + let x = 1; + let mut y = 0; + while y < x { + y += 1 + } +} + +pub fn main() { + mir(); +} diff --git a/src/test/run-pass/mir_void_return_2.rs b/src/test/run-pass/mir_void_return_2.rs new file mode 100644 index 0000000000000..a3ad343240918 --- /dev/null +++ b/src/test/run-pass/mir_void_return_2.rs @@ -0,0 +1,22 @@ +// Copyright 2015 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +fn nil() {} + +#[rustc_mir] +fn mir(){ + nil() +} + +pub fn main() { + mir(); +} From d1c644c1e94403f0cd42c7afc2ac06c0554422b7 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 22 Dec 2015 01:46:56 +0200 Subject: [PATCH 11/13] Merge Call and DivergingCall diffs into CallKind 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 | 116 +++++++------ src/librustc/mir/visit.rs | 35 +--- src/librustc_mir/build/expr/into.rs | 25 +-- src/librustc_mir/build/scope.rs | 19 ++- src/librustc_mir/transform/erase_regions.rs | 10 +- src/librustc_trans/trans/mir/block.rs | 171 +++++++++++--------- 6 files changed, 197 insertions(+), 179 deletions(-) diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index fa3573b383b68..b97d5939cf3b0 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -250,51 +250,58 @@ pub enum Terminator<'tcx> { func: Operand<'tcx>, /// Arguments the function is called with args: Vec>, - /// 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>, - /// Some, if there’s any cleanup to be done when function unwinds - cleanup: Option, - } } -#[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) -> 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> { 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 @@ impl<'tcx> Terminator<'tcx> { 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 @@ impl<'tcx> Terminator<'tcx> { 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 @@ impl<'tcx> Terminator<'tcx> { 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 @@ impl<'tcx> Terminator<'tcx> { 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 @@ impl<'tcx> Terminator<'tcx> { .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()], + }, } } } diff --git a/src/librustc/mir/visit.rs b/src/librustc/mir/visit.rs index 52bb9aa3d5c5a..c05e4c83cd4f0 100644 --- a/src/librustc/mir/visit.rs +++ b/src/librustc/mir/visit.rs @@ -136,23 +136,15 @@ pub trait Visitor<'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 @@ pub trait MutVisitor<'tcx> { 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); } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index e23b5517cadf3..44d1d52a06a34 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -224,17 +224,22 @@ impl<'a,'tcx> Builder<'a,'tcx> { 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() } diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 080b979c1ecc2..8167ffcff1ee9 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -302,7 +302,6 @@ impl<'a,'tcx> Builder<'a,'tcx> { 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 @@ impl<'a,'tcx> Builder<'a,'tcx> { // 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 @@ impl<'a,'tcx> Builder<'a,'tcx> { // 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) + } }); } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 01d873abc6f92..20a14cf415404 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -93,14 +93,10 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { 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); diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 55117a6db3957..969b9cd19d1a1 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -94,116 +94,137 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { 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); - } - } } } } From f9814242dc0066441db9391af9b56dea8fa4376f Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 22 Dec 2015 18:43:05 +0200 Subject: [PATCH 12/13] panic/panic_bounds_check to destructure tys Not any more beautiful. --- src/librustc_mir/build/expr/as_lvalue.rs | 2 +- src/librustc_mir/build/scope.rs | 88 ++++++++++++++---------- 2 files changed, 51 insertions(+), 39 deletions(-) diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 6baf3ad2fe191..4e03ed489eb9f 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -71,7 +71,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { cond: Operand::Consume(lt), targets: (success, failure), }); - this.panic_bound_check(failure, idx.clone(), Operand::Consume(len), expr_span); + this.panic_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span); success.and(slice.index(idx)) } ExprKind::SelfRef => { diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 8167ffcff1ee9..e912e933bd81f 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -90,7 +90,7 @@ use build::{BlockAnd, BlockAndExtension, Builder}; use rustc::middle::region::CodeExtent; use rustc::middle::lang_items; use rustc::middle::subst::Substs; -use rustc::middle::ty::{Ty, Region}; +use rustc::middle::ty::{self, Ty}; use rustc::mir::repr::*; use syntax::codemap::{Span, DUMMY_SP}; use syntax::parse::token::intern_and_get_ident; @@ -297,27 +297,34 @@ impl<'a,'tcx> Builder<'a,'tcx> { self.scopes.first().map(|scope| scope.extent).unwrap() } - pub fn panic_bound_check(&mut self, + pub fn panic_bounds_check(&mut self, block: BasicBlock, index: Operand<'tcx>, len: Operand<'tcx>, span: Span) { + // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> ! 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]); - // FIXME: ReStatic might be wrong here? - let ref_region = self.hir.tcx().mk_region(Region::ReStatic); - let ref_ty = self.hir.tcx().mk_imm_ref(ref_region, tup_ty.clone()); - let (file_arg, line_arg) = self.span_to_fileline_args(span); + let args = func.ty.fn_args(); + let ref_ty = args.skip_binder()[0]; + let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { + (region, tyandmut.ty) + } else { + self.hir.span_bug(span, &format!("unexpected panic_bound_check type: {:?}", func.ty)); + }; let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); - self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg); - Rvalue::Aggregate(AggregateKind::Tuple, vec![file_arg, line_arg])); - // FIXME: ReStatic might be wrong here? + let (file, line) = self.span_to_fileline_args(span); + let elems = vec![Operand::Constant(file), Operand::Constant(line)]; + // FIXME: We should have this as a constant, rather than a stack variable (to not pollute + // icache with cold branch code), however to achieve that we either have to rely on rvalue + // promotion or have some way, in MIR, to create constants. + self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (file_arg, line_arg); + Rvalue::Aggregate(AggregateKind::Tuple, elems)); + // FIXME: is this region really correct here? self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; - Rvalue::Ref(*ref_region, BorrowKind::Unique, tuple)); + Rvalue::Ref(*region, BorrowKind::Unique, tuple)); let cleanup = self.diverge_cleanup(); self.cfg.terminate(block, Terminator::Call { - func: func, + func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref), index, len], kind: match cleanup { None => CallKind::Diverging, @@ -328,31 +335,36 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// Create diverge cleanup and branch to it from `block`. pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) { + // fn(&(msg: &'static str filename: &'static str, line: u32)) -> ! let func = self.lang_function(lang_items::PanicFnLangItem); - - let str_ty = self.hir.tcx().mk_static_str(); - let tup_ty = self.hir.tcx().mk_tup(vec![str_ty, str_ty, self.hir.tcx().types.u32]); - // FIXME: ReStatic might be wrong here? - let ref_region = self.hir.tcx().mk_region(Region::ReStatic); - let ref_ty = self.hir.tcx().mk_imm_ref(ref_region, tup_ty.clone()); - let message_arg = Operand::Constant(Constant { + let args = func.ty.fn_args(); + let ref_ty = args.skip_binder()[0]; + let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { + (region, tyandmut.ty) + } else { + self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty)); + }; + let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); + let (file, line) = self.span_to_fileline_args(span); + let message = Constant { span: DUMMY_SP, - ty: str_ty, + ty: self.hir.tcx().mk_static_str(), literal: self.hir.str_literal(intern_and_get_ident(msg)) - }); - let (file_arg, line_arg) = self.span_to_fileline_args(span); - let tuple = self.temp(tup_ty); - let tuple_ref = self.temp(ref_ty); + }; + let elems = vec![Operand::Constant(message), + Operand::Constant(file), + Operand::Constant(line)]; + // FIXME: We should have this as a constant, rather than a stack variable (to not pollute + // icache with cold branch code), however to achieve that we either have to rely on rvalue + // promotion or have some way, in MIR, to create constants. self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg); - Rvalue::Aggregate(AggregateKind::Tuple, - vec![message_arg, file_arg, line_arg]) - ); - // FIXME: ReStatic might be wrong here? + Rvalue::Aggregate(AggregateKind::Tuple, elems)); + // FIXME: is this region really correct here? self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; - Rvalue::Ref(*ref_region, BorrowKind::Unique, tuple)); + Rvalue::Ref(*region, BorrowKind::Unique, tuple)); let cleanup = self.diverge_cleanup(); self.cfg.terminate(block, Terminator::Call { - func: func, + func: Operand::Constant(func), args: vec![Operand::Consume(tuple_ref)], kind: match cleanup { None => CallKind::Diverging, @@ -361,14 +373,14 @@ impl<'a,'tcx> Builder<'a,'tcx> { }); } - fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Operand<'tcx> { + fn lang_function(&mut self, lang_item: lang_items::LangItem) -> Constant<'tcx> { let funcdid = match self.hir.tcx().lang_items.require(lang_item) { Ok(d) => d, Err(m) => { self.hir.tcx().sess.fatal(&*m) } }; - Operand::Constant(Constant { + Constant { span: DUMMY_SP, ty: self.hir.tcx().lookup_item_type(funcdid).ty, literal: Literal::Item { @@ -376,19 +388,19 @@ impl<'a,'tcx> Builder<'a,'tcx> { kind: ItemKind::Function, substs: self.hir.tcx().mk_substs(Substs::empty()) } - }) + } } - fn span_to_fileline_args(&mut self, span: Span) -> (Operand<'tcx>, Operand<'tcx>) { + fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) { let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo); - (Operand::Constant(Constant { + (Constant { span: DUMMY_SP, ty: self.hir.tcx().mk_static_str(), literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name)) - }), Operand::Constant(Constant { + }, Constant { span: DUMMY_SP, ty: self.hir.tcx().types.u32, literal: self.hir.usize_literal(span_lines.line) - })) + }) } } From 36b3951c7338ae0d69e125383038e76d00fc7d77 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sat, 2 Jan 2016 00:45:21 +0200 Subject: [PATCH 13/13] Create personality slot when translating Resume This considerably simplifies code around calling functions and translation of Resume itself. This removes requirement that a block containing Resume terminator is always translated after something which creates a landing pad, thus allowing us to actually translate some valid MIRs we could not translate before. However, an assumption is added that translator is correct (in regards to landing pad generation) and code will never reach the Resume terminator without going through a landing pad first. Breaking these assumptions would pass an `undef` value into the personality functions. --- src/librustc_trans/trans/mir/block.rs | 61 ++++++++++++--------------- src/librustc_trans/trans/mir/mod.rs | 3 -- 2 files changed, 26 insertions(+), 38 deletions(-) diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 969b9cd19d1a1..aa0b3a25ebb0c 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::BasicBlockRef; +use llvm::{BasicBlockRef, ValueRef}; use rustc::mir::repr as mir; use trans::adt; use trans::base; @@ -80,13 +80,10 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } mir::Terminator::Resume => { - if let Some(personalityslot) = self.llpersonalityslot { - let lp = build::Load(bcx, personalityslot); - base::call_lifetime_end(bcx, personalityslot); - build::Resume(bcx, lp); - } else { - panic!("resume terminator without personality slot set") - } + let ps = self.get_personality_slot(bcx); + let lp = build::Load(bcx, ps); + base::call_lifetime_end(bcx, ps); + base::trans_unwind_resume(bcx, lp); } mir::Terminator::Return => { @@ -187,29 +184,17 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { build::Br(target, postinvoketarget.llbb, debugloc); } }, - // 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); - } + (_, &mir::CallKind::DivergingCleanup(_)) | + (_, &mir::CallKind::Diverging) => { build::Call(bcx, callee.immediate(), &llargs[..], Some(attrs), debugloc); build::Unreachable(bcx); } (_, k@&mir::CallKind::ConvergingCleanup { .. }) | (_, k@&mir::CallKind::Converging { .. }) => { - let ret = match *k { + // Bug #20046 + let target = match *k { + mir::CallKind::ConvergingCleanup { targets, .. } => targets.0, 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); - targets.0 - }, _ => unreachable!() }; let llret = build::Call(bcx, @@ -222,13 +207,26 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { .expect("return destination and type not set"); base::store_ty(bcx, llret, ret_dest.llval, ret_ty); } - build::Br(bcx, self.llblock(ret), debugloc) + build::Br(bcx, self.llblock(target), debugloc); } } } } } + fn get_personality_slot(&mut self, bcx: Block<'bcx, 'tcx>) -> ValueRef { + let ccx = bcx.ccx(); + if let Some(slot) = self.llpersonalityslot { + slot + } else { + let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); + let slot = base::alloca(bcx, llretty, "personalityslot"); + self.llpersonalityslot = Some(slot); + base::call_lifetime_start(bcx, slot); + slot + } + } + fn make_landing_pad(&mut self, cleanup: Block<'bcx, 'tcx>) -> Block<'bcx, 'tcx> { let bcx = cleanup.fcx.new_block(true, "cleanup", None); let ccx = bcx.ccx(); @@ -236,15 +234,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let llretty = Type::struct_(ccx, &[Type::i8p(ccx), Type::i32(ccx)], false); let llretval = build::LandingPad(bcx, llretty, llpersonality, 1); build::SetCleanup(bcx, llretval); - match self.llpersonalityslot { - Some(slot) => build::Store(bcx, llretval, slot), - None => { - let personalityslot = base::alloca(bcx, llretty, "personalityslot"); - self.llpersonalityslot = Some(personalityslot); - base::call_lifetime_start(bcx, personalityslot); - build::Store(bcx, llretval, personalityslot) - } - }; + let slot = self.get_personality_slot(bcx); + build::Store(bcx, llretval, slot); build::Br(bcx, cleanup.llbb, DebugLoc::None); bcx } diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 1aceb67dd1210..75ce33da2c9b9 100644 --- a/src/librustc_trans/trans/mir/mod.rs +++ b/src/librustc_trans/trans/mir/mod.rs @@ -136,9 +136,6 @@ pub fn trans_mir<'bcx, 'tcx>(bcx: Block<'bcx, 'tcx>) { // Translate the body of each block for &bb in &mir_blocks { - // NB that we do not handle the Resume terminator specially, because a block containing - // that terminator will have a higher block number than a function call which should take - // care of filling in that information. mircx.trans_block(bb); } }