about summary refs log tree commit diff
path: root/src/rt/activate_glue.s
diff options
context:
space:
mode:
Diffstat (limited to 'src/rt/activate_glue.s')
-rw-r--r--src/rt/activate_glue.s89
1 files changed, 89 insertions, 0 deletions
diff --git a/src/rt/activate_glue.s b/src/rt/activate_glue.s
new file mode 100644
index 00000000000..5b55f568880
--- /dev/null
+++ b/src/rt/activate_glue.s
@@ -0,0 +1,89 @@
+/*
+ * 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