Better handling of unreachable code in trans

The builder functions in trans_build now look at an 'unreachable' flag
in the block context and don't generate code (returning undefined
placeholder values) when this flag is set. Threading the unreachable
flag through context still requires some care, but this seems a more
sane approach than re-checking for terminated blocks throughout the
compiler.

When creating a block, if you use its closest dominator as parent, the
flag will be automatically passed through. If you can't do that,
because the dominator is a scope block that you're trying to get out
of, you'll have to do something like this to explicitly pass on the
flag:

    if bcx.unreachable { Unreachable(next_cx); }

Closes #949. Closes #946. Closes #942. Closes #895. Closes #894.
Closes #892. Closes #957. Closes #958.
This commit is contained in:
Marijn Haverbeke 2011-09-21 12:40:27 +02:00
parent b029789f02
commit 420484579d
6 changed files with 342 additions and 257 deletions

View file

@ -1512,17 +1512,11 @@ fn compare_scalar_types(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
}
}
ty::ty_type. {
trans_fail(cx, none, "attempt to compare values of type type");
// This is a bit lame, because we return a dummy block to the
// caller that's actually unreachable, but I don't think it
// matters.
ret rslt(new_sub_block_ctxt(cx, "after_fail_dummy"), C_bool(false));
ret trans_fail(cx, none, "attempt to compare values of type type");
}
ty::ty_native(_) {
trans_fail(cx, none::<span>,
ret trans_fail(cx, none::<span>,
"attempt to compare values of type native");
ret rslt(new_sub_block_ctxt(cx, "after_fail_dummy"), C_bool(false));
}
_ {
// Should never get here, because t is scalar.
@ -1592,9 +1586,9 @@ fn compare_scalar_values(cx: @block_ctxt, lhs: ValueRef, rhs: ValueRef,
let unreach_cx = new_sub_block_ctxt(cx, "unreach");
Unreachable(unreach_cx);
let llswitch = Switch(cx, llop, unreach_cx.llbb, 3u);
llvm::LLVMAddCase(llswitch, C_u8(abi::cmp_glue_op_eq), eq_cx.llbb);
llvm::LLVMAddCase(llswitch, C_u8(abi::cmp_glue_op_lt), lt_cx.llbb);
llvm::LLVMAddCase(llswitch, C_u8(abi::cmp_glue_op_le), le_cx.llbb);
AddCase(llswitch, C_u8(abi::cmp_glue_op_eq), eq_cx.llbb);
AddCase(llswitch, C_u8(abi::cmp_glue_op_lt), lt_cx.llbb);
AddCase(llswitch, C_u8(abi::cmp_glue_op_le), le_cx.llbb);
let last_result =
Phi(last_cx, T_i1(), [eq_result, lt_result, le_result],
[eq_cx.llbb, lt_cx.llbb, le_cx.llbb]);
@ -1716,7 +1710,7 @@ fn iter_structural_ty(cx: @block_ctxt, av: ValueRef, t: ty::t,
new_sub_block_ctxt(cx,
"tag-iter-variant-" +
uint::to_str(i, 10u));
llvm::LLVMAddCase(llswitch, C_int(i as int), variant_cx.llbb);
AddCase(llswitch, C_int(i as int), variant_cx.llbb);
variant_cx =
iter_variant(variant_cx, llunion_a_ptr, variant, tps, tid, f);
Br(variant_cx, next_cx.llbb);
@ -1984,20 +1978,20 @@ fn call_bzero(cx: @block_ctxt, dst: ValueRef, n_bytes: ValueRef,
}
fn memmove_ty(cx: @block_ctxt, dst: ValueRef, src: ValueRef, t: ty::t) ->
result {
@block_ctxt {
let ccx = bcx_ccx(cx);
if check type_has_static_size(ccx, t) {
if ty::type_is_structural(bcx_tcx(cx), t) {
let sp = cx.sp;
let llsz = llsize_of(type_of(ccx, sp, t));
ret call_memmove(cx, dst, src, llsz);
ret call_memmove(cx, dst, src, llsz).bcx;
}
ret rslt(cx, Store(cx, Load(cx, src), dst));
Store(cx, Load(cx, src), dst);
ret cx;
}
let llsz = size_of(cx, t);
ret call_memmove(llsz.bcx, dst, src, llsz.val);
ret call_memmove(llsz.bcx, dst, src, llsz.val).bcx;
}
tag copy_action { INIT; DROP_EXISTING; }
@ -2052,7 +2046,7 @@ fn copy_val_no_check(cx: @block_ctxt, action: copy_action, dst: ValueRef,
{
let bcx = cx;
if action == DROP_EXISTING { bcx = drop_ty(cx, dst, t); }
bcx = memmove_ty(bcx, dst, src, t).bcx;
bcx = memmove_ty(bcx, dst, src, t);
ret take_ty(bcx, dst, t);
}
ccx.sess.bug("unexpected type in trans::copy_val_no_check: " +
@ -2086,7 +2080,7 @@ fn move_val(cx: @block_ctxt, action: copy_action, dst: ValueRef,
} else if ty::type_is_unique(tcx, t) ||
type_is_structural_or_param(tcx, t) {
if action == DROP_EXISTING { cx = drop_ty(cx, dst, t); }
cx = memmove_ty(cx, dst, src_val, t).bcx;
cx = memmove_ty(cx, dst, src_val, t);
if src.is_mem { ret zero_alloca(cx, src_val, t).bcx; }
// If we're here, it must be a temporary.
@ -2242,13 +2236,6 @@ fn trans_compare(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
fn trans_eager_binop(cx: @block_ctxt, op: ast::binop, lhs: ValueRef,
lhs_t: ty::t, rhs: ValueRef, rhs_t: ty::t) -> result {
// If either is bottom, it diverges. So no need to do the
// operation.
if ty::type_is_bot(bcx_tcx(cx), lhs_t) ||
ty::type_is_bot(bcx_tcx(cx), rhs_t) {
ret rslt(cx, Unreachable(cx));
}
let is_float = false;
let intype = lhs_t;
if ty::type_is_bot(bcx_tcx(cx), intype) { intype = rhs_t; }
@ -2355,10 +2342,10 @@ fn trans_binary(cx: @block_ctxt, op: ast::binop, a: @ast::expr, b: @ast::expr)
ast::and. {
// Lazy-eval and
let lhs_res = trans_expr(cx, a);
let rhs_cx = new_scope_block_ctxt(cx, "rhs");
let rhs_cx = new_scope_block_ctxt(lhs_res.bcx, "rhs");
let rhs_res = trans_expr(rhs_cx, b);
let lhs_false_cx = new_scope_block_ctxt(cx, "lhs false");
let lhs_false_cx = new_scope_block_ctxt(lhs_res.bcx, "lhs false");
let lhs_false_res = rslt(lhs_false_cx, C_bool(false));
// The following line ensures that any cleanups for rhs
@ -2373,9 +2360,9 @@ fn trans_binary(cx: @block_ctxt, op: ast::binop, a: @ast::expr, b: @ast::expr)
ast::or. {
// Lazy-eval or
let lhs_res = trans_expr(cx, a);
let rhs_cx = new_scope_block_ctxt(cx, "rhs");
let rhs_cx = new_scope_block_ctxt(lhs_res.bcx, "rhs");
let rhs_res = trans_expr(rhs_cx, b);
let lhs_true_cx = new_scope_block_ctxt(cx, "lhs true");
let lhs_true_cx = new_scope_block_ctxt(lhs_res.bcx, "lhs true");
let lhs_true_res = rslt(lhs_true_cx, C_bool(true));
// see the and case for an explanation
@ -2401,7 +2388,7 @@ fn join_results(parent_cx: @block_ctxt, t: TypeRef, ins: [result]) -> result {
let vals: [ValueRef] = [];
let bbs: [BasicBlockRef] = [];
for r: result in ins {
if !is_terminated(r.bcx) {
if !r.bcx.unreachable {
live += [r];
vals += [r.val];
bbs += [r.bcx.llbb];
@ -2412,7 +2399,6 @@ fn join_results(parent_cx: @block_ctxt, t: TypeRef, ins: [result]) -> result {
// No incoming edges are live, so we're in dead-code-land.
// Arbitrarily pick the first dead edge, since the caller
// is just going to propagate it outward.
assert (std::vec::len::<result>(ins) >= 1u);
ret ins[0];
}
@ -2428,7 +2414,11 @@ fn join_results(parent_cx: @block_ctxt, t: TypeRef, ins: [result]) -> result {
fn join_branches(parent_cx: @block_ctxt, ins: [result]) -> @block_ctxt {
let out = new_sub_block_ctxt(parent_cx, "join");
for r: result in ins { if !is_terminated(r.bcx) { Br(r.bcx, out.llbb); } }
let branched = false;
for r: result in ins {
if !r.bcx.unreachable { Br(r.bcx, out.llbb); branched = true; }
}
if !branched { Unreachable(out); }
ret out;
}
@ -2436,20 +2426,11 @@ tag out_method { return; save_in(ValueRef); }
fn trans_if(cx: @block_ctxt, cond: @ast::expr, thn: ast::blk,
els: option::t<@ast::expr>, output: out_method) -> result {
let cond_res = trans_expr(cx, cond);
let {bcx, val: cond_val} = trans_expr(cx, cond);
if ty::type_is_bot(bcx_tcx(cx), ty::expr_ty(bcx_tcx(cx), cond)) {
// No need to generate code for comparison,
// since the cond diverges.
if !is_terminated(cx) {
ret rslt(cx, Unreachable(cx));
} else { ret cond_res; }
}
let then_cx = new_scope_block_ctxt(cx, "then");
let then_cx = new_scope_block_ctxt(bcx, "then");
let then_res = trans_block(then_cx, thn, output);
let else_cx = new_scope_block_ctxt(cx, "else");
let else_cx = new_scope_block_ctxt(bcx, "else");
// Synthesize a block here to act as the else block
// containing an if expression. Needed in order for the
// else scope to behave like a normal block scope. A tad
@ -2471,7 +2452,7 @@ fn trans_if(cx: @block_ctxt, cond: @ast::expr, thn: ast::blk,
}
_ { rslt(else_cx, C_nil()) }
};
CondBr(cond_res.bcx, cond_res.val, then_cx.llbb, else_cx.llbb);
CondBr(bcx, cond_val, then_cx.llbb, else_cx.llbb);
ret rslt(join_branches(cx, [then_res, else_res]), C_nil());
}
@ -2488,10 +2469,7 @@ fn trans_for(cx: @block_ctxt, local: @ast::local, seq: @ast::expr,
bcx = trans_alt::bind_irrefutable_pat(scope_cx, local.node.pat, curr,
bcx.fcx.lllocals, false);
bcx = trans_block(bcx, body, return).bcx;
if !is_terminated(bcx) {
Br(bcx, next_cx.llbb);
// otherwise, this code is unreachable
}
Br(bcx, next_cx.llbb);
ret next_cx;
}
let next_cx = new_sub_block_ctxt(cx, "next");
@ -2796,10 +2774,7 @@ fn trans_for_each(cx: @block_ctxt, local: @ast::local, seq: @ast::expr,
let r = trans_block(bcx, body, return);
finish_fn(fcx, lltop);
if !is_terminated(r.bcx) {
// if terminated is true, no need for the ret-fail
build_return(r.bcx);
}
build_return(r.bcx);
// Step 3: Call iter passing [lliterbody, llenv], plus other args.
alt seq.node {
@ -2834,14 +2809,6 @@ fn trans_do_while(cx: @block_ctxt, body: ast::blk, cond: @ast::expr) ->
new_loop_scope_block_ctxt(cx, option::none::<@block_ctxt>, next_cx,
"do-while loop body");
let body_res = trans_block(body_cx, body, return);
if is_terminated(body_res.bcx) {
// This is kind of ridiculous, but no permutations
// involving body_res or body_cx.val worked.
let rs = trans_block(cx, body, return);
if !is_terminated(next_cx) { Unreachable(next_cx); }
if !is_terminated(body_cx) { Unreachable(body_cx); }
ret rs;
}
let cond_res = trans_expr(body_res.bcx, cond);
CondBr(cond_res.bcx, cond_res.val, body_cx.llbb, next_cx.llbb);
Br(cx, body_cx.llbb);
@ -3657,20 +3624,7 @@ fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef,
let bcx: @block_ctxt = cx;
let ret_style = ty::ty_fn_ret_style(tcx, fn_ty);
let by_ref = ast_util::ret_by_ref(ret_style);
// Arg 0: Output pointer.
// FIXME: test case looks like
// f(1, fail, @42);
if is_terminated(bcx) {
// This means an earlier arg was divergent.
// So this arg can't be evaluated.
ret {bcx: bcx,
outer_cx: outer_cx,
args: [],
retslot: C_nil(),
to_zero: to_zero,
to_revoke: to_revoke};
}
let retty = ty::ty_fn_ret(tcx, fn_ty), full_retty = retty;
alt gen {
some(g) {
@ -3681,6 +3635,7 @@ fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef,
}
_ { }
}
// Arg 0: Output pointer.
let llretslot_res = if ty::type_is_nil(tcx, retty) {
rslt(cx, llvm::LLVMGetUndef(T_ptr(T_nil())))
} else if by_ref {
@ -3732,11 +3687,6 @@ fn trans_args(cx: @block_ctxt, outer_cx: @block_ctxt, llenv: ValueRef,
let arg_tys = type_of_explicit_args(ccx, cx.sp, args);
let i = 0u;
for e: @ast::expr in es {
if is_terminated(bcx) {
// This means an earlier arg was divergent.
// So this arg can't be evaluated.
break;
}
let is_referenced = alt ret_style {
ast::return_ref(_, arg_n) { i + 1u == arg_n }
_ { false }
@ -3761,9 +3711,9 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
// NB: 'f' isn't necessarily a function; it might be an entire self-call
// expression because of the hack that allows us to process self-calls
// with trans_call.
let fn_expr_ty = ty::expr_ty(bcx_tcx(in_cx), f);
let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(bcx_tcx(in_cx),
fn_expr_ty));
let tcx = bcx_tcx(in_cx);
let fn_expr_ty = ty::expr_ty(tcx, f);
let by_ref = ast_util::ret_by_ref(ty::ty_fn_ret_style(tcx, fn_expr_ty));
let cx = new_scope_block_ctxt(in_cx, "call");
let f_res = trans_callee(cx, f);
let bcx = f_res.bcx;
@ -3786,7 +3736,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
}
}
let ret_ty = ty::node_id_to_type(bcx_tcx(cx), id);
let ret_ty = ty::node_id_to_type(tcx, id);
let args_res =
trans_args(bcx, in_cx, llenv, f_res.generic, lliterbody, args,
fn_expr_ty);
@ -3808,38 +3758,38 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
type _|_. Since that means it diverges, the code
for the call itself is unreachable. */
let retval = C_nil();
if !is_terminated(bcx) {
bcx = invoke_fastcall(bcx, faddr, llargs,
args_res.to_zero, args_res.to_revoke).bcx;
alt lliterbody {
none. {
if !ty::type_is_nil(bcx_tcx(cx), ret_ty) {
if by_ref {
retval = Load(bcx, llretslot);
} else {
retval = load_if_immediate(bcx, llretslot, ret_ty);
// Retval doesn't correspond to anything really tangible
// in the frame, but it's a ref all the same, so we put a
// note here to drop it when we're done in this scope.
add_clean_temp(in_cx, retval, ret_ty);
}
bcx = invoke_fastcall(bcx, faddr, llargs,
args_res.to_zero, args_res.to_revoke);
alt lliterbody {
none. {
if !ty::type_is_nil(tcx, ret_ty) {
if by_ref {
retval = Load(bcx, llretslot);
} else {
retval = load_if_immediate(bcx, llretslot, ret_ty);
// Retval doesn't correspond to anything really tangible
// in the frame, but it's a ref all the same, so we put a
// note here to drop it when we're done in this scope.
add_clean_temp(in_cx, retval, ret_ty);
}
}
some(_) {
// If there was an lliterbody, it means we were calling an
// iter, and we are *not* the party using its 'output' value,
// we should ignore llretslot.
}
}
// Forget about anything we moved out.
bcx = zero_and_revoke(bcx, args_res.to_zero, args_res.to_revoke);
if !by_ref { bcx = trans_block_cleanups(bcx, cx); }
let next_cx = new_sub_block_ctxt(in_cx, "next");
Br(bcx, next_cx.llbb);
bcx = next_cx;
}
some(_) {
// If there was an lliterbody, it means we were calling an
// iter, and we are *not* the party using its 'output' value,
// we should ignore llretslot.
}
}
// Forget about anything we moved out.
bcx = zero_and_revoke(bcx, args_res.to_zero, args_res.to_revoke);
if !by_ref { bcx = trans_block_cleanups(bcx, cx); }
let next_cx = new_sub_block_ctxt(in_cx, "next");
if bcx.unreachable || ty::type_is_bot(tcx, ret_ty) {
Unreachable(next_cx);
}
Br(bcx, next_cx.llbb);
bcx = next_cx;
ret {res: rslt(bcx, retval), by_ref: by_ref};
}
@ -3857,14 +3807,15 @@ fn zero_and_revoke(bcx: @block_ctxt,
}
fn invoke(bcx: @block_ctxt, llfn: ValueRef,
llargs: [ValueRef]) -> result {
llargs: [ValueRef]) -> @block_ctxt {
ret invoke_(bcx, llfn, llargs, [], [], Invoke);
}
fn invoke_fastcall(bcx: @block_ctxt, llfn: ValueRef,
llargs: [ValueRef],
to_zero: [{v: ValueRef, t: ty::t}],
to_revoke: [{v: ValueRef, t: ty::t}]) -> result {
to_revoke: [{v: ValueRef, t: ty::t}])
-> @block_ctxt {
ret invoke_(bcx, llfn, llargs,
to_zero, to_revoke,
FastInvoke);
@ -3874,14 +3825,15 @@ fn invoke_(bcx: @block_ctxt, llfn: ValueRef, llargs: [ValueRef],
to_zero: [{v: ValueRef, t: ty::t}],
to_revoke: [{v: ValueRef, t: ty::t}],
invoker: fn(@block_ctxt, ValueRef, [ValueRef],
BasicBlockRef, BasicBlockRef) -> ValueRef) -> result {
BasicBlockRef, BasicBlockRef)) -> @block_ctxt {
// FIXME: May be worth turning this into a plain call when there are no
// cleanups to run
if bcx.unreachable { ret bcx; }
let normal_bcx = new_sub_block_ctxt(bcx, "normal return");
let retval = invoker(bcx, llfn, llargs,
normal_bcx.llbb,
get_landing_pad(bcx, to_zero, to_revoke));
ret rslt(normal_bcx, retval);
invoker(bcx, llfn, llargs,
normal_bcx.llbb,
get_landing_pad(bcx, to_zero, to_revoke));
ret normal_bcx;
}
fn get_landing_pad(bcx: @block_ctxt,
@ -3923,8 +3875,7 @@ fn get_landing_pad(bcx: @block_ctxt,
fn trans_landing_pad(bcx: @block_ctxt,
to_zero: [{v: ValueRef, t: ty::t}],
to_revoke: [{v: ValueRef, t: ty::t}]
) -> BasicBlockRef {
to_revoke: [{v: ValueRef, t: ty::t}]) -> BasicBlockRef {
// The landing pad return type (the type being propagated). Not sure what
// this represents but it's determined by the personality function and
// this is what the EH proposal example uses.
@ -4144,7 +4095,8 @@ fn trans_expr_out(cx: @block_ctxt, e: @ast::expr, output: out_method) ->
with_out_method(bind trans_block(sub_cx, blk, _), cx, e.id,
output);
Br(cx, sub_cx.llbb);
if !is_terminated(sub.bcx) { Br(sub.bcx, next_cx.llbb); }
Br(sub.bcx, next_cx.llbb);
if sub.bcx.unreachable { Unreachable(next_cx); }
ret rslt(next_cx, sub.val);
}
ast::expr_copy(a) {
@ -4479,7 +4431,7 @@ fn trans_fail_value(cx: @block_ctxt, sp_opt: option::t<span>,
let V_str = PointerCast(cx, V_fail_str, T_ptr(T_i8()));
V_filename = PointerCast(cx, V_filename, T_ptr(T_i8()));
let args = [cx.fcx.lltaskptr, V_str, V_filename, C_int(V_line)];
let cx = invoke(cx, bcx_ccx(cx).upcalls._fail, args).bcx;
let cx = invoke(cx, bcx_ccx(cx).upcalls._fail, args);
Unreachable(cx);
ret rslt(cx, C_nil());
}
@ -4517,9 +4469,10 @@ fn trans_put(in_cx: @block_ctxt, e: option::t<@ast::expr>) -> result {
llargs += [r.val];
}
}
bcx = invoke_fastcall(bcx, llcallee, llargs, [], []).bcx;
bcx = invoke_fastcall(bcx, llcallee, llargs, [], []);
bcx = trans_block_cleanups(bcx, cx);
let next_cx = new_sub_block_ctxt(in_cx, "next");
if bcx.unreachable { Unreachable(next_cx); }
Br(bcx, next_cx.llbb);
ret rslt(next_cx, C_nil());
}
@ -4541,8 +4494,8 @@ fn trans_break_cont(sp: span, cx: @block_ctxt, to_end: bool) -> result {
_ { Br(bcx, cleanup_cx.llbb); }
}
}
ret rslt(new_sub_block_ctxt(bcx, "break_cont.unreachable"),
C_nil());
Unreachable(bcx);
ret rslt(bcx, C_nil());
}
_ {
alt cleanup_cx.parent {
@ -4614,7 +4567,8 @@ fn trans_ret(cx: @block_ctxt, e: option::t<@ast::expr>) -> result {
}
}
build_return(bcx);
ret rslt(new_sub_block_ctxt(bcx, "ret.unreachable"), C_nil());
Unreachable(bcx);
ret rslt(bcx, C_nil());
}
fn build_return(bcx: @block_ctxt) { Br(bcx, bcx_fcx(bcx).llreturn); }
@ -4759,15 +4713,23 @@ fn new_block_ctxt(cx: @fn_ctxt, parent: block_parent, kind: block_kind,
}
let llbb: BasicBlockRef =
str::as_buf(s, {|buf| llvm::LLVMAppendBasicBlock(cx.llfn, buf) });
ret @{llbb: llbb,
mutable terminated: false,
parent: parent,
kind: kind,
mutable cleanups: [],
mutable lpad_dirty: true,
mutable lpad: option::none,
sp: cx.sp,
fcx: cx};
let bcx = @{llbb: llbb,
mutable terminated: false,
mutable unreachable: false,
parent: parent,
kind: kind,
mutable cleanups: [],
mutable lpad_dirty: true,
mutable lpad: option::none,
sp: cx.sp,
fcx: cx};
alt parent {
parent_some(cx) {
if cx.unreachable { Unreachable(bcx); }
}
_ {}
}
ret bcx;
}
@ -4797,6 +4759,7 @@ fn new_sub_block_ctxt(bcx: @block_ctxt, n: str) -> @block_ctxt {
fn new_raw_block_ctxt(fcx: @fn_ctxt, llbb: BasicBlockRef) -> @block_ctxt {
ret @{llbb: llbb,
mutable terminated: false,
mutable unreachable: false,
parent: parent_none,
kind: NON_SCOPE_BLOCK,
mutable cleanups: [],
@ -4814,9 +4777,9 @@ fn new_raw_block_ctxt(fcx: @fn_ctxt, llbb: BasicBlockRef) -> @block_ctxt {
// need to make sure those variables go out of scope when the block ends. We
// do that by running a 'cleanup' function for each variable.
// trans_block_cleanups runs all the cleanup functions for the block.
fn trans_block_cleanups(cx: @block_ctxt, cleanup_cx: @block_ctxt) ->
fn trans_block_cleanups(bcx: @block_ctxt, cleanup_cx: @block_ctxt) ->
@block_ctxt {
let bcx = cx;
if bcx.unreachable { ret bcx; }
if cleanup_cx.kind == NON_SCOPE_BLOCK {
assert (std::vec::len::<cleanup>(cleanup_cx.cleanups) == 0u);
}
@ -4864,6 +4827,7 @@ iter block_locals(b: ast::blk) -> @ast::local {
fn llstaticallocas_block_ctxt(fcx: @fn_ctxt) -> @block_ctxt {
ret @{llbb: fcx.llstaticallocas,
mutable terminated: false,
mutable unreachable: false,
parent: parent_none,
kind: SCOPE_BLOCK,
mutable cleanups: [],
@ -4876,6 +4840,7 @@ fn llstaticallocas_block_ctxt(fcx: @fn_ctxt) -> @block_ctxt {
fn llderivedtydescs_block_ctxt(fcx: @fn_ctxt) -> @block_ctxt {
ret @{llbb: fcx.llderivedtydescs,
mutable terminated: false,
mutable unreachable: false,
parent: parent_none,
kind: SCOPE_BLOCK,
mutable cleanups: [],
@ -4946,10 +4911,6 @@ fn trans_block(cx: @block_ctxt, b: ast::blk, output: out_method) -> result {
for s: @ast::stmt in b.node.stmts {
r = trans_stmt(bcx, *s);
bcx = r.bcx;
// If we hit a terminator, control won't go any further so
// we're in dead-code land. Stop here.
if is_terminated(bcx) { ret r; }
}
fn accept_out_method(expr: @ast::expr) -> bool {
ret alt expr.node {
@ -4967,12 +4928,10 @@ fn trans_block(cx: @block_ctxt, b: ast::blk, output: out_method) -> result {
if pass {
r = trans_expr_out(bcx, e, output);
bcx = r.bcx;
if is_terminated(bcx) || ty::type_is_bot(ccx.tcx, r_ty) { ret r; }
} else {
let lv = trans_lval(bcx, e);
r = {bcx: lv.bcx, val: lv.val};
bcx = r.bcx;
if is_terminated(bcx) || ty::type_is_bot(ccx.tcx, r_ty) { ret r; }
alt output {
save_in(target) {
// The output method is to save the value at target,
@ -5157,11 +5116,6 @@ fn copy_args_to_allocas(fcx: @fn_ctxt, scope: @block_ctxt, args: [ast::arg],
fcx.llcopyargs = llcopyargs.llbb;
}
fn is_terminated(cx: @block_ctxt) -> bool {
let inst = llvm::LLVMGetLastInstruction(cx.llbb);
ret llvm::LLVMIsATerminatorInst(inst) as int != 0;
}
fn arg_tys_of_fn(ccx: @crate_ctxt, id: ast::node_id) -> [ty::arg] {
alt ty::struct(ccx.tcx, ty::node_id_to_type(ccx.tcx, id)) {
ty::ty_fn(_, arg_tys, _, _, _) { ret arg_tys; }
@ -5293,7 +5247,7 @@ fn trans_closure(bcx_maybe: option::t<@block_ctxt>,
} else { trans_block(bcx, f.body, return) };
bcx = rslt.bcx;
if !is_terminated(bcx) {
if !bcx.unreachable {
// FIXME: until LLVM has a unit type, we are moving around
// C_nil values rather than their void type.
build_return(bcx);

View file

@ -294,8 +294,6 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
alt data.guard {
some(e) {
let guard_cx = new_scope_block_ctxt(bcx, "submatch_guard");
let next_cx = new_sub_block_ctxt(bcx, "submatch_next");
let else_cx = new_sub_block_ctxt(bcx, "submatch_else");
Br(bcx, guard_cx.llbb);
// Temporarily set bindings. They'll be rewritten to PHI nodes for
// the actual arm block.
@ -306,6 +304,8 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
let {bcx: guard_bcx, val: guard_val} =
trans::trans_expr(guard_cx, e);
guard_bcx = trans::trans_block_cleanups(guard_bcx, guard_cx);
let next_cx = new_sub_block_ctxt(guard_cx, "submatch_next");
let else_cx = new_sub_block_ctxt(guard_cx, "submatch_else");
CondBr(guard_bcx, guard_val, next_cx.llbb, else_cx.llbb);
compile_submatch(else_cx, vec::slice(m, 1u, vec::len(m)), vals, f,
exits);
@ -313,7 +313,9 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
}
_ { }
}
exits += [{bound: m[0].bound, from: bcx.llbb, to: data.body}];
if !bcx.unreachable {
exits += [{bound: m[0].bound, from: bcx.llbb, to: data.body}];
}
Br(bcx, data.body);
ret;
}
@ -417,10 +419,15 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
no_branch. | single. { bcx }
_ { new_sub_block_ctxt(bcx, "match_else") }
};
let sw =
if kind == switch {
Switch(bcx, test_val, else_cx.llbb, vec::len(opts))
} else { C_int(0) }; // Placeholder for when not using a switch
let sw;
if kind == switch {
sw = Switch(bcx, test_val, else_cx.llbb, vec::len(opts));
// FIXME This statement is purely here as a work-around for a bug that
// I expect to be the same as issue #951. If I remove it, sw ends up
// holding a corrupted value (when the compiler is optimized).
// This can be removed after our next LLVM upgrade.
val_ty(sw);
} else { sw = C_int(0); } // Placeholder for when not using a switch
// Compile subtrees for each option
for opt: opt in opts {
@ -430,7 +437,7 @@ fn compile_submatch(bcx: @block_ctxt, m: match, vals: [ValueRef], f: mk_fail,
switch. {
let r = trans_opt(bcx, opt);
bcx = r.bcx;
llvm::LLVMAddCase(sw, r.val, opt_cx.llbb);
AddCase(sw, r.val, opt_cx.llbb);
}
compare. {
let compare_cx = new_scope_block_ctxt(bcx, "compare_scope");
@ -514,17 +521,10 @@ fn trans_alt(cx: @block_ctxt, expr: @ast::expr, arms: [ast::arm],
let bodies = [];
let match: match = [];
let er = trans::trans_expr(cx, expr);
if ty::type_is_bot(bcx_tcx(cx), ty::expr_ty(bcx_tcx(cx), expr)) {
// No need to generate code for alt,
// since the disc diverges.
if !is_terminated(cx) {
ret rslt(cx, Unreachable(cx));
} else { ret er; }
}
if er.bcx.unreachable { ret er; }
for a: ast::arm in arms {
let body = new_scope_block_ctxt(cx, "case_body");
let body = new_scope_block_ctxt(er.bcx, "case_body");
let id_map = ast_util::pat_id_map(a.pats[0]);
bodies += [body];
for p: @ast::pat in a.pats {

View file

@ -3,7 +3,7 @@ import std::str::sbuf;
import lib::llvm::llvm;
import llvm::{ValueRef, TypeRef, BasicBlockRef, BuilderRef, Opcode,
ModuleRef};
import trans_common::block_ctxt;
import trans_common::{block_ctxt, T_ptr, T_nil, T_int, T_i8, T_i1, val_ty};
fn B(cx: @block_ctxt) -> BuilderRef {
let b = *cx.fcx.lcx.ccx.builder;
@ -11,262 +11,325 @@ fn B(cx: @block_ctxt) -> BuilderRef {
ret b;
}
fn RetVoid(cx: @block_ctxt) -> ValueRef {
// The difference between a block being unreachable and being terminated is
// somewhat obscure, and has to do with error checking. When a block is
// terminated, we're saying that trying to add any further statements in the
// block is an error. On the other hand, if something is unreachable, that
// means that the block was terminated in some way that we don't want to check
// for (fail/break/ret statements, call to diverging functions, etc), and
// further instructions to the block should simply be ignored.
fn RetVoid(cx: @block_ctxt) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
ret llvm::LLVMBuildRetVoid(B(cx));
llvm::LLVMBuildRetVoid(B(cx));
}
fn Ret(cx: @block_ctxt, V: ValueRef) -> ValueRef {
fn Ret(cx: @block_ctxt, V: ValueRef) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
ret llvm::LLVMBuildRet(B(cx), V);
llvm::LLVMBuildRet(B(cx), V);
}
fn AggregateRet(cx: @block_ctxt, RetVals: [ValueRef]) -> ValueRef {
fn AggregateRet(cx: @block_ctxt, RetVals: [ValueRef]) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
ret llvm::LLVMBuildAggregateRet(B(cx), vec::to_ptr(RetVals),
vec::len(RetVals));
llvm::LLVMBuildAggregateRet(B(cx), vec::to_ptr(RetVals),
vec::len(RetVals));
}
fn Br(cx: @block_ctxt, Dest: BasicBlockRef) -> ValueRef {
fn Br(cx: @block_ctxt, Dest: BasicBlockRef) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
ret llvm::LLVMBuildBr(B(cx), Dest);
llvm::LLVMBuildBr(B(cx), Dest);
}
fn CondBr(cx: @block_ctxt, If: ValueRef, Then: BasicBlockRef,
Else: BasicBlockRef) -> ValueRef {
Else: BasicBlockRef) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
ret llvm::LLVMBuildCondBr(B(cx), If, Then, Else);
llvm::LLVMBuildCondBr(B(cx), If, Then, Else);
}
fn Switch(cx: @block_ctxt, V: ValueRef, Else: BasicBlockRef, NumCases: uint)
-> ValueRef {
assert (!cx.terminated);
-> ValueRef {
if cx.unreachable { ret _Undef(V); }
assert !cx.terminated;
cx.terminated = true;
ret llvm::LLVMBuildSwitch(B(cx), V, Else, NumCases);
}
fn IndirectBr(cx: @block_ctxt, Addr: ValueRef, NumDests: uint) -> ValueRef {
fn AddCase(S: ValueRef, OnVal: ValueRef, Dest: BasicBlockRef) {
if llvm::LLVMIsUndef(S) == lib::llvm::True { ret; }
llvm::LLVMAddCase(S, OnVal, Dest);
}
fn IndirectBr(cx: @block_ctxt, Addr: ValueRef, NumDests: uint) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
ret llvm::LLVMBuildIndirectBr(B(cx), Addr, NumDests);
llvm::LLVMBuildIndirectBr(B(cx), Addr, NumDests);
}
fn Invoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
Then: BasicBlockRef, Catch: BasicBlockRef) -> ValueRef {
Then: BasicBlockRef, Catch: BasicBlockRef) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
ret str::as_buf("",
{|buf|
llvm::LLVMBuildInvoke(B(cx), Fn, vec::to_ptr(Args),
vec::len(Args), Then, Catch,
buf)
});
str::as_buf("", {|buf|
llvm::LLVMBuildInvoke(B(cx), Fn, vec::to_ptr(Args),
vec::len(Args), Then, Catch, buf)
});
}
fn FastInvoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
Then: BasicBlockRef, Catch: BasicBlockRef) -> ValueRef {
Then: BasicBlockRef, Catch: BasicBlockRef) {
if cx.unreachable { ret; }
assert (!cx.terminated);
cx.terminated = true;
let v = str::as_buf("",
{|buf|
llvm::LLVMBuildInvoke(B(cx), Fn,
vec::to_ptr(Args),
vec::len(Args), Then,
Catch, buf)
});
let v = str::as_buf("", {|buf|
llvm::LLVMBuildInvoke(B(cx), Fn, vec::to_ptr(Args),
vec::len(Args), Then, Catch, buf)
});
llvm::LLVMSetInstructionCallConv(v, lib::llvm::LLVMFastCallConv);
ret v;
}
fn Unreachable(cx: @block_ctxt) -> ValueRef {
assert (!cx.terminated);
cx.terminated = true;
ret llvm::LLVMBuildUnreachable(B(cx));
fn Unreachable(cx: @block_ctxt) {
if cx.unreachable { ret; }
cx.unreachable = true;
if !cx.terminated { llvm::LLVMBuildUnreachable(B(cx)); }
}
fn _Undef(val: ValueRef) -> ValueRef {
ret llvm::LLVMGetUndef(val_ty(val));
}
/* Arithmetic */
fn Add(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildAdd(B(cx), LHS, RHS, buf) });
}
fn NSWAdd(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNSWAdd(B(cx), LHS, RHS, buf) });
}
fn NUWAdd(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNUWAdd(B(cx), LHS, RHS, buf) });
}
fn FAdd(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildFAdd(B(cx), LHS, RHS, buf) });
}
fn Sub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildSub(B(cx), LHS, RHS, buf) });
}
fn NSWSub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNSWSub(B(cx), LHS, RHS, buf) });
}
fn NUWSub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNUWSub(B(cx), LHS, RHS, buf) });
}
fn FSub(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildFSub(B(cx), LHS, RHS, buf) });
}
fn Mul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildMul(B(cx), LHS, RHS, buf) });
}
fn NSWMul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNSWMul(B(cx), LHS, RHS, buf) });
}
fn NUWMul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNUWMul(B(cx), LHS, RHS, buf) });
}
fn FMul(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildFMul(B(cx), LHS, RHS, buf) });
}
fn UDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildUDiv(B(cx), LHS, RHS, buf) });
}
fn SDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildSDiv(B(cx), LHS, RHS, buf) });
}
fn ExactSDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildExactSDiv(B(cx), LHS, RHS, buf) });
}
fn FDiv(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildFDiv(B(cx), LHS, RHS, buf) });
}
fn URem(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildURem(B(cx), LHS, RHS, buf) });
}
fn SRem(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildSRem(B(cx), LHS, RHS, buf) });
}
fn FRem(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildFRem(B(cx), LHS, RHS, buf) });
}
fn Shl(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildShl(B(cx), LHS, RHS, buf) });
}
fn LShr(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildLShr(B(cx), LHS, RHS, buf) });
}
fn AShr(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildAShr(B(cx), LHS, RHS, buf) });
}
fn And(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildAnd(B(cx), LHS, RHS, buf) });
}
fn Or(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildOr(B(cx), LHS, RHS, buf) });
}
fn Xor(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("", {|buf| llvm::LLVMBuildXor(B(cx), LHS, RHS, buf) });
}
fn BinOp(cx: @block_ctxt, Op: Opcode, LHS: ValueRef, RHS: ValueRef) ->
ValueRef {
if cx.unreachable { ret _Undef(LHS); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildBinOp(B(cx), Op, LHS, RHS, buf) });
}
fn Neg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(V); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNeg(B(cx), V, buf) });
}
fn NSWNeg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(V); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNSWNeg(B(cx), V, buf) });
}
fn NUWNeg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(V); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNUWNeg(B(cx), V, buf) });
}
fn FNeg(cx: @block_ctxt, V: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(V); }
ret str::as_buf("", {|buf| llvm::LLVMBuildFNeg(B(cx), V, buf) });
}
fn Not(cx: @block_ctxt, V: ValueRef) -> ValueRef {
if cx.unreachable { ret _Undef(V); }
ret str::as_buf("", {|buf| llvm::LLVMBuildNot(B(cx), V, buf) });
}
/* Memory */
fn Malloc(cx: @block_ctxt, Ty: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
ret str::as_buf("", {|buf| llvm::LLVMBuildMalloc(B(cx), Ty, buf) });
}
fn ArrayMalloc(cx: @block_ctxt, Ty: TypeRef, Val: ValueRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildArrayMalloc(B(cx), Ty, Val, buf) });
}
fn Alloca(cx: @block_ctxt, Ty: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(Ty)); }
ret str::as_buf("", {|buf| llvm::LLVMBuildAlloca(B(cx), Ty, buf) });
}
fn ArrayAlloca(cx: @block_ctxt, Ty: TypeRef, Val: ValueRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(Ty)); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildArrayAlloca(B(cx), Ty, Val, buf) });
}
fn Free(cx: @block_ctxt, PointerVal: ValueRef) -> ValueRef {
ret llvm::LLVMBuildFree(B(cx), PointerVal);
fn Free(cx: @block_ctxt, PointerVal: ValueRef) {
if cx.unreachable { ret; }
llvm::LLVMBuildFree(B(cx), PointerVal);
}
fn Load(cx: @block_ctxt, PointerVal: ValueRef) -> ValueRef {
if cx.unreachable {
let ty = val_ty(PointerVal);
let eltty = if llvm::LLVMGetTypeKind(ty) == 11 {
llvm::LLVMGetElementType(ty) } else { T_int() };
ret llvm::LLVMGetUndef(eltty);
}
ret str::as_buf("", {|buf| llvm::LLVMBuildLoad(B(cx), PointerVal, buf) });
}
fn Store(cx: @block_ctxt, Val: ValueRef, Ptr: ValueRef) -> ValueRef {
ret llvm::LLVMBuildStore(B(cx), Val, Ptr);
fn Store(cx: @block_ctxt, Val: ValueRef, Ptr: ValueRef) {
if cx.unreachable { ret; }
llvm::LLVMBuildStore(B(cx), Val, Ptr);
}
fn GEP(cx: @block_ctxt, Pointer: ValueRef, Indices: [ValueRef]) -> ValueRef {
ret str::as_buf("",
{|buf|
llvm::LLVMBuildGEP(B(cx), Pointer,
vec::to_ptr(Indices),
vec::len(Indices), buf)
});
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); }
ret str::as_buf("", {|buf|
llvm::LLVMBuildGEP(B(cx), Pointer,
vec::to_ptr(Indices),
vec::len(Indices), buf)
});
}
fn InBoundsGEP(cx: @block_ctxt, Pointer: ValueRef, Indices: [ValueRef]) ->
ValueRef {
ret str::as_buf("",
{|buf|
llvm::LLVMBuildInBoundsGEP(B(cx), Pointer,
vec::to_ptr(Indices),
vec::len(Indices), buf)
});
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); }
ret str::as_buf("", {|buf|
llvm::LLVMBuildInBoundsGEP(B(cx), Pointer, vec::to_ptr(Indices),
vec::len(Indices), buf)
});
}
fn StructGEP(cx: @block_ctxt, Pointer: ValueRef, Idx: uint) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_nil())); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildStructGEP(B(cx), Pointer, Idx, buf)
@ -274,11 +337,13 @@ fn StructGEP(cx: @block_ctxt, Pointer: ValueRef, Idx: uint) -> ValueRef {
}
fn GlobalString(cx: @block_ctxt, _Str: sbuf) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildGlobalString(B(cx), _Str, buf) });
}
fn GlobalStringPtr(cx: @block_ctxt, _Str: sbuf) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_ptr(T_i8())); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildGlobalStringPtr(B(cx), _Str, buf)
@ -287,51 +352,61 @@ fn GlobalStringPtr(cx: @block_ctxt, _Str: sbuf) -> ValueRef {
/* Casts */
fn Trunc(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildTrunc(B(cx), Val, DestTy, buf) });
}
fn ZExt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildZExt(B(cx), Val, DestTy, buf) });
}
fn SExt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildSExt(B(cx), Val, DestTy, buf) });
}
fn FPToUI(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildFPToUI(B(cx), Val, DestTy, buf) });
}
fn FPToSI(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildFPToSI(B(cx), Val, DestTy, buf) });
}
fn UIToFP(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildUIToFP(B(cx), Val, DestTy, buf) });
}
fn SIToFP(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildSIToFP(B(cx), Val, DestTy, buf) });
}
fn FPTrunc(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildFPTrunc(B(cx), Val, DestTy, buf) });
}
fn FPExt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildFPExt(B(cx), Val, DestTy, buf) });
}
fn PtrToInt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildPtrToInt(B(cx), Val, DestTy, buf)
@ -339,6 +414,7 @@ fn PtrToInt(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
}
fn IntToPtr(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildIntToPtr(B(cx), Val, DestTy, buf)
@ -346,12 +422,14 @@ fn IntToPtr(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
}
fn BitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildBitCast(B(cx), Val, DestTy, buf) });
}
fn ZExtOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildZExtOrBitCast(B(cx), Val, DestTy, buf)
@ -360,6 +438,7 @@ fn ZExtOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
fn SExtOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildSExtOrBitCast(B(cx), Val, DestTy, buf)
@ -368,6 +447,7 @@ fn SExtOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
fn TruncOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildTruncOrBitCast(B(cx), Val, DestTy, buf)
@ -376,6 +456,7 @@ fn TruncOrBitCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) ->
fn Cast(cx: @block_ctxt, Op: Opcode, Val: ValueRef, DestTy: TypeRef,
_Name: sbuf) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildCast(B(cx), Op, Val, DestTy, buf)
@ -383,6 +464,7 @@ fn Cast(cx: @block_ctxt, Op: Opcode, Val: ValueRef, DestTy: TypeRef,
}
fn PointerCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildPointerCast(B(cx), Val, DestTy, buf)
@ -390,11 +472,13 @@ fn PointerCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
}
fn IntCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildIntCast(B(cx), Val, DestTy, buf) });
}
fn FPCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(DestTy); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildFPCast(B(cx), Val, DestTy, buf) });
}
@ -402,11 +486,13 @@ fn FPCast(cx: @block_ctxt, Val: ValueRef, DestTy: TypeRef) -> ValueRef {
/* Comparisons */
fn ICmp(cx: @block_ctxt, Op: uint, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildICmp(B(cx), Op, LHS, RHS, buf) });
}
fn FCmp(cx: @block_ctxt, Op: uint, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildFCmp(B(cx), Op, LHS, RHS, buf) });
}
@ -415,6 +501,7 @@ fn FCmp(cx: @block_ctxt, Op: uint, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
/* Miscellaneous instructions */
fn Phi(cx: @block_ctxt, Ty: TypeRef, vals: [ValueRef], bbs: [BasicBlockRef])
-> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(Ty); }
let phi = str::as_buf("", {|buf| llvm::LLVMBuildPhi(B(cx), Ty, buf) });
assert (vec::len::<ValueRef>(vals) == vec::len::<BasicBlockRef>(bbs));
llvm::LLVMAddIncoming(phi, vec::to_ptr(vals), vec::to_ptr(bbs),
@ -423,12 +510,21 @@ fn Phi(cx: @block_ctxt, Ty: TypeRef, vals: [ValueRef], bbs: [BasicBlockRef])
}
fn AddIncomingToPhi(phi: ValueRef, vals: [ValueRef], bbs: [BasicBlockRef]) {
if llvm::LLVMIsUndef(phi) == lib::llvm::True { ret; }
assert (vec::len::<ValueRef>(vals) == vec::len::<BasicBlockRef>(bbs));
llvm::LLVMAddIncoming(phi, vec::to_ptr(vals), vec::to_ptr(bbs),
vec::len(vals));
}
fn _UndefReturn(Fn: ValueRef) -> ValueRef {
let ty = val_ty(Fn);
let retty = if llvm::LLVMGetTypeKind(ty) == 8 {
llvm::LLVMGetReturnType(ty) } else { T_int() };
ret llvm::LLVMGetUndef(retty);
}
fn Call(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef]) -> ValueRef {
if cx.unreachable { ret _UndefReturn(Fn); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildCall(B(cx), Fn, vec::to_ptr(Args),
@ -437,6 +533,7 @@ fn Call(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef]) -> ValueRef {
}
fn FastCall(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef]) -> ValueRef {
if cx.unreachable { ret _UndefReturn(Fn); }
let v =
str::as_buf("",
{|buf|
@ -449,6 +546,7 @@ fn FastCall(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef]) -> ValueRef {
fn CallWithConv(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef], Conv: uint)
-> ValueRef {
if cx.unreachable { ret _UndefReturn(Fn); }
let v =
str::as_buf("",
{|buf|
@ -461,6 +559,7 @@ fn CallWithConv(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef], Conv: uint)
fn Select(cx: @block_ctxt, If: ValueRef, Then: ValueRef, Else: ValueRef) ->
ValueRef {
if cx.unreachable { ret _Undef(Then); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildSelect(B(cx), If, Then, Else, buf)
@ -468,11 +567,13 @@ fn Select(cx: @block_ctxt, If: ValueRef, Then: ValueRef, Else: ValueRef) ->
}
fn VAArg(cx: @block_ctxt, list: ValueRef, Ty: TypeRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(Ty); }
ret str::as_buf("", {|buf| llvm::LLVMBuildVAArg(B(cx), list, Ty, buf) });
}
fn ExtractElement(cx: @block_ctxt, VecVal: ValueRef, Index: ValueRef) ->
ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_nil()); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildExtractElement(B(cx), VecVal, Index,
@ -481,23 +582,23 @@ fn ExtractElement(cx: @block_ctxt, VecVal: ValueRef, Index: ValueRef) ->
}
fn InsertElement(cx: @block_ctxt, VecVal: ValueRef, EltVal: ValueRef,
Index: ValueRef) -> ValueRef {
ret str::as_buf("",
{|buf|
llvm::LLVMBuildInsertElement(B(cx), VecVal, EltVal,
Index, buf)
});
Index: ValueRef) {
if cx.unreachable { ret; }
str::as_buf("", {|buf|
llvm::LLVMBuildInsertElement(B(cx), VecVal, EltVal, Index, buf)
});
}
fn ShuffleVector(cx: @block_ctxt, V1: ValueRef, V2: ValueRef, Mask: ValueRef)
-> ValueRef {
ret str::as_buf("",
{|buf|
llvm::LLVMBuildShuffleVector(B(cx), V1, V2, Mask, buf)
});
fn ShuffleVector(cx: @block_ctxt, V1: ValueRef, V2: ValueRef,
Mask: ValueRef) {
if cx.unreachable { ret; }
str::as_buf("", {|buf|
llvm::LLVMBuildShuffleVector(B(cx), V1, V2, Mask, buf)
});
}
fn ExtractValue(cx: @block_ctxt, AggVal: ValueRef, Index: uint) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_nil()); }
ret str::as_buf("",
{|buf|
llvm::LLVMBuildExtractValue(B(cx), AggVal, Index, buf)
@ -505,28 +606,31 @@ fn ExtractValue(cx: @block_ctxt, AggVal: ValueRef, Index: uint) -> ValueRef {
}
fn InsertValue(cx: @block_ctxt, AggVal: ValueRef, EltVal: ValueRef,
Index: uint) -> ValueRef {
ret str::as_buf("",
{|buf|
llvm::LLVMBuildInsertValue(B(cx), AggVal, EltVal,
Index, buf)
});
Index: uint) {
if cx.unreachable { ret; }
str::as_buf("", {|buf|
llvm::LLVMBuildInsertValue(B(cx), AggVal, EltVal, Index, buf)
});
}
fn IsNull(cx: @block_ctxt, Val: ValueRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
ret str::as_buf("", {|buf| llvm::LLVMBuildIsNull(B(cx), Val, buf) });
}
fn IsNotNull(cx: @block_ctxt, Val: ValueRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_i1()); }
ret str::as_buf("", {|buf| llvm::LLVMBuildIsNotNull(B(cx), Val, buf) });
}
fn PtrDiff(cx: @block_ctxt, LHS: ValueRef, RHS: ValueRef) -> ValueRef {
if cx.unreachable { ret llvm::LLVMGetUndef(T_int()); }
ret str::as_buf("",
{|buf| llvm::LLVMBuildPtrDiff(B(cx), LHS, RHS, buf) });
}
fn Trap(cx: @block_ctxt) -> ValueRef {
fn Trap(cx: @block_ctxt) {
if cx.unreachable { ret; }
let b = B(cx);
let BB: BasicBlockRef = llvm::LLVMGetInsertBlock(b);
let FN: ValueRef = llvm::LLVMGetBasicBlockParent(BB);
@ -535,24 +639,17 @@ fn Trap(cx: @block_ctxt) -> ValueRef {
str::as_buf("llvm.trap", {|buf| llvm::LLVMGetNamedFunction(M, buf) });
assert (T as int != 0);
let Args: [ValueRef] = [];
ret str::as_buf("",
{|buf|
llvm::LLVMBuildCall(b, T, vec::to_ptr(Args),
vec::len(Args), buf)
});
str::as_buf("", {|buf|
llvm::LLVMBuildCall(b, T, vec::to_ptr(Args), vec::len(Args), buf)
});
}
fn LandingPad(cx: @block_ctxt, Ty: TypeRef, PersFn: ValueRef,
NumClauses: uint) -> ValueRef {
assert (!cx.terminated);
ret str::as_buf("",
{|buf|
llvm::LLVMBuildLandingPad(B(cx),
Ty,
PersFn,
NumClauses,
buf)
});
assert !cx.terminated && !cx.unreachable;
ret str::as_buf("", {|buf|
llvm::LLVMBuildLandingPad(B(cx), Ty, PersFn, NumClauses, buf)
});
}
fn SetCleanup(_cx: @block_ctxt, LandingPad: ValueRef) {

View file

@ -386,6 +386,7 @@ type block_ctxt =
// attached.
{llbb: BasicBlockRef,
mutable terminated: bool,
mutable unreachable: bool,
parent: block_parent,
kind: block_kind,
mutable cleanups: [cleanup],
@ -394,8 +395,6 @@ type block_ctxt =
sp: span,
fcx: @fn_ctxt};
fn is_terminated(cx: @block_ctxt) -> bool { ret cx.terminated; }
// FIXME: we should be able to use option::t<@block_parent> here but
// the infinite-tag check in rustboot gets upset.
tag block_parent { parent_none; parent_some(@block_ctxt); }

View file

@ -281,8 +281,8 @@ fn iter_vec_raw(bcx: @block_ctxt, vptrptr: ValueRef, vec_ty: ty::t,
let data_ptr = Phi(header_cx, val_ty(data_ptr), [data_ptr], [bcx.llbb]);
let not_yet_at_end =
ICmp(header_cx, lib::llvm::LLVMIntULT, data_ptr, data_end_ptr);
let body_cx = new_sub_block_ctxt(bcx, "iter_vec_loop_body");
let next_cx = new_sub_block_ctxt(bcx, "iter_vec_next");
let body_cx = new_sub_block_ctxt(header_cx, "iter_vec_loop_body");
let next_cx = new_sub_block_ctxt(header_cx, "iter_vec_next");
CondBr(header_cx, not_yet_at_end, body_cx.llbb, next_cx.llbb);
body_cx = f(body_cx, data_ptr, unit_ty);
let increment =

View file

@ -0,0 +1,35 @@
fn id(x: bool) -> bool { x }
fn call_id() {
let c <- fail;
id(c);
}
fn call_id_2() { id(true) && id(ret); }
fn call_id_3() { id(ret) && id(ret); }
fn call_id_4() { while id(break) { } }
iter put_break() -> int {
while true { put break; }
}
fn log_fail() { log_err fail; }
fn ret_ret() -> int { ret (ret 2) + 3; }
fn ret_guard() {
alt 2 {
x when (ret) { x; }
}
}
fn fail_then_concat() {
let x = [], y = [3];
fail;
x += y;
"good" + "bye";
}
fn main() {}