Skip to content

Commit 642cd46

Browse files
committed
Avoid quadratic growth of cleanup blocks
Currently, cleanup blocks are only reused when there are nested scopes, the child scope's cleanup block will terminate with a jump to the parent scope's cleanup block. But within a single scope, adding or revoking any cleanup will force a fresh cleanup block. This means quadratic growth with the number of allocations in a scope, because each allocation needs a landing pad. Instead of forcing a fresh cleanup block, we can keep a list chained cleanup blocks that form a prefix of the currently required cleanups. That way, the next cleanup block only has to handle newly added cleanups. And by keeping the whole list instead of just the latest block, we can also handle revocations more efficiently, by only dropping those blocks that are no longer required, instead of all of them. Reduces the size of librustc by about 5% and the time required to build it by about 10%.
1 parent ebca26c commit 642cd46

File tree

2 files changed

+31
-13
lines changed

2 files changed

+31
-13
lines changed

src/librustc/middle/trans/base.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -1317,26 +1317,38 @@ pub fn cleanup_and_leave(bcx: block,
13171317

13181318
match cur.kind {
13191319
block_scope(inf) if !inf.empty_cleanups() => {
1320-
let (sub_cx, inf_cleanups) = {
1320+
let (sub_cx, dest, inf_cleanups) = {
13211321
let inf = &mut *inf; // FIXME(#5074) workaround stage0
1322+
let mut skip = 0;
1323+
let mut dest = None;
13221324
{
1323-
let r = vec::find((*inf).cleanup_paths, |cp| cp.target == leave);
1325+
let r = vec::rfind((*inf).cleanup_paths, |cp| cp.target == leave);
13241326
for r.iter().advance |cp| {
1325-
Br(bcx, cp.dest);
1326-
return;
1327+
if cp.size == inf.cleanups.len() {
1328+
Br(bcx, cp.dest);
1329+
return;
1330+
}
1331+
1332+
skip = cp.size;
1333+
dest = Some(cp.dest);
13271334
}
13281335
}
13291336
let sub_cx = sub_block(bcx, "cleanup");
13301337
Br(bcx, sub_cx.llbb);
13311338
inf.cleanup_paths.push(cleanup_path {
13321339
target: leave,
1340+
size: inf.cleanups.len(),
13331341
dest: sub_cx.llbb
13341342
});
1335-
(sub_cx, copy inf.cleanups)
1343+
(sub_cx, dest, inf.cleanups.tailn(skip).to_owned())
13361344
};
13371345
bcx = trans_block_cleanups_(sub_cx,
13381346
inf_cleanups,
13391347
is_lpad);
1348+
for dest.iter().advance |&dest| {
1349+
Br(bcx, dest);
1350+
return;
1351+
}
13401352
}
13411353
_ => ()
13421354
}

src/librustc/middle/trans/common.rs

+14-8
Original file line numberDiff line numberDiff line change
@@ -325,11 +325,17 @@ pub enum cleanup {
325325
// target: none means the path ends in an resume instruction
326326
pub struct cleanup_path {
327327
target: Option<BasicBlockRef>,
328+
size: uint,
328329
dest: BasicBlockRef
329330
}
330331

331-
pub fn scope_clean_changed(scope_info: &mut scope_info) {
332-
if scope_info.cleanup_paths.len() > 0u { scope_info.cleanup_paths = ~[]; }
332+
pub fn shrink_scope_clean(scope_info: &mut scope_info, size: uint) {
333+
scope_info.landing_pad = None;
334+
scope_info.cleanup_paths = scope_info.cleanup_paths.iter()
335+
.take_while(|&cu| cu.size <= size).transform(|&x|x).collect();
336+
}
337+
338+
pub fn grow_scope_clean(scope_info: &mut scope_info) {
333339
scope_info.landing_pad = None;
334340
}
335341

@@ -374,7 +380,7 @@ pub fn add_clean(bcx: block, val: ValueRef, t: ty::t) {
374380
scope_info.cleanups.push(
375381
clean(|a| glue::drop_ty_root(a, root, rooted, t),
376382
cleanup_type));
377-
scope_clean_changed(scope_info);
383+
grow_scope_clean(scope_info);
378384
}
379385
}
380386

@@ -388,7 +394,7 @@ pub fn add_clean_temp_immediate(cx: block, val: ValueRef, ty: ty::t) {
388394
scope_info.cleanups.push(
389395
clean_temp(val, |a| glue::drop_ty_immediate(a, val, ty),
390396
cleanup_type));
391-
scope_clean_changed(scope_info);
397+
grow_scope_clean(scope_info);
392398
}
393399
}
394400
pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
@@ -402,7 +408,7 @@ pub fn add_clean_temp_mem(bcx: block, val: ValueRef, t: ty::t) {
402408
scope_info.cleanups.push(
403409
clean_temp(val, |a| glue::drop_ty_root(a, root, rooted, t),
404410
cleanup_type));
405-
scope_clean_changed(scope_info);
411+
grow_scope_clean(scope_info);
406412
}
407413
}
408414
pub fn add_clean_return_to_mut(bcx: block,
@@ -434,7 +440,7 @@ pub fn add_clean_return_to_mut(bcx: block,
434440
filename_val,
435441
line_val),
436442
normal_exit_only));
437-
scope_clean_changed(scope_info);
443+
grow_scope_clean(scope_info);
438444
}
439445
}
440446
pub fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
@@ -451,7 +457,7 @@ pub fn add_clean_free(cx: block, ptr: ValueRef, heap: heap) {
451457
do in_scope_cx(cx) |scope_info| {
452458
scope_info.cleanups.push(clean_temp(ptr, free_fn,
453459
normal_exit_and_unwind));
454-
scope_clean_changed(scope_info);
460+
grow_scope_clean(scope_info);
455461
}
456462
}
457463

@@ -474,7 +480,7 @@ pub fn revoke_clean(cx: block, val: ValueRef) {
474480
vec::slice(scope_info.cleanups,
475481
*i + 1u,
476482
scope_info.cleanups.len()));
477-
scope_clean_changed(scope_info);
483+
shrink_scope_clean(scope_info, *i);
478484
}
479485
}
480486
}

0 commit comments

Comments
 (0)