Avoid return blocks that have only a single predecessor

Currently, we always create a dedicated "return" basic block, but when
there's only a single predecessor for that block, it can be merged with
that predecessor. We can achieve that merge by only creating the return
block on demand, avoiding its creation when its not required.

Reduces the pre-optimization size of librustc.ll created with --passes ""
by about 90k lines which equals about 4%.
This commit is contained in:
Björn Steinbrink 2013-07-13 03:25:46 +02:00
parent a9eb868230
commit dcd5d14e6c
7 changed files with 113 additions and 68 deletions

View File

@ -1102,11 +1102,6 @@ pub fn trans_trace(bcx: block, sp_opt: Option<span>, trace_str: @str) {
Call(bcx, ccx.upcalls.trace, args);
}
pub fn build_return(bcx: block) {
let _icx = push_ctxt("build_return");
Br(bcx, bcx.fcx.llreturn);
}
pub fn ignore_lhs(_bcx: block, local: &ast::local) -> bool {
match local.node.pat.node {
ast::pat_wild => true, _ => false
@ -1364,6 +1359,42 @@ pub fn cleanup_and_leave(bcx: block,
}
}
pub fn cleanup_block(bcx: block, upto: Option<BasicBlockRef>) -> block{
let _icx = push_ctxt("cleanup_block");
let mut cur = bcx;
let mut bcx = bcx;
loop {
debug!("cleanup_block: %s", cur.to_str());
if bcx.sess().trace() {
trans_trace(
bcx, None,
(fmt!("cleanup_block(%s)", cur.to_str())).to_managed());
}
let mut cur_scope = cur.scope;
loop {
cur_scope = match cur_scope {
Some (inf) => {
bcx = trans_block_cleanups_(bcx, inf.cleanups.to_owned(), false);
inf.parent
}
None => break
}
}
match upto {
Some(bb) => { if cur.llbb == bb { break; } }
_ => ()
}
cur = match cur.parent {
Some(next) => next,
None => { assert!(upto.is_none()); break; }
};
}
bcx
}
pub fn cleanup_and_Br(bcx: block, upto: block, target: BasicBlockRef) {
let _icx = push_ctxt("cleanup_and_Br");
cleanup_and_leave(bcx, Some(upto.llbb), Some(target));
@ -1544,7 +1575,6 @@ pub fn arrayalloca(cx: block, ty: Type, v: ValueRef) -> ValueRef {
pub struct BasicBlocks {
sa: BasicBlockRef,
rt: BasicBlockRef
}
// Creates the standard set of basic blocks for a function
@ -1554,12 +1584,18 @@ pub fn mk_standard_basic_blocks(llfn: ValueRef) -> BasicBlocks {
BasicBlocks {
sa: str::as_c_str("static_allocas",
|buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf)),
rt: str::as_c_str("return",
|buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf))
}
}
}
pub fn mk_return_basic_block(llfn: ValueRef) -> BasicBlockRef {
unsafe {
let cx = task_llcx();
str::as_c_str("return",
|buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf))
}
}
// Creates and returns space for, or returns the argument representing, the
// slot where the return value of the function must go.
pub fn make_return_pointer(fcx: fn_ctxt, output_type: ty::t) -> ValueRef {
@ -1613,7 +1649,7 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext,
llretptr: None,
llstaticallocas: llbbs.sa,
llloadenv: None,
llreturn: llbbs.rt,
llreturn: None,
llself: None,
personality: None,
loop_ret: None,
@ -1757,16 +1793,24 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
// Ties up the llstaticallocas -> llloadenv -> lltop edges,
// and builds the return block.
pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef) {
pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef, last_bcx: block) {
let _icx = push_ctxt("finish_fn");
tie_up_header_blocks(fcx, lltop);
build_return_block(fcx);
let ret_cx = match fcx.llreturn {
Some(llreturn) => {
if !last_bcx.terminated {
Br(last_bcx, llreturn);
}
raw_block(fcx, false, llreturn)
}
None => last_bcx
};
build_return_block(fcx, ret_cx);
}
// Builds the return block for a function.
pub fn build_return_block(fcx: fn_ctxt) {
let ret_cx = raw_block(fcx, false, fcx.llreturn);
pub fn build_return_block(fcx: fn_ctxt, ret_cx: block) {
// Return the value if this function immediate; otherwise, return void.
if fcx.llretptr.is_some() && fcx.has_immediate_return_value {
Ret(ret_cx, Load(ret_cx, fcx.llretptr.get()))
@ -1854,16 +1898,21 @@ pub fn trans_closure(ccx: @mut CrateContext,
}
finish(bcx);
cleanup_and_Br(bcx, bcx_top, fcx.llreturn);
match fcx.llreturn {
Some(llreturn) => cleanup_and_Br(bcx, bcx_top, llreturn),
None => bcx = cleanup_block(bcx, Some(bcx_top.llbb))
};
// Put return block after all other blocks.
// This somewhat improves single-stepping experience in debugger.
unsafe {
llvm::LLVMMoveBasicBlockAfter(fcx.llreturn, bcx.llbb);
for fcx.llreturn.iter().advance |&llreturn| {
llvm::LLVMMoveBasicBlockAfter(llreturn, bcx.llbb);
}
}
// Insert the mandatory first few basic blocks before lltop.
finish_fn(fcx, lltop);
finish_fn(fcx, lltop, bcx);
}
// trans_fn: creates an LLVM function corresponding to a source language
@ -2046,8 +2095,7 @@ pub fn trans_enum_variant_or_tuple_like_struct<A:IdAndTy>(
let arg_ty = arg_tys[i];
memcpy_ty(bcx, lldestptr, llarg, arg_ty);
}
build_return(bcx);
finish_fn(fcx, lltop);
finish_fn(fcx, lltop, bcx);
}
pub fn trans_enum_def(ccx: @mut CrateContext, enum_definition: &ast::enum_def,
@ -2288,8 +2336,7 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext,
let args = ~[llenvarg];
Call(bcx, main_llfn, args);
build_return(bcx);
finish_fn(fcx, lltop);
finish_fn(fcx, lltop, bcx);
return llfdecl;
}

View File

@ -704,7 +704,7 @@ pub fn trans_call_inner(in_cx: block,
Store(bcx, C_bool(false), bcx.fcx.llretptr.get());
}
}
base::cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
base::cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
Unreachable(bcx);
bcx
}

