From 9f4206cdc4e731d0b172d76b2d652bf0ecaa9ca1 Mon Sep 17 00:00:00 2001 From: Marijn Haverbeke Date: Fri, 17 Feb 2012 11:18:14 +0100 Subject: [PATCH] Clean up block_ctxt representation --- src/comp/middle/trans/base.rs | 145 ++++++++++++++------------------ src/comp/middle/trans/common.rs | 126 ++++++++++++++------------- 2 files changed, 131 insertions(+), 140 deletions(-) diff --git a/src/comp/middle/trans/base.rs b/src/comp/middle/trans/base.rs index b562c726df4..0e09899a5a4 100644 --- a/src/comp/middle/trans/base.rs +++ b/src/comp/middle/trans/base.rs @@ -2108,7 +2108,7 @@ fn trans_for(cx: @block_ctxt, local: @ast::local, seq: @ast::expr, body: ast::blk, outer_next_cx: @block_ctxt) -> @block_ctxt { let next_cx = new_sub_block_ctxt(bcx, "next"); let scope_cx = - new_loop_scope_block_ctxt(bcx, option::some(next_cx), + new_loop_scope_block_ctxt(bcx, cont_other(next_cx), outer_next_cx, "for loop scope", body.span); Br(bcx, scope_cx.llbb); @@ -2138,7 +2138,7 @@ fn trans_for(cx: @block_ctxt, local: @ast::local, seq: @ast::expr, fn trans_while(cx: @block_ctxt, cond: @ast::expr, body: ast::blk) -> @block_ctxt { let next_cx = new_sub_block_ctxt(cx, "while next"); - let cond_cx = new_loop_scope_block_ctxt(cx, none, next_cx, + let cond_cx = new_loop_scope_block_ctxt(cx, cont_self, next_cx, "while cond", body.span); let body_cx = new_scope_block_ctxt(cond_cx, "while loop body"); Br(cx, cond_cx.llbb); @@ -2154,7 +2154,7 @@ fn trans_do_while(cx: @block_ctxt, body: ast::blk, cond: @ast::expr) -> @block_ctxt { let next_cx = new_sub_block_ctxt(cx, "next"); let body_cx = - new_loop_scope_block_ctxt(cx, option::none::<@block_ctxt>, next_cx, + new_loop_scope_block_ctxt(cx, cont_self, next_cx, "do-while loop body", body.span); let body_end = trans_block(body_cx, body, ignore); let cond_cx = new_scope_block_ctxt(body_cx, "do-while cond"); @@ -2995,27 +2995,32 @@ fn invoke_(bcx: @block_ctxt, llfn: ValueRef, llargs: [ValueRef], } fn get_landing_pad(bcx: @block_ctxt) -> BasicBlockRef { - fn find_scope_for_lpad(bcx: @block_ctxt) -> @block_ctxt { - let scope_bcx = bcx; + fn in_lpad_scope_cx(bcx: @block_ctxt, f: fn(scope_info)) { + let bcx = bcx; while true { - if vec::is_not_empty(scope_bcx.cleanups) { break; } - scope_bcx = alt scope_bcx.parent { - parent_some(b) { b } - parent_none { break; } - }; + alt bcx.kind { + scope_block(info) { + if info.cleanups.len() > 0u || bcx.parent == parent_none { + f(info); ret; + } + } + _ {} + } + bcx = alt check bcx.parent { parent_some(b) { b } }; } - scope_bcx } - let scope_bcx = find_scope_for_lpad(bcx); - // If there is a valid landing pad still around, use it - alt scope_bcx.landing_pad { - some(target) { ret target; } - none {} + let cached = none, pad_bcx = bcx; // Guaranteed to be set below + in_lpad_scope_cx(bcx) {|info| + // If there is a valid landing pad still around, use it + alt info.landing_pad { + some(target) { cached = some(target); ret; } + none {} + } + pad_bcx = new_sub_block_ctxt(bcx, "unwind"); + info.landing_pad = some(pad_bcx.llbb); } - - let pad_bcx = new_sub_block_ctxt(bcx, "unwind"); - scope_bcx.landing_pad = some(pad_bcx.llbb); + alt cached { some(b) { ret b; } none {} } // Can't return from block above // 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. @@ -3573,13 +3578,13 @@ fn trans_break_cont(bcx: @block_ctxt, to_end: bool) let unwind = bcx, target = bcx; while true { alt unwind.kind { - LOOP_SCOPE_BLOCK(_cont, _break) { + scope_block({is_loop: some({cnt, brk}), _}) { target = if to_end { - _break + brk } else { - alt _cont { - option::some(_cont) { _cont } - _ { unwind } + alt cnt { + cont_other(o) { o } + cont_self { unwind } } }; break; @@ -3738,16 +3743,14 @@ fn new_block_ctxt(cx: @fn_ctxt, parent: block_parent, kind: block_kind, if cx.ccx.sess.opts.save_temps || cx.ccx.sess.opts.debuginfo { s = cx.ccx.names(name); } - let llbb: BasicBlockRef = - str::as_buf(s, {|buf| llvm::LLVMAppendBasicBlock(cx.llfn, buf) }); + let llbb: BasicBlockRef = str::as_buf(s, {|buf| + llvm::LLVMAppendBasicBlock(cx.llfn, buf) + }); let bcx = @{llbb: llbb, mutable terminated: false, mutable unreachable: false, parent: parent, kind: kind, - mutable cleanups: [], - mutable cleanup_paths: [], - mutable landing_pad: none, block_span: block_span, fcx: cx}; alt parent { @@ -3759,34 +3762,43 @@ fn new_block_ctxt(cx: @fn_ctxt, parent: block_parent, kind: block_kind, ret bcx; } +fn simple_scope_block() -> block_kind { + scope_block({is_loop: none, mutable cleanups: [], + mutable cleanup_paths: [], mutable landing_pad: none}) +} // Use this when you're at the top block of a function or the like. fn new_top_block_ctxt(fcx: @fn_ctxt, sp: option) -> @block_ctxt { - ret new_block_ctxt(fcx, parent_none, SCOPE_BLOCK, "function top level", - sp); + ret new_block_ctxt(fcx, parent_none, simple_scope_block(), + "function top level", sp); } - // Use this when you're at a curly-brace or similar lexical scope. fn new_scope_block_ctxt(bcx: @block_ctxt, n: str) -> @block_ctxt { - ret new_block_ctxt(bcx.fcx, parent_some(bcx), SCOPE_BLOCK, n, none); + ret new_block_ctxt(bcx.fcx, parent_some(bcx), simple_scope_block(), + n, none); } fn new_real_block_ctxt(bcx: @block_ctxt, n: str, sp: span) -> @block_ctxt { - ret new_block_ctxt(bcx.fcx, parent_some(bcx), SCOPE_BLOCK, n, some(sp)); + ret new_block_ctxt(bcx.fcx, parent_some(bcx), simple_scope_block(), + n, some(sp)); } -fn new_loop_scope_block_ctxt(bcx: @block_ctxt, _cont: option<@block_ctxt>, +fn new_loop_scope_block_ctxt(bcx: @block_ctxt, _cont: loop_cont, _break: @block_ctxt, n: str, sp: span) -> @block_ctxt { - ret new_block_ctxt(bcx.fcx, parent_some(bcx), - LOOP_SCOPE_BLOCK(_cont, _break), n, some(sp)); + ret new_block_ctxt(bcx.fcx, parent_some(bcx), scope_block({ + is_loop: some({cnt: _cont, brk: _break}), + mutable cleanups: [], + mutable cleanup_paths: [], + mutable landing_pad: none + }), n, some(sp)); } // Use this when you're making a general CFG BB within a scope. fn new_sub_block_ctxt(bcx: @block_ctxt, n: str) -> @block_ctxt { - ret new_block_ctxt(bcx.fcx, parent_some(bcx), NON_SCOPE_BLOCK, n, none); + ret new_block_ctxt(bcx.fcx, parent_some(bcx), non_scope_block, n, none); } fn new_raw_block_ctxt(fcx: @fn_ctxt, llbb: BasicBlockRef) -> @block_ctxt { @@ -3794,10 +3806,7 @@ fn new_raw_block_ctxt(fcx: @fn_ctxt, llbb: BasicBlockRef) -> @block_ctxt { mutable terminated: false, mutable unreachable: false, parent: parent_none, - kind: NON_SCOPE_BLOCK, - mutable cleanups: [], - mutable cleanup_paths: [], - mutable landing_pad: none, + kind: non_scope_block, block_span: none, fcx: fcx}; } @@ -3813,13 +3822,13 @@ fn new_raw_block_ctxt(fcx: @fn_ctxt, llbb: BasicBlockRef) -> @block_ctxt { fn trans_block_cleanups(bcx: @block_ctxt, cleanup_cx: @block_ctxt) -> @block_ctxt { if bcx.unreachable { ret bcx; } - let i = cleanup_cx.cleanups.len(), bcx = bcx; - if cleanup_cx.kind == NON_SCOPE_BLOCK { assert i == 0u; } - while i > 0u { - i -= 1u; - alt cleanup_cx.cleanups[i] { - clean(cfn) | clean_temp(_, cfn) { bcx = cfn(bcx); } + let bcx = bcx; + alt check cleanup_cx.kind { + scope_block({cleanups, _}) { + vec::riter(cleanups) {|cu| + alt cu { clean(cfn) | clean_temp(_, cfn) { bcx = cfn(bcx); } } } + } } ret bcx; } @@ -3831,9 +3840,9 @@ fn cleanup_and_leave(bcx: @block_ctxt, upto: option, leave: option) { let cur = bcx, bcx = bcx; while true { - if cur.cleanups.len() > 0u { - assert cur.kind != NON_SCOPE_BLOCK; - for exists in cur.cleanup_paths { + alt cur.kind { + scope_block(info) if info.cleanups.len() > 0u { + for exists in info.cleanup_paths { if exists.target == leave { Br(bcx, exists.dest); ret; @@ -3841,8 +3850,10 @@ fn cleanup_and_leave(bcx: @block_ctxt, upto: option, } let sub_cx = new_sub_block_ctxt(bcx, "cleanup"); Br(bcx, sub_cx.llbb); - cur.cleanup_paths += [{target: leave, dest: sub_cx.llbb}]; + info.cleanup_paths += [{target: leave, dest: sub_cx.llbb}]; bcx = trans_block_cleanups(sub_cx, cur); + } + _ {} } alt upto { some(bb) { if cur.llbb == bb { break; } } @@ -3890,33 +3901,6 @@ fn block_locals(b: ast::blk, it: fn(@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: [], - mutable cleanup_paths: [], - mutable landing_pad: none, - block_span: none, - fcx: fcx}; -} - -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: [], - mutable cleanup_paths: [], - mutable landing_pad: none, - block_span: none, - fcx: fcx}; -} - - fn alloc_ty(cx: @block_ctxt, t: ty::t) -> result { let bcx = cx, ccx = bcx_ccx(cx); let llty = type_of(ccx, t); @@ -3927,7 +3911,8 @@ fn alloc_ty(cx: @block_ctxt, t: ty::t) -> result { // block_ctxt built on the llderivedtydescs block for the fn, // so that the size dominates the array_alloca that // comes next. - let n = size_of(llderivedtydescs_block_ctxt(bcx.fcx), t); + let n = size_of(new_raw_block_ctxt(cx.fcx, cx.fcx.llderivedtydescs), + t); bcx.fcx.llderivedtydescs = n.bcx.llbb; PointerCast(bcx, dynastack_alloca(bcx, T_i8(), n.val, t), T_ptr(llty)) }; diff --git a/src/comp/middle/trans/common.rs b/src/comp/middle/trans/common.rs index 0ed6e33886f..783e8d15070 100644 --- a/src/comp/middle/trans/common.rs +++ b/src/comp/middle/trans/common.rs @@ -233,16 +233,17 @@ enum cleanup { type cleanup_path = {target: option, dest: BasicBlockRef}; -fn scope_clean_changed(cx: @block_ctxt) { - cx.cleanup_paths = []; - cx.landing_pad = none; +fn scope_clean_changed(info: scope_info) { + if info.cleanup_paths.len() > 0u { info.cleanup_paths = []; } + info.landing_pad = none; } fn add_clean(cx: @block_ctxt, val: ValueRef, ty: ty::t) { if !ty::type_needs_drop(bcx_tcx(cx), ty) { ret; } - let scope_cx = find_scope_cx(cx); - scope_cx.cleanups += [clean(bind drop_ty(_, val, ty))]; - scope_clean_changed(scope_cx); + in_scope_cx(cx) {|info| + info.cleanups += [clean(bind drop_ty(_, val, ty))]; + scope_clean_changed(info); + } } fn add_clean_temp(cx: @block_ctxt, val: ValueRef, ty: ty::t) { if !ty::type_needs_drop(bcx_tcx(cx), ty) { ret; } @@ -254,23 +255,25 @@ fn add_clean_temp(cx: @block_ctxt, val: ValueRef, ty: ty::t) { ret drop_ty(bcx, val, ty); } } - let scope_cx = find_scope_cx(cx); - scope_cx.cleanups += - [clean_temp(val, bind do_drop(_, val, ty))]; - scope_clean_changed(scope_cx); + in_scope_cx(cx) {|info| + info.cleanups += [clean_temp(val, bind do_drop(_, val, ty))]; + scope_clean_changed(info); + } } fn add_clean_temp_mem(cx: @block_ctxt, val: ValueRef, ty: ty::t) { if !ty::type_needs_drop(bcx_tcx(cx), ty) { ret; } - let scope_cx = find_scope_cx(cx); - scope_cx.cleanups += [clean_temp(val, bind drop_ty(_, val, ty))]; - scope_clean_changed(scope_cx); + in_scope_cx(cx) {|info| + info.cleanups += [clean_temp(val, bind drop_ty(_, val, ty))]; + scope_clean_changed(info); + } } fn add_clean_free(cx: @block_ctxt, ptr: ValueRef, shared: bool) { - let scope_cx = find_scope_cx(cx); let free_fn = if shared { bind base::trans_shared_free(_, ptr) } else { bind base::trans_free(_, ptr) }; - scope_cx.cleanups += [clean_temp(ptr, free_fn)]; - scope_clean_changed(scope_cx); + in_scope_cx(cx) {|info| + info.cleanups += [clean_temp(ptr, free_fn)]; + scope_clean_changed(info); + } } // Note that this only works for temporaries. We should, at some point, move @@ -278,27 +281,22 @@ fn add_clean_free(cx: @block_ctxt, ptr: ValueRef, shared: bool) { // this will be more involved. For now, we simply zero out the local, and the // drop glue checks whether it is zero. fn revoke_clean(cx: @block_ctxt, val: ValueRef) { - let sc_cx = find_scope_cx(cx); - let found = -1; - let i = 0; - for c: cleanup in sc_cx.cleanups { - alt c { - clean_temp(v, _) { - if v as uint == val as uint { found = i; break; } - } - _ { } + in_scope_cx(cx) {|info| + let i = 0u; + for cu in info.cleanups { + alt cu { + clean_temp(v, _) if v == val { + info.cleanups = + vec::slice(info.cleanups, 0u, i) + + vec::slice(info.cleanups, i + 1u, info.cleanups.len()); + scope_clean_changed(info); + ret; + } + _ {} + } + i += 1u; } - i += 1; } - // The value does not have a cleanup associated with it. - if found == -1 { ret; } - // We found the cleanup and remove it - sc_cx.cleanups = - vec::slice(sc_cx.cleanups, 0u, found as uint) + - vec::slice(sc_cx.cleanups, (found as uint) + 1u, - sc_cx.cleanups.len()); - scope_clean_changed(sc_cx); - ret; } fn get_res_dtor(ccx: @crate_ctxt, did: ast::def_id, inner_t: ty::t) @@ -325,47 +323,53 @@ enum block_kind { // cleaned up. May correspond to an actual block in the language, but also // to an implicit scope, for example, calls introduce an implicit scope in // which the arguments are evaluated and cleaned up. - SCOPE_BLOCK, - // A basic block created from the body of a loop. Contains pointers to - // which block to jump to in the case of "continue" or "break". - LOOP_SCOPE_BLOCK(option<@block_ctxt>, @block_ctxt), + scope_block(scope_info), // A non-scope block is a basic block created as a translation artifact // from translating code that expresses conditional logic rather than by // explicit { ... } block structure in the source language. It's called a // non-scope block because it doesn't introduce a new variable scope. - NON_SCOPE_BLOCK + non_scope_block, } +enum loop_cont { cont_self, cont_other(@block_ctxt), } + +type scope_info = { + is_loop: option<{cnt: loop_cont, brk: @block_ctxt}>, + // A list of functions that must be run at when leaving this + // block, cleaning up any variables that were introduced in the + // block. + mutable cleanups: [cleanup], + // Existing cleanup paths that may be reused, indexed by destination and + // cleared when the set of cleanups changes. + mutable cleanup_paths: [cleanup_path], + // Unwinding landing pad. Also cleared when cleanups change. + mutable landing_pad: option, +}; + // Basic block context. We create a block context for each basic block // (single-entry, single-exit sequence of instructions) we generate from Rust // code. Each basic block we generate is attached to a function, typically // with many basic blocks per function. All the basic blocks attached to a // function are organized as a directed graph. -type block_ctxt = +type block_ctxt = { // The BasicBlockRef returned from a call to // llvm::LLVMAppendBasicBlock(llfn, name), which adds a basic // block to the function pointed to by llfn. We insert // instructions into that block by way of this block context. // The block pointing to this one in the function's digraph. + llbb: BasicBlockRef, + mutable terminated: bool, + mutable unreachable: bool, + parent: block_parent, // The 'kind' of basic block this is. - // A list of functions that run at the end of translating this - // block, cleaning up any variables that were introduced in the - // block and need to go out of scope at the end of it. - // The source span where this block comes from, for error - // reporting. FIXME this is not currently reliable + kind: block_kind, + // The source span where the block came from, if it is a block that + // actually appears in the source code. + block_span: option, // The function context for the function to which this block is // attached. - {llbb: BasicBlockRef, - mutable terminated: bool, - mutable unreachable: bool, - parent: block_parent, - kind: block_kind, - // FIXME the next five fields should probably only appear in scope blocks - mutable cleanups: [cleanup], - mutable cleanup_paths: [cleanup_path], - mutable landing_pad: option, - block_span: option, - fcx: @fn_ctxt}; + fcx: @fn_ctxt +}; // FIXME: we should be able to use option<@block_parent> here but // the infinite-enum check in rustboot gets upset. @@ -395,13 +399,15 @@ fn struct_elt(llstructty: TypeRef, n: uint) -> TypeRef unsafe { ret llvm::LLVMGetElementType(elt_tys[n]); } -fn find_scope_cx(cx: @block_ctxt) -> @block_ctxt { +fn in_scope_cx(cx: @block_ctxt, f: fn(scope_info)) { let cur = cx; while true { - if cur.kind != NON_SCOPE_BLOCK { break; } + alt cur.kind { + scope_block(info) { f(info); ret; } + _ {} + } cur = alt check cur.parent { parent_some(b) { b } }; } - cur } // Accessors