about summary refs log tree commit diff
path: root/src/comp
diff options
context:
space:
mode:
authorBrian Anderson <banderson@mozilla.com>2011-09-12 09:36:51 -0700
committerBrian Anderson <banderson@mozilla.com>2011-09-12 09:36:51 -0700
commit393deeb06ff8017a93b0fd26c1f6968fdff2b15b (patch)
tree3dacb71e604095ec94a8f350f44467b7640f289d /src/comp
parentedde2e0c457de6e5d17373bfa90ef319df4a1566 (diff)
parent6ffcfba6b990d6f27243e4dd6ddfffab141e1f44 (diff)
downloadrust-393deeb06ff8017a93b0fd26c1f6968fdff2b15b.tar.gz
rust-393deeb06ff8017a93b0fd26c1f6968fdff2b15b.zip
Merge branch 'unwind'
Conflicts:
	src/comp/middle/trans.rs
	src/comp/middle/trans_build.rs
	src/lib/run_program.rs
	src/test/compiletest/runtest.rs
Diffstat (limited to 'src/comp')
-rw-r--r--src/comp/back/upcall.rs7
-rw-r--r--src/comp/lib/llvm.rs9
-rw-r--r--src/comp/middle/trans.rs118
-rw-r--r--src/comp/middle/trans_build.rs38
4 files changed, 144 insertions, 28 deletions
diff --git a/src/comp/back/upcall.rs b/src/comp/back/upcall.rs
index 3b1ee9c9cee..021617d1f18 100644
--- a/src/comp/back/upcall.rs
+++ b/src/comp/back/upcall.rs
@@ -37,7 +37,8 @@ type upcalls =
      log_type: ValueRef,
      dynastack_mark: ValueRef,
      dynastack_alloc: ValueRef,
-     dynastack_free: ValueRef};
+     dynastack_free: ValueRef,
+     rust_personality: ValueRef};
 
 fn declare_upcalls(_tn: type_names, tydesc_type: TypeRef,
                    taskptr_type: TypeRef, llmod: ModuleRef) -> @upcalls {
@@ -89,7 +90,9 @@ fn declare_upcalls(_tn: type_names, tydesc_type: TypeRef,
           dynastack_alloc:
               d("dynastack_alloc_2", [T_size_t(), T_ptr(tydesc_type)],
                 T_ptr(T_i8())),
-          dynastack_free: d("dynastack_free", [T_ptr(T_i8())], T_void())};
+          dynastack_free: d("dynastack_free", [T_ptr(T_i8())], T_void()),
+          rust_personality: dr("rust_personality", [], T_i32())
+         };
 }
 //
 // Local Variables:
