about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--mk/platform.mk2
-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
-rwxr-xr-xsrc/etc/combine-tests.py3
-rw-r--r--src/lib/run_program.rs43
-rw-r--r--src/lib/test.rs7
-rw-r--r--src/rt/memory_region.cpp11
-rw-r--r--src/rt/memory_region.h5
-rw-r--r--src/rt/rust_builtin.cpp7
-rw-r--r--src/rt/rust_internal.h3
-rw-r--r--src/rt/rust_kernel.cpp2
-rw-r--r--src/rt/rust_scheduler.cpp2
-rw-r--r--src/rt/rust_task.cpp12
-rw-r--r--src/rt/rust_upcall.cpp22
-rw-r--r--src/rt/rustrt.def.in2
-rw-r--r--src/rustllvm/rustllvm.def.in4
-rw-r--r--src/test/bench/shootout-nbody.rs1
-rw-r--r--src/test/compiletest/compiletest.rs3
-rw-r--r--src/test/compiletest/header.rs18
-rw-r--r--src/test/compiletest/procsrv.rs2
-rw-r--r--src/test/compiletest/runtest.rs27
-rw-r--r--src/test/run-fail/explicit-fail-msg.rs1
-rw-r--r--src/test/run-fail/fmt-fail.rs1
-rw-r--r--src/test/run-fail/linked-failure.rs3
-rw-r--r--src/test/run-fail/unwind-assert.rs6
-rw-r--r--src/test/run-fail/unwind-box.rs10
-rw-r--r--src/test/run-fail/unwind-check.rs8
-rw-r--r--src/test/run-fail/unwind-closure.rs10
-rw-r--r--src/test/run-fail/unwind-fail.rs6
-rw-r--r--src/test/run-fail/unwind-initializer-indirect.rs7
-rw-r--r--src/test/run-fail/unwind-initializer.rs7
-rw-r--r--src/test/run-fail/unwind-iter.rs12
-rw-r--r--src/test/run-fail/unwind-iter2.rs12
-rw-r--r--src/test/run-fail/unwind-lambda.rs16
-rw-r--r--src/test/run-fail/unwind-misc-1.rs28
-rw-r--r--src/test/run-fail/unwind-nested.rs11
-rw-r--r--src/test/run-fail/unwind-resource-fail.rs12
-rw-r--r--src/test/run-fail/unwind-stacked.rs16
-rw-r--r--src/test/run-fail/unwind-uninitialized.rs10
-rw-r--r--src/test/run-fail/vec-overrun.rs1
-rw-r--r--src/test/run-fail/vec-underrun.rs1
-rw-r--r--src/test/run-pass/native-llvm.rs10
-rw-r--r--src/test/run-pass/task-comm-15.rs1
-rw-r--r--src/test/run-pass/unwind-box.rs14
-rw-r--r--src/test/run-pass/unwind-resource.rs21
-rw-r--r--src/test/run-pass/unwind-resource2.rs18
-rw-r--r--src/test/stdtest/run.rs7
49 files changed, 491 insertions, 106 deletions
diff --git a/mk/platform.mk b/mk/platform.mk
index c8c4fef41fd..a647e9cb3c5 100644
--- a/mk/platform.mk
+++ b/mk/platform.mk
@@ -114,7 +114,7 @@ ifdef CFG_UNIXY
   endif
   ifdef CFG_VALGRIND
     CFG_VALGRIND += --leak-check=full \
-                    --error-exitcode=1 \
+                    --error-exitcode=100 \
                     --quiet --suppressions=$(CFG_SRC_DIR)src/etc/x86.supp
   endif
 endif
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
diff --git a/src/etc/combine-tests.py b/src/etc/combine-tests.py
index 163004c4605..09b80ba448f 100755
--- a/src/etc/combine-tests.py
+++ b/src/etc/combine-tests.py
@@ -27,7 +27,8 @@ for t in os.listdir(run_pass):
         f = codecs.open(os.path.join(run_pass, t), "r", "utf8")
         s = f.read()
         if not ("xfail-test" in s or
-                "xfail-fast" in s):
+                "xfail-fast" in s or
+                "xfail-win32" in s):
             stage2_tests.append(t)
             if "main(args: [str])" in s:
                 take_args[t] = True
diff --git a/src/lib/run_program.rs b/src/lib/run_program.rs
index 43fe4bc3d95..31bccf32037 100644
--- a/src/lib/run_program.rs
+++ b/src/lib/run_program.rs
@@ -6,6 +6,7 @@ export run_program;
 export start_program;
 export program_output;
 export spawn_process;
