diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 75a588d424ebb..b97d5939cf3b0 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,8 @@ impl Debug for BasicBlock { #[derive(Debug, RustcEncodable, RustcDecodable)] pub struct BasicBlockData<'tcx> { pub statements: Vec>, - pub terminator: Terminator<'tcx>, + pub terminator: Option>, + pub is_cleanup: bool, } #[derive(RustcEncodable, RustcDecodable)] @@ -204,12 +202,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>, @@ -243,40 +235,88 @@ 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, /// Indicates a normal return. The ReturnPointer lvalue should /// have been filled in by now. This should only occur in the /// `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>, + /// The kind of call with associated information + kind: CallKind<'tcx>, }, } +#[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<'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 successors_mut(&mut self) -> &mut [BasicBlock] { + match *self { + 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 destination(&self) -> Option> { + match *self { + CallKind::Converging { ref destination, .. } | + CallKind::ConvergingCleanup { ref destination, .. } => Some(destination.clone()), + CallKind::Diverging | + CallKind::DivergingCleanup(_) => None + } + } +} + impl<'tcx> Terminator<'tcx> { pub fn successors(&self) -> &[BasicBlock] { use self::Terminator::*; 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 => &[], + Resume => &[], Return => &[], - Call { data: _, targets: ref b } => b.as_slice(), + Call { ref kind, .. } => kind.successors(), } } @@ -284,36 +324,36 @@ 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 { 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 [], + Resume => &mut [], Return => &mut [], - Call { data: _, targets: ref mut b } => b.as_mut_slice(), + Call { ref mut kind, .. } => kind.successors_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> { + pub fn new(terminator: Option>) -> BasicBlockData<'tcx> { BasicBlockData { statements: vec![], terminator: terminator, + is_cleanup: false, } } + + /// 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> { @@ -351,15 +391,17 @@ 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), - 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() { + Resume => write!(fmt, "resume"), + 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, ", ")); } @@ -374,10 +416,9 @@ impl<'tcx> Terminator<'tcx> { pub fn fmt_successor_labels(&self) -> Vec> { use self::Terminator::*; match *self { - Diverge | Return => vec![], - Goto { .. } | Panic { .. } => vec!["".into_cow()], + Return | Resume => vec![], + Goto { .. } => vec!["".into_cow()], If { .. } => vec!["true".into_cow(), "false".into_cow()], - Call { .. } => vec!["return".into_cow(), "unwind".into_cow()], Switch { ref adt_def, .. } => { adt_def.variants .iter() @@ -394,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 00d21d3c16e1f..c05e4c83cd4f0 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>) { @@ -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); } @@ -133,17 +132,19 @@ pub trait Visitor<'tcx> { } } - Terminator::Diverge | + Terminator::Resume | 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 kind } => { + if let Some(ref destination) = kind.destination() { + self.visit_lvalue(destination, LvalueContext::Store); + } + self.visit_operand(func); + for arg in args { self.visit_operand(arg); } - for &target in targets.as_slice() { + for &target in kind.successors() { self.visit_branch(block, target); } } @@ -364,7 +365,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, @@ -394,8 +395,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); } @@ -420,17 +420,19 @@ pub trait MutVisitor<'tcx> { } } - Terminator::Diverge | + Terminator::Resume | 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 kind } => { + if let Some(ref mut destination) = kind.destination() { + self.visit_lvalue(destination, LvalueContext::Store); + } + self.visit_operand(func); + for arg in args { self.visit_operand(arg); } - for &target in targets.as_slice() { + for &target in kind.successors() { self.visit_branch(block, target); } } 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/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 1c96addcea0d9..4e03ed489eb9f 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_bounds_check(failure, idx.clone(), Operand::Consume(len), expr_span); success.and(slice.index(idx)) } ExprKind::SelfRef => { diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 802c55ce7647a..44d1d52a06a34 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,23 +211,35 @@ 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(); - this.cfg.terminate(block, - Terminator::Call { - data: CallData { - destination: destination.clone(), - func: fun, - args: args, - }, - targets: (success, panic), - }); + let cleanup = this.diverge_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) + } + } + }); success.unit() } 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/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/build/scope.rs b/src/librustc_mir/build/scope.rs index b5e029ed94bd1..e912e933bd81f 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -86,11 +86,14 @@ 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::middle::lang_items; +use rustc::middle::subst::Substs; +use rustc::middle::ty::{self, Ty}; 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, @@ -227,17 +230,39 @@ 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; + } - /// Create diverge cleanup and branch to it from `block`. - pub fn panic(&mut self, block: BasicBlock) { - let cleanup = self.diverge_cleanup(); - self.cfg.terminate(block, Terminator::Panic { target: cleanup }); + 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.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() { + 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() } /// Indicates that `lvalue` should be dropped on exit from @@ -249,14 +274,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)); } } @@ -267,29 +296,111 @@ impl<'a,'tcx> Builder<'a,'tcx> { pub fn extent_of_outermost_scope(&self) -> CodeExtent { 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; + 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 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)); + 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(*region, BorrowKind::Unique, tuple)); + let cleanup = self.diverge_cleanup(); + self.cfg.terminate(block, Terminator::Call { + func: Operand::Constant(func), + args: vec![Operand::Consume(tuple_ref), index, len], + kind: match cleanup { + None => CallKind::Diverging, + Some(c) => CallKind::DivergingCleanup(c) + } + }); } - let (remaining, scope) = scopes.split_at_mut(len - 1); - let scope = &mut scope[0]; - - if let Some(b) = scope.cached_block { - return b; + /// 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 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: self.hir.tcx().mk_static_str(), + literal: self.hir.str_literal(intern_and_get_ident(msg)) + }; + 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, elems)); + // FIXME: is this region really correct here? + self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; + Rvalue::Ref(*region, BorrowKind::Unique, tuple)); + let cleanup = self.diverge_cleanup(); + self.cfg.terminate(block, Terminator::Call { + func: Operand::Constant(func), + args: vec![Operand::Consume(tuple_ref)], + kind: match cleanup { + None => CallKind::Diverging, + Some(c) => CallKind::DivergingCleanup(c) + } + }); } - let block = cfg.start_new_block(); - for &(kind, span, ref lvalue) in &scope.drops { - cfg.push_drop(block, span, kind, lvalue); + 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) + } + }; + 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()) + } + } } - scope.cached_block = Some(block); - let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining); - cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block }); - block + 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); + (Constant { + span: DUMMY_SP, + ty: self.hir.tcx().mk_static_str(), + literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name)) + }, Constant { + span: DUMMY_SP, + ty: self.hir.tcx().types.u32, + literal: self.hir.usize_literal(span_lines.line) + }) + } } 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/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/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/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>, }, diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index 1eb3bfd7e0229..20a14cf415404 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,9 +79,8 @@ impl<'a, 'tcx> EraseRegions<'a, 'tcx> { terminator: &mut Terminator<'tcx>) { match *terminator { Terminator::Goto { .. } | - Terminator::Diverge | - Terminator::Return | - Terminator::Panic { .. } => { + Terminator::Resume | + Terminator::Return => { /* nothing to do */ } Terminator::If { ref mut cond, .. } => { @@ -90,23 +89,14 @@ 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 - }, - .. - } => { - self.erase_regions_lvalue(destination); + Terminator::Call { ref mut func, ref mut args, ref mut kind } => { + if let Some(ref mut destination) = kind.destination() { + self.erase_regions_lvalue(destination); + } self.erase_regions_operand(func); for arg in &mut *args { self.erase_regions_operand(arg); 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/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 265969c52b39d..aa0b3a25ebb0c 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -8,16 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use llvm::BasicBlockRef; -use middle::infer; -use middle::ty; +use llvm::{BasicBlockRef, ValueRef}; 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}; @@ -33,17 +33,13 @@ 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) } - 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); @@ -60,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()); @@ -86,18 +79,11 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } - mir::Terminator::Diverge => { - 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); - } + mir::Terminator::Resume => { + 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 => { @@ -105,65 +91,163 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { base::build_return_block(bcx.fcx, bcx, return_ty, DebugLoc::None); } - mir::Terminator::Call { ref data, targets } => { - // The location we'll write the result of the call into. - let call_dest = self.trans_lvalue(bcx, &data.destination); - - // 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 - } else { - panic!("trans_block: expected TyBareFn as callee"); - }; + 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); - // 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 { + // 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(call_dest.llval); + llargs.push(dest.llval); + (Some((dest, ret_ty)), false) + } else { + (Some((dest, ret_ty)), !common::type_is_zero_size(bcx.ccx(), ret_ty)) } - } + } else { + (None, false) + }; // Process the rest of the args. - for arg in &data.args { - let arg_op = self.trans_operand(bcx, arg); - match arg_op.val { + for arg in args { + 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); } } } - // 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); + // 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.unreachable_block().llbb, + landingpad.llbb, + Some(attrs), + debugloc); + }, + (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); + } + }, + (_, &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 { .. }) => { + // Bug #20046 + let target = match *k { + mir::CallKind::ConvergingCleanup { targets, .. } => targets.0, + mir::CallKind::Converging { target, .. } => target, + _ => unreachable!() + }; + let llret = build::Call(bcx, + callee.immediate(), + &llargs[..], + Some(attrs), + debugloc); + 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(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(); + 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); + let slot = self.get_personality_slot(bcx); + build::Store(bcx, llretval, slot); + build::Br(bcx, cleanup.llbb, DebugLoc::None); + bcx + } - build::Br(bcx, self.llblock(targets.0), DebugLoc::None) + 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 } } } 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); diff --git a/src/librustc_trans/trans/mir/mod.rs b/src/librustc_trans/trans/mir/mod.rs index 0ed76ebeb4362..75ce33da2c9b9 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>, @@ -107,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 @@ -116,8 +125,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, @@ -125,16 +136,8 @@ 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); - } + 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 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(); +}