Skip to content

Commit dcd5d14

Browse files
committed
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%.
1 parent a9eb868 commit dcd5d14

File tree

7 files changed

+113
-68
lines changed

7 files changed

+113
-68
lines changed

src/librustc/middle/trans/base.rs

Lines changed: 68 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,11 +1102,6 @@ pub fn trans_trace(bcx: block, sp_opt: Option<span>, trace_str: @str) {
11021102
Call(bcx, ccx.upcalls.trace, args);
11031103
}
11041104

1105-
pub fn build_return(bcx: block) {
1106-
let _icx = push_ctxt("build_return");
1107-
Br(bcx, bcx.fcx.llreturn);
1108-
}
1109-
11101105
pub fn ignore_lhs(_bcx: block, local: &ast::local) -> bool {
11111106
match local.node.pat.node {
11121107
ast::pat_wild => true, _ => false
@@ -1364,6 +1359,42 @@ pub fn cleanup_and_leave(bcx: block,
13641359
}
13651360
}
13661361

1362+
pub fn cleanup_block(bcx: block, upto: Option<BasicBlockRef>) -> block{
1363+
let _icx = push_ctxt("cleanup_block");
1364+
let mut cur = bcx;
1365+
let mut bcx = bcx;
1366+
loop {
1367+
debug!("cleanup_block: %s", cur.to_str());
1368+
1369+
if bcx.sess().trace() {
1370+
trans_trace(
1371+
bcx, None,
1372+
(fmt!("cleanup_block(%s)", cur.to_str())).to_managed());
1373+
}
1374+
1375+
let mut cur_scope = cur.scope;
1376+
loop {
1377+
cur_scope = match cur_scope {
1378+
Some (inf) => {
1379+
bcx = trans_block_cleanups_(bcx, inf.cleanups.to_owned(), false);
1380+
inf.parent
1381+
}
1382+
None => break
1383+
}
1384+
}
1385+
1386+
match upto {
1387+
Some(bb) => { if cur.llbb == bb { break; } }
1388+
_ => ()
1389+
}
1390+
cur = match cur.parent {
1391+
Some(next) => next,
1392+
None => { assert!(upto.is_none()); break; }
1393+
};
1394+
}
1395+
bcx
1396+
}
1397+
13671398
pub fn cleanup_and_Br(bcx: block, upto: block, target: BasicBlockRef) {
13681399
let _icx = push_ctxt("cleanup_and_Br");
13691400
cleanup_and_leave(bcx, Some(upto.llbb), Some(target));
@@ -1544,7 +1575,6 @@ pub fn arrayalloca(cx: block, ty: Type, v: ValueRef) -> ValueRef {
15441575

15451576
pub struct BasicBlocks {
15461577
sa: BasicBlockRef,
1547-
rt: BasicBlockRef
15481578
}
15491579

15501580
// Creates the standard set of basic blocks for a function
@@ -1554,12 +1584,18 @@ pub fn mk_standard_basic_blocks(llfn: ValueRef) -> BasicBlocks {
15541584
BasicBlocks {
15551585
sa: str::as_c_str("static_allocas",
15561586
|buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf)),
1557-
rt: str::as_c_str("return",
1558-
|buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf))
15591587
}
15601588
}
15611589
}
15621590

1591+
pub fn mk_return_basic_block(llfn: ValueRef) -> BasicBlockRef {
1592+
unsafe {
1593+
let cx = task_llcx();
1594+
str::as_c_str("return",
1595+
|buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf))
1596+
}
1597+
}
1598+
15631599
// Creates and returns space for, or returns the argument representing, the
15641600
// slot where the return value of the function must go.
15651601
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,
16131649
llretptr: None,
16141650
llstaticallocas: llbbs.sa,
16151651
llloadenv: None,
1616-
llreturn: llbbs.rt,
1652+
llreturn: None,
16171653
llself: None,
16181654
personality: None,
16191655
loop_ret: None,
@@ -1757,16 +1793,24 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt,
17571793