diff --git a/src/comp/lib/llvm.rs b/src/comp/lib/llvm.rs
index fa7871042b8..8e00c8d63cd 100644
--- a/src/comp/lib/llvm.rs
+++ b/src/comp/lib/llvm.rs
@@ -572,6 +572,9 @@ native "cdecl" mod llvm = "rustllvm" {
     fn LLVMBuildInvoke(B: BuilderRef, Fn: ValueRef, Args: *ValueRef,
                        NumArgs: uint, Then: BasicBlockRef,
                        Catch: BasicBlockRef, Name: sbuf) -> ValueRef;
+    fn LLVMBuildLandingPad(B: BuilderRef, Ty: TypeRef, PersFn: ValueRef,
+                           NumClauses: uint, Name: sbuf) -> ValueRef;
+    fn LLVMBuildResume(B: BuilderRef, Exn: ValueRef) -> ValueRef;
     fn LLVMBuildUnreachable(B: BuilderRef) -> ValueRef;
 
     /* Add a case to the switch instruction */
@@ -580,6 +583,12 @@ native "cdecl" mod llvm = "rustllvm" {
     /* Add a destination to the indirectbr instruction */
     fn LLVMAddDestination(IndirectBr: ValueRef, Dest: BasicBlockRef);
 
+    /* Add a clause to the landing pad instruction */
+    fn LLVMAddClause(LandingPad: ValueRef, ClauseVal: ValueRef);
+
+    /* Set the cleanup on a landing pad instruction */
+    fn LLVMSetCleanup(LandingPad: ValueRef, Val: Bool);
+
     /* Arithmetic */
     fn LLVMBuildAdd(B: BuilderRef, LHS: ValueRef, RHS: ValueRef, Name: sbuf)
        -> ValueRef;
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 2276d348611..391d56526df 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -3715,7 +3715,7 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
        for the call itself is unreachable. */
     let retval = C_nil();
     if !is_terminated(bcx) {
-        FastCall(bcx, faddr, llargs);
+        bcx = invoke_fastcall(bcx, faddr, llargs).bcx;
         alt lliterbody {
           none. {
             if !ty::type_is_nil(bcx_tcx(cx), ret_ty) {
@@ -3748,6 +3748,67 @@ fn trans_call(in_cx: @block_ctxt, f: @ast::expr,
     ret rslt(bcx, retval);
 }
 
+fn invoke(bcx: @block_ctxt, llfn: ValueRef,
+          llargs: [ValueRef]) -> result {
+    ret invoke_(bcx, llfn, llargs, Invoke);
+}
+
+fn invoke_fastcall(bcx: @block_ctxt, llfn: ValueRef,
+                   llargs: [ValueRef]) -> result {
+    ret invoke_(bcx, llfn, llargs, FastInvoke);
+}
+
+fn invoke_(bcx: @block_ctxt, llfn: ValueRef,
+           llargs: [ValueRef],
+           invoker: fn(@block_ctxt, ValueRef, [ValueRef],
+                       BasicBlockRef, BasicBlockRef) -> ValueRef) -> result {
+    // FIXME: May be worth turning this into a plain call when there are no
+    // cleanups to run
+    let normal_bcx = new_sub_block_ctxt(bcx, "normal return");
+    let unwind_bcx = new_sub_block_ctxt(bcx, "unwind");
+    let retval = invoker(bcx, llfn, llargs,
+                         normal_bcx.llbb,
+                         unwind_bcx.llbb);
+    trans_landing_pad(unwind_bcx);
+    ret rslt(normal_bcx, retval);
+}
+
+fn trans_landing_pad(bcx: @block_ctxt) {
+    // 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.
+    let llretty = T_struct([T_ptr(T_i8()), T_i32()]);
+    // The exception handling personality function. This is the C++
+    // personality function __gxx_personality_v0, wrapped in our naming
+    // convention.
+    let personality = bcx_ccx(bcx).upcalls.rust_personality;
+    // The only landing pad clause will be 'cleanup'
+    let clauses = 1u;
+    let llpad = LandingPad(bcx, llretty, personality, clauses);
+    // The landing pad result is used both for modifying the landing pad
+    // in the C API and as the exception value
+    let llretval = llpad;
+    // The landing pad block is a cleanup
+    SetCleanup(bcx, llpad);
+
+    // FIXME: This seems like a very naive and redundant way to generate the
+    // landing pads, as we're re-generating all in-scope cleanups for each
+    // function call. Probably good optimization opportunities here.
+    let bcx = bcx;
+    let scope_cx = bcx;
+    while true {
+        scope_cx = find_scope_cx(scope_cx);
+        bcx = trans_block_cleanups(bcx, scope_cx);
+        scope_cx = alt scope_cx.parent {
+          parent_some(b) { b }
+          parent_none. { break; }
+        };
+    }
+
+    // Continue unwinding
+    Resume(bcx, llretval);
+}
+
 fn trans_tup(cx: @block_ctxt, elts: [@ast::expr], id: ast::node_id) ->
    result {
     let bcx = cx;
@@ -4211,7 +4272,7 @@ fn trans_fail_value(cx: @block_ctxt, sp_opt: option::t<span>,
     let V_str = PointerCast(cx, V_fail_str, T_ptr(T_i8()));
     V_filename = PointerCast(cx, V_filename, T_ptr(T_i8()));
     let args = [cx.fcx.lltaskptr, V_str, V_filename, C_int(V_line)];
-    Call(cx, bcx_ccx(cx).upcalls._fail, args);
+    let cx = invoke(cx, bcx_ccx(cx).upcalls._fail, args).bcx;
     Unreachable(cx);
     ret rslt(cx, C_nil());
 }
@@ -4247,7 +4308,7 @@ fn trans_put(in_cx: @block_ctxt, e: option::t<@ast::expr>) -> result {
         llargs += [r.val];
       }
     }
-    FastCall(bcx, llcallee, llargs);
+    bcx = invoke_fastcall(bcx, llcallee, llargs).bcx;
     bcx = trans_block_cleanups(bcx, cx);
     let next_cx = new_sub_block_ctxt(in_cx, "next");
     Br(bcx, next_cx.llbb);
@@ -4379,7 +4440,9 @@ fn init_local(bcx: @block_ctxt, local: @ast::local) -> result {
     // Make a note to drop this slot on the way out.
     add_clean(bcx, llptr, ty);
 
-    if must_zero(local) { bcx = zero_alloca(bcx, llptr, ty).bcx; }
+    if must_zero(bcx_ccx(bcx), local) {
+        bcx = zero_alloca(bcx, llptr, ty).bcx;
+    }
 
     alt local.node.init {
       some(init) {
@@ -4405,35 +4468,38 @@ fn init_local(bcx: @block_ctxt, local: @ast::local) -> result {
                                         bcx.fcx.lllocals, false);
     ret rslt(bcx, llptr);
 
-    fn must_zero(local: @ast::local) -> bool {
+    fn must_zero(ccx: @crate_ctxt, local: @ast::local) -> bool {
         alt local.node.init {
-          some(init) { might_not_init(init.expr) }
+          some(init) { might_not_init(ccx, init.expr) }
           none. { true }
         }
     }
 
-    fn might_not_init(expr: @ast::expr) -> bool {
-        type env = @mutable bool;
-        let e = @mutable false;
-        // FIXME: Probably also need to account for expressions that
-        // fail but since we don't unwind yet, it doesn't seem to be a
-        // problem
+    fn might_not_init(ccx: @crate_ctxt, expr: @ast::expr) -> bool {
+        type env = {mutable mightnt: bool,
+                    ccx: @crate_ctxt};
+        let e = {mutable mightnt: false,
+                 ccx: ccx};
+        fn visit_expr(ex: @ast::expr, e: env, v: vt<env>) {
+            let might_not_init = alt ex.node {
+              ast::expr_ret(_) { true }
+              ast::expr_break. { true }
+              ast::expr_cont. { true }
+              ast::expr_call(_, _) { true }
+              _ {
+                let ex_ty = ty::expr_ty(e.ccx.tcx, ex);
+                ty::type_is_bot(e.ccx.tcx, ex_ty)
+              }
+            };
+            if might_not_init {
+                e.mightnt = true;
+            } else { visit::visit_expr(ex, e, v); }
+        }
         let visitor =
-            visit::mk_vt(@{visit_expr:
-                               fn (ex: @ast::expr, e: env, v: vt<env>) {
-                                   let might_not_init =
-                                       alt ex.node {
-                                         ast::expr_ret(_) { true }
-                                         ast::expr_break. { true }
-                                         ast::expr_cont. { true }
-                                         _ { false }
-                                       };
-                                   if might_not_init {
-                                       *e = true;
-                                   } else { visit::visit_expr(ex, e, v); }
-                               } with *visit::default_visitor()});
+            visit::mk_vt(@{visit_expr: visit_expr
+                           with *visit::default_visitor()});
         visitor.visit_expr(expr, e, visitor);
-        ret *e;
+        ret e.mightnt;
     }
 }
 
diff --git a/src/comp/middle/trans_build.rs b/src/comp/middle/trans_build.rs
index ebf1b20c0ca..5c2a763603b 100644
--- a/src/comp/middle/trans_build.rs
+++ b/src/comp/middle/trans_build.rs
@@ -68,6 +68,21 @@ fn Invoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
                     });
 }
 
+fn FastInvoke(cx: @block_ctxt, Fn: ValueRef, Args: [ValueRef],
+              Then: BasicBlockRef, Catch: BasicBlockRef) -> ValueRef {
+    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)
+                        });
+    llvm::LLVMSetInstructionCallConv(v, lib::llvm::LLVMFastCallConv);
+    ret v;
+}
+
 fn Unreachable(cx: @block_ctxt) -> ValueRef {
     assert (!cx.terminated);
     cx.terminated = true;
@@ -527,6 +542,29 @@ fn Trap(cx: @block_ctxt) -> ValueRef {
                     });
 }
 
+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)
+                    });
+}
+
+fn SetCleanup(_cx: @block_ctxt, LandingPad: ValueRef) {
+    llvm::LLVMSetCleanup(LandingPad, lib::llvm::True);
+}
+
+fn Resume(cx: @block_ctxt, Exn: ValueRef) -> ValueRef {
+    assert (!cx.terminated);
+    cx.terminated = true;
+    ret llvm::LLVMBuildResume(B(cx), Exn);
+}
+
 //
 // Local Variables:
 // mode: rust