Skip to content

Commit

Permalink
Work out the kinks in the fiber stack setup
Browse files Browse the repository at this point in the history
  • Loading branch information
evanphx committed Jan 10, 2012
1 parent 5c86a26 commit 75dce8b
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 120 deletions.
115 changes: 14 additions & 101 deletions vm/builtin/fiber.cpp
Expand Up @@ -20,85 +20,6 @@

#include "on_stack.hpp"

namespace rubinius {

#ifdef FIBER_ENABLED

#if defined(FIBER_ASM_X8664)

static void fiber_wrap_main(void) {
__asm__ __volatile__ ("\tmovq %r13, %rdi\n\tjmpq *%r12\n");
}

static inline void fiber_switch(fiber_context_t* from, fiber_context_t* to) {
__asm__ __volatile__ (
"leaq 1f(%%rip), %%rax\n\t"
"movq %%rax, (%0)\n\t" "movq %%rsp, 8(%0)\n\t" "movq %%rbp, 16(%0)\n\t"
"movq %%rbx, 24(%0)\n\t" "movq %%r12, 32(%0)\n\t" "movq %%r13, 40(%0)\n\t"
"movq %%r14, 48(%0)\n\t" "movq %%r15, 56(%0)\n\t"
"movq 56(%1), %%r15\n\t" "movq 48(%1), %%r14\n\t" "movq 40(%1), %%r13\n\t"
"movq 32(%1), %%r12\n\t" "movq 24(%1), %%rbx\n\t" "movq 16(%1), %%rbp\n\t"
"movq 8(%1), %%rsp\n\t" "jmpq *(%1)\n" "1:\n"
: "+S" (from), "+D" (to) :
: "rax", "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
}

static void fiber_makectx(fiber_context_t* ctx, void* func, void** stack_bottom,
int stack_size)
{
// Get a pointer to the highest address as stack that is properly aligned
// with room for the fake return value.
uintptr_t s = ((uintptr_t)stack_bottom) + stack_size;
uintptr_t diff = s % 16;

void** stack = (void**)(s - diff) - 1;

*--stack = (void*)0xdeadcafedeadcafe; /* Dummy return address. */
ctx->rip = (void*)fiber_wrap_main;
ctx->rsp = stack;
ctx->rbp = 0;
ctx->rbx = 0;
ctx->r12 = func;
ctx->r13 = 0;
ctx->r14 = 0;
ctx->r15 = 0;

}

#elif defined(FIBER_ASM_X8632)

static inline void fiber_switch(fiber_context_t* from, fiber_context_t* to) {
__asm__ __volatile__ (
"call 1f\n" "1:\tpopl %%eax\n\t" "addl $(2f-1b),%%eax\n\t"
"movl %%eax, (%0)\n\t" "movl %%esp, 4(%0)\n\t"
"movl %%ebp, 8(%0)\n\t" "movl %%ebx, 12(%0)\n\t"
"movl 12(%1), %%ebx\n\t" "movl 8(%1), %%ebp\n\t"
"movl 4(%1), %%esp\n\t" "jmp *(%1)\n" "2:\n"
: "+S" (from), "+D" (to) : : "eax", "ecx", "edx", "memory", "cc");
}

static void fiber_makectx(fiber_context_t* ctx, void* func, void** stack_bottom,
int stack_size)
{
// Get a pointer to the highest address as stack that is properly aligned
// with room for the fake return value.
uintptr_t s = ((uintptr_t)stack_bottom) + stack_size;
uintptr_t diff = s % 16;

void** stack = (void**)(s - diff) - 1;

*--stack = (void*)0xdeadcafe;

ctx->eip = func;
ctx->esp = stack;
ctx->ebp = 0;
}
#endif

#endif

}

namespace rubinius {

void Fiber::init(STATE) {
Expand Down Expand Up @@ -128,7 +49,7 @@ namespace rubinius {
fib->status_ = Fiber::eRunning;
fib->vm_ = state->vm();

fib->data_ = new FiberData;
fib->data_ = new FiberData(true);

state->memory()->needs_finalization(fib, (FinalizerFunction)&Fiber::finalize);

Expand Down Expand Up @@ -178,14 +99,11 @@ namespace rubinius {
}
}

fib->data_->orphan(&state);

dest->run();
dest->value(&state, result);
state.vm()->set_current_fiber(dest);

fiber_context_t dummy;
fiber_switch(&dummy, dest->ucontext());
dest->data_->switch_and_orphan(&state, fib->data_);

assert(0 && "fatal start_on_stack error");
#else
Expand Down Expand Up @@ -238,24 +156,12 @@ namespace rubinius {
Fiber* cur = Fiber::current(state);
prev(state, cur);

if(data_->uninitialized_p()) {
FiberStack* stack = data_->allocate_stack(state);

data_->take_stack(state);

fiber_makectx(ucontext(), (void*)start_on_stack,
(void**)stack->address(),
stack->size());
} else {
data_->take_stack(state);
}

cur->sleep(calling_environment);

run();
state->vm()->set_current_fiber(this);

fiber_switch(cur->ucontext(), ucontext());
data_->switch_to(state, cur->data_);

// Back here when someone yields back to us!
// Beware here, because the GC has probably run so GC pointers on the C++ stack
Expand Down Expand Up @@ -303,7 +209,7 @@ namespace rubinius {
run();
state->vm()->set_current_fiber(this);

fiber_switch(cur->ucontext(), ucontext());
data_->switch_to(state, cur->data_);

// Back here when someone transfers back to us!
// Beware here, because the GC has probably run so GC pointers on the C++ stack
Expand Down Expand Up @@ -352,7 +258,7 @@ namespace rubinius {
dest_fib->run();
state->vm()->set_current_fiber(dest_fib);

fiber_switch(cur->ucontext(), dest_fib->ucontext());
dest_fib->data_->switch_to(state, cur->data_);

// Back here when someone yields back to us!
// Beware here, because the GC has probably run so GC pointers on the C++ stack
Expand All @@ -377,8 +283,8 @@ namespace rubinius {

void Fiber::finalize(STATE, Fiber* fib) {
#ifdef FIBER_ENABLED
fib->data_->orphan(state);
delete fib->data_;
// if(fib->stack_ && !fib->root_) free(fib->stack_);
#endif
}

Expand All @@ -388,9 +294,16 @@ namespace rubinius {
mark.remember_object(obj);

Fiber* fib = (Fiber*)obj;

AddressDisplacement dis(fib->data_->data_offset(),
fib->data_->data_lower_bound(),
fib->data_->data_upper_bound());

if(CallFrame* cf = fib->call_frame()) {
mark.gc->walk_call_frame(cf, fib->data_->data_offset());
mark.gc->walk_call_frame(cf, &dis);
}

mark.gc->scan(fib->data_->variable_root_buffers(), false, &dis);
}
}

15 changes: 14 additions & 1 deletion vm/builtin/fiber.hpp
Expand Up @@ -31,11 +31,16 @@ namespace rubinius {
bool root_;

FiberData* data_;

public:
attr_accessor(starter, Object);
attr_accessor(value, Array);
attr_accessor(prev, Fiber);
attr_accessor(exception, Exception)
attr_accessor(exception, Exception);

bool root_p() {
return root_;
}

CallFrame* call_frame() {
return top_;
Expand All @@ -59,6 +64,10 @@ namespace rubinius {
return vm_;
}

FiberData* data() {
return data_;
}

void* stack() {
return data_->stack_address();
}
Expand All @@ -67,6 +76,10 @@ namespace rubinius {
return data_->stack_size();
}

VariableRootBuffers& variable_root_buffers() {
return data_->variable_root_buffers();
}

public:
static void init(STATE);

Expand Down
151 changes: 151 additions & 0 deletions vm/fiber_data.cpp
@@ -1,5 +1,96 @@
#include "vm/vm.hpp"
#include "vm/fiber_data.hpp"
#include "builtin/fiber.hpp"

namespace rubinius {

#ifdef FIBER_ENABLED

#if defined(FIBER_ASM_X8664)

static void fiber_wrap_main(void) {
__asm__ __volatile__ ("\tmovq %r13, %rdi\n\tjmpq *%r12\n");
}

static inline void fiber_switch(fiber_context_t* from, fiber_context_t* to) {
__asm__ __volatile__ (
"leaq 1f(%%rip), %%rax\n\t"
"movq %%rax, (%0)\n\t"
"movq %%rsp, 8(%0)\n\t"
"movq %%rbp, 16(%0)\n\t"
"movq %%rbx, 24(%0)\n\t"
"movq %%r12, 32(%0)\n\t"
"movq %%r13, 40(%0)\n\t"
"movq %%r14, 48(%0)\n\t"
"movq %%r15, 56(%0)\n\t"
"movq 56(%1), %%r15\n\t"
"movq 48(%1), %%r14\n\t"
"movq 40(%1), %%r13\n\t"
"movq 32(%1), %%r12\n\t"
"movq 24(%1), %%rbx\n\t"
"movq 16(%1), %%rbp\n\t"
"movq 8(%1), %%rsp\n\t"
"jmpq *(%1)\n" "1:\n"

: "+S" (from), "+D" (to) :
: "rax", "rcx", "rdx", "r8", "r9", "r10", "r11", "memory", "cc");
}

static void fiber_makectx(fiber_context_t* ctx, void* func, void** stack_bottom,
int stack_size)
{
// Get a pointer to the highest address as stack that is properly aligned
// with room for the fake return value.
uintptr_t s = ((uintptr_t)stack_bottom) + stack_size;
uintptr_t diff = s % 16;

void** stack = (void**)(s - diff) - 1;

*--stack = (void*)0xdeadcafedeadcafe; /* Dummy return address. */
ctx->rip = (void*)fiber_wrap_main;
ctx->rsp = stack;
ctx->rbp = 0;
ctx->rbx = 0;
ctx->r12 = func;
ctx->r13 = 0;
ctx->r14 = 0;
ctx->r15 = 0;

}

#elif defined(FIBER_ASM_X8632)

static inline void fiber_switch(fiber_context_t* from, fiber_context_t* to) {
__asm__ __volatile__ (
"call 1f\n" "1:\tpopl %%eax\n\t" "addl $(2f-1b),%%eax\n\t"
"movl %%eax, (%0)\n\t" "movl %%esp, 4(%0)\n\t"
"movl %%ebp, 8(%0)\n\t" "movl %%ebx, 12(%0)\n\t"
"movl 12(%1), %%ebx\n\t" "movl 8(%1), %%ebp\n\t"
"movl 4(%1), %%esp\n\t" "jmp *(%1)\n" "2:\n"
: "+S" (from), "+D" (to) : : "eax", "ecx", "edx", "memory", "cc");
}

static void fiber_makectx(fiber_context_t* ctx, void* func, void** stack_bottom,
int stack_size)
{
// Get a pointer to the highest address as stack that is properly aligned
// with room for the fake return value.
uintptr_t s = ((uintptr_t)stack_bottom) + stack_size;
uintptr_t diff = s % 16;

void** stack = (void**)(s - diff) - 1;

*--stack = (void*)0xdeadcafe;

ctx->eip = func;
ctx->esp = stack;
ctx->ebp = 0;
}
#endif

#endif

}

namespace rubinius {
void FiberData::take_stack(STATE) {
Expand All @@ -20,6 +111,9 @@ namespace rubinius {
}

void FiberData::copy_to_heap(STATE) {
assert(status_ != eDead);
assert(stack_);

heap_size_ = (uintptr_t)stack_->top_address() - (uintptr_t)stack_bottom();
if(heap_capacity_ < heap_size_) {
// Round to nearest 1k
Expand All @@ -42,7 +136,64 @@ namespace rubinius {

void FiberData::orphan(STATE) {
status_ = eDead;
if(!stack_) return;

stack_->orphan(state, this);
stack_ = 0;
}

static void save_and_switch() {
VM* vm = VM::current();
State state(vm);

Fiber* fib = Fiber::current(&state);

fib->data()->take_stack(&state);

fiber_context_t tmp;
fiber_switch(&tmp, fib->ucontext());
}

void FiberData::switch_to(STATE, FiberData* from) {
if(uninitialized_p()) {
FiberStack* stack = allocate_stack(state);

fiber_makectx(machine(), (void*)Fiber::start_on_stack,
(void**)stack->address(),
stack->size());
}

if(currently_on_stack_p(from)) {
fiber_context_t dest;

fiber_makectx(&dest, (void*)save_and_switch,
(void**)state->vm()->fiber_trampoline(),
4096);

fiber_switch(from->machine(), &dest);
} else {
take_stack(state);
fiber_switch(from->machine(), machine());
}
}

void FiberData::switch_and_orphan(STATE, FiberData* from) {
fiber_context_t dummy;

if(currently_on_stack_p(from)) {
from->orphan(state);

fiber_context_t dest;

fiber_makectx(&dest, (void*)save_and_switch,
(void**)state->vm()->fiber_trampoline(),
4096);

fiber_switch(&dummy, &dest);
} else {
from->orphan(state);
take_stack(state);
fiber_switch(&dummy, machine());
}
}
}

0 comments on commit 75dce8b

Please sign in to comment.