+export waitpid;
 
 native "rust" mod rustrt {
     fn rust_run_program(argv: *sbuf, in_fd: int, out_fd: int, err_fd: int) ->
@@ -33,7 +34,7 @@ fn spawn_process(prog: str, args: [str], in_fd: int, out_fd: int, err_fd: int)
 }
 
 fn run_program(prog: str, args: [str]) -> int {
-    ret os::waitpid(spawn_process(prog, args, 0, 0, 0));
+    ret waitpid(spawn_process(prog, args, 0, 0, 0));
 }
 
 type program =
@@ -87,7 +88,7 @@ fn start_program(prog: str, args: [str]) -> @program_res {
             if finished { ret 0; }
             finished = true;
             self.close_input();
-            ret os::waitpid(pid);
+            ret waitpid(pid);
         }
         fn destroy() {
             self.finish();
@@ -117,6 +118,44 @@ fn program_output(prog: str, args: [str]) ->
          out: read_all(pr.output()),
          err: read_all(pr.err())};
 }
+
+/* Returns an exit status */
+#[cfg(target_os = "win32")]
+fn waitpid(pid: int) -> int {
+    os::waitpid(pid)
+}
+
+#[cfg(target_os = "linux")]
+#[cfg(target_os = "macos")]
+fn waitpid(pid: int) -> int {
+    #[cfg(target_os = "linux")]
+    fn WIFEXITED(status: int) -> bool {
+        (status & 0xff) == 0
+    }
+
+    #[cfg(target_os = "macos")]
+    fn WIFEXITED(status: int) -> bool {
+        (status & 0x7f) == 0
+    }
+
+    #[cfg(target_os = "linux")]
+    fn WEXITSTATUS(status: int) -> int {
+        (status >> 8) & 0xff
+    }
+
+    #[cfg(target_os = "macos")]
+    fn WEXITSTATUS(status: int) -> int {
+        status >> 8
+    }
+
+    let status = os::waitpid(pid);
+    ret if WIFEXITED(status) {
+        WEXITSTATUS(status)
+    } else {
+        1
+    };
+}
+
 // Local Variables:
 // mode: rust
 // fill-column: 78;
diff --git a/src/lib/test.rs b/src/lib/test.rs
index 1ad70db6f17..70de4565365 100644
--- a/src/lib/test.rs
+++ b/src/lib/test.rs
@@ -26,7 +26,6 @@ export configure_test_task;
 export joinable;
 
 native "rust" mod rustrt {
-    fn hack_allow_leaks();
     fn sched_threads() -> uint;
 }
 
@@ -324,12 +323,6 @@ fn configure_test_task() {
     // If this task fails we don't want that failure to propagate to the
     // test runner or else we couldn't keep running tests
     task::unsupervise();
-
-    // FIXME (236): Hack supreme - unwinding doesn't work yet so if this
-    // task fails memory will not be freed correctly. This turns off the
-    // sanity checks in the runtime's memory region for the task, so that
-    // the test runner can continue.
-    rustrt::hack_allow_leaks();
 }
 
 // Local Variables:
diff --git a/src/rt/memory_region.cpp b/src/rt/memory_region.cpp
index ef8a92b427f..a55d073543d 100644
--- a/src/rt/memory_region.cpp
+++ b/src/rt/memory_region.cpp
@@ -15,13 +15,13 @@ memory_region::alloc_header *memory_region::get_header(void *mem) {
 memory_region::memory_region(rust_srv *srv, bool synchronized) :
     _srv(srv), _parent(NULL), _live_allocations(0),
     _detailed_leaks(srv->env->detailed_leaks),
-    _synchronized(synchronized), _hack_allow_leaks(false) {
+    _synchronized(synchronized) {
 }
 
 memory_region::memory_region(memory_region *parent) :
     _srv(parent->_srv), _parent(parent), _live_allocations(0),
     _detailed_leaks(parent->_detailed_leaks),
-    _synchronized(parent->_synchronized), _hack_allow_leaks(false) {
+    _synchronized(parent->_synchronized) {
 }
 
 void memory_region::add_alloc() {
@@ -127,7 +127,7 @@ memory_region::~memory_region() {
         assert(leak_count == _live_allocations);
     }
 #endif
-    if (!_hack_allow_leaks && _live_allocations > 0) {
+    if (_live_allocations > 0) {
         _srv->fatal(msg, __FILE__, __LINE__,
                     "%d objects", _live_allocations);
     }
@@ -135,11 +135,6 @@ memory_region::~memory_region() {
 }
 
 void
-memory_region::hack_allow_leaks() {
-    _hack_allow_leaks = true;
-}
-
-void
 memory_region::release_alloc(void *mem) {
     alloc_header *alloc = get_header(mem);
     assert(alloc->magic == MAGIC);
diff --git a/src/rt/memory_region.h b/src/rt/memory_region.h
index 0197057268c..9d2106c1eaf 100644
--- a/src/rt/memory_region.h
+++ b/src/rt/memory_region.h
@@ -32,7 +32,6 @@ private:
     const bool _detailed_leaks;
     const bool _synchronized;
     lock_and_signal _lock;
-    bool _hack_allow_leaks;
 
     void add_alloc();
     void dec_alloc();
@@ -46,10 +45,6 @@ public:
     void *realloc(void *mem, size_t size);
     void free(void *mem);
     virtual ~memory_region();
-    // FIXME (236: This is a temporary hack to allow failing tasks that leak
-    // to not kill the entire process, which the test runner needs. Please
-    // kill with prejudice once unwinding works.
-    void hack_allow_leaks();
 
     void release_alloc(void *mem);
     void claim_alloc(void *mem);
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index 5e0c9dbf4c2..2499dea0328 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -224,13 +224,6 @@ debug_opaque(rust_task *task, type_desc *t, uint8_t *front)
     }
 }
 
-extern "C" CDECL void
-hack_allow_leaks(rust_task *task)
-{
-    LOG(task, stdlib, "hack_allow_leaks");
-    task->local_region.hack_allow_leaks();
-}
-
 struct rust_box {
     RUST_REFCOUNTED(rust_box)
 
diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h
index d2c8574280b..c01fc5f3127 100644
--- a/src/rt/rust_internal.h
+++ b/src/rt/rust_internal.h
@@ -99,6 +99,9 @@ static size_t const TIME_SLICE_IN_MS = 10;
 
 static size_t const BUF_BYTES = 2048;
 
+// The error status to use when the process fails
+#define PROC_FAIL_CODE 101;
+
 // Every reference counted object should use this macro and initialize
 // ref_count.
 
diff --git a/src/rt/rust_kernel.cpp b/src/rt/rust_kernel.cpp
index 54b1cc98d43..e5234b9d6f5 100644
--- a/src/rt/rust_kernel.cpp
+++ b/src/rt/rust_kernel.cpp
@@ -140,7 +140,7 @@ rust_kernel::fail() {
     // Runtime to terminate it in an unusual way" when trying to shutdown
     // cleanly.
 #if defined(__WIN32__)
-    exit(1);
+    exit(rval);
 #endif
     for(size_t i = 0; i < num_threads; ++i) {
         rust_scheduler *thread = threads[i];
diff --git a/src/rt/rust_scheduler.cpp b/src/rt/rust_scheduler.cpp
index 3a69184d3fe..b127ec77efa 100644
--- a/src/rt/rust_scheduler.cpp
+++ b/src/rt/rust_scheduler.cpp
@@ -71,7 +71,7 @@ rust_scheduler::fail() {
     log(NULL, log_err, "domain %s @0x%" PRIxPTR " root task failed",
         name, this);
     I(this, kernel->rval == 0);
-    kernel->rval = 1;
+    kernel->rval = PROC_FAIL_CODE;
     kernel->fail();
 }
 
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index 57b5d16e427..b861c60b521 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -138,18 +138,6 @@ struct rust_closure_env {
 };
 
 extern "C" CDECL
-void task_exit(rust_closure_env *env, int rval, rust_task *task) {
-    LOG(task, task, "task exited with value %d", rval);
-    if(env) {
-        // free the environment.
-        I(task->sched, 1 == env->ref_count); // the ref count better be 1
-        //env->td->drop_glue(NULL, task, NULL, env->td->first_param, env);
-        //env->td->free_glue(NULL, task, NULL, env->td->first_param, env);
-        task->free(env);
-    }
-}
-
-extern "C" CDECL
 void task_start_wrapper(spawn_args *a)
 {
     rust_task *task = a->task;
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 2d7791e876f..0f5c11537fe 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -1,6 +1,8 @@
 #include "rust_gc.h"
 #include "rust_internal.h"
 #include "rust_upcall.h"
+#include <stdint.h>
+#include <unwind.h>
 
 // Upcalls.
 
@@ -190,6 +192,26 @@ upcall_dynastack_free(rust_task *task, void *ptr) {
     return task->dynastack.free(ptr);
 }
 
+extern "C" _Unwind_Reason_Code
+__gxx_personality_v0(int version,
+                     _Unwind_Action actions,
+                     uint64_t exception_class,
+                     _Unwind_Exception *ue_header,
+                     _Unwind_Context *context);
+
+extern "C" _Unwind_Reason_Code
+upcall_rust_personality(int version,
+                        _Unwind_Action actions,
+                        uint64_t exception_class,
+                        _Unwind_Exception *ue_header,
+                        _Unwind_Context *context) {
+    return __gxx_personality_v0(version,
+                                actions,
+                                exception_class,
+                                ue_header,
+                                context);
+}
+
 //
 // Local Variables:
 // mode: C++
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index a0e7c972eeb..bf8155b7998 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -28,7 +28,6 @@ get_task_id
 get_task_pointer
 get_task_trampoline
 get_time
-hack_allow_leaks
 last_os_error
 leak
 migrate_alloc
@@ -78,5 +77,6 @@ upcall_vec_grow
 upcall_vec_push
 upcall_log_type
 upcall_malloc
+upcall_rust_personality
 upcall_shared_malloc
 upcall_shared_free
diff --git a/src/rustllvm/rustllvm.def.in b/src/rustllvm/rustllvm.def.in
index 25185f3cde5..deac70f3495 100644
--- a/src/rustllvm/rustllvm.def.in
+++ b/src/rustllvm/rustllvm.def.in
@@ -25,6 +25,7 @@ LLVMAddAttribute
 LLVMAddBasicAliasAnalysisPass
 LLVMAddCFGSimplificationPass
 LLVMAddCase
+LLVMAddClause
 LLVMAddConstantMergePass
 LLVMAddConstantPropagationPass
 LLVMAddCorrelatedValuePropagationPass
@@ -122,6 +123,7 @@ LLVMBuildIntToPtr
 LLVMBuildInvoke
 LLVMBuildIsNotNull
 LLVMBuildIsNull
+LLVMBuildLandingPad
 LLVMBuildLShr
 LLVMBuildLoad
 LLVMBuildMalloc
@@ -141,6 +143,7 @@ LLVMBuildPhi
 LLVMBuildPointerCast
 LLVMBuildPtrDiff
 LLVMBuildPtrToInt
+LLVMBuildResume
 LLVMBuildRet
 LLVMBuildRetVoid
 LLVMBuildSDiv
@@ -548,6 +551,7 @@ LLVMRunPassManager
 LLVMRunStaticConstructors
 LLVMRunStaticDestructors
 LLVMSetAlignment
+LLVMSetCleanup
 LLVMSetCurrentDebugLocation
 LLVMSetDataLayout
 LLVMSetFunctionCallConv
diff --git a/src/test/bench/shootout-nbody.rs b/src/test/bench/shootout-nbody.rs
index 92da06f186a..aa8786f8d61 100644
--- a/src/test/bench/shootout-nbody.rs
+++ b/src/test/bench/shootout-nbody.rs
@@ -1,3 +1,4 @@
+// xfail-test
 // based on:
 // http://shootout.alioth.debian.org/u32/benchmark.php?test=nbody&lang=java
 
diff --git a/src/test/compiletest/compiletest.rs b/src/test/compiletest/compiletest.rs
index 03bc0e72cc7..0961191918b 100644
--- a/src/test/compiletest/compiletest.rs
+++ b/src/test/compiletest/compiletest.rs
@@ -110,8 +110,9 @@ fn run_tests(config: config) {
     let opts = test_opts(config);
     let cx = {config: config, procsrv: procsrv::mk()};
     let tests = make_tests(cx);
-    test::run_tests_console_(opts, tests.tests, tests.to_task);
+    let res = test::run_tests_console_(opts, tests.tests, tests.to_task);
     procsrv::close(cx.procsrv);
+    if !res { fail "Some tests failed"; }
 }
 
 fn test_opts(config: config) -> test::test_opts {
diff --git a/src/test/compiletest/header.rs b/src/test/compiletest/header.rs
index 9f896c48e74..890818d4574 100644
--- a/src/test/compiletest/header.rs
+++ b/src/test/compiletest/header.rs
@@ -16,10 +16,7 @@ type test_props = {
     compile_flags: option::t<str>,
     // If present, the name of a file that this test should match when
     // pretty-printed
-    pp_exact: option::t<str>,
-    // FIXME: no-valgrind is a temporary directive until all of run-fail
-    // is valgrind-clean
-    no_valgrind: bool
+    pp_exact: option::t<str>
 };
 
 // Load any test directives embedded in the file
@@ -27,7 +24,6 @@ fn load_props(testfile: str) -> test_props {
     let error_patterns = [];
     let compile_flags = option::none;
     let pp_exact = option::none;
-    let no_valgrind = false;
     for each ln: str in iter_header(testfile) {
         alt parse_error_pattern(ln) {
           option::some(ep) { error_patterns += [ep]; }
@@ -41,16 +37,11 @@ fn load_props(testfile: str) -> test_props {
         if option::is_none(pp_exact) {
             pp_exact = parse_pp_exact(ln, testfile);
         }
-
-        if no_valgrind == false {
-            no_valgrind = parse_name_directive(ln, "no-valgrind");
-        }
     }
     ret {
         error_patterns: error_patterns,
         compile_flags: compile_flags,
-        pp_exact: pp_exact,
-        no_valgrind: no_valgrind
+        pp_exact: pp_exact
     };
 }
 
@@ -59,11 +50,16 @@ fn is_test_ignored(config: config, testfile: str) -> bool {
     for each ln: str in iter_header(testfile) {
         // FIXME: Can't return or break from iterator
         found = found || parse_name_directive(ln, "xfail-test");
+        found = found || parse_name_directive(ln, xfail_target());
         if (config.mode == common::mode_pretty) {
             found = found || parse_name_directive(ln, "xfail-pretty");
         }
     }
     ret found;
+
+    fn xfail_target() -> str {
+        "xfail-" + std::os::target_os()
+    }
 }
 
 iter iter_header(testfile: str) -> str {
diff --git a/src/test/compiletest/procsrv.rs b/src/test/compiletest/procsrv.rs
index ae98853976e..6a40aaccaf4 100644
--- a/src/test/compiletest/procsrv.rs
+++ b/src/test/compiletest/procsrv.rs
@@ -66,7 +66,7 @@ fn run(handle: handle, lib_path: str, prog: str, args: [str],
     writeclose(resp.infd, input);
     let output = readclose(resp.outfd);
     let errput = readclose(resp.errfd);
-    let status = os::waitpid(resp.pid);
+    let status = run::waitpid(resp.pid);
     ret {status: status, out: output, err: errput};
 }
 
diff --git a/src/test/compiletest/runtest.rs b/src/test/compiletest/runtest.rs
index 8ed938094c9..46b74e799f4 100644
--- a/src/test/compiletest/runtest.rs
+++ b/src/test/compiletest/runtest.rs
@@ -51,19 +51,21 @@ fn run_rfail_test(cx: cx, props: test_props, testfile: str) {
 
     procres = exec_compiled_test(cx, props, testfile);
 
-    if procres.status == 0 {
-        fatal_procres("run-fail test didn't produce an error!", procres);
-    }
-
-    // This is the value valgrind returns on failure
-    // FIXME: Why is this value neither the value we pass to
-    // valgrind as --error-exitcode (1), nor the value we see as the
-    // exit code on the command-line (137)?
-    const valgrind_err: int = 9;
+    // The value our Makefile configures valgrind to return on failure
+    const valgrind_err: int = 100;
     if procres.status == valgrind_err {
         fatal_procres("run-fail test isn't valgrind-clean!", procres);
     }
 
+    // The value the rust runtime returns on failure
+    const rust_err: int = 101;
+    if procres.status != rust_err {
+        fatal_procres(
+            #fmt("run-fail test produced the wrong error code: %d",
+                 procres.status),
+            procres);
+    }
+
     check_error_patterns(props, testfile, procres);
 }
 
@@ -251,10 +253,9 @@ fn make_exe_name(config: config, testfile: str) -> str {
     output_base_name(config, testfile) + os::exec_suffix()
 }
 
-fn make_run_args(config: config, props: test_props, testfile: str) ->
+fn make_run_args(config: config, _props: test_props, testfile: str) ->
    procargs {
-    let toolargs =
-        if !props.no_valgrind {
+    let toolargs = {
             // If we've got another tool to run under (valgrind),
             // then split apart its command
             let runtool =
@@ -263,7 +264,7 @@ fn make_run_args(config: config, props: test_props, testfile: str) ->
                   option::none. { option::none }
                 };
             split_maybe_args(runtool)
-        } else { [] };
+        };
 
     let args = toolargs + [make_exe_name(config, testfile)];
     ret {prog: args[0], args: vec::slice(args, 1u, vec::len(args))};
diff --git a/src/test/run-fail/explicit-fail-msg.rs b/src/test/run-fail/explicit-fail-msg.rs
index b45e443759c..527a103e342 100644
--- a/src/test/run-fail/explicit-fail-msg.rs
+++ b/src/test/run-fail/explicit-fail-msg.rs
@@ -1,3 +1,2 @@
 // error-pattern:wooooo
-// no-valgrind
 fn main() { let a = 1; if 1 == 1 { a = 2; } fail "woooo" + "o"; }
diff --git a/src/test/run-fail/fmt-fail.rs b/src/test/run-fail/fmt-fail.rs
index 90256271fd2..5ca0ab74147 100644
--- a/src/test/run-fail/fmt-fail.rs
+++ b/src/test/run-fail/fmt-fail.rs
@@ -1,5 +1,4 @@
 // error-pattern:meh
-// no-valgrind
 use std;
 
 fn main() { let str_var: str = "meh"; fail #fmt["%s", str_var]; }
diff --git a/src/test/run-fail/linked-failure.rs b/src/test/run-fail/linked-failure.rs
index 033a3bc9ec2..24cfff6edb5 100644
--- a/src/test/run-fail/linked-failure.rs
+++ b/src/test/run-fail/linked-failure.rs
@@ -1,8 +1,7 @@
 // -*- rust -*-
 
 // error-pattern:1 == 2
-// no-valgrind
-
+// xfail-test
 use std;
 import std::task;
 import std::comm::port;
diff --git a/src/test/run-fail/unwind-assert.rs b/src/test/run-fail/unwind-assert.rs
new file mode 100644
index 00000000000..e7aeedcf505
--- /dev/null
+++ b/src/test/run-fail/unwind-assert.rs
@@ -0,0 +1,6 @@
+// error-pattern:fail
+
+fn main() {
+    let a = @0;
+    assert false;
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-box.rs b/src/test/run-fail/unwind-box.rs
new file mode 100644
index 00000000000..19906bda0ae
--- /dev/null
+++ b/src/test/run-fail/unwind-box.rs
@@ -0,0 +1,10 @@
+// error-pattern:fail
+
+fn failfn() {
+    fail;
+}
+
+fn main() {
+    @0;
+    failfn();
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-check.rs b/src/test/run-fail/unwind-check.rs
new file mode 100644
index 00000000000..e01cc969cea
--- /dev/null
+++ b/src/test/run-fail/unwind-check.rs
@@ -0,0 +1,8 @@
+// error-pattern:fail
+
+pure fn p(a: @int) -> bool { false }
+
+fn main() {
+    let a = @0;
+    check p(a);
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-closure.rs b/src/test/run-fail/unwind-closure.rs
new file mode 100644
index 00000000000..216ae054947
--- /dev/null
+++ b/src/test/run-fail/unwind-closure.rs
@@ -0,0 +1,10 @@
+// error-pattern:fail
+
+fn f(a: @int) {
+    fail;
+}
+
+fn main() {
+    let g = bind f(@0);
+    g();
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-fail.rs b/src/test/run-fail/unwind-fail.rs
new file mode 100644
index 00000000000..2d4f138e715
--- /dev/null
+++ b/src/test/run-fail/unwind-fail.rs
@@ -0,0 +1,6 @@
+// error-pattern:fail
+
+fn main() {
+    @0;
+    fail;
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-initializer-indirect.rs b/src/test/run-fail/unwind-initializer-indirect.rs
new file mode 100644
index 00000000000..726dc3d9040
--- /dev/null
+++ b/src/test/run-fail/unwind-initializer-indirect.rs
@@ -0,0 +1,7 @@
+// error-pattern:fail
+
+fn f() -> @int { fail; }
+
+fn main() {
+    let a: @int = f();
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-initializer.rs b/src/test/run-fail/unwind-initializer.rs
new file mode 100644
index 00000000000..b3dc0d3eaeb
--- /dev/null
+++ b/src/test/run-fail/unwind-initializer.rs
@@ -0,0 +1,7 @@
+// error-pattern:fail
+
+fn main() {
+    let a: @int = {
+        fail;
+    };
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-iter.rs b/src/test/run-fail/unwind-iter.rs
new file mode 100644
index 00000000000..fa64b0ad78c
--- /dev/null
+++ b/src/test/run-fail/unwind-iter.rs
@@ -0,0 +1,12 @@
+// error-pattern:fail
+
+iter x() -> int {
+    fail;
+    put 0;
+}
+
+fn main() {
+    let a = @0;
+    for each x in x() {
+    }
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-iter2.rs b/src/test/run-fail/unwind-iter2.rs
new file mode 100644
index 00000000000..a924bb13616
--- /dev/null
+++ b/src/test/run-fail/unwind-iter2.rs
@@ -0,0 +1,12 @@
+// error-pattern:fail
+
+iter x() -> int {
+    let a = @0;
+    put 1;
+}
+
+fn main() {
+    for each x in x() {
+        fail;
+    }
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-lambda.rs b/src/test/run-fail/unwind-lambda.rs
new file mode 100644
index 00000000000..6980cc16bd1
--- /dev/null
+++ b/src/test/run-fail/unwind-lambda.rs
@@ -0,0 +1,16 @@
+// error-pattern:fail
+
+fn main() {
+    let cheese = "roquefort";
+    let carrots = @"crunchy";
+
+    fn (tasties: @str, macerate: block(str)) {
+        macerate(*tasties);
+    } (carrots, { |food|
+        let mush = food + cheese;
+        lambda() {
+            let chew = mush + cheese;
+            fail "so yummy"
+        } ();
+    });
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-misc-1.rs b/src/test/run-fail/unwind-misc-1.rs
new file mode 100644
index 00000000000..15d433ae363
--- /dev/null
+++ b/src/test/run-fail/unwind-misc-1.rs
@@ -0,0 +1,28 @@
+// error-pattern:fail
+
+use std;
+import std::map;
+import std::uint;
+
+fn main() {
+    let count = @mutable 0u;
+    let hash = bind fn (_s: [@str], count: @mutable uint) -> uint {
+        *count += 1u;
+        if *count == 10u {
+            fail;
+        } else {
+            ret *count;
+        }
+    } (_, count);
+
+    fn eq(s: [@str], t: [@str]) -> bool {
+        ret s == t;
+    }
+
+    let map = map::mk_hashmap(hash, eq);
+    let arr = [];
+    for each i in uint::range(0u, 10u) {
+        arr += [@"key stuff"];
+        map.insert(arr, arr + [@"value stuff"]);
+    }
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-nested.rs b/src/test/run-fail/unwind-nested.rs
new file mode 100644
index 00000000000..48e3063c6dd
--- /dev/null
+++ b/src/test/run-fail/unwind-nested.rs
@@ -0,0 +1,11 @@
+// error-pattern:fail
+
+fn main() {
+    let a = @0;
+    {
+        let b = @0;
+        {
+            fail;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-resource-fail.rs b/src/test/run-fail/unwind-resource-fail.rs
new file mode 100644
index 00000000000..4ab8dc95085
--- /dev/null
+++ b/src/test/run-fail/unwind-resource-fail.rs
@@ -0,0 +1,12 @@
+// error-pattern:fail
+// xfail-test
+
+resource r(i: int) {
+    // What happens when destructors throw?
+    fail;
+}
+
+fn main() {
+    @0;
+    let r <- r(0);
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-stacked.rs b/src/test/run-fail/unwind-stacked.rs
new file mode 100644
index 00000000000..bf5258cee7d
--- /dev/null
+++ b/src/test/run-fail/unwind-stacked.rs
@@ -0,0 +1,16 @@
+// error-pattern:fail
+
+fn f() {
+    let a = @0;
+    fail;
+}
+
+fn g() {
+    let b = @0;
+    f();
+}
+
+fn main() {
+    let a = @0;
+    g();
+}
\ No newline at end of file
diff --git a/src/test/run-fail/unwind-uninitialized.rs b/src/test/run-fail/unwind-uninitialized.rs
new file mode 100644
index 00000000000..35269b38714
--- /dev/null
+++ b/src/test/run-fail/unwind-uninitialized.rs
@@ -0,0 +1,10 @@
+// error-pattern:fail
+
+fn f() {
+    fail;
+}
+
+fn main() {
+    f();
+    let a = @0;
+}
\ No newline at end of file
diff --git a/src/test/run-fail/vec-overrun.rs b/src/test/run-fail/vec-overrun.rs
index 54329ca63b5..7a429e08cae 100644
--- a/src/test/run-fail/vec-overrun.rs
+++ b/src/test/run-fail/vec-overrun.rs
@@ -1,7 +1,6 @@
 // -*- rust -*-
 
 // error-pattern:bounds check
-// no-valgrind
 fn main() {
     let v: [int] = [10];
     let x: int = 0;
diff --git a/src/test/run-fail/vec-underrun.rs b/src/test/run-fail/vec-underrun.rs
index 87c8295840e..9caf82d1ae0 100644
--- a/src/test/run-fail/vec-underrun.rs
+++ b/src/test/run-fail/vec-underrun.rs
@@ -1,7 +1,6 @@
 // -*- rust -*-
 
 // error-pattern:bounds check
-// no-valgrind
 fn main() {
     let v: [int] = [10, 20];
     let x: int = 0;
diff --git a/src/test/run-pass/native-llvm.rs b/src/test/run-pass/native-llvm.rs
new file mode 100644
index 00000000000..450f052bc62
--- /dev/null
+++ b/src/test/run-pass/native-llvm.rs
@@ -0,0 +1,10 @@
+// xfail-test
+
+native "llvm" mod llvm {
+    fn thesqrt(n: float) -> float = "sqrt.f64";
+}
+
+fn main() {
+    let s = llvm::thesqrt(4.0);
+    assert 1.9 < s && s < 2.1;
+}
\ No newline at end of file
diff --git a/src/test/run-pass/task-comm-15.rs b/src/test/run-pass/task-comm-15.rs
index 2b89d8fde38..56cf41b50c3 100644
--- a/src/test/run-pass/task-comm-15.rs
+++ b/src/test/run-pass/task-comm-15.rs
@@ -1,3 +1,4 @@
+// xfail-win32
 use std;
 import std::comm;
 import std::task;
diff --git a/src/test/run-pass/unwind-box.rs b/src/test/run-pass/unwind-box.rs
new file mode 100644
index 00000000000..0c4f1e4616c
--- /dev/null
+++ b/src/test/run-pass/unwind-box.rs
@@ -0,0 +1,14 @@
+// xfail-win32
+use std;
+import std::task;
+
+fn f() {
+    task::unsupervise();
+    let a = @0;
+    fail;
+}
+
+fn main() {
+    let g = f;
+    task::spawn(g);
+}
\ No newline at end of file
diff --git a/src/test/run-pass/unwind-resource.rs b/src/test/run-pass/unwind-resource.rs
new file mode 100644
index 00000000000..e8cf0bab44f
--- /dev/null
+++ b/src/test/run-pass/unwind-resource.rs
@@ -0,0 +1,21 @@
+// xfail-win32
+use std;
+import std::task;
+import std::comm;
+
+resource complainer(c: comm::chan<bool>) {
+    comm::send(c, true);
+}
+
+fn f(-c: comm::chan<bool>) {
+    task::unsupervise();
+    let c <- complainer(c);
+    fail;
+}
+
+fn main() {
+    let p = comm::port();
+    let c = comm::chan(p);
+    task::spawn(bind f(c));
+    assert comm::recv(p);
+}
\ No newline at end of file
diff --git a/src/test/run-pass/unwind-resource2.rs b/src/test/run-pass/unwind-resource2.rs
new file mode 100644
index 00000000000..1d7a46151fc
--- /dev/null
+++ b/src/test/run-pass/unwind-resource2.rs
@@ -0,0 +1,18 @@
+// xfail-win32
+use std;
+import std::task;
+import std::comm;
+
+resource complainer(c: @int) {
+}
+
+fn f() {
+    task::unsupervise();
+    let c <- complainer(@0);
+    fail;
+}
+
+fn main() {
+    let g = f;
+    task::spawn(g);
+}
\ No newline at end of file
diff --git a/src/test/stdtest/run.rs b/src/test/stdtest/run.rs
index 218889c766b..e5c36d37041 100644
--- a/src/test/stdtest/run.rs
+++ b/src/test/stdtest/run.rs
@@ -65,3 +65,10 @@ fn test_pipes() {
         ret buf;
     }
 }
+
+#[test]
+fn waitpid() {
+    let pid = run::spawn_process("false", [], 0, 0, 0);
+    let status = run::waitpid(pid);
+    assert status == 1;
+}