about summary refs log tree commit diff
diff options
context:
space:
mode:
authorEric Holk <eholk@mozilla.com>2011-05-31 17:44:54 -0700
committerGraydon Hoare <graydon@mozilla.com>2011-06-13 18:14:13 -0700
commitd1857d30fc05f29fb82231336b229e50948a8336 (patch)
tree8a1ad8ad6dcda631e4858902008e3d7b8227b271
parent1595c9d767de72340358028e87d3eb386af0adfe (diff)
downloadrust-d1857d30fc05f29fb82231336b229e50948a8336.tar.gz
rust-d1857d30fc05f29fb82231336b229e50948a8336.zip
This is the mega-ucontext commit. It replaces the task switching mechanism with a new one inspired by ucontext. It works under Linux, OS X and Windows, and is Valgrind clean on Linux and OS X (provided the runtime is built with gcc).
This commit also moves yield and join to the standard library, as requested in #42. Join is currently a no-op though.
-rwxr-xr-xconfigure4
-rw-r--r--mk/rt.mk13
-rw-r--r--src/comp/back/abi.rs4
-rw-r--r--src/comp/back/upcall.rs2
-rw-r--r--src/comp/middle/trans.rs11
-rw-r--r--src/lib/std.rc4
-rw-r--r--src/lib/task.rs (renamed from src/lib/_task.rs)10
-rw-r--r--src/rt/activate_glue.s89
-rw-r--r--src/rt/arch/i386/_context.s86
-rw-r--r--src/rt/arch/i386/context.cpp81
-rw-r--r--src/rt/arch/i386/context.h35
-rw-r--r--src/rt/globals.h15
-rw-r--r--src/rt/new_exit.ll43
-rw-r--r--src/rt/rust.cpp4
-rw-r--r--src/rt/rust.h3
-rw-r--r--src/rt/rust_builtin.cpp10
-rw-r--r--src/rt/rust_dom.cpp19
-rw-r--r--src/rt/rust_internal.h5
-rw-r--r--src/rt/rust_kernel.cpp4
-rw-r--r--src/rt/rust_kernel.h1
-rw-r--r--src/rt/rust_task.cpp255
-rw-r--r--src/rt/rust_task.h14
-rw-r--r--src/rt/rust_upcall.cpp22
-rw-r--r--src/rt/rustrt.def.in2
-rw-r--r--src/rt/sync/lock_and_signal.cpp20
-rw-r--r--src/rt/yield_glue.s42
-rw-r--r--src/test/run-pass/comm.rs8
-rw-r--r--src/test/run-pass/yield.rs29
-rw-r--r--src/test/run-pass/yield1.rs17
-rw-r--r--src/test/run-pass/yield2.rs8
30 files changed, 418 insertions, 442 deletions
diff --git a/configure b/configure
index 4270292792b..e4d79318269 100755
--- a/configure
+++ b/configure
@@ -176,11 +176,11 @@ fi
 step_msg "making directories"
 for i in \
     doc \
-    rt rt/isaac rt/bigint rt/sync rt/test \
+    rt rt/isaac rt/bigint rt/sync rt/test rt/arch/i386 \
     rustllvm \
     dl stage0 stage1 stage2 stage3 \
     test/run-pass test/run-fail test/compile-fail \
-    test/bench/99-bottles test/bench/shootout
+    test/bench/99-bottles test/bench/shootout     
 do
     make_dir $i
 done
diff --git a/mk/rt.mk b/mk/rt.mk
index 566e5f285d4..cd18e0597d8 100644
--- a/mk/rt.mk
+++ b/mk/rt.mk
@@ -27,11 +27,12 @@ RUNTIME_CS := rt/sync/timer.cpp \
               rt/memory_region.cpp \
               rt/test/rust_test_harness.cpp \
               rt/test/rust_test_runtime.cpp \
-              rt/test/rust_test_util.cpp
+              rt/test/rust_test_util.cpp \
+              rt/arch/i386/context.cpp \
 
-RUNTIME_LL := rt/new_exit.ll rt/vec_append.ll
+RUNTIME_LL := rt/vec_append.ll
 
-RUNTIME_S := rt/activate_glue.s rt/yield_glue.s
+RUNTIME_S := rt/arch/i386/_context.s
 
 RUNTIME_HDR := rt/globals.h \
                rt/rust.h \
@@ -60,10 +61,12 @@ RUNTIME_HDR := rt/globals.h \
                rt/memory.h \
                rt/test/rust_test_harness.h \
                rt/test/rust_test_runtime.h \
-               rt/test/rust_test_util.h
+               rt/test/rust_test_util.h \
+               rt/arch/i386/context.h \
 
 RUNTIME_DEF := rt/rustrt$(CFG_DEF_SUFFIX)
-RUNTIME_INCS := -I $(S)src/rt/isaac -I $(S)src/rt/uthash
+RUNTIME_INCS := -I $(S)src/rt/isaac -I $(S)src/rt/uthash \
+                -I $(S)src/rt/arch/i386
 RUNTIME_OBJS := $(RUNTIME_CS:.cpp=.o) $(RUNTIME_LL:.ll=.o) $(RUNTIME_S:.s=.o)
 RUNTIME_LIBS := $(CFG_GCCISH_POST_LIB_FLAGS)
 
diff --git a/src/comp/back/abi.rs b/src/comp/back/abi.rs
index 249884da123..42a4dddd564 100644
--- a/src/comp/back/abi.rs
+++ b/src/comp/back/abi.rs
@@ -93,10 +93,6 @@ fn vec_append_glue_name() -> str {
     ret "rust_vec_append_glue";
 }
 
-fn yield_glue_name() -> str {
-    ret "rust_yield_glue";
-}
-
 fn no_op_type_glue_name() -> str {
     ret "rust_no_op_type_glue";
 }