17581794
// Ties up the llstaticallocas -> llloadenv -> lltop edges,
17591795
// and builds the return block.
1760-
pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef) {
1796+
pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef, last_bcx: block) {
17611797
let _icx = push_ctxt("finish_fn");
17621798
tie_up_header_blocks(fcx, lltop);
1763-
build_return_block(fcx);
1799+
1800+
let ret_cx = match fcx.llreturn {
1801+
Some(llreturn) => {
1802+
if !last_bcx.terminated {
1803+
Br(last_bcx, llreturn);
1804+
}
1805+
raw_block(fcx, false, llreturn)
1806+
}
1807+
None => last_bcx
1808+
};
1809+
build_return_block(fcx, ret_cx);
17641810
}
17651811

17661812
// Builds the return block for a function.
1767-
pub fn build_return_block(fcx: fn_ctxt) {
1768-
let ret_cx = raw_block(fcx, false, fcx.llreturn);
1769-
1813+
pub fn build_return_block(fcx: fn_ctxt, ret_cx: block) {
17701814
// Return the value if this function immediate; otherwise, return void.
17711815
if fcx.llretptr.is_some() && fcx.has_immediate_return_value {
17721816
Ret(ret_cx, Load(ret_cx, fcx.llretptr.get()))
@@ -1854,16 +1898,21 @@ pub fn trans_closure(ccx: @mut CrateContext,
18541898
}
18551899

18561900
finish(bcx);
1857-
cleanup_and_Br(bcx, bcx_top, fcx.llreturn);
1901+
match fcx.llreturn {
1902+
Some(llreturn) => cleanup_and_Br(bcx, bcx_top, llreturn),
1903+
None => bcx = cleanup_block(bcx, Some(bcx_top.llbb))
1904+
};
18581905

18591906
// Put return block after all other blocks.
18601907
// This somewhat improves single-stepping experience in debugger.
18611908
unsafe {
1862-
llvm::LLVMMoveBasicBlockAfter(fcx.llreturn, bcx.llbb);
1909+
for fcx.llreturn.iter().advance |&llreturn| {
1910+
llvm::LLVMMoveBasicBlockAfter(llreturn, bcx.llbb);
1911+
}
18631912
}
18641913

18651914
// Insert the mandatory first few basic blocks before lltop.
1866-
finish_fn(fcx, lltop);
1915+
finish_fn(fcx, lltop, bcx);
18671916
}
18681917

18691918
// 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>(
20462095
let arg_ty = arg_tys[i];
20472096
memcpy_ty(bcx, lldestptr, llarg, arg_ty);
20482097
}
2049-
build_return(bcx);
2050-
finish_fn(fcx, lltop);
2098+
finish_fn(fcx, lltop, bcx);
20512099
}
20522100

20532101
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,
22882336
let args = ~[llenvarg];
22892337
Call(bcx, main_llfn, args);
22902338

2291-
build_return(bcx);
2292-
finish_fn(fcx, lltop);
2339+
finish_fn(fcx, lltop, bcx);
22932340
return llfdecl;
22942341
}
22952342

src/librustc/middle/trans/callee.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ pub fn trans_call_inner(in_cx: block,
704704
Store(bcx, C_bool(false), bcx.fcx.llretptr.get());
705705
}
706706
}
707-
base::cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
707+
base::cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
708708
Unreachable(bcx);
709709
bcx
710710
}

src/librustc/middle/trans/common.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ pub struct fn_ctxt_ {
184184
// (LLVM requires that arguments be copied to local allocas before
185185
// allowing most any operation to be performed on them.)
186186
llloadenv: Option<BasicBlockRef>,
187-
llreturn: BasicBlockRef,
187+
llreturn: Option<BasicBlockRef>,
188188
// The 'self' value currently in use in this function, if there
189189
// is one.
190190
//
@@ -251,6 +251,13 @@ impl fn_ctxt_ {
251251
}
252252
}
253253

254+
pub fn get_llreturn(&mut self) -> BasicBlockRef {
255+
if self.llreturn.is_none() {
256+
self.llreturn = Some(base::mk_return_basic_block(self.llfn));
257+
}
258+
259+
self.llreturn.get()
260+
}
254261
}
255262

256263
pub type fn_ctxt = @mut fn_ctxt_;

