Skip to content

Commit 64c69aa

Browse files
committed
Start on a piecemeal conversion to DPS
Issue #667 Wires in a basic framework for destination-passing style, with backwards-compatibility to the old approach, so that expression types can be moved over to it one at a time (by moving them from trans_expr to trans_expr_dps).
1 parent d114ded commit 64c69aa

File tree

3 files changed

+177
-126
lines changed

3 files changed

+177
-126
lines changed

src/comp/middle/trans.rs

+162-115
Original file line numberDiff line numberDiff line change
@@ -2412,6 +2412,7 @@ fn join_results(parent_cx: @block_ctxt, t: TypeRef, ins: [result]) -> result {
24122412
ret rslt(join_cx, phi);
24132413
}
24142414

2415+
// FIXME remove once all uses have been converted to join_returns
24152416
fn join_branches(parent_cx: @block_ctxt, ins: [result]) -> @block_ctxt {
24162417
let out = new_sub_block_ctxt(parent_cx, "join");
24172418
let branched = false;
@@ -2422,38 +2423,107 @@ fn join_branches(parent_cx: @block_ctxt, ins: [result]) -> @block_ctxt {
24222423
ret out;
24232424
}
24242425

2425-
tag out_method { return; save_in(ValueRef); }
2426+
tag dest {
2427+
by_val(@mutable ValueRef);
2428+
by_ref(@mutable ValueRef);
2429+
save_in(ValueRef);
2430+
ignore;
2431+
}
2432+
2433+
fn empty_dest_cell() -> @mutable ValueRef {
2434+
ret @mutable llvm::LLVMGetUndef(T_nil());
2435+
}
2436+
2437+
fn dup_for_join(dest: dest) -> dest {
2438+
alt dest {
2439+
by_val(_) { by_val(empty_dest_cell()) }
2440+
by_ref(_) { by_ref(empty_dest_cell()) }
2441+
_ { dest }
2442+
}
2443+
}
2444+
2445+
fn join_returns(parent_cx: @block_ctxt, in_cxs: [@block_ctxt],
2446+
in_ds: [dest], out_dest: dest) -> @block_ctxt {
2447+
let out = new_sub_block_ctxt(parent_cx, "join");
2448+
let reachable = false, i = 0u, phi = none;
2449+
for cx in in_cxs {
2450+
if !cx.unreachable {
2451+
Br(cx, out.llbb);
2452+
reachable = true;
2453+
alt in_ds[i] {
2454+
by_val(cell) | by_ref(cell) {
2455+
if option::is_none(phi) {
2456+
phi = some(EmptyPhi(out, val_ty(*cell)));
2457+
}
2458+
AddIncomingToPhi(option::get(phi), [*cell], [cx.llbb]);
2459+
}
2460+
_ {}
2461+
}
2462+
}
2463+
i += 1u;
2464+
}
2465+
if !reachable {
2466+
Unreachable(out);
2467+
} else {
2468+
alt out_dest {
2469+
by_val(cell) | by_ref(cell) { *cell = option::get(phi); }
2470+
_ {}
2471+
}
2472+
}
2473+
ret out;
2474+
}
2475+
2476+
// Wrapper through which legacy non-DPS code can use DPS functions
2477+
fn dps_to_result(bcx: @block_ctxt,
2478+
work: block(@block_ctxt, dest) -> @block_ctxt,
2479+
ty: ty::t) -> result {
2480+
let tcx = bcx_tcx(bcx);
2481+
if ty::type_is_nil(tcx, ty) || ty::type_is_bot(tcx, ty) {
2482+
ret rslt(work(bcx, ignore), C_nil());
2483+
} else if type_is_immediate(bcx_ccx(bcx), ty) {
2484+
let cell = empty_dest_cell();
2485+
bcx = work(bcx, by_val(cell));
2486+
add_clean_temp(bcx, *cell, ty);
2487+
ret rslt(bcx, *cell);
2488+
} else {
2489+
let {bcx, val: alloca} = alloc_ty(bcx, ty);
2490+
bcx = zero_alloca(bcx, alloca, ty);
2491+
bcx = work(bcx, save_in(alloca));
2492+
add_clean_temp(bcx, alloca, ty);
2493+
ret rslt(bcx, alloca);
2494+
}
2495+
}
24262496

24272497
fn trans_if(cx: @block_ctxt, cond: @ast::expr, thn: ast::blk,
2428-
els: option::t<@ast::expr>, output: out_method) -> result {
2498+
els: option::t<@ast::expr>, dest: dest)
2499+
-> @block_ctxt {
24292500
let {bcx, val: cond_val} = trans_expr(cx, cond);
24302501

2502+
let then_dest = dup_for_join(dest);
2503+
let else_dest = dup_for_join(dest);
24312504
let then_cx = new_scope_block_ctxt(bcx, "then");
2432-
let then_res = trans_block(then_cx, thn, output);
24332505
let else_cx = new_scope_block_ctxt(bcx, "else");
2434-
// Synthesize a block here to act as the else block
2435-
// containing an if expression. Needed in order for the
2436-
// else scope to behave like a normal block scope. A tad
2437-
// ugly.
2506+
CondBr(bcx, cond_val, then_cx.llbb, else_cx.llbb);
2507+
then_cx = trans_block_dps(then_cx, thn, then_dest);
24382508
// Calling trans_block directly instead of trans_expr
24392509
// because trans_expr will create another scope block
24402510
// context for the block, but we've already got the
24412511
// 'else' context
2442-
let else_res =
2443-
alt els {
2444-
some(elexpr) {
2445-
alt elexpr.node {
2446-
ast::expr_if(_, _, _) {
2447-
let elseif_blk = ast_util::block_from_expr(elexpr);
2448-
trans_block(else_cx, elseif_blk, output)
2449-
}
2450-
ast::expr_block(blk) { trans_block(else_cx, blk, output) }
2451-
}
2512+
alt els {
2513+
some(elexpr) {
2514+
alt elexpr.node {
2515+
ast::expr_if(_, _, _) {
2516+
let elseif_blk = ast_util::block_from_expr(elexpr);
2517+
else_cx = trans_block_dps(else_cx, elseif_blk, else_dest);
24522518
}
2453-
_ { rslt(else_cx, C_nil()) }
2454-
};
2455-
CondBr(bcx, cond_val, then_cx.llbb, else_cx.llbb);
2456-
ret rslt(join_branches(cx, [then_res, else_res]), C_nil());
2519+
ast::expr_block(blk) {
2520+
else_cx = trans_block_dps(else_cx, blk, else_dest);
2521+
}
2522+
}
2523+
}
2524+
_ {}
2525+
}
2526+
ret join_returns(cx, [then_cx, else_cx], [then_dest, else_dest], dest);
24572527
}
24582528

24592529
fn trans_for(cx: @block_ctxt, local: @ast::local, seq: @ast::expr,
@@ -2468,7 +2538,7 @@ fn trans_for(cx: @block_ctxt, local: @ast::local, seq: @ast::expr,
24682538
curr = PointerCast(bcx, curr, T_ptr(type_of_or_i8(bcx, t)));
24692539
bcx = trans_alt::bind_irrefutable_pat(scope_cx, local.node.pat, curr,
24702540
bcx.fcx.lllocals, false);
2471-
bcx = trans_block(bcx, body, return).bcx;
2541+
bcx = trans_block_dps(bcx, body, ignore);
24722542
Br(bcx, next_cx.llbb);
24732543
ret next_cx;
24742544
}
@@ -2771,7 +2841,7 @@ fn trans_for_each(cx: @block_ctxt, local: @ast::local, seq: @ast::expr,
27712841
llvm::LLVMGetParam(fcx.llfn, 3u),
27722842
bcx.fcx.lllocals, false);
27732843
let lltop = bcx.llbb;
2774-
let r = trans_block(bcx, body, return);
2844+
let r = trans_block(bcx, body);
27752845
finish_fn(fcx, lltop);
27762846

27772847
build_return(r.bcx);
@@ -2793,7 +2863,7 @@ fn trans_while(cx: @block_ctxt, cond: @ast::expr, body: ast::blk) -> result {
27932863
new_loop_scope_block_ctxt(cx, option::none::<@block_ctxt>, next_cx,
27942864
"while cond");
27952865
let body_cx = new_scope_block_ctxt(cond_cx, "while loop body");
2796-
let body_res = trans_block(body_cx, body, return);
2866+
let body_res = trans_block(body_cx, body);
27972867
let cond_res = trans_expr(cond_cx, cond);
27982868
Br(body_res.bcx, cond_cx.llbb);
27992869
let cond_bcx = trans_block_cleanups(cond_res.bcx, cond_cx);
@@ -2808,7 +2878,7 @@ fn trans_do_while(cx: @block_ctxt, body: ast::blk, cond: @ast::expr) ->
28082878
let body_cx =
28092879
new_loop_scope_block_ctxt(cx, option::none::<@block_ctxt>, next_cx,
28102880
"do-while loop body");
2811-
let body_res = trans_block(body_cx, body, return);
2881+
let body_res = trans_block(body_cx, body);
28122882
let cond_res = trans_expr(body_res.bcx, cond);
28132883
CondBr(cond_res.bcx, cond_res.val, body_cx.llbb, next_cx.llbb);
28142884
Br(cx, body_cx.llbb);
@@ -4034,36 +4104,16 @@ fn trans_rec(cx: @block_ctxt, fields: [ast::field],
40344104
}
40354105

40364106
fn trans_expr(cx: @block_ctxt, e: @ast::expr) -> result {
4037-
trans_expr_out(cx, e, return)
4038-
}
4039-
4040-
fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
4041-
result {
40424107
// Fixme Fill in cx.sp
40434108
alt e.node {
40444109
ast::expr_lit(lit) { ret trans_lit(cx, *lit); }
40454110
ast::expr_binary(op, x, y) { ret trans_binary(cx, op, x, y); }
4046-
ast::expr_if(cond, thn, els) {
4047-
ret with_out_method(bind trans_if(cx, cond, thn, els, _), cx, e.id,
4048-
output);
4049-
}
4050-
ast::expr_if_check(cond, thn, els) {
4051-
ret with_out_method(bind trans_if(cx, cond, thn, els, _), cx, e.id,
4052-
output);
4053-
}
4054-
ast::expr_ternary(_, _, _) {
4055-
ret trans_expr_out(cx, ast_util::ternary_to_if(e), output);
4056-
}
40574111
ast::expr_for(decl, seq, body) { ret trans_for(cx, decl, seq, body); }
40584112
ast::expr_for_each(decl, seq, body) {
40594113
ret trans_for_each(cx, decl, seq, body);
40604114
}
40614115
ast::expr_while(cond, body) { ret trans_while(cx, cond, body); }
40624116
ast::expr_do_while(body, cond) { ret trans_do_while(cx, body, cond); }
4063-
ast::expr_alt(expr, arms) {
4064-
ret with_out_method(bind trans_alt::trans_alt(cx, expr, arms, _), cx,
4065-
e.id, output);
4066-
}
40674117
ast::expr_fn(f) {
40684118
let ccx = bcx_ccx(cx);
40694119
let fty = node_id_type(ccx, e.id);
@@ -4088,17 +4138,6 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
40884138
};
40894139
ret rslt(fn_pair.bcx, fn_pair.fn_pair);
40904140
}
4091-
ast::expr_block(blk) {
4092-
let sub_cx = new_scope_block_ctxt(cx, "block-expr body");
4093-
let next_cx = new_sub_block_ctxt(cx, "next");
4094-
let sub =
4095-
with_out_method(bind trans_block(sub_cx, blk, _), cx, e.id,
4096-
output);
4097-
Br(cx, sub_cx.llbb);
4098-
Br(sub.bcx, next_cx.llbb);
4099-
if sub.bcx.unreachable { Unreachable(next_cx); }
4100-
ret rslt(next_cx, sub.val);
4101-
}
41024141
ast::expr_copy(a) {
41034142
let e_ty = ty::expr_ty(bcx_tcx(cx), a);
41044143
let lv = trans_lval(cx, a);
@@ -4256,27 +4295,62 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
42564295
ast::expr_unary(op, x) {
42574296
ret trans_unary(cx, op, x, e.id);
42584297
}
4298+
// Fall through to DPS-style
4299+
_ {
4300+
ret dps_to_result(cx, {|bcx, dest| trans_expr_dps(bcx, e, dest)},
4301+
ty::expr_ty(bcx_tcx(cx), e));
4302+
}
42594303
}
42604304
}
42614305

4262-
fn with_out_method(work: fn(out_method) -> result, cx: @block_ctxt,
4263-
id: ast::node_id, outer_output: out_method) -> result {
4264-
let ccx = bcx_ccx(cx);
4265-
if outer_output != return {
4266-
ret work(outer_output);
4267-
} else {
4268-
let tp = node_id_type(ccx, id);
4269-
if ty::type_is_nil(ccx.tcx, tp) { ret work(return); }
4270-
let res_alloca = alloc_ty(cx, tp);
4271-
cx = zero_alloca(res_alloca.bcx, res_alloca.val, tp);
4272-
let done = work(save_in(res_alloca.val));
4273-
let loaded = load_if_immediate(done.bcx, res_alloca.val, tp);
4274-
add_clean_temp(cx, loaded, tp);
4275-
ret rslt(done.bcx, loaded);
4306+
fn trans_expr_dps(bcx: @block_ctxt, e: @ast::expr, dest: dest)
4307+
-> @block_ctxt {
4308+
alt e.node {
4309+
ast::expr_if(cond, thn, els) | ast::expr_if_check(cond, thn, els) {
4310+
ret trans_if(bcx, cond, thn, els, dest);
4311+
}
4312+
ast::expr_ternary(_, _, _) {
4313+
ret trans_expr_dps(bcx, ast_util::ternary_to_if(e), dest);
4314+
}
4315+
ast::expr_alt(expr, arms) {
4316+
ret trans_alt::trans_alt(bcx, expr, arms, dest);
4317+
}
4318+
ast::expr_block(blk) {
4319+
let sub_cx = new_scope_block_ctxt(bcx, "block-expr body");
4320+
Br(bcx, sub_cx.llbb);
4321+
sub_cx = trans_block_dps(sub_cx, blk, dest);
4322+
let next_cx = new_sub_block_ctxt(bcx, "next");
4323+
Br(sub_cx, next_cx.llbb);
4324+
if sub_cx.unreachable { Unreachable(next_cx); }
4325+
ret next_cx;
4326+
}
4327+
// Convert back from result to DPS
4328+
_ {
4329+
let lv = trans_lval(bcx, e);
4330+
let {bcx, val, is_mem} = lv;
4331+
let ty = ty::expr_ty(bcx_tcx(bcx), e);
4332+
alt dest {
4333+
by_val(cell) {
4334+
if is_mem {
4335+
bcx = take_ty(bcx, val, ty);
4336+
*cell = Load(bcx, val);
4337+
} else {
4338+
revoke_clean(bcx, val);
4339+
*cell = val;
4340+
}
4341+
}
4342+
by_ref(cell) {
4343+
assert is_mem;
4344+
*cell = val;
4345+
}
4346+
save_in(loc) { bcx = move_val_if_temp(bcx, INIT, loc, lv, ty); }
4347+
ignore. {}
4348+
}
4349+
ret bcx;
4350+
}
42764351
}
42774352
}
42784353

4279-
42804354
// We pass structural values around the compiler "by pointer" and
42814355
// non-structural values (scalars, boxes, pointers) "by value". We call the
42824356
// latter group "immediates" and, in some circumstances when we know we have a
@@ -4900,54 +4974,27 @@ fn alloc_local(cx: @block_ctxt, local: @ast::local) -> result {
49004974
ret r;
49014975
}
49024976

4903-
fn trans_block(cx: @block_ctxt, b: ast::blk, output: out_method) -> result {
4904-
let bcx = cx;
4977+
fn trans_block(bcx: @block_ctxt, b: ast::blk) -> result {
4978+
dps_to_result(bcx, {|bcx, dest| trans_block_dps(bcx, b, dest)},
4979+
ty::node_id_to_type(bcx_tcx(bcx), b.node.id))
4980+
}
4981+
4982+
fn trans_block_dps(bcx: @block_ctxt, b: ast::blk, dest: dest)
4983+
-> @block_ctxt {
49054984
for each local: @ast::local in block_locals(b) {
49064985
// FIXME Update bcx.sp
49074986
let r = alloc_local(bcx, local);
49084987
bcx = r.bcx;
49094988
bcx.fcx.lllocals.insert(local.node.id, r.val);
49104989
}
4911-
let r = rslt(bcx, C_nil());
49124990
for s: @ast::stmt in b.node.stmts {
49134991
bcx = trans_stmt(bcx, *s);
49144992
}
4915-
fn accept_out_method(expr: @ast::expr) -> bool {
4916-
ret alt expr.node {
4917-
ast::expr_if(_, _, _) { true }
4918-
ast::expr_alt(_, _) { true }
4919-
ast::expr_block(_) { true }
4920-
_ { false }
4921-
};
4922-
}
49234993
alt b.node.expr {
4924-
some(e) {
4925-
let ccx = bcx_ccx(cx);
4926-
let r_ty = ty::expr_ty(ccx.tcx, e);
4927-
let pass = output != return && accept_out_method(e);
4928-
if pass {
4929-
r = trans_expr_out(bcx, e, output);
4930-
bcx = r.bcx;
4931-
} else {
4932-
let lv = trans_lval(bcx, e);
4933-
r = {bcx: lv.bcx, val: lv.val};
4934-
bcx = r.bcx;
4935-
alt output {
4936-
save_in(target) {
4937-
// The output method is to save the value at target,
4938-
// and we didn't pass it to the recursive trans_expr
4939-
// call.
4940-
bcx = move_val_if_temp(bcx, INIT, target, lv, r_ty);
4941-
r = rslt(bcx, C_nil());
4942-
}
4943-
return. { }
4944-
}
4945-
}
4946-
}
4947-
none. { r = rslt(bcx, C_nil()); }
4994+
some(e) { bcx = trans_expr_dps(bcx, e, dest); }
4995+
_ { assert dest == ignore || bcx.unreachable; }
49484996
}
4949-
bcx = trans_block_cleanups(bcx, find_scope_cx(bcx));
4950-
ret rslt(bcx, r.val);
4997+
ret trans_block_cleanups(bcx, find_scope_cx(bcx));
49514998
}
49524999

49535000
fn new_local_ctxt(ccx: @crate_ctxt) -> @local_ctxt {
@@ -5238,13 +5285,13 @@ fn trans_closure(bcx_maybe: option::t<@block_ctxt>,
52385285
// translation calls that don't have a return value (trans_crate,
52395286
// trans_mod, trans_item, trans_obj, et cetera) and those that do
52405287
// (trans_block, trans_expr, et cetera).
5241-
let rslt =
5242-
if !ty::type_is_bot(cx.ccx.tcx, block_ty) &&
5243-
!ty::type_is_nil(cx.ccx.tcx, block_ty) &&
5244-
f.proto != ast::proto_iter {
5245-
trans_block(bcx, f.body, save_in(fcx.llretptr))
5246-
} else { trans_block(bcx, f.body, return) };
5247-
bcx = rslt.bcx;
5288+
let dest = if !ty::type_is_bot(cx.ccx.tcx, block_ty) &&
5289+
!ty::type_is_nil(cx.ccx.tcx, block_ty) &&
5290+
f.proto != ast::proto_iter &&
5291+
option::is_some(f.body.node.expr) {
5292+
save_in(fcx.llretptr)
5293+
} else { ignore };
5294+
bcx = trans_block_dps(bcx, f.body, dest);
52485295

52495296
if !bcx.unreachable {
52505297
// FIXME: until LLVM has a unit type, we are moving around

0 commit comments

Comments
 (0)