Skip to content

Commit 7d68cbd

Browse files
committed
Have blocks return their result by writing to a reserved space
This makes it possible to pass the location of that space through to nested blocks, resulting in less copying, taking, and dropping. This makes the compiler slightly faster and 19k smaller. A FIXME is to use 'move' semantics when returning the values from the block -- don't bump the refcount and drop it again for the returning block, but simply assign ownership to the receiver. To do this, we'll need a way to (safely) scrub things from a block's cleanup list.
1 parent 81fc2d8 commit 7d68cbd

File tree

1 file changed

+103
-71
lines changed

1 file changed

+103
-71
lines changed

src/comp/middle/trans.rs

+103-71
Original file line numberDiff line numberDiff line change
@@ -3712,14 +3712,28 @@ fn join_results(&@block_ctxt parent_cx,
37123712
ret res(join_cx, phi);
37133713
}
37143714

3715+
fn join_branches(&@block_ctxt parent_cx, &vec[result] ins) -> @block_ctxt {
3716+
auto out = new_sub_block_ctxt(parent_cx, "join");
3717+
for (result r in ins) {
3718+
if (!is_terminated(r.bcx)) {
3719+
r.bcx.build.Br(out.llbb);
3720+
}
3721+
}
3722+
ret out;
3723+
}
3724+
3725+
tag out_method {
3726+
return;
3727+
save_in(ValueRef);
3728+
}
3729+
37153730
fn trans_if(&@block_ctxt cx, &@ast::expr cond,
37163731
&ast::block thn, &option::t[@ast::expr] els,
3717-
&ast::ann ann) -> result {
3718-
3732+
&ast::ann ann, &out_method output) -> result {
37193733
auto cond_res = trans_expr(cx, cond);
37203734

37213735
auto then_cx = new_scope_block_ctxt(cx, "then");
3722-
auto then_res = trans_block(then_cx, thn);
3736+
auto then_res = trans_block(then_cx, thn, output);
37233737

37243738
auto else_cx = new_scope_block_ctxt(cx, "else");
37253739

@@ -3739,14 +3753,14 @@ fn trans_if(&@block_ctxt cx, &@ast::expr cond,
37393753
a = ann);
37403754
auto elseif_blk = rec(node = elseif_blk_,
37413755
span = elexpr.span);
3742-
else_res = trans_block(else_cx, elseif_blk);
3756+
else_res = trans_block(else_cx, elseif_blk, output);
37433757
}
37443758
case (ast::expr_block(?blk, _)) {
37453759
// Calling trans_block directly instead of trans_expr
37463760
// because trans_expr will create another scope block
37473761
// context for the block, but we've already got the
37483762
// 'else' context
3749-
else_res = trans_block(else_cx, blk);
3763+
else_res = trans_block(else_cx, blk, output);
37503764
}
37513765
}
37523766

@@ -3771,9 +3785,7 @@ fn trans_if(&@block_ctxt cx, &@ast::expr cond,
37713785
cond_res.bcx.build.CondBr(cond_res.val,
37723786
then_cx.llbb,
37733787
else_cx.llbb);
3774-
3775-
ret join_results(cx, expr_llty,
3776-
[then_res, else_res]);
3788+
ret res(join_branches(cx, [then_res, else_res]), C_nil());
37773789
}
37783790

