about summary refs log tree commit diff
path: root/src/rt/rust_task.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/rt/rust_task.cpp')
-rw-r--r--src/rt/rust_task.cpp141
1 files changed, 114 insertions, 27 deletions
diff --git a/src/rt/rust_task.cpp b/src/rt/rust_task.cpp
index 8d7157ed77c..bed4e57b194 100644
--- a/src/rt/rust_task.cpp
+++ b/src/rt/rust_task.cpp
@@ -142,6 +142,18 @@ rust_task::start(uintptr_t exit_task_glue,
                  uintptr_t args,
                  size_t callsz)
 {
+    if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL)
+        start_rustboot(exit_task_glue, spawnee_fn, args, callsz);
+    else
+        start_rustc(exit_task_glue, spawnee_fn, args, callsz);
+}
+
+void
+rust_task::start_rustboot(uintptr_t exit_task_glue,
+                          uintptr_t spawnee_fn,
+                          uintptr_t args,
+                          size_t callsz)
+{
     LOGPTR(dom, "exit-task glue", exit_task_glue);
     LOGPTR(dom, "from spawnee", spawnee_fn);
 
@@ -176,30 +188,25 @@ rust_task::start(uintptr_t exit_task_glue,
 
     uintptr_t exit_task_frame_base = 0;
 
-    if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL) {
-        for (size_t j = 0; j < n_callee_saves; ++j) {
-
-            // We want 'frame_base' to point to the old fp in this (exit-task)
-            // frame, because we're going to inject this frame-pointer into
-            // the callee-save frame pointer value in the *next* (spawnee)
-            // frame. A cheap trick, but this means the spawnee frame will
-            // restore the proper frame pointer of the glue frame as it runs
-            // its epilogue.
-            if (j == callee_save_fp)
-                exit_task_frame_base = (uintptr_t)spp;
+    for (size_t j = 0; j < n_callee_saves; ++j) {
 
-            *spp-- = 0;
-        }
+        // We want 'frame_base' to point to the old fp in this (exit-task)
+        // frame, because we're going to inject this frame-pointer into
+        // the callee-save frame pointer value in the *next* (spawnee)
+        // frame. A cheap trick, but this means the spawnee frame will
+        // restore the proper frame pointer of the glue frame as it runs
+        // its epilogue.
+        if (j == callee_save_fp)
+            exit_task_frame_base = (uintptr_t)spp;
 
-        *spp-- = (uintptr_t) dom->root_crate;  // crate ptr
-        *spp-- = (uintptr_t) 0;                // frame_glue_fns
+        *spp-- = 0;
     }
 
+    *spp-- = (uintptr_t) dom->root_crate;  // crate ptr
+    *spp-- = (uintptr_t) 0;                // frame_glue_fns
+
     I(dom, args);
-    if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL)
-        make_aligned_room_for_bytes(spp, callsz - sizeof(uintptr_t));
-    else
-        make_aligned_room_for_bytes(spp, callsz - 3 * sizeof(uintptr_t));
+    make_aligned_room_for_bytes(spp, callsz - sizeof(uintptr_t));
 
     // Copy args from spawner to spawnee.
     uintptr_t *src = (uintptr_t *)args;
@@ -222,16 +229,96 @@ rust_task::start(uintptr_t exit_task_glue,
     // activating:
     *spp-- = (uintptr_t) 0x0;               // closure-or-obj
 
-    if (spawnee_abi == ABI_X86_RUSTBOOT_CDECL) {
-        // in CDECL mode we write the task + outptr to the spawnee stack.
-        *spp-- = (uintptr_t) this;            // task
-        *spp-- = (uintptr_t) 0;               // output addr
-    } else {
-        // in FASTCALL mode we don't, the outptr will be in ecx and the task
-        // in edx, and the activate_glue will make sure to set that up.
-        I(dom, spawnee_abi == ABI_X86_RUSTC_FASTCALL);
+    // in CDECL mode we write the task + outptr to the spawnee stack.
+    *spp-- = (uintptr_t) this;            // task
+    *spp-- = (uintptr_t) 0;               // output addr
+
+    I(dom, spp+1 == align_down(spp+1));
+    *spp-- = (uintptr_t) exit_task_glue;  // retpc
+
+    // The context the activate_glue needs to switch stack.
+    *spp-- = (uintptr_t) spawnee_fn;      // instruction to start at
+    for (size_t j = 0; j < n_callee_saves; ++j) {
+        // callee-saves to carry in when we activate
+        if (j == callee_save_fp)
+            *spp-- = exit_task_frame_base;
+        else
+            *spp-- = (uintptr_t)NULL;
     }
 
+    // Back up one, we overshot where sp should be.
+    rust_sp = (uintptr_t) (spp+1);
+
+    transition(&dom->newborn_tasks, &dom->running_tasks);
+}
+
+void
+rust_task::start_rustc(uintptr_t exit_task_glue,
+                          uintptr_t spawnee_fn,
+                          uintptr_t args,
+                          size_t callsz)
+{
+    LOGPTR(dom, "exit-task glue", exit_task_glue);
+    LOGPTR(dom, "from spawnee", spawnee_fn);
+
+    // Set sp to last uintptr_t-sized cell of segment
+    rust_sp -= 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"
+
+
+    // Begin synthesizing frames. There are two: a "fully formed"
+    // exit-task frame at the top of the stack -- that pretends to be
+    // mid-execution -- and a just-starting frame beneath it that
+    // starts executing the first instruction of the spawnee. The
+    // spawnee *thinks* it was called by the exit-task frame above
+    // it. It wasn't; we put that fake frame in place here, but the
+    // illusion is enough for the spawnee to return to the exit-task
+    // frame when it's done, and exit.
+    uintptr_t *spp = (uintptr_t *)rust_sp;
+
+
+    // The exit_task_glue frame we synthesize above the frame we activate:
+    make_aligned_room_for_bytes(spp, 2 * sizeof(uintptr_t));
+    *spp-- = (uintptr_t) 0;          // closure-or-obj
+    *spp-- = (uintptr_t) this;       // task
+    I(dom, spp == align_down(spp));
+    *spp-- = (uintptr_t) 0x0;        // output
+    *spp-- = (uintptr_t) 0x0;        // retpc
+
+    uintptr_t exit_task_frame_base = 0;
+
+    I(dom, args);
+    make_aligned_room_for_bytes(spp, callsz - 3 * sizeof(uintptr_t));
+
+    // 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
+
+    // Undo previous sp-- so we're pointing at the last word pushed.
+    ++spp;
+
+    // Memcpy all but the task, output and env pointers
+    callsz -= (3 * sizeof(uintptr_t));
+    spp = (uintptr_t*) (((uintptr_t)spp) - callsz);
+    memcpy(spp, src, callsz);
+
+    // Move sp down to point to last implicit-arg cell (env).
+    spp--;
+
+    // The *implicit* incoming args to the spawnee frame we're
+    // activating:
+    *spp-- = (uintptr_t) 0x0;               // closure-or-obj
+
+    // in FASTCALL mode we don't, the outptr will be in ecx and the task
+    // in edx, and the activate_glue will make sure to set that up.
+
     I(dom, spp+1 == align_down(spp+1));
     *spp-- = (uintptr_t) exit_task_glue;  // retpc