diff options
Diffstat (limited to 'src/rt/rust_task.cpp')
| -rw-r--r-- | src/rt/rust_task.cpp | 255 |
1 files changed, 58 insertions, 197 deletions
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; |