37793791
fn trans_for(&@block_ctxt cx,
@@ -3795,7 +3807,7 @@ fn trans_for(&@block_ctxt cx,
37953807
auto bcx = copy_val(local_res.bcx, INIT, local_res.val, curr, t).bcx;
37963808
scope_cx.cleanups +=
37973809
[clean(bind drop_slot(_, local_res.val, t))];
3798-
bcx = trans_block(bcx, body).bcx;
3810+
bcx = trans_block(bcx, body, return).bcx;
37993811
bcx.build.Br(next_cx.llbb);
38003812
ret res(next_cx, C_nil());
38013813
}
@@ -4053,7 +4065,7 @@ fn trans_for_each(&@block_ctxt cx,
40534065

40544066
auto bcx = new_top_block_ctxt(fcx);
40554067
auto lltop = bcx.llbb;
4056-
auto r = trans_block(bcx, body);
4068+
auto r = trans_block(bcx, body, return);
40574069

40584070
finish_fn(fcx, lltop);
40594071

@@ -4099,7 +4111,7 @@ fn trans_while(&@block_ctxt cx, &@ast::expr cond,
40994111
auto body_cx = new_loop_scope_block_ctxt(cx, option::none[@block_ctxt],
41004112
next_cx, "while loop body");
41014113

4102-
auto body_res = trans_block(body_cx, body);
4114+
auto body_res = trans_block(body_cx, body, return);
41034115
auto cond_res = trans_expr(cond_cx, cond);
41044116

41054117
body_res.bcx.build.Br(cond_cx.llbb);
@@ -4118,7 +4130,7 @@ fn trans_do_while(&@block_ctxt cx, &ast::block body,
41184130
auto body_cx = new_loop_scope_block_ctxt(cx, option::none[@block_ctxt],
41194131
next_cx, "do-while loop body");
41204132

4121-
auto body_res = trans_block(body_cx, body);
4133+
auto body_res = trans_block(body_cx, body, return);
41224134
auto cond_res = trans_expr(body_res.bcx, cond);
41234135

41244136
cond_res.bcx.build.CondBr(cond_res.val,
@@ -4259,7 +4271,8 @@ fn trans_pat_binding(&@block_ctxt cx, &@ast::pat pat,
42594271
}
42604272

42614273
fn trans_alt(&@block_ctxt cx, &@ast::expr expr,
4262-
&vec[ast::arm] arms, &ast::ann ann) -> result {
4274+
&vec[ast::arm] arms, &ast::ann ann,
4275+
&out_method output) -> result {
42634276
auto expr_res = trans_expr(cx, expr);
42644277

42654278
auto this_cx = expr_res.bcx;
@@ -4275,12 +4288,13 @@ fn trans_alt(&@block_ctxt cx, &@ast::expr expr,
42754288
auto binding_res = trans_pat_binding(binding_cx, arm.pat,
42764289
expr_res.val, false);
42774290

4278-
auto block_res = trans_block(binding_res.bcx, arm.block);
4291+
auto block_res = trans_block(binding_res.bcx, arm.block, output);
42794292
arm_results += [block_res];
42804293

42814294
this_cx = next_cx;
42824295
}
42834296

4297+
42844298
auto default_cx = this_cx;
42854299
auto default_res = trans_fail(default_cx, some[common::span](expr.span),
42864300
"non-exhaustive match failure");
@@ -4297,7 +4311,7 @@ fn trans_alt(&@block_ctxt cx, &@ast::expr expr,
42974311
}
42984312
}
42994313

4300-
ret join_results(cx, expr_llty, arm_results);
4314+
ret res(join_branches(cx, arm_results), C_nil());
43014315
}
43024316

43034317
type generic_info = rec(ty::t item_type,
@@ -4454,7 +4468,7 @@ fn trans_path(&@block_ctxt cx, &ast::path p, &ast::ann ann) -> lval_result {
44544468

44554469
auto lltagty;
44564470
if (ty::type_has_dynamic_size(cx.fcx.lcx.ccx.tcx,
4457-
tag_ty)) {
4471+
tag_ty)) {
44584472
lltagty = T_opaque_tag(cx.fcx.lcx.ccx.tn);
44594473
} else {
44604474
lltagty = type_of(cx.fcx.lcx.ccx, p.span, tag_ty);
@@ -5428,6 +5442,11 @@ fn trans_rec(&@block_ctxt cx, &vec[ast::field] fields,
54285442
}
54295443

54305444
fn trans_expr(&@block_ctxt cx, &@ast::expr e) -> result {
5445+
be trans_expr_out(cx, e, return);
5446+
}
5447+
5448+
fn trans_expr_out(&@block_ctxt cx, &@ast::expr e, out_method output)
5449+
-> result {
54315450
*cx = rec(sp=e.span with *cx);
54325451
alt (e.node) {
54335452
case (ast::expr_lit(?lit, ?ann)) {
@@ -5445,7 +5464,8 @@ fn trans_expr(&@block_ctxt cx, &@ast::expr e) -> result {
54455464
}
54465465

54475466
case (ast::expr_if(?cond, ?thn, ?els, ?ann)) {
5448-
ret trans_if(cx, cond, thn, els, ann);
5467+
ret with_out_method(bind trans_if(cx, cond, thn, els, ann, _),
5468+
cx, ann, output);
54495469
}
54505470

54515471
case (ast::expr_for(?decl, ?seq, ?body, _)) {
@@ -5465,18 +5485,18 @@ fn trans_expr(&@block_ctxt cx, &@ast::expr e) -> result {
54655485
}
54665486

54675487
case (ast::expr_alt(?expr, ?arms, ?ann)) {
5468-
ret trans_alt(cx, expr, arms, ann);
5488+
ret with_out_method(bind trans_alt(cx, expr, arms, ann, _),
5489+
cx, ann, output);
54695490
}
54705491

5471-
case (ast::expr_block(?blk, _)) {
5492+
case (ast::expr_block(?blk, ?ann)) {
54725493
*cx = rec(sp=blk.span with *cx);
54735494
auto sub_cx = new_scope_block_ctxt(cx, "block-expr body");
54745495
auto next_cx = new_sub_block_ctxt(cx, "next");
5475-
auto sub = trans_block(sub_cx, blk);
5476-
5496+
auto sub = with_out_method(bind trans_block(sub_cx, blk, _),
5497+
cx, ann, output);
54775498
cx.build.Br(sub_cx.llbb);
54785499
sub.bcx.build.Br(next_cx.llbb);
5479-
54805500
ret res(next_cx, sub.val);
54815501
}
54825502

@@ -5617,6 +5637,34 @@ fn trans_expr(&@block_ctxt cx, &@ast::expr e) -> result {
56175637
ret res(sub.res.bcx, load_if_immediate(sub.res.bcx, sub.res.val, t));
56185638
}
56195639

5640+
fn with_out_method(fn(&out_method) -> result work, &@block_ctxt cx,
5641+
&ast::ann ann, &out_method outer_output) -> result {
5642+
auto ccx = cx.fcx.lcx.ccx;
5643+
if (outer_output != return) {
5644+
ret work(outer_output);
5645+
} else {
5646+
auto tp = node_ann_type(ccx, ann);
5647+
if (ty::type_is_nil(ccx.tcx, tp)) {
5648+
ret work(return);
5649+
}
5650+
auto res_alloca = alloc_ty(cx, tp);
5651+
cx = zero_alloca(res_alloca.bcx, res_alloca.val, tp).bcx;
5652+
5653+
fn drop_hoisted_ty(&@block_ctxt cx,
5654+
ValueRef target,
5655+
ty::t t) -> result {
5656+
auto reg_val = load_if_immediate(cx, target, t);
5657+
ret drop_ty(cx, reg_val, t);
5658+
}
5659+
auto cleanup = bind drop_hoisted_ty(_, res_alloca.val, tp);
5660+
find_scope_cx(cx).cleanups += [clean(cleanup)];
5661+
5662+
auto done = work(save_in(res_alloca.val));
5663+
done.val = load_if_immediate(done.bcx, res_alloca.val, tp);
5664+
ret done;
5665+
}
5666+
}
5667+
56205668
// We pass structural values around the compiler "by pointer" and
56215669
// non-structural values (scalars, boxes, pointers) "by value". We call the
56225670
// latter group "immediates" and, in some circumstances when we know we have a
@@ -6472,9 +6520,8 @@ fn alloc_local(&@block_ctxt cx, &@ast::local local) -> result {
64726520
ret r;
64736521
}
64746522

6475-
fn trans_block(&@block_ctxt cx, &ast::block b) -> result {
6523+
fn trans_block(&@block_ctxt cx, &ast::block b, &out_method output) -> result {
64766524
auto bcx = cx;
6477-
64786525
for each (@ast::local local in block_locals(b)) {
64796526
*bcx = rec(sp=local_rhs_span(local, cx.sp) with *bcx);
64806527
bcx = alloc_local(bcx, local).bcx;
@@ -6491,58 +6538,43 @@ fn trans_block(&@block_ctxt cx, &ast::block b) -> result {
64916538
}
64926539
}
64936540

6541+
fn accept_out_method(&@ast::expr expr) -> bool {
6542+
ret alt (expr.node) {
6543+
case (ast::expr_if(_, _, _, _)) { true }
6544+
case (ast::expr_alt(_, _, _)) { true }
6545+
case (ast::expr_block(_, _)) { true }
6546+
case (_) { false }
6547+
};
6548+
}
6549+
64946550
alt (b.node.expr) {
64956551
case (some(?e)) {
6496-
// Hold onto the context for this scope since we'll need it to
6497-
// find the outer scope
6498-
auto scope_bcx = bcx;
6499-
r = trans_expr(bcx, e);
6552+
auto pass = output != return && accept_out_method(e);
6553+
if (pass) {
6554+
r = trans_expr_out(bcx, e, output);
6555+
} else {
6556+
r = trans_expr(bcx, e);
6557+
}
65006558
bcx = r.bcx;
65016559

6502-
if (is_terminated(bcx)) {
6560+
auto ccx = cx.fcx.lcx.ccx;
6561+
auto r_ty = ty::expr_ty(ccx.tcx, e);
6562+
if (is_terminated(bcx) ||
6563+
ty::type_is_bot(ccx.tcx, r_ty)) {
65036564
ret r;
6504-
} else {
6505-
auto r_ty = ty::expr_ty(cx.fcx.lcx.ccx.tcx, e);
6506-
if (!ty::type_is_nil(cx.fcx.lcx.ccx.tcx, r_ty)
6507-
&& !ty::type_is_bot(cx.fcx.lcx.ccx.tcx, r_ty)) {
6508-
// The value resulting from the block gets copied into an
6509-
// alloca created in an outer scope and its refcount
6510-
// bumped so that it can escape this block. This means
6511-
// that it will definitely live until the end of the
6512-
// enclosing scope, even if nobody uses it, which may be
6513-
// something of a surprise.
6514-
6515-
// It's possible we never hit this block, so the alloca
6516-
// must be initialized to null, then when the potential
6517-
// value finally goes out of scope the drop glue will see
6518-
// that it was never used and ignore it.
6519-
6520-
// NB: Here we're building and initalizing the alloca in
6521-
// the alloca context, not this block's context.
6522-
auto res_alloca = alloc_ty(bcx, r_ty);
6523-
auto llbcx = llallocas_block_ctxt(bcx.fcx);
6524-
zero_alloca(llbcx, res_alloca.val, r_ty);
6525-
6526-
// Now we're working in our own block context again
6527-
auto res_copy = copy_val(bcx, INIT,
6528-
res_alloca.val, r.val, r_ty);
6529-
bcx = res_copy.bcx;
6530-
6531-
fn drop_hoisted_ty(&@block_ctxt cx,
6532-
ValueRef alloca_val,
6533-
ty::t t) -> result {
6534-
auto reg_val = load_if_immediate(cx,
6535-
alloca_val, t);
6536-
ret drop_ty(cx, reg_val, t);
6565+
} else if (!pass) {
6566+
alt (output) {
6567+
case (save_in(?target)) {
6568+
// The output method is to save the value at target,
6569+
// and we didn't pass it to the recursive trans_expr
6570+
// call.
6571+
// FIXME Use move semantics!
6572+
auto res_copy = copy_val(bcx, INIT, target,
6573+
r.val, r_ty);
6574+
bcx = res_copy.bcx;
6575+
r = res(bcx, C_nil());
65376576
}
6538-
6539-
auto cleanup = bind drop_hoisted_ty(_, res_alloca.val,
6540-
r_ty);
6541-
auto outer_scope_cx = find_outer_scope_cx(scope_bcx);
6542-
outer_scope_cx.cleanups += [clean(cleanup)];
6543-
6544-
r = res(bcx, load_if_immediate(bcx,
6545-
res_alloca.val, r_ty));
6577+
case (return) {}
65466578
}
65476579
}
65486580
}
@@ -6860,7 +6892,7 @@ fn trans_fn(@local_ctxt cx, &ast::span sp, &ast::_fn f, ast::def_id fid,
68606892

68616893
auto lltop = bcx.llbb;
68626894

6863-
auto res = trans_block(bcx, f.body);
6895+
auto res = trans_block(bcx, f.body, return);
68646896
if (!is_terminated(res.bcx)) {
68656897
// FIXME: until LLVM has a unit type, we are moving around
68666898
// C_nil values rather than their void type.

0 commit comments

Comments
 (0)