2012-12-03 18:48:01 -06:00
|
|
|
// Copyright 2012 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.
|
|
|
|
|
2013-08-03 19:13:14 -05:00
|
|
|
use std::c_str::ToCStr;
|
2013-05-17 17:28:44 -05:00
|
|
|
|
2013-02-25 13:11:21 -06:00
|
|
|
use back::link;
|
|
|
|
use lib;
|
|
|
|
use lib::llvm::*;
|
2013-07-15 22:42:13 -05:00
|
|
|
use middle::lang_items::{FailFnLangItem, FailBoundsCheckFnLangItem};
|
|
|
|
use middle::lang_items::LogTypeFnLangItem;
|
2012-12-13 15:05:22 -06:00
|
|
|
use middle::trans::base::*;
|
2013-02-25 13:11:21 -06:00
|
|
|
use middle::trans::build::*;
|
2012-12-23 16:41:37 -06:00
|
|
|
use middle::trans::callee;
|
2012-12-13 15:05:22 -06:00
|
|
|
use middle::trans::common::*;
|
2013-02-25 13:11:21 -06:00
|
|
|
use middle::trans::expr;
|
|
|
|
use middle::ty;
|
|
|
|
use util::common::indenter;
|
|
|
|
use util::ppaux;
|
2012-08-28 17:54:45 -05:00
|
|
|
|
2013-06-16 05:52:44 -05:00
|
|
|
use middle::trans::type_::Type;
|
|
|
|
|
2013-02-25 13:11:21 -06:00
|
|
|
use syntax::ast;
|
2013-09-01 19:50:59 -05:00
|
|
|
use syntax::ast::Ident;
|
2013-02-25 13:11:21 -06:00
|
|
|
use syntax::ast_map::path_mod;
|
|
|
|
use syntax::ast_util;
|
2013-08-31 11:13:04 -05:00
|
|
|
use syntax::codemap::Span;
|
2012-12-23 16:41:37 -06:00
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_block(bcx: @mut Block, b: &ast::Block, dest: expr::Dest) -> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_block");
|
2012-08-28 17:54:45 -05:00
|
|
|
let mut bcx = bcx;
|
2013-08-03 11:45:23 -05:00
|
|
|
for s in b.stmts.iter() {
|
2013-04-17 11:15:37 -05:00
|
|
|
bcx = trans_stmt(bcx, *s);
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
2013-07-16 13:08:35 -05:00
|
|
|
match b.expr {
|
2012-08-28 17:54:45 -05:00
|
|
|
Some(e) => {
|
|
|
|
bcx = expr::trans_into(bcx, e, dest);
|
|
|
|
}
|
|
|
|
None => {
|
2013-03-28 20:39:09 -05:00
|
|
|
assert!(dest == expr::Ignore || bcx.unreachable);
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return bcx;
|
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_if(bcx: @mut Block,
|
2012-08-28 17:54:45 -05:00
|
|
|
cond: @ast::expr,
|
2013-07-19 00:38:55 -05:00
|
|
|
thn: &ast::Block,
|
2012-08-28 17:54:45 -05:00
|
|
|
els: Option<@ast::expr>,
|
|
|
|
dest: expr::Dest)
|
2013-07-17 05:12:08 -05:00
|
|
|
-> @mut Block {
|
2012-08-28 17:54:45 -05:00
|
|
|
debug!("trans_if(bcx=%s, cond=%s, thn=%?, dest=%s)",
|
2013-07-16 13:08:35 -05:00
|
|
|
bcx.to_str(), bcx.expr_to_str(cond), thn.id,
|
2012-08-28 17:54:45 -05:00
|
|
|
dest.to_str(bcx.ccx()));
|
|
|
|
let _indenter = indenter();
|
|
|
|
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_if");
|
2013-07-17 03:13:41 -05:00
|
|
|
|
2013-07-25 03:53:27 -05:00
|
|
|
let Result {bcx, val: cond_val} =
|
|
|
|
expr::trans_to_datum(bcx, cond).to_result();
|
|
|
|
|
|
|
|
let cond_val = bool_to_i1(bcx, cond_val);
|
|
|
|
|
|
|
|
// Drop branches that are known to be impossible
|
|
|
|
if is_const(cond_val) && !is_undef(cond_val) {
|
|
|
|
if const_to_uint(cond_val) == 1 {
|
|
|
|
// if true { .. } [else { .. }]
|
2013-07-25 10:03:46 -05:00
|
|
|
return do with_scope(bcx, thn.info(), "if_true_then") |bcx| {
|
|
|
|
let bcx_out = trans_block(bcx, thn, dest);
|
|
|
|
trans_block_cleanups(bcx_out, block_cleanups(bcx))
|
|
|
|
}
|
2013-07-25 03:53:27 -05:00
|
|
|
} else {
|
|
|
|
match els {
|
|
|
|
// if false { .. } else { .. }
|
|
|
|
Some(elexpr) => {
|
2013-07-25 10:03:46 -05:00
|
|
|
return do with_scope(bcx, elexpr.info(), "if_false_then") |bcx| {
|
|
|
|
let bcx_out = trans_if_else(bcx, elexpr, dest);
|
|
|
|
trans_block_cleanups(bcx_out, block_cleanups(bcx))
|
|
|
|
}
|
2013-07-17 03:13:41 -05:00
|
|
|
}
|
2013-07-25 03:53:27 -05:00
|
|
|
// if false { .. }
|
|
|
|
None => return bcx,
|
2013-07-17 03:13:41 -05:00
|
|
|
}
|
2013-07-25 03:53:27 -05:00
|
|
|
}
|
2013-07-17 03:13:41 -05:00
|
|
|
}
|
|
|
|
|
2013-05-02 03:16:07 -05:00
|
|
|
let then_bcx_in = scope_block(bcx, thn.info(), "then");
|
2013-02-06 16:28:02 -06:00
|
|
|
|
2012-08-28 17:54:45 -05:00
|
|
|
let then_bcx_out = trans_block(then_bcx_in, thn, dest);
|
|
|
|
let then_bcx_out = trans_block_cleanups(then_bcx_out,
|
|
|
|
block_cleanups(then_bcx_in));
|
|
|
|
|
|
|
|
// Calling trans_block directly instead of trans_expr
|
|
|
|
// because trans_expr will create another scope block
|
|
|
|
// context for the block, but we've already got the
|
|
|
|
// 'else' context
|
2013-07-12 21:10:41 -05:00
|
|
|
let (else_bcx_in, next_bcx) = match els {
|
2012-08-28 17:54:45 -05:00
|
|
|
Some(elexpr) => {
|
2013-07-25 10:03:46 -05:00
|
|
|
let else_bcx_in = scope_block(bcx, elexpr.info(), "else");
|
|
|
|
let else_bcx_out = trans_if_else(else_bcx_in, elexpr, dest);
|
2013-07-17 03:13:41 -05:00
|
|
|
(else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out]))
|
2013-07-12 21:10:41 -05:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
let next_bcx = sub_block(bcx, "next");
|
|
|
|
Br(then_bcx_out, next_bcx.llbb);
|
|
|
|
|
|
|
|
(next_bcx, next_bcx)
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-07-12 21:10:41 -05:00
|
|
|
debug!("then_bcx_in=%s, else_bcx_in=%s",
|
|
|
|
then_bcx_in.to_str(), else_bcx_in.to_str());
|
|
|
|
|
|
|
|
CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb);
|
2013-07-17 03:13:41 -05:00
|
|
|
return next_bcx;
|
|
|
|
|
|
|
|
// trans `else [ if { .. } ... | { .. } ]`
|
2013-07-25 10:03:46 -05:00
|
|
|
fn trans_if_else(else_bcx_in: @mut Block, elexpr: @ast::expr,
|
|
|
|
dest: expr::Dest) -> @mut Block {
|
2013-07-17 03:13:41 -05:00
|
|
|
let else_bcx_out = match elexpr.node {
|
|
|
|
ast::expr_if(_, _, _) => {
|
|
|
|
let elseif_blk = ast_util::block_from_expr(elexpr);
|
|
|
|
trans_block(else_bcx_in, &elseif_blk, dest)
|
|
|
|
}
|
|
|
|
ast::expr_block(ref blk) => {
|
|
|
|
trans_block(else_bcx_in, blk, dest)
|
|
|
|
}
|
|
|
|
// would be nice to have a constraint on ifs
|
2013-07-25 10:03:46 -05:00
|
|
|
_ => else_bcx_in.tcx().sess.bug("strange alternative in if")
|
2013-07-17 03:13:41 -05:00
|
|
|
};
|
2013-07-25 10:03:46 -05:00
|
|
|
trans_block_cleanups(else_bcx_out, block_cleanups(else_bcx_in))
|
2013-07-17 03:13:41 -05:00
|
|
|
}
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn join_blocks(parent_bcx: @mut Block, in_cxs: &[@mut Block]) -> @mut Block {
|
2013-05-02 03:16:07 -05:00
|
|
|
let out = sub_block(parent_bcx, "join");
|
2012-08-28 17:54:45 -05:00
|
|
|
let mut reachable = false;
|
2013-08-03 11:45:23 -05:00
|
|
|
for bcx in in_cxs.iter() {
|
2012-08-28 17:54:45 -05:00
|
|
|
if !bcx.unreachable {
|
2012-09-18 23:41:37 -05:00
|
|
|
Br(*bcx, out.llbb);
|
2012-08-28 17:54:45 -05:00
|
|
|
reachable = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !reachable {
|
|
|
|
Unreachable(out);
|
|
|
|
}
|
|
|
|
return out;
|
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_while(bcx: @mut Block, cond: @ast::expr, body: &ast::Block) -> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_while");
|
2013-05-02 03:16:07 -05:00
|
|
|
let next_bcx = sub_block(bcx, "while next");
|
2012-08-28 17:54:45 -05:00
|
|
|
|
|
|
|
// bcx
|
|
|
|
// |
|
|
|
|
// loop_bcx
|
|
|
|
// |
|
|
|
|
// cond_bcx_in <--------+
|
|
|
|
// | |
|
|
|
|
// cond_bcx_out |
|
|
|
|
// | | |
|
|
|
|
// | body_bcx_in |
|
|
|
|
// +------+ | |
|
|
|
|
// | body_bcx_out --+
|
|
|
|
// next_bcx
|
|
|
|
|
2013-05-02 03:16:07 -05:00
|
|
|
let loop_bcx = loop_scope_block(bcx, next_bcx, None, "`while`",
|
2012-10-18 14:20:18 -05:00
|
|
|
body.info());
|
2013-05-02 03:16:07 -05:00
|
|
|
let cond_bcx_in = scope_block(loop_bcx, cond.info(), "while loop cond");
|
|
|
|
let body_bcx_in = scope_block(loop_bcx, body.info(), "while loop body");
|
2012-08-28 17:54:45 -05:00
|
|
|
Br(bcx, loop_bcx.llbb);
|
|
|
|
Br(loop_bcx, cond_bcx_in.llbb);
|
|
|
|
|
|
|
|
// compile the condition
|
|
|
|
let Result {bcx: cond_bcx_out, val: cond_val} =
|
2012-09-11 23:25:01 -05:00
|
|
|
expr::trans_to_datum(cond_bcx_in, cond).to_result();
|
2013-02-06 16:28:02 -06:00
|
|
|
let cond_val = bool_to_i1(cond_bcx_out, cond_val);
|
2012-08-28 17:54:45 -05:00
|
|
|
let cond_bcx_out =
|
|
|
|
trans_block_cleanups(cond_bcx_out, block_cleanups(cond_bcx_in));
|
|
|
|
CondBr(cond_bcx_out, cond_val, body_bcx_in.llbb, next_bcx.llbb);
|
|
|
|
|
|
|
|
// loop body:
|
|
|
|
let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
|
|
|
|
cleanup_and_Br(body_bcx_out, body_bcx_in, cond_bcx_in.llbb);
|
|
|
|
|
|
|
|
return next_bcx;
|
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_loop(bcx:@mut Block,
|
2013-07-19 00:38:55 -05:00
|
|
|
body: &ast::Block,
|
2013-09-01 19:50:59 -05:00
|
|
|
opt_label: Option<Ident>)
|
2013-07-17 05:12:08 -05:00
|
|
|
-> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_loop");
|
2013-05-02 03:16:07 -05:00
|
|
|
let next_bcx = sub_block(bcx, "next");
|
|
|
|
let body_bcx_in = loop_scope_block(bcx, next_bcx, opt_label, "`loop`",
|
2012-10-18 14:20:18 -05:00
|
|
|
body.info());
|
2012-08-28 17:54:45 -05:00
|
|
|
Br(bcx, body_bcx_in.llbb);
|
|
|
|
let body_bcx_out = trans_block(body_bcx_in, body, expr::Ignore);
|
|
|
|
cleanup_and_Br(body_bcx_out, body_bcx_in, body_bcx_in.llbb);
|
|
|
|
return next_bcx;
|
|
|
|
}
|
|
|
|
|
2013-06-27 08:04:22 -05:00
|
|
|
pub fn trans_log(log_ex: &ast::expr,
|
2013-01-29 19:57:02 -06:00
|
|
|
lvl: @ast::expr,
|
2013-07-17 05:12:08 -05:00
|
|
|
bcx: @mut Block,
|
|
|
|
e: @ast::expr) -> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_log");
|
2012-08-28 17:54:45 -05:00
|
|
|
let ccx = bcx.ccx();
|
|
|
|
let mut bcx = bcx;
|
|
|
|
if ty::type_is_bot(expr_ty(bcx, lvl)) {
|
|
|
|
return expr::trans_into(bcx, lvl, expr::Ignore);
|
|
|
|
}
|
|
|
|
|
2013-03-16 13:11:31 -05:00
|
|
|
let (modpath, modname) = {
|
|
|
|
let path = &mut bcx.fcx.path;
|
2013-07-01 21:38:19 -05:00
|
|
|
let mut modpath = ~[path_mod(ccx.sess.ident_of(ccx.link_meta.name))];
|
2013-08-03 11:45:23 -05:00
|
|
|
for e in path.iter() {
|
2013-07-01 21:38:19 -05:00
|
|
|
match *e {
|
|
|
|
path_mod(_) => { modpath.push(*e) }
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
2013-03-16 13:11:31 -05:00
|
|
|
let modname = path_str(ccx.sess, modpath);
|
|
|
|
(modpath, modname)
|
|
|
|
};
|
2012-08-28 17:54:45 -05:00
|
|
|
|
2013-02-08 16:08:02 -06:00
|
|
|
let global = if ccx.module_data.contains_key(&modname) {
|
2013-05-05 11:17:59 -05:00
|
|
|
ccx.module_data.get_copy(&modname)
|
2012-08-28 17:54:45 -05:00
|
|
|
} else {
|
2012-09-18 13:46:39 -05:00
|
|
|
let s = link::mangle_internal_name_by_path_and_seq(
|
2013-05-02 03:16:07 -05:00
|
|
|
ccx, modpath, "loglevel");
|
2013-01-10 23:23:07 -06:00
|
|
|
let global;
|
|
|
|
unsafe {
|
2013-08-14 21:21:59 -05:00
|
|
|
global = do s.with_c_str |buf| {
|
2013-06-16 05:52:44 -05:00
|
|
|
llvm::LLVMAddGlobal(ccx.llmod, Type::i32().to_ref(), buf)
|
2013-07-22 23:41:46 -05:00
|
|
|
};
|
2013-01-10 23:23:07 -06:00
|
|
|
llvm::LLVMSetGlobalConstant(global, False);
|
2013-06-15 22:45:48 -05:00
|
|
|
llvm::LLVMSetInitializer(global, C_null(Type::i32()));
|
2013-01-10 23:23:07 -06:00
|
|
|
lib::llvm::SetLinkage(global, lib::llvm::InternalLinkage);
|
|
|
|
}
|
2012-08-28 17:54:45 -05:00
|
|
|
ccx.module_data.insert(modname, global);
|
|
|
|
global
|
|
|
|
};
|
|
|
|
let current_level = Load(bcx, global);
|
|
|
|
let level = unpack_result!(bcx, {
|
2013-05-02 03:16:07 -05:00
|
|
|
do with_scope_result(bcx, lvl.info(), "level") |bcx| {
|
2012-09-11 23:25:01 -05:00
|
|
|
expr::trans_to_datum(bcx, lvl).to_result()
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let llenabled = ICmp(bcx, lib::llvm::IntUGE, current_level, level);
|
|
|
|
do with_cond(bcx, llenabled) |bcx| {
|
2013-05-02 03:16:07 -05:00
|
|
|
do with_scope(bcx, log_ex.info(), "log") |bcx| {
|
2012-08-28 17:54:45 -05:00
|
|
|
let mut bcx = bcx;
|
|
|
|
|
|
|
|
// Translate the value to be logged
|
|
|
|
let val_datum = unpack_datum!(bcx, expr::trans_to_datum(bcx, e));
|
|
|
|
|
|
|
|
// Call the polymorphic log function
|
|
|
|
let val = val_datum.to_ref_llval(bcx);
|
2013-07-15 22:42:13 -05:00
|
|
|
let did = langcall(bcx, Some(e.span), "", LogTypeFnLangItem);
|
2013-02-27 20:34:04 -06:00
|
|
|
let bcx = callee::trans_lang_call_with_type_params(
|
2013-05-19 00:07:44 -05:00
|
|
|
bcx, did, [level, val], [val_datum.ty], expr::Ignore);
|
2012-08-28 17:54:45 -05:00
|
|
|
bcx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_break_cont(bcx: @mut Block,
|
2013-09-01 19:50:59 -05:00
|
|
|
opt_label: Option<Ident>,
|
2013-01-29 19:57:02 -06:00
|
|
|
to_end: bool)
|
2013-07-17 05:12:08 -05:00
|
|
|
-> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_break_cont");
|
2012-08-28 17:54:45 -05:00
|
|
|
// Locate closest loop block, outputting cleanup as we go.
|
|
|
|
let mut unwind = bcx;
|
Implement scopes independent of LLVM basic blocks
Currently, scopes are tied to LLVM basic blocks. For each scope, there
are two new basic blocks, which means two extra jumps in the unoptimized
IR. These blocks aren't actually required, but only used to act as the
boundary for cleanups.
By keeping track of the current scope within a single basic block, we
can avoid those extra blocks and jumps, shrinking the pre-optimization
IR quite considerably. For example, the IR for trans_intrinsic goes
from ~22k lines to ~16k lines, almost 30% less.
The impact on the build times of optimized builds is rather small (about
1%), but unoptimized builds are about 11% faster. The testsuite for
unoptimized builds runs between 15% (CPU time) and 7.5% (wallclock time on
my i7) faster.
Also, in some situations this helps LLVM to generate better code by
inlining functions that it previously considered to be too large.
Likely because of the pointless blocks/jumps that were still present at
the time the inlining pass runs.
Refs #7462
2013-07-07 07:53:57 -05:00
|
|
|
let mut cur_scope = unwind.scope;
|
2013-07-12 03:56:40 -05:00
|
|
|
let mut target;
|
2012-08-28 17:54:45 -05:00
|
|
|
loop {
|
Implement scopes independent of LLVM basic blocks
Currently, scopes are tied to LLVM basic blocks. For each scope, there
are two new basic blocks, which means two extra jumps in the unoptimized
IR. These blocks aren't actually required, but only used to act as the
boundary for cleanups.
By keeping track of the current scope within a single basic block, we
can avoid those extra blocks and jumps, shrinking the pre-optimization
IR quite considerably. For example, the IR for trans_intrinsic goes
from ~22k lines to ~16k lines, almost 30% less.
The impact on the build times of optimized builds is rather small (about
1%), but unoptimized builds are about 11% faster. The testsuite for
unoptimized builds runs between 15% (CPU time) and 7.5% (wallclock time on
my i7) faster.
Also, in some situations this helps LLVM to generate better code by
inlining functions that it previously considered to be too large.
Likely because of the pointless blocks/jumps that were still present at
the time the inlining pass runs.
Refs #7462
2013-07-07 07:53:57 -05:00
|
|
|
cur_scope = match cur_scope {
|
2013-07-17 05:12:08 -05:00
|
|
|
Some(@ScopeInfo {
|
Implement scopes independent of LLVM basic blocks
Currently, scopes are tied to LLVM basic blocks. For each scope, there
are two new basic blocks, which means two extra jumps in the unoptimized
IR. These blocks aren't actually required, but only used to act as the
boundary for cleanups.
By keeping track of the current scope within a single basic block, we
can avoid those extra blocks and jumps, shrinking the pre-optimization
IR quite considerably. For example, the IR for trans_intrinsic goes
from ~22k lines to ~16k lines, almost 30% less.
The impact on the build times of optimized builds is rather small (about
1%), but unoptimized builds are about 11% faster. The testsuite for
unoptimized builds runs between 15% (CPU time) and 7.5% (wallclock time on
my i7) faster.
Also, in some situations this helps LLVM to generate better code by
inlining functions that it previously considered to be too large.
Likely because of the pointless blocks/jumps that were still present at
the time the inlining pass runs.
Refs #7462
2013-07-07 07:53:57 -05:00
|
|
|
loop_break: Some(brk),
|
|
|
|
loop_label: l,
|
|
|
|
parent,
|
|
|
|
_
|
|
|
|
}) => {
|
|
|
|
// If we're looking for a labeled loop, check the label...
|
|
|
|
target = if to_end {
|
|
|
|
brk
|
|
|
|
} else {
|
|
|
|
unwind
|
|
|
|
};
|
|
|
|
match opt_label {
|
|
|
|
Some(desired) => match l {
|
|
|
|
Some(actual) if actual == desired => break,
|
|
|
|
// If it doesn't match the one we want,
|
|
|
|
// don't break
|
|
|
|
_ => parent,
|
|
|
|
},
|
|
|
|
None => break,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Some(inf) => inf.parent,
|
|
|
|
None => {
|
|
|
|
unwind = match unwind.parent {
|
|
|
|
Some(bcx) => bcx,
|
|
|
|
// This is a return from a loop body block
|
|
|
|
None => {
|
2013-08-03 18:59:24 -05:00
|
|
|
Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.unwrap());
|
2013-07-12 20:25:46 -05:00
|
|
|
cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
|
Implement scopes independent of LLVM basic blocks
Currently, scopes are tied to LLVM basic blocks. For each scope, there
are two new basic blocks, which means two extra jumps in the unoptimized
IR. These blocks aren't actually required, but only used to act as the
boundary for cleanups.
By keeping track of the current scope within a single basic block, we
can avoid those extra blocks and jumps, shrinking the pre-optimization
IR quite considerably. For example, the IR for trans_intrinsic goes
from ~22k lines to ~16k lines, almost 30% less.
The impact on the build times of optimized builds is rather small (about
1%), but unoptimized builds are about 11% faster. The testsuite for
unoptimized builds runs between 15% (CPU time) and 7.5% (wallclock time on
my i7) faster.
Also, in some situations this helps LLVM to generate better code by
inlining functions that it previously considered to be too large.
Likely because of the pointless blocks/jumps that were still present at
the time the inlining pass runs.
Refs #7462
2013-07-07 07:53:57 -05:00
|
|
|
Unreachable(bcx);
|
|
|
|
return bcx;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
unwind.scope
|
|
|
|
}
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
cleanup_and_Br(bcx, unwind, target.llbb);
|
|
|
|
Unreachable(bcx);
|
|
|
|
return bcx;
|
|
|
|
}
|
|
|
|
|
2013-09-01 19:50:59 -05:00
|
|
|
pub fn trans_break(bcx: @mut Block, label_opt: Option<Ident>) -> @mut Block {
|
2012-10-18 14:20:18 -05:00
|
|
|
return trans_break_cont(bcx, label_opt, true);
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
|
2013-09-01 19:50:59 -05:00
|
|
|
pub fn trans_cont(bcx: @mut Block, label_opt: Option<Ident>) -> @mut Block {
|
2012-10-18 14:20:18 -05:00
|
|
|
return trans_break_cont(bcx, label_opt, false);
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_ret(bcx: @mut Block, e: Option<@ast::expr>) -> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_ret");
|
2012-08-28 17:54:45 -05:00
|
|
|
let mut bcx = bcx;
|
2013-08-11 20:12:57 -05:00
|
|
|
let dest = match bcx.fcx.llretptr {
|
2013-06-20 09:42:44 -05:00
|
|
|
None => expr::Ignore,
|
|
|
|
Some(retptr) => expr::SaveIn(retptr),
|
2012-08-28 17:54:45 -05:00
|
|
|
};
|
|
|
|
match e {
|
2013-08-11 20:12:57 -05:00
|
|
|
Some(x) => {
|
|
|
|
bcx = expr::trans_into(bcx, x, dest);
|
|
|
|
}
|
|
|
|
_ => ()
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
2013-07-12 20:25:46 -05:00
|
|
|
cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
|
2012-08-28 17:54:45 -05:00
|
|
|
Unreachable(bcx);
|
|
|
|
return bcx;
|
|
|
|
}
|
2013-01-29 19:57:02 -06:00
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_fail_expr(bcx: @mut Block,
|
2013-08-31 11:13:04 -05:00
|
|
|
sp_opt: Option<Span>,
|
2013-01-29 19:57:02 -06:00
|
|
|
fail_expr: Option<@ast::expr>)
|
2013-07-17 05:12:08 -05:00
|
|
|
-> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_fail_expr");
|
2012-08-28 17:54:45 -05:00
|
|
|
let mut bcx = bcx;
|
|
|
|
match fail_expr {
|
|
|
|
Some(arg_expr) => {
|
2013-06-04 23:43:41 -05:00
|
|
|
let ccx = bcx.ccx();
|
|
|
|
let tcx = ccx.tcx;
|
2012-08-28 17:54:45 -05:00
|
|
|
let arg_datum = unpack_datum!(
|
|
|
|
bcx, expr::trans_to_datum(bcx, arg_expr));
|
|
|
|
|
|
|
|
if ty::type_is_str(arg_datum.ty) {
|
2013-05-03 15:26:43 -05:00
|
|
|
let (lldata, _) = arg_datum.get_vec_base_and_len_no_root(bcx);
|
2012-08-28 17:54:45 -05:00
|
|
|
return trans_fail_value(bcx, sp_opt, lldata);
|
|
|
|
} else if bcx.unreachable || ty::type_is_bot(arg_datum.ty) {
|
|
|
|
return bcx;
|
|
|
|
} else {
|
|
|
|
bcx.sess().span_bug(
|
|
|
|
arg_expr.span, ~"fail called with unsupported type " +
|
|
|
|
ppaux::ty_to_str(tcx, arg_datum.ty));
|
|
|
|
}
|
|
|
|
}
|
2013-06-12 12:02:55 -05:00
|
|
|
_ => trans_fail(bcx, sp_opt, @"explicit failure")
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
pub fn trans_fail(bcx: @mut Block,
|
2013-08-31 11:13:04 -05:00
|
|
|
sp_opt: Option<Span>,
|
2013-06-12 12:02:55 -05:00
|
|
|
fail_str: @str)
|
2013-07-17 05:12:08 -05:00
|
|
|
-> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_fail");
|
2012-08-28 17:54:45 -05:00
|
|
|
let V_fail_str = C_cstr(bcx.ccx(), fail_str);
|
|
|
|
return trans_fail_value(bcx, sp_opt, V_fail_str);
|
|
|
|
}
|
|
|
|
|
2013-07-17 05:12:08 -05:00
|
|
|
fn trans_fail_value(bcx: @mut Block,
|
2013-08-31 11:13:04 -05:00
|
|
|
sp_opt: Option<Span>,
|
2013-01-29 19:57:02 -06:00
|
|
|
V_fail_str: ValueRef)
|
2013-07-17 05:12:08 -05:00
|
|
|
-> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_fail_value");
|
2012-08-28 17:54:45 -05:00
|
|
|
let ccx = bcx.ccx();
|
2013-02-19 01:40:42 -06:00
|
|
|
let (V_filename, V_line) = match sp_opt {
|
2012-08-28 17:54:45 -05:00
|
|
|
Some(sp) => {
|
|
|
|
let sess = bcx.sess();
|
2012-11-12 20:24:56 -06:00
|
|
|
let loc = sess.parse_sess.cm.lookup_char_pos(sp.lo);
|
2013-06-12 12:02:55 -05:00
|
|
|
(C_cstr(bcx.ccx(), loc.file.name),
|
2013-02-20 18:41:21 -06:00
|
|
|
loc.line as int)
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
None => {
|
2013-06-12 12:02:55 -05:00
|
|
|
(C_cstr(bcx.ccx(), @"<runtime>"), 0)
|
2012-08-28 17:54:45 -05:00
|
|
|
}
|
|
|
|
};
|
2013-06-15 22:45:48 -05:00
|
|
|
let V_str = PointerCast(bcx, V_fail_str, Type::i8p());
|
|
|
|
let V_filename = PointerCast(bcx, V_filename, Type::i8p());
|
2012-08-28 17:54:45 -05:00
|
|
|
let args = ~[V_str, V_filename, C_int(ccx, V_line)];
|
2013-07-15 22:42:13 -05:00
|
|
|
let did = langcall(bcx, sp_opt, "", FailFnLangItem);
|
|
|
|
let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;
|
2012-08-28 17:54:45 -05:00
|
|
|
Unreachable(bcx);
|
|
|
|
return bcx;
|
|
|
|
}
|
2012-09-29 06:34:11 -05:00
|
|
|
|
2013-08-31 11:13:04 -05:00
|
|
|
pub fn trans_fail_bounds_check(bcx: @mut Block, sp: Span,
|
2013-07-17 05:12:08 -05:00
|
|
|
index: ValueRef, len: ValueRef) -> @mut Block {
|
2013-06-16 23:23:24 -05:00
|
|
|
let _icx = push_ctxt("trans_fail_bounds_check");
|
2013-05-04 13:29:32 -05:00
|
|
|
let (filename, line) = filename_and_line_num_from_span(bcx, sp);
|
2012-09-29 06:34:11 -05:00
|
|
|
let args = ~[filename, line, index, len];
|
2013-07-15 22:42:13 -05:00
|
|
|
let did = langcall(bcx, Some(sp), "", FailBoundsCheckFnLangItem);
|
|
|
|
let bcx = callee::trans_lang_call(bcx, did, args, Some(expr::Ignore)).bcx;
|
2012-09-29 06:34:11 -05:00
|
|
|
Unreachable(bcx);
|
|
|
|
return bcx;
|
|
|
|
}
|