src/librustc/middle/trans/controlflow.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ pub fn trans_break_cont(bcx: block,
279279
// This is a return from a loop body block
280280
None => {
281281
Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.get());
282-
cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
282+
cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
283283
Unreachable(bcx);
284284
return bcx;
285285
}
@@ -328,7 +328,7 @@ pub fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block {
328328
}
329329
_ => ()
330330
}
331-
cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn));
331+
cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn()));
332332
Unreachable(bcx);
333333
return bcx;
334334
}

src/librustc/middle/trans/foreign.rs

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,10 @@ fn build_shim_fn_(ccx: @mut CrateContext,
164164
// follow the normal Rust calling conventions.
165165
tie_up_header_blocks(fcx, lltop);
166166

167-
let ret_cx = raw_block(fcx, false, fcx.llreturn);
167+
let ret_cx = match fcx.llreturn {
168+
Some(llreturn) => raw_block(fcx, false, llreturn),
169+
None => bcx
170+
};
168171
RetVoid(ret_cx);
169172

170173
return llshimfn;
@@ -217,7 +220,10 @@ fn build_wrap_fn_(ccx: @mut CrateContext,
217220
tie_up_header_blocks(fcx, lltop);
218221

219222
// Then return according to the C ABI.
220-
let return_context = raw_block(fcx, false, fcx.llreturn);
223+
let return_context = match fcx.llreturn {
224+
Some(llreturn) => raw_block(fcx, false, llreturn),
225+
None => bcx
226+
};
221227

222228
let llfunctiontype = val_ty(llwrapfn);
223229
let llfunctiontype = llfunctiontype.element_type();
@@ -388,7 +394,6 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
388394
tys.ret_def,
389395
llargbundle,
390396
llretval);
391-
build_return(bcx);
392397
}
393398

394399
let lname = link_name(ccx, foreign_item);
@@ -438,8 +443,7 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
438443
if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
439444
Store(bcx, retval, fcx.llretptr.get());
440445
}
441-
build_return(bcx);
442-
finish_fn(fcx, lltop);
446+
finish_fn(fcx, lltop, bcx);
443447
}
444448

445449
// FIXME (#2535): this is very shaky and probably gets ABIs wrong all
@@ -467,8 +471,7 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
467471
if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) {
468472
Store(bcx, retval, fcx.llretptr.get());
469473
}
470-
build_return(bcx);
471-
finish_fn(fcx, lltop);
474+
finish_fn(fcx, lltop, bcx);
472475
}
473476

474477
fn build_wrap_fn(ccx: @mut CrateContext,
@@ -534,7 +537,6 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext,
534537
let llretptr = load_inbounds(bcx, llargbundle, [0, arg_count]);
535538
Store(bcx, Load(bcx, llretptr), retptr);
536539
}
537-
build_return(bcx);
538540
}
539541
}
540542
}
@@ -629,8 +631,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
629631
}
630632
}
631633

632-
build_return(bcx);
633-
finish_fn(fcx, lltop);
634+
finish_fn(fcx, lltop, bcx);
634635

635636
return;
636637
}
@@ -1124,8 +1125,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
11241125
ccx.sess.span_bug(item.span, "unknown intrinsic");
11251126
}
11261127
}
1127-
build_return(bcx);
1128-
finish_fn(fcx, lltop);
1128+
finish_fn(fcx, lltop, bcx);
11291129
}
11301130

11311131
/**
@@ -1257,8 +1257,6 @@ pub fn trans_foreign_fn(ccx: @mut CrateContext,
12571257
// NB: The return pointer in the Rust ABI function is wired
12581258
// directly into the return slot in the shim struct.
12591259
}
1260-
1261-
build_return(bcx);
12621260
}
12631261

12641262
let shim_name = link::mangle_internal_name_by_path(
@@ -1314,7 +1312,6 @@ pub fn trans_foreign_fn(ccx: @mut CrateContext,
13141312
fn build_ret(bcx: block, tys: &ShimTypes, llargbundle: ValueRef) {
13151313
let _icx = push_ctxt("foreign::foreign::wrap::build_ret");
13161314
tys.fn_ty.build_wrap_ret(bcx, tys.llsig.llarg_tys, llargbundle);
1317-
build_return(bcx);
13181315
}
13191316
}
13201317

0 commit comments

Comments
 (0)