diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs index d4083615f48..865417031d0 100644 --- a/src/comp/middle/trans.rs +++ b/src/comp/middle/trans.rs @@ -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::, + ret trans_fail(cx, none::, "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::(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, 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_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); diff --git a/src/comp/middle/trans_alt.rs b/src/comp/middle/trans_alt.rs index eea4704c3d0..c50c6c87d00 100644 --- a/src/comp/middle/trans_alt.rs +++ b/src/comp/middle/trans_alt.rs @@ -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 { diff --git a/src/comp/middle/trans_build.rs b/src/comp/middle/trans_build.rs index 5c2a763603b..979d6813077 100644 --- a/src/comp/middle/trans_build.rs +++ b/src/comp/middle/trans_build.rs @@ -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::(vals) == vec::len::(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::(vals) == vec::len::(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) { diff --git a/src/comp/middle/trans_common.rs b/src/comp/middle/trans_common.rs index ce49024a88b..fb32e6bfe9a 100644 --- a/src/comp/middle/trans_common.rs +++ b/src/comp/middle/trans_common.rs @@ -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); } diff --git a/src/comp/middle/trans_vec.rs b/src/comp/middle/trans_vec.rs index 5c04aafeeda..386a9688969 100644 --- a/src/comp/middle/trans_vec.rs +++ b/src/comp/middle/trans_vec.rs @@ -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 = diff --git a/src/test/run-pass/unreachable-code.rs b/src/test/run-pass/unreachable-code.rs new file mode 100644 index 00000000000..4441ac99a00 --- /dev/null +++ b/src/test/run-pass/unreachable-code.rs @@ -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() {}