diff --git a/src/librustc/middle/trans/base.rs b/src/librustc/middle/trans/base.rs index c0336c8e60d52..1173fa4941963 100644 --- a/src/librustc/middle/trans/base.rs +++ b/src/librustc/middle/trans/base.rs @@ -1102,11 +1102,6 @@ pub fn trans_trace(bcx: block, sp_opt: Option, trace_str: @str) { Call(bcx, ccx.upcalls.trace, args); } -pub fn build_return(bcx: block) { - let _icx = push_ctxt("build_return"); - Br(bcx, bcx.fcx.llreturn); -} - pub fn ignore_lhs(_bcx: block, local: &ast::local) -> bool { match local.node.pat.node { ast::pat_wild => true, _ => false @@ -1364,6 +1359,42 @@ pub fn cleanup_and_leave(bcx: block, } } +pub fn cleanup_block(bcx: block, upto: Option) -> block{ + let _icx = push_ctxt("cleanup_block"); + let mut cur = bcx; + let mut bcx = bcx; + loop { + debug!("cleanup_block: %s", cur.to_str()); + + if bcx.sess().trace() { + trans_trace( + bcx, None, + (fmt!("cleanup_block(%s)", cur.to_str())).to_managed()); + } + + let mut cur_scope = cur.scope; + loop { + cur_scope = match cur_scope { + Some (inf) => { + bcx = trans_block_cleanups_(bcx, inf.cleanups.to_owned(), false); + inf.parent + } + None => break + } + } + + match upto { + Some(bb) => { if cur.llbb == bb { break; } } + _ => () + } + cur = match cur.parent { + Some(next) => next, + None => { assert!(upto.is_none()); break; } + }; + } + bcx +} + pub fn cleanup_and_Br(bcx: block, upto: block, target: BasicBlockRef) { let _icx = push_ctxt("cleanup_and_Br"); cleanup_and_leave(bcx, Some(upto.llbb), Some(target)); @@ -1526,7 +1557,7 @@ pub fn alloca_maybe_zeroed(cx: block, ty: Type, name: &str, zero: bool) -> Value return llvm::LLVMGetUndef(ty.to_ref()); } } - let initcx = base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas); + let initcx = base::raw_block(cx.fcx, false, cx.fcx.get_llstaticallocas()); let p = Alloca(initcx, ty, name); if zero { memzero(initcx, p, ty); } p @@ -1539,24 +1570,26 @@ pub fn arrayalloca(cx: block, ty: Type, v: ValueRef) -> ValueRef { return llvm::LLVMGetUndef(ty.to_ref()); } } - return ArrayAlloca(base::raw_block(cx.fcx, false, cx.fcx.llstaticallocas), ty, v); + return ArrayAlloca(base::raw_block(cx.fcx, false, cx.fcx.get_llstaticallocas()), ty, v); } pub struct BasicBlocks { sa: BasicBlockRef, - rt: BasicBlockRef } -// Creates the standard set of basic blocks for a function -pub fn mk_standard_basic_blocks(llfn: ValueRef) -> BasicBlocks { +pub fn mk_staticallocas_basic_block(llfn: ValueRef) -> BasicBlockRef { unsafe { let cx = task_llcx(); - BasicBlocks { - sa: str::as_c_str("static_allocas", - |buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf)), - rt: str::as_c_str("return", - |buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf)) - } + str::as_c_str("static_allocas", + |buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf)) + } +} + +pub fn mk_return_basic_block(llfn: ValueRef) -> BasicBlockRef { + unsafe { + let cx = task_llcx(); + str::as_c_str("return", + |buf| llvm::LLVMAppendBasicBlockInContext(cx, llfn, buf)) } } @@ -1568,7 +1601,7 @@ pub fn make_return_pointer(fcx: fn_ctxt, output_type: ty::t) -> ValueRef { llvm::LLVMGetParam(fcx.llfn, 0) } else { let lloutputtype = type_of::type_of(fcx.ccx, output_type); - alloca(raw_block(fcx, false, fcx.llstaticallocas), lloutputtype, + alloca(raw_block(fcx, false, fcx.get_llstaticallocas()), lloutputtype, "__make_return_pointer") } } @@ -1596,8 +1629,6 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext, id, param_substs.repr(ccx.tcx)); - let llbbs = mk_standard_basic_blocks(llfndecl); - let substd_output_type = match param_substs { None => output_type, Some(substs) => { @@ -1611,9 +1642,9 @@ pub fn new_fn_ctxt_w_id(ccx: @mut CrateContext, llvm::LLVMGetUndef(Type::i8p().to_ref()) }, llretptr: None, - llstaticallocas: llbbs.sa, + llstaticallocas: None, llloadenv: None, - llreturn: llbbs.rt, + llreturn: None, llself: None, personality: None, loop_ret: None, @@ -1757,16 +1788,24 @@ pub fn copy_args_to_allocas(fcx: fn_ctxt, // Ties up the llstaticallocas -> llloadenv -> lltop edges, // and builds the return block. -pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef) { +pub fn finish_fn(fcx: fn_ctxt, lltop: BasicBlockRef, last_bcx: block) { let _icx = push_ctxt("finish_fn"); tie_up_header_blocks(fcx, lltop); - build_return_block(fcx); + + let ret_cx = match fcx.llreturn { + Some(llreturn) => { + if !last_bcx.terminated { + Br(last_bcx, llreturn); + } + raw_block(fcx, false, llreturn) + } + None => last_bcx + }; + build_return_block(fcx, ret_cx); } // Builds the return block for a function. -pub fn build_return_block(fcx: fn_ctxt) { - let ret_cx = raw_block(fcx, false, fcx.llreturn); - +pub fn build_return_block(fcx: fn_ctxt, ret_cx: block) { // Return the value if this function immediate; otherwise, return void. if fcx.llretptr.is_some() && fcx.has_immediate_return_value { Ret(ret_cx, Load(ret_cx, fcx.llretptr.get())) @@ -1777,14 +1816,24 @@ pub fn build_return_block(fcx: fn_ctxt) { pub fn tie_up_header_blocks(fcx: fn_ctxt, lltop: BasicBlockRef) { let _icx = push_ctxt("tie_up_header_blocks"); - match fcx.llloadenv { + let llnext = match fcx.llloadenv { Some(ll) => { - Br(raw_block(fcx, false, fcx.llstaticallocas), ll); + unsafe { + llvm::LLVMMoveBasicBlockBefore(ll, lltop); + } Br(raw_block(fcx, false, ll), lltop); + ll } - None => { - Br(raw_block(fcx, false, fcx.llstaticallocas), lltop); + None => lltop + }; + match fcx.llstaticallocas { + Some(ll) => { + unsafe { + llvm::LLVMMoveBasicBlockBefore(ll, llnext); + } + Br(raw_block(fcx, false, ll), llnext); } + None => () } } @@ -1854,16 +1903,21 @@ pub fn trans_closure(ccx: @mut CrateContext, } finish(bcx); - cleanup_and_Br(bcx, bcx_top, fcx.llreturn); + match fcx.llreturn { + Some(llreturn) => cleanup_and_Br(bcx, bcx_top, llreturn), + None => bcx = cleanup_block(bcx, Some(bcx_top.llbb)) + }; // Put return block after all other blocks. // This somewhat improves single-stepping experience in debugger. unsafe { - llvm::LLVMMoveBasicBlockAfter(fcx.llreturn, bcx.llbb); + for fcx.llreturn.iter().advance |&llreturn| { + llvm::LLVMMoveBasicBlockAfter(llreturn, bcx.llbb); + } } // Insert the mandatory first few basic blocks before lltop. - finish_fn(fcx, lltop); + finish_fn(fcx, lltop, bcx); } // trans_fn: creates an LLVM function corresponding to a source language @@ -2046,8 +2100,7 @@ pub fn trans_enum_variant_or_tuple_like_struct( let arg_ty = arg_tys[i]; memcpy_ty(bcx, lldestptr, llarg, arg_ty); } - build_return(bcx); - finish_fn(fcx, lltop); + finish_fn(fcx, lltop, bcx); } pub fn trans_enum_def(ccx: @mut CrateContext, enum_definition: &ast::enum_def, @@ -2288,8 +2341,7 @@ pub fn create_entry_wrapper(ccx: @mut CrateContext, let args = ~[llenvarg]; Call(bcx, main_llfn, args); - build_return(bcx); - finish_fn(fcx, lltop); + finish_fn(fcx, lltop, bcx); return llfdecl; } diff --git a/src/librustc/middle/trans/callee.rs b/src/librustc/middle/trans/callee.rs index 01bd69f24fc95..7b7989879a639 100644 --- a/src/librustc/middle/trans/callee.rs +++ b/src/librustc/middle/trans/callee.rs @@ -704,7 +704,7 @@ pub fn trans_call_inner(in_cx: block, Store(bcx, C_bool(false), bcx.fcx.llretptr.get()); } } - base::cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + base::cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn())); Unreachable(bcx); bcx } diff --git a/src/librustc/middle/trans/common.rs b/src/librustc/middle/trans/common.rs index 1a45ce36af2f3..83029a90260ff 100644 --- a/src/librustc/middle/trans/common.rs +++ b/src/librustc/middle/trans/common.rs @@ -178,13 +178,13 @@ pub struct fn_ctxt_ { // the function, due to LLVM's quirks. // A block for all the function's static allocas, so that LLVM // will coalesce them into a single alloca call. - llstaticallocas: BasicBlockRef, + llstaticallocas: Option, // A block containing code that copies incoming arguments to space // already allocated by code in one of the llallocas blocks. // (LLVM requires that arguments be copied to local allocas before // allowing most any operation to be performed on them.) llloadenv: Option, - llreturn: BasicBlockRef, + llreturn: Option, // The 'self' value currently in use in this function, if there // is one. // @@ -251,6 +251,21 @@ impl fn_ctxt_ { } } + pub fn get_llstaticallocas(&mut self) -> BasicBlockRef { + if self.llstaticallocas.is_none() { + self.llstaticallocas = Some(base::mk_staticallocas_basic_block(self.llfn)); + } + + self.llstaticallocas.get() + } + + pub fn get_llreturn(&mut self) -> BasicBlockRef { + if self.llreturn.is_none() { + self.llreturn = Some(base::mk_return_basic_block(self.llfn)); + } + + self.llreturn.get() + } } pub type fn_ctxt = @mut fn_ctxt_; diff --git a/src/librustc/middle/trans/controlflow.rs b/src/librustc/middle/trans/controlflow.rs index 6cbd6304847e3..81260428f241b 100644 --- a/src/librustc/middle/trans/controlflow.rs +++ b/src/librustc/middle/trans/controlflow.rs @@ -67,13 +67,8 @@ pub fn trans_if(bcx: block, expr::trans_to_datum(bcx, cond).to_result(); let then_bcx_in = scope_block(bcx, thn.info(), "then"); - let else_bcx_in = scope_block(bcx, els.info(), "else"); let cond_val = bool_to_i1(bcx, cond_val); - CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); - - debug!("then_bcx_in=%s, else_bcx_in=%s", - then_bcx_in.to_str(), else_bcx_in.to_str()); let then_bcx_out = trans_block(then_bcx_in, thn, dest); let then_bcx_out = trans_block_cleanups(then_bcx_out, @@ -83,9 +78,10 @@ pub fn trans_if(bcx: block, // because trans_expr will create another scope block // context for the block, but we've already got the // 'else' context - let else_bcx_out = match els { + let (else_bcx_in, next_bcx) = match els { Some(elexpr) => { - match elexpr.node { + let else_bcx_in = scope_block(bcx, els.info(), "else"); + let else_bcx_out = match elexpr.node { ast::expr_if(_, _, _) => { let elseif_blk = ast_util::block_from_expr(elexpr); trans_block(else_bcx_in, &elseif_blk, dest) @@ -95,14 +91,25 @@ pub fn trans_if(bcx: block, } // would be nice to have a constraint on ifs _ => bcx.tcx().sess.bug("strange alternative in if") - } + }; + let else_bcx_out = trans_block_cleanups(else_bcx_out, + block_cleanups(else_bcx_in)); + + (else_bcx_in, join_blocks(bcx, [then_bcx_out, else_bcx_out])) + } + _ => { + let next_bcx = sub_block(bcx, "next"); + Br(then_bcx_out, next_bcx.llbb); + + (next_bcx, next_bcx) } - _ => else_bcx_in }; - let else_bcx_out = trans_block_cleanups(else_bcx_out, - block_cleanups(else_bcx_in)); - return join_blocks(bcx, [then_bcx_out, else_bcx_out]); + debug!("then_bcx_in=%s, else_bcx_in=%s", + then_bcx_in.to_str(), else_bcx_in.to_str()); + + CondBr(bcx, cond_val, then_bcx_in.llbb, else_bcx_in.llbb); + next_bcx } pub fn join_blocks(parent_bcx: block, in_cxs: &[block]) -> block { @@ -279,7 +286,7 @@ pub fn trans_break_cont(bcx: block, // This is a return from a loop body block None => { Store(bcx, C_bool(!to_end), bcx.fcx.llretptr.get()); - cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn())); Unreachable(bcx); return bcx; } @@ -328,7 +335,7 @@ pub fn trans_ret(bcx: block, e: Option<@ast::expr>) -> block { } _ => () } - cleanup_and_leave(bcx, None, Some(bcx.fcx.llreturn)); + cleanup_and_leave(bcx, None, Some(bcx.fcx.get_llreturn())); Unreachable(bcx); return bcx; } diff --git a/src/librustc/middle/trans/foreign.rs b/src/librustc/middle/trans/foreign.rs index 9019cd72ff8a7..edf003e3e529e 100644 --- a/src/librustc/middle/trans/foreign.rs +++ b/src/librustc/middle/trans/foreign.rs @@ -164,7 +164,10 @@ fn build_shim_fn_(ccx: @mut CrateContext, // follow the normal Rust calling conventions. tie_up_header_blocks(fcx, lltop); - let ret_cx = raw_block(fcx, false, fcx.llreturn); + let ret_cx = match fcx.llreturn { + Some(llreturn) => raw_block(fcx, false, llreturn), + None => bcx + }; RetVoid(ret_cx); return llshimfn; @@ -194,7 +197,7 @@ fn build_wrap_fn_(ccx: @mut CrateContext, // the C ABI. if needs_c_return && !ty::type_is_immediate(ccx.tcx, tys.fn_sig.output) { let lloutputtype = type_of::type_of(fcx.ccx, tys.fn_sig.output); - fcx.llretptr = Some(alloca(raw_block(fcx, false, fcx.llstaticallocas), + fcx.llretptr = Some(alloca(raw_block(fcx, false, fcx.get_llstaticallocas()), lloutputtype, "")); } @@ -217,7 +220,10 @@ fn build_wrap_fn_(ccx: @mut CrateContext, tie_up_header_blocks(fcx, lltop); // Then return according to the C ABI. - let return_context = raw_block(fcx, false, fcx.llreturn); + let return_context = match fcx.llreturn { + Some(llreturn) => raw_block(fcx, false, llreturn), + None => bcx + }; let llfunctiontype = val_ty(llwrapfn); let llfunctiontype = llfunctiontype.element_type(); @@ -388,7 +394,6 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext, tys.ret_def, llargbundle, llretval); - build_return(bcx); } let lname = link_name(ccx, foreign_item); @@ -438,8 +443,7 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext, if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) { Store(bcx, retval, fcx.llretptr.get()); } - build_return(bcx); - finish_fn(fcx, lltop); + finish_fn(fcx, lltop, bcx); } // FIXME (#2535): this is very shaky and probably gets ABIs wrong all @@ -467,8 +471,7 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext, if !ty::type_is_nil(ret_ty) && !ty::type_is_bot(ret_ty) { Store(bcx, retval, fcx.llretptr.get()); } - build_return(bcx); - finish_fn(fcx, lltop); + finish_fn(fcx, lltop, bcx); } fn build_wrap_fn(ccx: @mut CrateContext, @@ -534,7 +537,6 @@ pub fn trans_foreign_mod(ccx: @mut CrateContext, let llretptr = load_inbounds(bcx, llargbundle, [0, arg_count]); Store(bcx, Load(bcx, llretptr), retptr); } - build_return(bcx); } } } @@ -629,8 +631,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext, } } - build_return(bcx); - finish_fn(fcx, lltop); + finish_fn(fcx, lltop, bcx); return; } @@ -1124,8 +1125,7 @@ pub fn trans_intrinsic(ccx: @mut CrateContext, ccx.sess.span_bug(item.span, "unknown intrinsic"); } } - build_return(bcx); - finish_fn(fcx, lltop); + finish_fn(fcx, lltop, bcx); } /** @@ -1257,8 +1257,6 @@ pub fn trans_foreign_fn(ccx: @mut CrateContext, // NB: The return pointer in the Rust ABI function is wired // directly into the return slot in the shim struct. } - - build_return(bcx); } let shim_name = link::mangle_internal_name_by_path( @@ -1314,7 +1312,6 @@ pub fn trans_foreign_fn(ccx: @mut CrateContext, fn build_ret(bcx: block, tys: &ShimTypes, llargbundle: ValueRef) { let _icx = push_ctxt("foreign::foreign::wrap::build_ret"); tys.fn_ty.build_wrap_ret(bcx, tys.llsig.llarg_tys, llargbundle); - build_return(bcx); } } diff --git a/src/librustc/middle/trans/glue.rs b/src/librustc/middle/trans/glue.rs index b6f226683df9f..418c89e603d07 100644 --- a/src/librustc/middle/trans/glue.rs +++ b/src/librustc/middle/trans/glue.rs @@ -348,9 +348,9 @@ pub fn call_tydesc_glue(cx: block, v: ValueRef, t: ty::t, field: uint) return cx; } -pub fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) { +pub fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) -> block { let _icx = push_ctxt("make_visit_glue"); - let bcx = do with_scope(bcx, None, "visitor cleanup") |bcx| { + do with_scope(bcx, None, "visitor cleanup") |bcx| { let mut bcx = bcx; let (visitor_trait, object_ty) = ty::visitor_object_ty(bcx.tcx()); let v = PointerCast(bcx, v, type_of::type_of(bcx.ccx(), object_ty).ptr_to()); @@ -358,14 +358,13 @@ pub fn make_visit_glue(bcx: block, v: ValueRef, t: ty::t) { // The visitor is a boxed object and needs to be dropped add_clean(bcx, v, object_ty); bcx - }; - build_return(bcx); + } } -pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { +pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) -> block { // NB: v0 is an *alias* of type t here, not a direct value. let _icx = push_ctxt("make_free_glue"); - let bcx = match ty::get(t).sty { + match ty::get(t).sty { ty::ty_box(body_mt) => { let v = Load(bcx, v); let body = GEPi(bcx, v, [0u, abi::box_field_body]); @@ -389,9 +388,7 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { tvec::make_uniq_free_glue(bcx, v, t) } ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => { - make_free_glue(bcx, v, - tvec::expand_boxed_vec_ty(bcx.tcx(), t)); - return; + make_free_glue(bcx, v, tvec::expand_boxed_vec_ty(bcx.tcx(), t)) } ty::ty_closure(_) => { closure::make_closure_glue(bcx, v, t, free_ty) @@ -400,8 +397,7 @@ pub fn make_free_glue(bcx: block, v: ValueRef, t: ty::t) { closure::make_opaque_cbox_free_glue(bcx, ck, v) } _ => bcx - }; - build_return(bcx); + } } pub fn trans_struct_drop_flag(bcx: block, t: ty::t, v0: ValueRef, dtor_did: ast::def_id, @@ -475,11 +471,11 @@ pub fn trans_struct_drop(mut bcx: block, t: ty::t, v0: ValueRef, dtor_did: ast:: bcx } -pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { +pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) -> block { // NB: v0 is an *alias* of type t here, not a direct value. let _icx = push_ctxt("make_drop_glue"); let ccx = bcx.ccx(); - let bcx = match ty::get(t).sty { + match ty::get(t).sty { ty::ty_box(_) | ty::ty_opaque_box | ty::ty_estr(ty::vstore_box) | ty::ty_evec(_, ty::vstore_box) => { decr_refcnt_maybe_free(bcx, Load(bcx, v0), Some(v0), t) @@ -542,8 +538,7 @@ pub fn make_drop_glue(bcx: block, v0: ValueRef, t: ty::t) { iter_structural_ty(bcx, v0, t, drop_ty) } else { bcx } } - }; - build_return(bcx); + } } // box_ptr_ptr is optional, it is constructed if not supplied. @@ -569,10 +564,10 @@ pub fn decr_refcnt_maybe_free(bcx: block, box_ptr: ValueRef, } -pub fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) { +pub fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) -> block { let _icx = push_ctxt("make_take_glue"); // NB: v is a *pointer* to type t here, not a direct value. - let bcx = match ty::get(t).sty { + match ty::get(t).sty { ty::ty_box(_) | ty::ty_opaque_box | ty::ty_evec(_, ty::vstore_box) | ty::ty_estr(ty::vstore_box) => { incr_refcnt_of_boxed(bcx, Load(bcx, v)); bcx @@ -638,9 +633,7 @@ pub fn make_take_glue(bcx: block, v: ValueRef, t: ty::t) { iter_structural_ty(bcx, v, t, take_ty) } _ => bcx - }; - - build_return(bcx); + } } pub fn incr_refcnt_of_boxed(cx: block, box_ptr: ValueRef) { @@ -690,7 +683,7 @@ pub fn declare_tydesc(ccx: &mut CrateContext, t: ty::t) -> @mut tydesc_info { return inf; } -pub type glue_helper<'self> = &'self fn(block, ValueRef, ty::t); +pub type glue_helper<'self> = &'self fn(block, ValueRef, ty::t) -> block; pub fn declare_generic_glue(ccx: &mut CrateContext, t: ty::t, llfnty: Type, name: &str) -> ValueRef { @@ -723,11 +716,9 @@ pub fn make_generic_glue_inner(ccx: @mut CrateContext, let llrawptr0 = unsafe { llvm::LLVMGetParam(llfn, rawptr0_arg as c_uint) }; let llty = type_of(ccx, t); let llrawptr0 = PointerCast(bcx, llrawptr0, llty.ptr_to()); - helper(bcx, llrawptr0, t); + let bcx = helper(bcx, llrawptr0, t); - // This is from the general finish fn, but that emits a ret {} that we don't want - Br(raw_block(fcx, false, fcx.llstaticallocas), lltop); - RetVoid(raw_block(fcx, false, fcx.llreturn)); + finish_fn(fcx, lltop, bcx); return llfn; } diff --git a/src/librustc/middle/trans/reflect.rs b/src/librustc/middle/trans/reflect.rs index a3b544dbc6195..dee40f32169ee 100644 --- a/src/librustc/middle/trans/reflect.rs +++ b/src/librustc/middle/trans/reflect.rs @@ -299,12 +299,15 @@ impl Reflector { // llvm::LLVMGetParam(llfdecl, fcx.arg_pos(0u) as c_uint) }; - let bcx = top_scope_block(fcx, None); + let mut bcx = top_scope_block(fcx, None); let arg = BitCast(bcx, arg, llptrty); let ret = adt::trans_get_discr(bcx, repr, arg); Store(bcx, ret, fcx.llretptr.get()); - cleanup_and_Br(bcx, bcx, fcx.llreturn); - finish_fn(fcx, bcx.llbb); + match fcx.llreturn { + Some(llreturn) => cleanup_and_Br(bcx, bcx, llreturn), + None => bcx = cleanup_block(bcx, Some(bcx.llbb)) + }; + finish_fn(fcx, bcx.llbb, bcx); llfdecl };