View File

@ -184,7 +184,7 @@ pub struct fn_ctxt_ {
// (LLVM requires that arguments be copied to local allocas before
// allowing most any operation to be performed on them.)
llloadenv: Option<BasicBlockRef>,
llreturn: BasicBlockRef,
llreturn: Option<BasicBlockRef>,
// The 'self' value currently in use in this function, if there
// is one.
//
@ -251,6 +251,13 @@ impl fn_ctxt_ {
}
}
pub fn get_llreturn(&mut self) -> BasicBlockRef {
if self.llreturn.is_none() {
self.llreturn = Some(base::mk_return_basic_block(self.llfn));
}
self.llreturn.get()
}
}
pub type fn_ctxt = @mut fn_ctxt_;

View File

@ -279,7 +279,7 @@ pub fn trans_break_cont(bcx: block,
// This is a return from a loop body block
None => {
Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.get());
cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
Unreachable(bcx);
return bcx;
}
@ -328,7 +328,7 @@ pub fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block {
}
_ => ()
}
cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
Unreachable(bcx);
return bcx;
}

View File

@ -164,7 +164,10 @@ fn build_shim_fn_(ccx: @mut CrateContext,
// follow the normal Rust calling conventions.
tie_up_header_blocks(fcx, lltop);
let ret_cx = raw_block(fcx, false, fcx.llreturn);
let ret_cx = match fcx.llreturn {
Some(llreturn) => raw_block(fcx, false, llreturn),
None => bcx
};
RetVoid(ret_cx);
return llshimfn;
@ -217,7 +220,10 @@ fn build_wrap_fn_(ccx: @mut CrateContext,
tie_up_header_blocks(fcx, lltop);
// Then return according to the C ABI.
let return_context = raw_block(fcx, false, fcx.llreturn);
let return_context = match fcx.llreturn {
Some(llreturn) => raw_block(fcx, false, llreturn),
None => bcx
};
let llfunctiontype = val_ty(llwrapfn);
let llfunctiontype = llfunctiontype.element_type();
@ -388,7 +394,6 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
tys.ret_def,
llargbundle,
llretval);
build_return(bcx);
}
let lname = link_name(ccx, foreign_item);
@ -438,8 +443,7 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
Store(bcx, retval, fcx.llretptr.get());
}
build_return(bcx);
finish_fn(fcx, lltop);
finish_fn(fcx, lltop, bcx);
}
// FIXME (#2535): this is very shaky and probably gets ABIs wrong all
@ -467,8 +471,7 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
Store(bcx, retval, fcx.llretptr.get());
}
build_return(bcx);
finish_fn(fcx, lltop);
finish_fn(fcx, lltop, bcx);
}
fn build_wrap_fn(ccx: @mut CrateContext,
@ -534,7 +537,6 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
let llretptr = load_inbounds(bcx, llargbundle, [0, arg_count]);
Store(bcx, Load(bcx, llretptr), retptr);
}
build_return(bcx);
}
}
}
@ -629,8 +631,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
}
}
build_return(bcx);
finish_fn(fcx, lltop);
finish_fn(fcx, lltop, bcx);
return;
}
@ -1124,8 +1125,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
ccx.sess.span_bug(item.span, "unknown intrinsic");
}
}
build_return(bcx);
finish_fn(fcx, lltop);
finish_fn(fcx, lltop, bcx);
}
/**
@ -1257,8 +1257,6 @@ pub fn trans_foreign_fn(ccx: @mut CrateContext,
// NB: The return pointer in the Rust ABI function is wired
// directly into the return slot in the shim struct.
}
build_return(bcx);
}
let shim_name = link::mangle_internal_name_by_path(
@ -1314,7 +1312,6 @@ pub fn trans_foreign_fn(ccx: @mut CrateContext,
fn build_ret(bcx: block, tys: &ShimTypes, llargbundle: ValueRef) {
let _icx = push_ctxt("foreign::foreign::wrap::build_ret");
tys.fn_ty.build_wrap_ret(bcx, tys.llsig.llarg_tys, llargbundle);
build_return(bcx);
}
}

View File

@ -348,9 +348,9 @@ pub fn call_tydesc_glue(cx: block, v: ValueRef, t: ty::t, field: uint)
return cx;
}
pub fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) {
pub fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) -> block {
let _icx = push_ctxt("make_visit_glue");
let bcx = do with_scope(bcx, None, "visitor cleanup") |bcx| {
do with_scope(bcx, None, "visitor cleanup") |bcx| {
let mut bcx = bcx;
let (visitor_trait, object_ty) = ty::visitor_object_ty(bcx.tcx());
let v = PointerCast(bcx, v, type_of::type_of(bcx.ccx(), object_ty).ptr_to());
@ -358,14 +358,13 @@ pub fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) {
// The visitor is a boxed object and needs to be dropped
add_clean(bcx, v, object_ty);
bcx
};
build_return(bcx);
}
}
pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) {
pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) -> block {
// NB: v0 is an *alias* of type t here, not a direct value.
let _icx = push_ctxt("make_free_glue");
let bcx = match ty::get(t).sty {
match ty::get(t).sty {
ty::ty_box(body_mt) => {
let v = Load(bcx, v);
let body = GEPi(bcx, v, [0u, abi::box_field_body]);
@ -389,9 +388,7 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) {
tvec::make_uniq_free_glue(bcx, v, t)
}
ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => {
make_free_glue(bcx, v,
tvec::expand_boxed_vec_ty(bcx.tcx(), t));
return;
make_free_glue(bcx, v, tvec::expand_boxed_vec_ty(bcx.tcx(), t))
}
ty::ty_closure(_) => {
closure::make_closure_glue(bcx, v, t, free_ty)
@ -400,8 +397,7 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) {
closure::make_opaque_cbox_free_glue(bcx, ck, v)
}
_ => bcx
};
build_return(bcx);
}
}
pub fn trans_struct_drop_flag(bcx: block, t: ty::t, v0: ValueRef, dtor_did: ast::def_id,
@ -475,11 +471,11 @@ pub fn trans_struct_drop(mut bcx: block, t: ty::t, v0: ValueRef, dtor_did: ast::
bcx
}
pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) {
pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) -> block {
// NB: v0 is an *alias* of type t here, not a direct value.
let _icx = push_ctxt("make_drop_glue");
let ccx = bcx.ccx();
let bcx = match ty::get(t).sty {
match ty::get(t).sty {
ty::ty_box(_) | ty::ty_opaque_box |
ty::ty_estr(ty::vstore_box) | ty::ty_evec(_, ty::vstore_box) => {
decr_refcnt_maybe_free(bcx, Load(bcx, v0), Some(v0), t)
@ -542,8 +538,7 @@ pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) {
iter_structural_ty(bcx, v0, t, drop_ty)
} else { bcx }
}
};
build_return(bcx);
}
}
// box_ptr_ptr is optional, it is constructed if not supplied.
@ -569,10 +564,10 @@ pub fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef,
}
pub fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) {
pub fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) -> block {
let _icx = push_ctxt("make_take_glue");
// NB: v is a *pointer* to type t here, not a direct value.
let bcx = match ty::get(t).sty {
match ty::get(t).sty {
ty::ty_box(_) | ty::ty_opaque_box |
ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => {
incr_refcnt_of_boxed(bcx, Load(bcx, v)); bcx
@ -638,9 +633,7 @@ pub fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) {
iter_structural_ty(bcx, v, t, take_ty)
}
_ => bcx
};
build_return(bcx);
}
}
pub fn incr_refcnt_of_boxed(cx: block, box_ptr: ValueRef) {
@ -690,7 +683,7 @@ pub fn declare_tydesc(ccx: &mut CrateContext, t: ty::t) -> @mut tydesc_info {
return inf;
}
pub type glue_helper<'self> = &'self fn(block, ValueRef, ty::t);
pub type glue_helper<'self> = &'self fn(block, ValueRef, ty::t) -> block;
pub fn declare_generic_glue(ccx: &mut CrateContext, t: ty::t, llfnty: Type,
name: &str) -> ValueRef {
@ -723,11 +716,9 @@ pub fn make_generic_glue_inner(ccx: @mut CrateContext,
let llrawptr0 = unsafe { llvm::LLVMGetParam(llfn, rawptr0_arg as c_uint) };
let llty = type_of(ccx, t);
let llrawptr0 = PointerCast(bcx, llrawptr0, llty.ptr_to());
helper(bcx, llrawptr0, t);
let bcx = helper(bcx, llrawptr0, t);
// This is from the general finish fn, but that emits a ret {} that we don't want
Br(raw_block(fcx, false, fcx.llstaticallocas), lltop);
RetVoid(raw_block(fcx, false, fcx.llreturn));
finish_fn(fcx, lltop, bcx);
return llfn;
}

View File

@ -299,12 +299,15 @@ impl Reflector {
//
llvm::LLVMGetParam(llfdecl, fcx.arg_pos(0u) as c_uint)
};
let bcx = top_scope_block(fcx, None);
let mut bcx = top_scope_block(fcx, None);
let arg = BitCast(bcx, arg, llptrty);
let ret = adt::trans_get_discr(bcx, repr, arg);
Store(bcx, ret, fcx.llretptr.get());
cleanup_and_Br(bcx, bcx, fcx.llreturn);
finish_fn(fcx, bcx.llbb);
match fcx.llreturn {
Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn),
None => bcx = cleanup_block(bcx, Some(bcx.llbb))
};
finish_fn(fcx, bcx.llbb, bcx);
llfdecl
};