diff --git a/src/comp/back/upcall.rs b/src/comp/back/upcall.rs
index f6f6c26f6ae..12bd64af23e 100644
--- a/src/comp/back/upcall.rs
+++ b/src/comp/back/upcall.rs
@@ -114,7 +114,7 @@ fn declare_upcalls(type_names tn, ModuleRef llmod) -> @upcalls {
                         T_ptr(T_tydesc(tn))),
         new_task=d("new_task", [T_ptr(T_str())], T_taskptr(tn)),
         start_task=d("start_task", [T_taskptr(tn), 
-                                    T_int(), T_int()],
+                                    T_int(), T_int(), T_size_t()],
                      T_taskptr(tn)),
         new_thread=d("new_thread", [T_ptr(T_i8())], T_taskptr(tn)),
         start_thread=d("start_thread", [T_taskptr(tn), T_int(), T_int(),
diff --git a/src/comp/middle/trans.rs b/src/comp/middle/trans.rs
index 6c419be8b34..02bcc0f8191 100644
--- a/src/comp/middle/trans.rs
+++ b/src/comp/middle/trans.rs
@@ -85,8 +85,7 @@ state obj namegen(mutable int i) {
 
 type derived_tydesc_info = rec(ValueRef lltydesc, bool escapes);
 
-type glue_fns = rec(ValueRef yield_glue,
-                    ValueRef no_op_type_glue,
+type glue_fns = rec(ValueRef no_op_type_glue,
                     ValueRef vec_append_glue);
 
 type tydesc_info = rec(ty::t ty,
@@ -6454,13 +6453,12 @@ fn trans_spawn(&@block_ctxt cx,
     auto wrapper = mk_spawn_wrapper(bcx, func, args_ty);
     bcx = wrapper.bcx;
     auto llfnptr_i = bcx.build.PointerCast(wrapper.val, T_int());
-    // TODO: this next line might be necessary...
-    //llfnptr_i = bcx.build.Load(llfnptr_i);
 
     // And start the task
+    auto args_size = size_of(bcx, args_ty).val;
     bcx.build.Call(bcx.fcx.lcx.ccx.upcalls.start_task,
                    [bcx.fcx.lltaskptr, new_task,
-                    llfnptr_i, llargs_i]);
+                    llfnptr_i, llargs_i, args_size]);
 
     auto task_ty = node_ann_type(bcx.fcx.lcx.ccx, ann);
     auto dropref = clean(bind drop_ty(_, new_task, task_ty));
@@ -8545,8 +8543,7 @@ fn vec_p0(&@block_ctxt bcx, ValueRef v) -> ValueRef {
 }
 
 fn make_glues(ModuleRef llmod, &type_names tn) -> @glue_fns {
-    ret @rec(yield_glue = decl_glue(llmod, tn, abi::yield_glue_name()),
-             no_op_type_glue = decl_no_op_type_glue(llmod, tn),
+    ret @rec(no_op_type_glue = decl_no_op_type_glue(llmod, tn),
              vec_append_glue = make_vec_append_glue(llmod, tn));
 }
 
diff --git a/src/lib/std.rc b/src/lib/std.rc
index ec6b23d866c..60cecd7cf68 100644
--- a/src/lib/std.rc
+++ b/src/lib/std.rc
@@ -18,7 +18,7 @@ mod str;
 
 mod io;
 mod sys;
-mod _task;
+mod task;
 
 // Utility modules.
 
@@ -33,7 +33,7 @@ auth os_fs = unsafe;
 auth run = unsafe;
 auth str = unsafe;
 auth vec = unsafe;
-auth _task = unsafe;
+auth task = unsafe;
 
 auth dbg = unsafe;
 
diff --git a/src/lib/_task.rs b/src/lib/task.rs
index b6b754f0cb2..0173143cda2 100644
--- a/src/lib/_task.rs
+++ b/src/lib/task.rs
@@ -1,5 +1,6 @@
 native "rust" mod rustrt {
     fn task_sleep(uint time_in_us);
+    fn task_yield();
 }
 
 /**
@@ -11,6 +12,15 @@ fn sleep(uint time_in_us) {
     ret rustrt::task_sleep(time_in_us);
 }
 
+fn yield() {
+    ret rustrt::task_yield();
+}
+
+fn join(task t) {
+    // TODO: figure out how to pass tasks to the runtime and call the builtin
+    // join.
+}
+
 // Local Variables:
 // mode: rust;
 // fill-column: 78;
diff --git a/src/rt/activate_glue.s b/src/rt/activate_glue.s
deleted file mode 100644
index 5b55f568880..00000000000
--- a/src/rt/activate_glue.s
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * This is a bit of glue-code.
- *
- *   - save regs on C stack
- *   - save sp to task.runtime_sp (runtime_sp is thus always aligned)
- *   - load saved task sp (switch stack)
- *   - restore saved task regs
- *   - return to saved task pc
- *
- * Our incoming stack looks like this:
- *
- *   *esp+4        = [arg1   ] = task ptr
- *   *esp          = [retpc  ]
- */
-
-	.globl new_rust_activate_glue
-	.balign 4
-new_rust_activate_glue:
-	movl  4(%esp), %ecx    # ecx = rust_task
-	pushl %ebp
-	pushl %edi
-	pushl %esi
-	pushl %ebx
-	movl  %esp, 12(%ecx)
-	movl  16(%ecx), %esp
-
-        /*
-         * There are two paths we can arrive at this code from:
-         *
-         *
-         *   1. We are activating a task for the first time. When we switch
-         *      into the task stack and 'ret' to its first instruction, we'll
-         *      start doing whatever the first instruction says. Probably
-         *      saving registers and starting to establish a frame. Harmless
-         *      stuff, doesn't look at task->rust_sp again except when it
-         *      clobbers it during a later native call.
-         *
-         *
-         *   2. We are resuming a task that was descheduled by the yield glue
-         *      below.  When we switch into the task stack and 'ret', we'll be
-         *      ret'ing to a very particular instruction:
-         *
-         *              "esp <- task->rust_sp"
-         *
-         *      this is the first instruction we 'ret' to after this glue,
-         *      because it is the first instruction following *any* native
-         *      call, and the task we are activating was descheduled
-         *      mid-native-call.
-         *
-         *      Unfortunately for us, we have already restored esp from
-         *      task->rust_sp and are about to eat the 5 words off the top of
-         *      it.
-         *
-         *
-         *      | ...    | <-- where esp will be once we restore + ret, below,
-         *      | retpc  |     and where we'd *like* task->rust_sp to wind up.
-         *      | ebp    |
-         *      | edi    |
-         *      | esi    |
-         *      | ebx    | <-- current task->rust_sp == current esp
-         *
-         *
-         *      This is a problem. If we return to "esp <- task->rust_sp" it
-         *      will push esp back down by 5 words. This manifests as a rust
-         *      stack that grows by 5 words on each yield/reactivate. Not
-         *      good.
-         *
-         *      So what we do here is just adjust task->rust_sp up 5 words as
-         *      well, to mirror the movement in esp we're about to
-         *      perform. That way the "esp <- task->rust_sp" we 'ret' to below
-         *      will be a no-op. Esp won't move, and the task's stack won't
-         *      grow.
-         */
-	addl  $20, 16(%ecx)
-
-        /*
-         * In most cases, the function we're returning to (activating)
-         * will have saved any caller-saves before it yielded via native call,
-         * so no work to do here. With one exception: when we're initially
-         * activating, the task needs to be in the fastcall 2nd parameter
-         * expected by the rust main function. That's edx.
-         */
-        mov  %ecx, %edx
-
-	popl  %ebx
-	popl  %esi
-	popl  %edi
-	popl  %ebp
-	ret
diff --git a/src/rt/arch/i386/_context.s b/src/rt/arch/i386/_context.s
new file mode 100644
index 00000000000..8b49d35de1b
--- /dev/null
+++ b/src/rt/arch/i386/_context.s
@@ -0,0 +1,86 @@
+	.text
+
+/*
+Callee save registers:
+	ebp, ebx, esi, edi
+
+Caller save registers:
+	eax, ecx, edx
+*/
+	
+/*
+Saves a set of registers. This is used by our implementation of
+getcontext.
+
+The registers_t variable is in (%esp)
+*/
+	
+.globl get_registers
+get_registers:
+	movl 4(%esp), %eax
+	movl %eax, 0(%eax)
+	movl %ebx, 4(%eax)
+	movl %ecx, 8(%eax)
+	movl %edx, 12(%eax)
+	movl %ebp, 16(%eax)
+	movl %esi, 20(%eax)
+	movl %edi, 24(%eax)
+	movl %esp, 28(%eax)
+	movw %cs, 32(%eax)
+	movw %ds, 34(%eax)
+	movw %ss, 36(%eax)
+	movw %es, 38(%eax)
+	movw %fs, 40(%eax)
+	movw %gs, 42(%eax)
+
+	// save the flags
+	pushf
+	popl %ecx
+	movl %ecx, 44(%eax)
+
+	// save the return address as the instruction pointer
+	movl 0(%esp), %ecx
+	movl %ecx, 48(%eax)
+
+	// return 0
+	xor %eax, %eax
+	ret
+
+.globl set_registers
+set_registers:
+	movl 4(%esp), %eax
+
+	movl 4(%eax), %ebx
+	// save ecx for later...
+	movl 12(%eax), %edx
+	movl 16(%eax), %ebp
+	movl 20(%eax), %esi
+	movl 24(%eax), %edi
+	movl 28(%eax), %esp
+	// We can't actually change this...
+	//movl 32(%eax), %cs
+	movw 34(%eax), %ds
+	movw 36(%eax), %ss
+	movw 38(%eax), %es
+	movw 40(%eax), %fs
+	movw 42(%eax), %gs
+
+	// restore the flags
+	movl 44(%eax), %ecx
+	push %ecx
+	popf
+
+	// get ready to return back to the old eip
+	// We could write this directly to 0(%esp), but Valgrind on OS X
+	// complains.
+	pop %ecx
+	mov 48(%eax), %ecx
+	push %ecx	
+	//movl %ecx, 0(%esp)
+	
+	// okay, now we can restore ecx.
+	movl 8(%eax), %ecx
+
+	// return 1 to the saved eip
+	movl $1, %eax
+	ret
diff --git a/src/rt/arch/i386/context.cpp b/src/rt/arch/i386/context.cpp
new file mode 100644
index 00000000000..8f5dbacbcf4
--- /dev/null
+++ b/src/rt/arch/i386/context.cpp
@@ -0,0 +1,81 @@
+#include "context.h"
+
+#include "../../rust.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+extern "C" uint32_t CDECL get_registers(registers_t *regs) 
+  asm ("get_registers");
+extern "C" uint32_t CDECL set_registers(registers_t *regs)
+  asm ("set_registers");
+
+context::context()
+  : next(NULL)
+{
+  get_registers(&regs);
+}
+
+void context::set()
+{
+  //printf("Activating %p...\n", this);
+  set_registers(&regs);
+}
+
+void context::swap(context &out)
+{
+  //printf("Swapping to %p and saving in %p\n", this, &out);
+  uint32_t r = get_registers(&out.regs);
+  //printf("get_registers = %d, sp = 0x%x\n", r, out.regs.esp);
+  if(!r) {
+    set();
+  }
+  //printf("Resumed %p...\n", &out);
+}
+
+void context::call(void *f, void *arg, void *stack) {
+  // set up the trampoline frame
+  uint32_t *sp = (uint32_t *)stack;
+  *--sp = (uint32_t)this;
+  *--sp = (uint32_t)arg;
+  *--sp = 0xdeadbeef; //(uint32_t)ctx_trampoline1;
+  *--sp = 0xdeadbeef;
+
+  regs.esp = (uint32_t)sp;
+  regs.eip = (uint32_t)f;
+}
+
+#if 0
+// This is some useful code to check how the registers struct got
+// layed out in memory.
+int main() {
+  registers_t regs;
+
+  printf("Register offsets\n");
+
+#define REG(r) \
+  printf("  %6s: +%ld\n", #r, (intptr_t)&regs.r - (intptr_t)&regs);
+
+  REG(eax);
+  REG(ebx);
+  REG(ecx);
+  REG(edx);
+  REG(ebp);
+  REG(esi);
+  REG(edi);
+  REG(esp);
+
+  REG(cs);
+  REG(ds);
+  REG(ss);
+  REG(es);
+  REG(fs);
+  REG(gs);
+
+  REG(eflags);
+
+  REG(eip);
+
+  return 0;
+}
+#endif
diff --git a/src/rt/arch/i386/context.h b/src/rt/arch/i386/context.h
new file mode 100644
index 00000000000..0ddbee6988f
--- /dev/null
+++ b/src/rt/arch/i386/context.h
@@ -0,0 +1,35 @@
+// -*- mode: c++ -*-
+
+#ifndef CONTEXT_H
+#define CONTEXT_H
+
+#include <inttypes.h>
+
+struct registers_t {
+  // general purpose registers
+  uint32_t eax, ebx, ecx, edx, ebp, esi, edi, esp;
+
+  // segment registers
+  uint16_t cs, ds, ss, es, fs, gs;
+
+  uint32_t eflags;
+
+  uint32_t eip;
+};
+
+class context {
+  registers_t regs;
+
+public:
+  context();
+  
+  context *next;
+
+  void set();
+  
+  void swap(context &out);
+
+  void call(void *f, void *arg, void *sp);
+};
+
+#endif
diff --git a/src/rt/globals.h b/src/rt/globals.h
index 85aa5860c78..aa696dd9092 100644
--- a/src/rt/globals.h
+++ b/src/rt/globals.h
@@ -1,9 +1,12 @@
 #ifndef GLOBALS_H
 #define GLOBALS_H
 
+#ifndef RUST_INTERNAL_H
+// these are defined in two files and GCC complains
 #define __STDC_LIMIT_MACROS 1
 #define __STDC_CONSTANT_MACROS 1
 #define __STDC_FORMAT_MACROS 1
+#endif
 
 #include <stdlib.h>
 #include <stdint.h>
@@ -32,4 +35,16 @@ extern "C" {
 #error "Platform not supported."
 #endif
 
+#define CHECKED(call)                                               \
+    {                                                               \
+    int res = (call);                                               \
+        if(0 != res) {                                              \
+            fprintf(stderr,                                         \
+                    #call " failed in %s at line %d, result = %d "  \
+                    "(%s) \n",                                      \
+                    __FILE__, __LINE__, res, strerror(res));        \
+            abort();                                                \
+        }                                                           \
+    }                                                               
+
 #endif /* GLOBALS_H */
diff --git a/src/rt/new_exit.ll b/src/rt/new_exit.ll
deleted file mode 100644
index 07f52e7759d..00000000000
--- a/src/rt/new_exit.ll
+++ /dev/null
@@ -1,43 +0,0 @@
-declare fastcc i32 @"\01rust_native_rust_local_copy"(i32, i32)
-
-module asm "\09.globl rust_native_rust_local_copy"
-module asm "\09.balign 4"
-module asm "rust_native_rust_local_copy:"
-module asm "\09.cfi_startproc"
-module asm "\09pushl %ebp"
-module asm "\09.cfi_def_cfa_offset 8"
-module asm "\09.cfi_offset %ebp, -8"
-module asm "\09pushl %edi"
-module asm "\09.cfi_def_cfa_offset 12"
-module asm "\09pushl %esi"
-module asm "\09.cfi_def_cfa_offset 16"
-module asm "\09pushl %ebx"
-module asm "\09.cfi_def_cfa_offset 20"
-module asm "\09movl  %esp, %ebp     # ebp = rust_sp"
-module asm "\09.cfi_def_cfa_register %ebp"
-module asm "\09movl  %esp, 16(%edx)"
-module asm "\09movl  12(%edx), %esp"
-module asm "\09subl  $4, %esp   # esp -= args"
-module asm "\09andl  $~0xf, %esp    # align esp down"
-module asm "\09movl  %edx, (%esp)"
-module asm "\09movl  %edx, %edi     # save task from edx to edi"
-module asm "\09call  *%ecx          # call *%ecx"
-module asm "\09movl  %edi, %edx     # restore edi-saved task to edx"
-module asm "\09movl  16(%edx), %esp"
-module asm "\09popl  %ebx"
-module asm "\09popl  %esi"
-module asm "\09popl  %edi"
-module asm "\09popl  %ebp"
-module asm "\09ret"
-module asm "\09.cfi_endproc"
-
-
-declare i32 @upcall_exit(i32)
-
-define void @rust_new_exit_task_glue(i32, i32, i32, i32, i32) {
-entry:
-  %5 = inttoptr i32 %0 to void (i32, i32, i32, i32)*
-  tail call fastcc void %5(i32 %1, i32 %2, i32 %3, i32 %4)
-  %6 = tail call fastcc i32 @"\01rust_native_rust_local_copy"(i32 ptrtoint (i32 (i32)* @upcall_exit to i32), i32 %2)
-  ret void
-}
diff --git a/src/rt/rust.cpp b/src/rt/rust.cpp
index 6d092eea618..ae150acab17 100644
--- a/src/rt/rust.cpp
+++ b/src/rt/rust.cpp
@@ -93,9 +93,13 @@ rust_start(uintptr_t main_fn, int argc, char **argv, void* crate_map) {
         DLOG(dom, dom, "startup: arg[%d] = '%s'", i, args->argv[i]);
     }
 
+    /*
     uintptr_t main_args[4] = {0, 0, 0, (uintptr_t)args->args};
     dom->root_task->start(main_fn,
                           (uintptr_t)&main_args, sizeof(main_args));
+    */
+    dom->root_task->start(main_fn,
+                          (uintptr_t)args->args, sizeof(args->args));
 
     int ret = dom->start_main_loop();
     delete args;
diff --git a/src/rt/rust.h b/src/rt/rust.h
index 66185727cff..1ca5dddd5e6 100644
--- a/src/rt/rust.h
+++ b/src/rt/rust.h
@@ -10,11 +10,14 @@
 // 'cdecl' ABI only means anything on i386
 #ifdef __WIN32__
 #define CDECL __cdecl
+#define FASTCALL __fastcall
 #else
 #define CDECL __attribute__((cdecl))
+#define FASTCALL __attribute__((fastcall))
 #endif
 #else
 #define CDECL
+#define FASTCALL
 #endif
 
 /*
diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp
index f4b2639b137..65c9d7e76a1 100644
--- a/src/rt/rust_builtin.cpp
+++ b/src/rt/rust_builtin.cpp
@@ -354,6 +354,16 @@ task_sleep(rust_task *task, size_t time_in_us) {
     upcall_sleep(task, time_in_us);
 }
 
+extern "C" CDECL void
+task_yield(rust_task *task) {
+    task->yield(1);
+}
+
+extern "C" CDECL void
+task_join(rust_task *task, rust_task *join_task) {
+    // TODO
+}
+
 /* Debug builtins for std.dbg. */
 
 static void
diff --git a/src/rt/rust_dom.cpp b/src/rt/rust_dom.cpp
index 6a5c4631f5e..1a5e1463509 100644
--- a/src/rt/rust_dom.cpp
+++ b/src/rt/rust_dom.cpp
@@ -1,6 +1,7 @@
 
 #include <stdarg.h>
 #include "rust_internal.h"
+#include "globals.h"
 
 rust_dom::rust_dom(rust_kernel *kernel,
     rust_message_queue *message_queue, rust_srv *srv,
@@ -44,13 +45,17 @@ rust_dom::~rust_dom() {
 #endif
 }
 
-extern "C" void new_rust_activate_glue(rust_task *)
-    asm("new_rust_activate_glue");
-
 void
 rust_dom::activate(rust_task *task) {
     curr_task = task;
-    new_rust_activate_glue(task);
+
+    context ctx;
+
+    task->ctx.next = &ctx;
+    DLOG(this, task, "descheduling...");
+    task->ctx.swap(ctx);
+    DLOG(this, task, "task has returned");
+
     curr_task = NULL;
 }
 
@@ -308,10 +313,14 @@ rust_dom::start_main_loop() {
                  scheduled_task->state->name,
                  scheduled_task->rust_sp);
 
+        /*
+          // These invariants are no longer valid, as rust_sp is not
+          // updated.
         I(this, scheduled_task->rust_sp >=
           (uintptr_t) &scheduled_task->stk->data[0]);
         I(this, scheduled_task->rust_sp < scheduled_task->stk->limit);
-
+        */
+        
         reap_dead_tasks();
     }
 
diff --git a/src/rt/rust_internal.h b/src/rt/rust_internal.h
index 6b38004bc26..5545d613a32 100644
--- a/src/rt/rust_internal.h
+++ b/src/rt/rust_internal.h
@@ -1,9 +1,12 @@
 #ifndef RUST_INTERNAL_H
 #define RUST_INTERNAL_H
 
+#ifndef GLOBALS_H
+// these are defined in two files, and GCC complains.
 #define __STDC_LIMIT_MACROS 1
 #define __STDC_CONSTANT_MACROS 1
 #define __STDC_FORMAT_MACROS 1
+#endif
 
 #define ERROR 0
 
@@ -203,8 +206,6 @@ struct rust_timer {
 
 #include "rust_util.h"
 
-typedef void CDECL (*activate_glue_ty)(rust_task *);
-
 struct type_desc {
     // First part of type_desc is known to compiler.
     // first_param = &descs[1] if dynamic, null if static.
diff --git a/src/rt/rust_kernel.cpp b/src/rt/rust_kernel.cpp
index 3c2ad01128e..f72da483c35 100644
--- a/src/rt/rust_kernel.cpp
+++ b/src/rt/rust_kernel.cpp
@@ -187,8 +187,11 @@ rust_kernel::~rust_kernel() {
     KLOG("freeing handles");
 
     free_handles(_task_handles);
+    KLOG("..task handles freed");
     free_handles(_port_handles);
+    KLOG("..port handles freed");
     free_handles(_dom_handles);
+    KLOG("..dom handles freed");
 
     KLOG("freeing queues");
 
@@ -214,6 +217,7 @@ rust_kernel::free_handles(hash_map<T*, rust_handle<T>* > &map) {
     T* key;
     rust_handle<T> *value;
     while (map.pop(&key, &value)) {
+        KLOG("...freeing " PTR, value);
         delete value;
     }
 }
diff --git a/src/rt/rust_kernel.h b/src/rt/rust_kernel.h
index 9b81e84a9ae..0c3df20358a 100644
--- a/src/rt/rust_kernel.h
+++ b/src/rt/rust_kernel.h
@@ -1,3 +1,4 @@
+// -*- c++ -*-
 #ifndef RUST_KERNEL_H
 #define RUST_KERNEL_H
 
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index b23da1b1284..6caaf25db82 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -8,6 +8,8 @@
 #include <execinfo.h>
 #endif
 
+#include "globals.h"
+
 // Stacks
 
 // FIXME (issue #151): This should be 0x300; the change here is for
@@ -50,30 +52,6 @@ del_stk(rust_dom *dom, stk_seg *stk)
 size_t const n_callee_saves = 4;
 size_t const callee_save_fp = 0;
 
-static uintptr_t
-align_down(uintptr_t sp)
-{
-    // There is no platform we care about that needs more than a
-    // 16-byte alignment.
-    return sp & ~(16 - 1);
-}
-
-static uintptr_t*
-align_down(uintptr_t* sp)
-{
-    return (uintptr_t*) align_down((uintptr_t)sp);
-}
-
-
-static void
-make_aligned_room_for_bytes(uintptr_t*& sp, size_t n)
-{
-    uintptr_t tmp = (uintptr_t) sp;
-    tmp = align_down(tmp - n) + n;
-    sp = (uintptr_t*) tmp;
-}
-
-
 rust_task::rust_task(rust_dom *dom, rust_task_list *state,
                      rust_task *spawner, const char *name) :
     maybe_proxy<rust_task>(this),
@@ -94,6 +72,7 @@ rust_task::rust_task(rust_dom *dom, rust_task_list *state,
     handle(NULL)
 {
     LOGPTR(dom, "new task", (uintptr_t)this);
+    DLOG(dom, task, "sizeof(task) = %d (0x%x)", sizeof *this, sizeof *this);
 
     if (spawner == NULL) {
         ref_count = 0;
@@ -135,6 +114,42 @@ rust_task::~rust_task()
 
 extern "C" void rust_new_exit_task_glue();
 
+struct spawn_args {
+    rust_task *task;
+    uintptr_t a3;
+    uintptr_t a4;
+    void (*FASTCALL f)(int *, rust_task *, 
+                       uintptr_t, uintptr_t);
+};
+
+// TODO: rewrite this in LLVM assembly so we can be sure the calling
+// conventions will match.
+extern "C" CDECL
+void task_start_wrapper(spawn_args *a)
+{
+    rust_task *task = a->task;
+    int rval = 42;
+    
+    // This is used by the context switching code. LLVM generates fastcall
+    // functions, but ucontext needs cdecl functions. This massages the
+    // calling conventions into the right form.
+    a->f(&rval, task, a->a3, a->a4);
+
+    LOG(task, task, "task exited with value %d", rval);
+
+    // TODO: the old exit glue does some magical argument copying stuff. This
+    // is probably still needed.
+
+    // This is duplicated from upcall_exit, which is probably dead code by
+    // now.
+    LOG(task, task, "task ref_count: %d", task->ref_count);
+    A(task->dom, task->ref_count >= 0,
+      "Task ref_count should not be negative on exit!");
+    task->die();
+    task->notify_tasks_waiting_to_join();
+    task->yield(1);
+}
+
 void
 rust_task::start(uintptr_t spawnee_fn,
                  uintptr_t args,
@@ -142,54 +157,23 @@ rust_task::start(uintptr_t spawnee_fn,
 {
     LOGPTR(dom, "from spawnee", spawnee_fn);
 
-    // Set sp to last uintptr_t-sized cell of segment
-    rust_sp -= sizeof(uintptr_t);
-
-    // Begin synthesizing the exit_task_glue frame. We will return to
-    // exit_task_glue and it is responsible for calling the user code
-    // and passing the value returned by the user to the system
-    // exit routine.
-    uintptr_t *spp = (uintptr_t *)rust_sp;
-
-    uintptr_t dummy_ret = (uintptr_t) spp--;
-
-    uintptr_t args_size = callsz - 3*sizeof(uintptr_t);
-    uintptr_t frame_size = args_size + 4*sizeof(uintptr_t);
-
-
-    // NB: Darwin needs "16-byte aligned" stacks *at the point of the call
-    // instruction in the caller*. This means that the address at which the
-    // word before retpc is pushed must always be 16-byte aligned.
-    //
-    // see: "Mac OS X ABI Function Call Guide"
+    I(dom, stk->data != NULL);
 
-    make_aligned_room_for_bytes(spp, frame_size - sizeof(uintptr_t));
+    char *sp = (char *)stk->limit;
 
-    // Copy args from spawner to spawnee.
-    uintptr_t *src = (uintptr_t *)args;
-    src += 1;                  // spawn-call output slot
-    src += 1;                  // spawn-call task slot
-    src += 1;                  // spawn-call closure-or-obj slot
+    sp -= sizeof(spawn_args);
 
-    *spp-- = (uintptr_t) *src;       // vec
-    *spp-- = (uintptr_t) 0x0;        // closure-or-obj
-    *spp-- = (uintptr_t) this;       // task
-    *spp-- = (uintptr_t) dummy_ret;  // output address
+    spawn_args *a = (spawn_args *)sp;
 
-    I(dom, spp == align_down(spp));
-    *spp-- = (uintptr_t) (uintptr_t) spawnee_fn;
+    a->task = this;
+    a->a3 = 0xca11ab1e;
+    a->a4 = args;
+    void **f = (void **)&a->f;
+    *f = (void *)spawnee_fn;
 
-    *spp-- = (uintptr_t) 0x0;        // retp
-
-    *spp-- = (uintptr_t) rust_new_exit_task_glue;
-
-    for (size_t j = 0; j < n_callee_saves; ++j) {
-        *spp-- = (uintptr_t)NULL;
-    }
-
-    // Back up one, we overshot where sp should be.
-    rust_sp = (uintptr_t) (spp+1);
+    ctx.call((void *)task_start_wrapper, a, sp);
 
+    yield_timer.reset(0);
     transition(&dom->newborn_tasks, &dom->running_tasks);
 }
 
@@ -201,112 +185,6 @@ rust_task::grow(size_t n_frame_bytes)
     // the presence of non-word-aligned pointers.
     abort();
 
-#if 0
-    stk_seg *old_stk = this->stk;
-    uintptr_t old_top = (uintptr_t) old_stk->limit;
-    uintptr_t old_bottom = (uintptr_t) &old_stk->data[0];
-    uintptr_t rust_sp_disp = old_top - this->rust_sp;
-    size_t ssz = old_top - old_bottom;
-    DLOG(dom, task, "upcall_grow_task(%" PRIdPTR
-         "), old size %" PRIdPTR " bytes (old lim: 0x%" PRIxPTR ")",
-         n_frame_bytes, ssz, old_top);
-    ssz *= 2;
-    if (ssz < n_frame_bytes)
-        ssz = n_frame_bytes;
-    ssz = next_power_of_two(ssz);
-
-    DLOG(dom, task, "upcall_grow_task growing stk 0x%"
-         PRIxPTR " to %d bytes", old_stk, ssz);
-
-    stk_seg *nstk = new_stk(dom, ssz);
-    uintptr_t new_top = (uintptr_t) &nstk->data[ssz];
-    size_t n_copy = old_top - old_bottom;
-    DLOG(dom, task,
-         "copying %d bytes of stack from [0x%" PRIxPTR ", 0x%" PRIxPTR "]"
-         " to [0x%" PRIxPTR ", 0x%" PRIxPTR "]",
-         n_copy,
-         old_bottom, old_bottom + n_copy,
-         new_top - n_copy, new_top);
-
-    VALGRIND_MAKE_MEM_DEFINED((void*)old_bottom, n_copy);
-    memcpy((void*)(new_top - n_copy), (void*)old_bottom, n_copy);
-
-    nstk->limit = new_top;
-    this->stk = nstk;
-    this->rust_sp = new_top - rust_sp_disp;
-
-    DLOG(dom, task, "processing relocations");
-
-    // FIXME (issue #32): this is the most ridiculously crude
-    // relocation scheme ever. Try actually, you know, writing out
-    // reloc descriptors?
-    size_t n_relocs = 0;
-    for (uintptr_t* p = (uintptr_t*)(new_top - n_copy);
-         p < (uintptr_t*)new_top; ++p) {
-        if (old_bottom <= *p && *p < old_top) {
-            //DLOG(dom, mem, "relocating pointer 0x%" PRIxPTR
-            //        " by %d bytes", *p, (new_top - old_top));
-            n_relocs++;
-            *p += (new_top - old_top);
-        }
-    }
-    DLOG(dom, task, "processed %d relocations", n_relocs);
-    del_stk(dom, old_stk);
-    LOGPTR(dom, "grown stk limit", new_top);
-#endif
-}
-
-void
-push_onto_thread_stack(uintptr_t &sp, uintptr_t value)
-{
-    asm("xchgl %0, %%esp\n"
-        "push %2\n"
-        "xchgl %0, %%esp\n"
-        : "=r" (sp)
-        : "0" (sp), "r" (value)
-        : "eax");
-}
-
-void
-rust_task::run_after_return(size_t nargs, uintptr_t glue)
-{
-    // This is only safe to call if we're the currently-running task.
-    check_active();
-
-    uintptr_t sp = runtime_sp;
-
-    // The compiler reserves nargs + 1 word for oldsp on the stack and
-    // then aligns it.
-    sp = align_down(sp - nargs * sizeof(uintptr_t));
-
-    uintptr_t *retpc = ((uintptr_t *) sp) - 1;
-    DLOG(dom, task,
-         "run_after_return: overwriting retpc=0x%" PRIxPTR
-         " @ runtime_sp=0x%" PRIxPTR
-         " with glue=0x%" PRIxPTR,
-         *retpc, sp, glue);
-
-    // Move the current return address (which points into rust code)
-    // onto the rust stack and pretend we just called into the glue.
-    push_onto_thread_stack(rust_sp, *retpc);
-    *retpc = glue;
-}
-
-void
-rust_task::run_on_resume(uintptr_t glue)
-{
-    // This is only safe to call if we're suspended.
-    check_suspended();
-
-    // Inject glue as resume address in the suspended frame.
-    uintptr_t* rsp = (uintptr_t*) rust_sp;
-    rsp += n_callee_saves;
-    DLOG(dom, task,
-             "run_on_resume: overwriting retpc=0x%" PRIxPTR
-             " @ rust_sp=0x%" PRIxPTR
-             " with glue=0x%" PRIxPTR,
-             *rsp, rsp, glue);
-    *rsp = glue;
 }
 
 void
@@ -314,20 +192,17 @@ rust_task::yield(size_t nargs) {
     yield(nargs, 0);
 }
 
-extern "C" void new_rust_yield_glue(void) asm("new_rust_yield_glue");
-
 void
 rust_task::yield(size_t nargs, size_t time_in_us) {
     LOG(this, task, "task %s @0x%" PRIxPTR " yielding for %d us",
         name, this, time_in_us);
+
+    // TODO: what is nargs for, and is it safe to ignore?
+
     yield_timer.reset(time_in_us);
-    run_after_return(nargs, (uintptr_t) new_rust_yield_glue);
-}
 
-static inline uintptr_t
-get_callee_save_fp(uintptr_t *top_of_callee_saves)
-{
-    return top_of_callee_saves[n_callee_saves - (callee_save_fp + 1)];
+    // Return to the scheduler.
+    ctx.next->swap(ctx);
 }
 
 void
@@ -410,20 +285,6 @@ rust_task::notify_tasks_waiting_to_join() {
     }
 }
 
-uintptr_t
-rust_task::get_fp() {
-    // sp in any suspended task points to the last callee-saved reg on
-    // the task stack.
-    return get_callee_save_fp((uintptr_t*)rust_sp);
-}
-
-uintptr_t
-rust_task::get_previous_fp(uintptr_t fp) {
-    // FIXME: terribly X86-specific.
-    // *fp == previous_fp.
-    return *((uintptr_t*)fp);
-}
-
 frame_glue_fns*
 rust_task::get_frame_glue_fns(uintptr_t fp) {
     fp -= sizeof(uintptr_t);
@@ -548,10 +409,10 @@ rust_task::free(void *p, bool is_gc)
 
 void
 rust_task::transition(rust_task_list *src, rust_task_list *dst) {
-    I(dom, state == src);
     DLOG(dom, task,
-             "task %s " PTR " state change '%s' -> '%s'",
-             name, (uintptr_t)this, src->name, dst->name);
+         "task %s " PTR " state change '%s' -> '%s' while in '%s'",
+         name, (uintptr_t)this, src->name, dst->name, state->name);
+    I(dom, state == src);
     src->remove(this);
     dst->append(this);
     state = dst;
diff --git a/src/rt/rust_task.h b/src/rt/rust_task.h
index 65c0df4e457..82792c1e1e5 100644
--- a/src/rt/rust_task.h
+++ b/src/rt/rust_task.h
@@ -7,6 +7,8 @@
 
 #include "util/array_list.h"
 
+#include "context.h"
+
 struct
 rust_task : public maybe_proxy<rust_task>,
             public dom_owned<rust_task>
@@ -47,6 +49,8 @@ rust_task : public maybe_proxy<rust_task>,
 
     rust_handle<rust_task> *handle;
 
+    context ctx;
+
     // Only a pointer to 'name' is kept, so it must live as long as this task.
     rust_task(rust_dom *dom,
               rust_task_list *state,
@@ -83,14 +87,6 @@ rust_task : public maybe_proxy<rust_task>,
     // Print a backtrace, if the "bt" logging option is on.
     void backtrace();
 
-    // Swap in some glue code to run when we have returned to the
-    // task's context (assuming we're the active task).
-    void run_after_return(size_t nargs, uintptr_t glue);
-
-    // Swap in some glue code to run when we're next activated
-    // (assuming we're the suspended task).
-    void run_on_resume(uintptr_t glue);
-
     // Save callee-saved registers and return to the main loop.
     void yield(size_t nargs);
 
@@ -114,8 +110,6 @@ rust_task : public maybe_proxy<rust_task>,
 
     rust_handle<rust_task> * get_handle();
 
-    uintptr_t get_fp();
-    uintptr_t get_previous_fp(uintptr_t fp);
     frame_glue_fns *get_frame_glue_fns(uintptr_t fp);
     rust_crate_cache * get_crate_cache();
 };
diff --git a/src/rt/rust_upcall.cpp b/src/rt/rust_upcall.cpp
index 6d8734bd430..fb7e3edc4f8 100644
--- a/src/rt/rust_upcall.cpp
+++ b/src/rt/rust_upcall.cpp
@@ -462,11 +462,22 @@ upcall_new_task(rust_task *spawner, rust_vec *name) {
     return task;
 }
 
+// TODO: This is copied from rust_task.cpp. Both copies should be moved to a
+// common location.
+static uintptr_t
+align_down(uintptr_t sp)
+{
+    // There is no platform we care about that needs more than a
+    // 16-byte alignment.
+    return sp & ~(16 - 1);
+}
+
 extern "C" CDECL rust_task *
 upcall_start_task(rust_task *spawner,
                   rust_task *task,
                   uintptr_t spawnee_fn,
-                  uintptr_t args) {
+                  uintptr_t args,
+                  size_t args_sz) {
     LOG_UPCALL_ENTRY(spawner);
 
     rust_dom *dom = spawner->dom;
@@ -478,7 +489,14 @@ upcall_start_task(rust_task *spawner,
 
     // we used to be generating this tuple in rustc, but it's easier to do it
     // here.
-    uintptr_t start_args[] = {0, 0, 0, args};
+    //
+    // The args tuple is stack-allocated. We need to move it over to the new
+    // stack.
+    task->rust_sp -= args_sz;
+    memcpy((void*)task->rust_sp, (void*)args, args_sz);
+    uintptr_t start_args[] = {0, 0, 0, task->rust_sp};
+    
+    task->rust_sp = align_down(task->rust_sp);
     
     task->start(spawnee_fn, (uintptr_t)start_args, sizeof(start_args));
     return task;
diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in
index 2c189d18d1d..bea25d633ff 100644
--- a/src/rt/rustrt.def.in
+++ b/src/rt/rustrt.def.in
@@ -36,6 +36,8 @@ str_push_byte
 str_slice
 str_vec
 task_sleep
+task_yield
+task_join
 unsafe_vec_to_mut
 unsupervise
 upcall_clone_chan
diff --git a/src/rt/sync/lock_and_signal.cpp b/src/rt/sync/lock_and_signal.cpp
index 7a5d26fc6a9..4f262285826 100644
--- a/src/rt/sync/lock_and_signal.cpp
+++ b/src/rt/sync/lock_and_signal.cpp
@@ -22,8 +22,8 @@ lock_and_signal::lock_and_signal() {
 
 #else
 lock_and_signal::lock_and_signal() {
-    pthread_cond_init(&_cond, NULL);
-    pthread_mutex_init(&_mutex, NULL);
+    CHECKED(pthread_cond_init(&_cond, NULL));
+    CHECKED(pthread_mutex_init(&_mutex, NULL));
 }
 #endif
 
@@ -31,8 +31,8 @@ lock_and_signal::~lock_and_signal() {
 #if defined(__WIN32__)
     CloseHandle(_event);
 #else
-    pthread_cond_destroy(&_cond);
-    pthread_mutex_destroy(&_mutex);
+    CHECKED(pthread_cond_destroy(&_cond));
+    CHECKED(pthread_mutex_destroy(&_mutex));
 #endif
 }
 
@@ -40,7 +40,7 @@ void lock_and_signal::lock() {
 #if defined(__WIN32__)
     EnterCriticalSection(&_cs);
 #else
-    pthread_mutex_lock(&_mutex);
+    CHECKED(pthread_mutex_lock(&_mutex));
 #endif
 }
 
@@ -48,7 +48,7 @@ void lock_and_signal::unlock() {
 #if defined(__WIN32__)
     LeaveCriticalSection(&_cs);
 #else
-    pthread_mutex_unlock(&_mutex);
+    CHECKED(pthread_mutex_unlock(&_mutex));
 #endif
 }
 
@@ -66,14 +66,14 @@ void lock_and_signal::timed_wait(size_t timeout_in_ns) {
     EnterCriticalSection(&_cs);
 #else
     if (timeout_in_ns == 0) {
-        pthread_cond_wait(&_cond, &_mutex);
+        CHECKED(pthread_cond_wait(&_cond, &_mutex));
     } else {
         timeval time_val;
         gettimeofday(&time_val, NULL);
         timespec time_spec;
         time_spec.tv_sec = time_val.tv_sec + 0;
         time_spec.tv_nsec = time_val.tv_usec * 1000 + timeout_in_ns;
-        pthread_cond_timedwait(&_cond, &_mutex, &time_spec);
+        CHECKED(pthread_cond_timedwait(&_cond, &_mutex, &time_spec));
     }
 #endif
 }
@@ -85,7 +85,7 @@ void lock_and_signal::signal() {
 #if defined(__WIN32__)
     SetEvent(_event);
 #else
-    pthread_cond_signal(&_cond);
+    CHECKED(pthread_cond_signal(&_cond));
 #endif
 }
 
@@ -96,7 +96,7 @@ void lock_and_signal::signal_all() {
 #if defined(__WIN32__)
     SetEvent(_event);
 #else
-    pthread_cond_broadcast(&_cond);
+    CHECKED(pthread_cond_broadcast(&_cond));
 #endif
 }
 
diff --git a/src/rt/yield_glue.s b/src/rt/yield_glue.s
deleted file mode 100644
index 767c9c5cae6..00000000000
--- a/src/rt/yield_glue.s
+++ /dev/null
@@ -1,42 +0,0 @@
-/* More glue code, this time the 'bottom half' of yielding.
- *
- * We arrived here because an native call decided to deschedule the
- * running task. So the native call's return address got patched to the
- * first instruction of this glue code.
- *
- * When the native call does 'ret' it will come here, and its esp will be
- * pointing to the last argument pushed on the C stack before making
- * the native call: the 0th argument to the native call, which is always
- * the task ptr performing the native call. That's where we take over.
- *
- * Our goal is to complete the descheduling
- *
- *   - Switch over to the task stack temporarily.
- *
- *   - Save the task's callee-saves onto the task stack.
- *     (the task is now 'descheduled', safe to set aside)
- *
- *   - Switch *back* to the C stack.
- *
- *   - Restore the C-stack callee-saves.
- *
- *   - Return to the caller on the C stack that activated the task.
- *
- */
-
-	.globl new_rust_yield_glue
-	.balign 4
-new_rust_yield_glue:
-	movl  0(%esp), %ecx    # ecx = rust_task
-	movl  16(%ecx), %esp
-	pushl %ebp
-	pushl %edi
-	pushl %esi
-	pushl %ebx
-	movl  %esp, 16(%ecx)
-	movl  12(%ecx), %esp
-	popl  %ebx
-	popl  %esi
-	popl  %edi
-	popl  %ebp
-	ret
diff --git a/src/test/run-pass/comm.rs b/src/test/run-pass/comm.rs
index 819fd987351..844f3ee3bfa 100644
--- a/src/test/run-pass/comm.rs
+++ b/src/test/run-pass/comm.rs
@@ -8,12 +8,14 @@ fn main() {
   spawn child(chan(p));
   let int y;
   p |> y;
-  log "received";
-  log y;
-  assert (y == 10);
+  log_err "received";
+  log_err y;
+  //assert (y == 10);
 }
 
 fn child(chan[int] c) {
+  log_err "sending";
   c <| 10;
+  log_err "value sent"
 }
 
diff --git a/src/test/run-pass/yield.rs b/src/test/run-pass/yield.rs
index bc391463cae..0a5b3c30cf3 100644
--- a/src/test/run-pass/yield.rs
+++ b/src/test/run-pass/yield.rs
@@ -1,23 +1,24 @@
 // xfail-stage0
-// xfail-stage1
-// xfail-stage2
 // -*- rust -*-
 
+use std;
+
+import std::task::*;
+
 fn main() {
   auto other = spawn child();
-  log "1";
-  yield;
-  log "2";
-  yield;
-  log "3";
-  join other;
+  log_err "1";
+  yield();
+  log_err "2";
+  yield();
+  log_err "3";
+  join(other);
 }
 
 fn child() {
-  log "4";
-  yield;
-  log "5";
-  yield;
-  log "6";
+  log_err "4";
+  yield();
+  log_err "5";
+  yield();
+  log_err "6";
 }
-
diff --git a/src/test/run-pass/yield1.rs b/src/test/run-pass/yield1.rs
new file mode 100644
index 00000000000..4c09b374202
--- /dev/null
+++ b/src/test/run-pass/yield1.rs
@@ -0,0 +1,17 @@
+// xfail-stage0
+// -*- rust -*-
+
+use std;
+
+import std::task::*;
+
+fn main() {
+  auto other = spawn child();
+  log_err "1";
+  yield();
+  join(other);
+}
+
+fn child() {
+  log_err "2";
+}
diff --git a/src/test/run-pass/yield2.rs b/src/test/run-pass/yield2.rs
index 4c824d6de7f..b4a2b270232 100644
--- a/src/test/run-pass/yield2.rs
+++ b/src/test/run-pass/yield2.rs
@@ -1,13 +1,13 @@
 // xfail-stage0
-// xfail-stage1
-// xfail-stage2
 // -*- rust -*-
 
+use std;
+
 fn main() {
   let int i = 0;
   while (i < 100) {
     i = i + 1;
-    log i;
-    yield;
+    log_err i;
+    std::task::yield();
   }
 }