Auto merge of #30481 - nagisa:mir-calls-2, r=nikomatsakis

r? @nikomatsakis

This is a pretty big PR conflating changes to a few different block terminators (Call, DivergingCall, Panic, Resume, Diverge), because they are somewhat closely related.

Each commit has a pretty good description on what is being changed in each commit. The end result is greatly simplified CFG and translation for calls (no success branch if the function is diverging, no cleanup branch if there’s nothing to cleanup etc).

Fixes https://github.com/rust-lang/rust/issues/30480
Fixes https://github.com/rust-lang/rust/issues/29767
Partialy solves https://github.com/rust-lang/rust/issues/29575
Fixes https://github.com/rust-lang/rust/issues/29573
This commit is contained in:
bors 2016-01-06 13:11:18 +00:00
commit d5ac1a1da3
31 changed files with 894 additions and 267 deletions

View File

@ -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<BasicBlock> {
(0..self.basic_blocks.len())
@ -194,7 +191,8 @@ impl Debug for BasicBlock {
#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct BasicBlockData<'tcx> {
pub statements: Vec<Statement<'tcx>>,
pub terminator: Terminator<'tcx>,
pub terminator: Option<Terminator<'tcx>>,
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<BasicBlock>,
},
/// 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 thats being called
func: Operand<'tcx>,
/// Arguments the function is called with
args: Vec<Operand<'tcx>>,
/// 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<Lvalue<'tcx>> {
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<Operand<'tcx>>,
}
impl<'tcx> BasicBlockData<'tcx> {
pub fn new(terminator: Terminator<'tcx>) -> BasicBlockData<'tcx> {
pub fn new(terminator: Option<Terminator<'tcx>>) -> 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<Cow<'static, str>> {
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()],
},
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -63,7 +63,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
this.cfg.push_assign(block, expr_span, // lt = idx < len
&lt, 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 => {

View File

@ -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()
}

View File

@ -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

View File

@ -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,

View File

@ -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 theres no
/// cleanup to do at this point.
pub fn diverge_cleanup(&mut self) -> Option<BasicBlock> {
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 were 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;
}
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
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)
}
});
}
/// 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)
}
});
}
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())
}
}
}
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)
})
}
}

View File

@ -62,7 +62,7 @@ fn write_node<W: Write>(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#"<tr><td align="left">{}</td></tr>"#, dot::escape_html(&terminator_head)));
// Close the table, node label, and the node itself.
@ -71,7 +71,7 @@ fn write_node<W: Write>(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<W: Write>(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) {

View File

@ -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,
}

View File

@ -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) }
}

View File

@ -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<ExprRef<'tcx>>,
},

View File

@ -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);

View File

@ -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();
}

View File

@ -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()];
}
}

View File

@ -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 {

View File

@ -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);
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);
// 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
// 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 {
panic!("trans_block: expected TyBareFn as callee");
(None, false)
};
// 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 {
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 LLVMs `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);
}
}
}
}
}
build::Br(bcx, self.llblock(targets.0), DebugLoc::None)
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
}
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
}
}
}

View File

@ -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, theres no place to store the value and
// there cannot really be one (or storing into it doesnt 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);

View File

@ -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<Block<'bcx, 'tcx>>,
/// Cached unreachable block
unreachable_block: Option<Block<'bcx, 'tcx>>,
/// An LLVM alloca for each MIR `VarDecl`
vars: Vec<LvalueRef<'tcx>>,
@ -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<Block<'bcx,'tcx>> =
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

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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();
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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();
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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();
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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");
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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");
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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();
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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);
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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();
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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();
}