about summary refs log tree commit diff
path: root/src/comp/middle
diff options
context:
space:
mode:
authorMarijn Haverbeke <marijnh@gmail.com>2012-02-17 11:18:14 +0100
committerMarijn Haverbeke <marijnh@gmail.com>2012-02-17 22:47:03 +0100
commit9f4206cdc4e731d0b172d76b2d652bf0ecaa9ca1 (patch)
tree605d9bf5fdf885032954f328d94cefaf110ce06c /src/comp/middle
parent54d7bffbb8f79d4680b6200ee50e0ca8eb2dc3c3 (diff)
downloadrust-9f4206cdc4e731d0b172d76b2d652bf0ecaa9ca1.tar.gz
rust-9f4206cdc4e731d0b172d76b2d652bf0ecaa9ca1.zip
Clean up block_ctxt representation
Diffstat (limited to 'src/comp/middle')
-rw-r--r--src/comp/middle/trans/base.rs145
-rw-r--r--src/comp/middle/trans/common.rs126
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<span>) -> @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<BasicBlockRef>,
                      leave: option<BasicBlockRef>) {
     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<BasicBlockRef>,
             }
             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<BasicBlockRef>,
                      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<BasicBlockRef>,
+};
+
 // 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<span>,
     // 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<BasicBlockRef>,
-     block_span: option<span>,
-     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