rust/src/rt/rust_upcall.cpp

454 lines
13 KiB
C++
Raw Normal View History

2011-08-19 13:03:46 -07:00
#include "rust_gc.h"
2010-06-23 21:03:09 -07:00
#include "rust_internal.h"
2011-06-15 22:04:31 -07:00
#include "rust_upcall.h"
2010-06-23 21:03:09 -07:00
// Upcalls.
2010-08-31 14:36:51 -07:00
extern "C" CDECL char const *
str_buf(rust_task *task, rust_str *s);
2010-06-23 21:03:09 -07:00
#ifdef __i386__
void
check_stack(rust_task *task) {
void *esp;
asm volatile("movl %%esp,%0" : "=r" (esp));
if (esp < task->stk->data)
task->kernel->fatal("Out of stack space, sorry");
}
#else
#warning "Stack checks are not supported on this architecture"
void
check_stack(rust_task *task) {
// TODO
}
#endif
2010-08-31 14:36:51 -07:00
extern "C" void
upcall_grow_task(rust_task *task, size_t n_frame_bytes) {
I(task->sched, false);
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
task->grow(n_frame_bytes);
}
2010-08-31 14:36:51 -07:00
extern "C" CDECL
void upcall_log_int(rust_task *task, uint32_t level, int32_t i) {
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
if (task->sched->log_lvl >= level)
task->sched->log(task, level, "rust: %" PRId32 " (0x%" PRIx32 ")",
i, i);
2010-06-23 21:03:09 -07:00
}
extern "C" CDECL
void upcall_log_float(rust_task *task, uint32_t level, float f) {
LOG_UPCALL_ENTRY(task);
if (task->sched->log_lvl >= level)
task->sched->log(task, level, "rust: %12.12f", f);
}
extern "C" CDECL
void upcall_log_double(rust_task *task, uint32_t level, double *f) {
LOG_UPCALL_ENTRY(task);
if (task->sched->log_lvl >= level)
task->sched->log(task, level, "rust: %12.12f", *f);
}
extern "C" CDECL void
upcall_log_str(rust_task *task, uint32_t level, rust_str *str) {
LOG_UPCALL_ENTRY(task);
if (task->sched->log_lvl >= level) {
const char *c = str_buf(task, str);
task->sched->log(task, level, "rust: %s", c);
}
2010-06-23 21:03:09 -07:00
}
extern "C" CDECL void
upcall_log_istr(rust_task *task, uint32_t level, rust_ivec *str) {
LOG_UPCALL_ENTRY(task);
if (task->sched->log_lvl < level)
return;
const char *buf = (const char *)
(str->fill ? str->payload.data : str->payload.ptr->data);
task->sched->log(task, level, "rust: %s", buf);
}
2010-08-31 14:36:51 -07:00
extern "C" CDECL void
upcall_yield(rust_task *task) {
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
LOG(task, comm, "upcall yield()");
2010-06-23 21:03:09 -07:00
task->yield(1);
}
2010-08-31 14:36:51 -07:00
extern "C" CDECL void
upcall_sleep(rust_task *task, size_t time_in_us) {
LOG_UPCALL_ENTRY(task);
LOG(task, task, "elapsed %" PRIu64 " us",
task->yield_timer.elapsed_us());
LOG(task, task, "sleep %d us", time_in_us);
task->yield(time_in_us);
}
2010-08-31 14:36:51 -07:00
extern "C" CDECL void
upcall_fail(rust_task *task,
char const *expr,
char const *file,
size_t line) {
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
LOG_ERR(task, upcall, "upcall fail '%s', %s:%" PRIdPTR, expr, file, line);
task->die();
task->fail();
task->notify_tasks_waiting_to_join();
task->yield(4);
2010-06-23 21:03:09 -07:00
}
/**
* Called whenever a task's ref count drops to zero.
*/
2010-06-23 21:03:09 -07:00
extern "C" CDECL void
upcall_kill(rust_task *task, rust_task_id tid) {
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
rust_task *target = task->kernel->get_task_by_id(tid);
2011-07-27 14:51:25 -07:00
target->kill();
target->deref();
2010-06-23 21:03:09 -07:00
}
/**
* Called by the exit glue when the task terminates.
*/
2010-06-23 21:03:09 -07:00
extern "C" CDECL void
upcall_exit(rust_task *task) {
LOG_UPCALL_ENTRY(task);
task->die();
task->notify_tasks_waiting_to_join();
2010-06-23 21:03:09 -07:00
task->yield(1);
}
extern "C" CDECL uintptr_t
2010-08-31 14:36:51 -07:00
upcall_malloc(rust_task *task, size_t nbytes, type_desc *td) {
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
LOG(task, mem,
"upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ")"
" with gc-chain head = 0x%" PRIxPTR,
nbytes, td, task->gc_alloc_chain);
2011-08-19 13:03:46 -07:00
gc::maybe_gc(task);
// TODO: Maybe use dladdr here to find a more useful name for the
// type_desc.
void *p = task->malloc(nbytes, "tdesc", td);
LOG(task, mem,
"upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR
") = 0x%" PRIxPTR
" with gc-chain head = 0x%" PRIxPTR,
nbytes, td, (uintptr_t)p, task->gc_alloc_chain);
2010-06-23 21:03:09 -07:00
return (uintptr_t) p;
}
/**
* Called whenever an object's ref count drops to zero.
*/
2010-06-23 21:03:09 -07:00
extern "C" CDECL void
2010-08-31 14:36:51 -07:00
upcall_free(rust_task *task, void* ptr, uintptr_t is_gc) {
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
rust_scheduler *sched = task->sched;
DLOG(sched, mem,
"upcall free(0x%" PRIxPTR ", is_gc=%" PRIdPTR ")",
(uintptr_t)ptr, is_gc);
task->free(ptr, (bool) is_gc);
}
extern "C" CDECL uintptr_t
upcall_shared_malloc(rust_task *task, size_t nbytes, type_desc *td) {
LOG_UPCALL_ENTRY(task);
LOG(task, mem,
"upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR ")",
nbytes, td);
void *p = task->kernel->malloc(nbytes, "shared malloc");
LOG(task, mem,
"upcall shared_malloc(%" PRIdPTR ", 0x%" PRIxPTR
") = 0x%" PRIxPTR,
nbytes, td, (uintptr_t)p);
return (uintptr_t) p;
}
/**
* Called whenever an object's ref count drops to zero.
*/
extern "C" CDECL void
upcall_shared_free(rust_task *task, void* ptr) {
LOG_UPCALL_ENTRY(task);
rust_scheduler *sched = task->sched;
DLOG(sched, mem,
"upcall shared_free(0x%" PRIxPTR")",
(uintptr_t)ptr);
task->kernel->free(ptr);
}
rust_str *make_str(rust_task *task, char const *s, size_t fill) {
2010-06-23 21:03:09 -07:00
size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
void *mem = task->malloc(alloc, "rust_str (make_str)");
2010-06-23 21:03:09 -07:00
if (!mem) {
task->fail();
2010-06-23 21:03:09 -07:00
return NULL;
}
rust_str *st = new (mem) rust_str(alloc, fill,
(uint8_t const *) s);
LOG(task, mem,
"upcall new_str('%s', %" PRIdPTR ") = 0x%" PRIxPTR,
s, fill, st);
2010-06-23 21:03:09 -07:00
return st;
}
extern "C" CDECL rust_str *
upcall_new_str(rust_task *task, char const *s, size_t fill) {
LOG_UPCALL_ENTRY(task);
return make_str(task, s, fill);
}
2011-08-18 14:43:17 -07:00
static rust_evec *
2011-06-15 18:03:15 -07:00
vec_grow(rust_task *task,
2011-08-18 14:43:17 -07:00
rust_evec *v,
2011-06-15 18:03:15 -07:00
size_t n_bytes,
uintptr_t *need_copy,
type_desc *td)
{
rust_scheduler *sched = task->sched;
LOG(task, mem,
2011-06-15 18:03:15 -07:00
"vec_grow(0x%" PRIxPTR ", %" PRIdPTR
2011-04-28 13:14:25 -07:00
"), rc=%" PRIdPTR " alloc=%" PRIdPTR ", fill=%" PRIdPTR
", need_copy=0x%" PRIxPTR,
v, n_bytes, v->ref_count, v->alloc, v->fill, need_copy);
*need_copy = 0;
2011-08-18 14:43:17 -07:00
size_t alloc = next_power_of_two(sizeof(rust_evec) + v->fill + n_bytes);
if (v->ref_count == 1) {
2010-06-23 21:03:09 -07:00
// Fastest path: already large enough.
if (v->alloc >= alloc) {
LOG(task, mem, "no-growth path");
2010-06-23 21:03:09 -07:00
return v;
}
// Second-fastest path: can at least realloc.
LOG(task, mem, "realloc path");
2011-08-18 14:43:17 -07:00
v = (rust_evec*) task->realloc(v, alloc, td->is_stateful);
2010-06-23 21:03:09 -07:00
if (!v) {
task->fail();
2010-06-23 21:03:09 -07:00
return NULL;
}
v->alloc = alloc;
} else {
/**
* Slowest path: make a new vec.
*
2011-08-18 14:43:17 -07:00
* 1. Allocate a new rust_evec with desired additional space.
* 2. Down-ref the shared rust_evec, point to the new one instead.
* 3. Copy existing elements into the new rust_evec.
*
* Step 3 is a bit tricky. We don't know how to properly copy the
* elements in the runtime (all we have are bits in a buffer; no
* type information and no copy glue). What we do instead is set the
* need_copy outparam flag to indicate to our caller (vec-copy glue)
* that we need the copies performed for us.
*/
LOG(task, mem, "new vec path");
2011-08-18 14:43:17 -07:00
void *mem = task->malloc(alloc, "rust_evec (vec_grow)", td);
2010-06-23 21:03:09 -07:00
if (!mem) {
task->fail();
2010-06-23 21:03:09 -07:00
return NULL;
}
if (v->ref_count != CONST_REFCOUNT)
v->deref();
2011-08-18 14:43:17 -07:00
v = new (mem) rust_evec(alloc, 0, NULL);
*need_copy = 1;
2010-06-23 21:03:09 -07:00
}
2011-08-18 14:43:17 -07:00
I(sched, sizeof(rust_evec) + v->fill <= v->alloc);
2010-06-23 21:03:09 -07:00
return v;
}
// Copy elements from one vector to another,
// dealing with reference counts
static inline void
copy_elements(rust_task *task, type_desc *elem_t,
void *pdst, void *psrc, size_t n)
{
char *dst = (char *)pdst, *src = (char *)psrc;
memmove(dst, src, n);
// increment the refcount of each element of the vector
2011-08-19 10:24:13 +02:00
if (elem_t->take_glue) {
glue_fn *take_glue = elem_t->take_glue;
size_t elem_size = elem_t->size;
const type_desc **tydescs = elem_t->first_param;
for (char *p = dst; p < dst+n; p += elem_size) {
2011-08-19 10:24:13 +02:00
take_glue(NULL, task, NULL, tydescs, p);
}
}
}
extern "C" CDECL void
upcall_evec_append(rust_task *task, type_desc *t, type_desc *elem_t,
2011-08-18 14:43:17 -07:00
rust_evec **dst_ptr, rust_evec *src, bool skip_null)
{
LOG_UPCALL_ENTRY(task);
2011-08-18 14:43:17 -07:00
rust_evec *dst = *dst_ptr;
uintptr_t need_copy;
size_t n_src_bytes = skip_null ? src->fill - 1 : src->fill;
size_t n_dst_bytes = skip_null ? dst->fill - 1 : dst->fill;
2011-08-18 14:43:17 -07:00
rust_evec *new_vec = vec_grow(task, dst, n_src_bytes, &need_copy, t);
// If src and dst are the same (due to "v += v"), then dst getting
// resized causes src to move as well.
if (dst == src && !need_copy) {
src = new_vec;
}
if (need_copy) {
// Copy any dst elements in, omitting null if doing str.
copy_elements(task, elem_t, &new_vec->data, &dst->data, n_dst_bytes);
}
// Copy any src elements in, carrying along null if doing str.
void *new_end = (void *)((char *)new_vec->data + n_dst_bytes);
copy_elements(task, elem_t, new_end, &src->data, src->fill);
new_vec->fill = n_dst_bytes + src->fill;
// Write new_vec back through the alias we were given.
*dst_ptr = new_vec;
}
// FIXME: Transitional. Please remove.
extern "C" CDECL void
upcall_vec_append(rust_task *task, type_desc *t, type_desc *elem_t,
2011-08-18 14:43:17 -07:00
rust_evec **dst_ptr, rust_evec *src, bool skip_null) {
upcall_evec_append(task, t, elem_t, dst_ptr, src, skip_null);
}
2010-06-23 21:03:09 -07:00
extern "C" CDECL type_desc *
upcall_get_type_desc(rust_task *task,
void *curr_crate, // ignored, legacy compat.
size_t size,
size_t align,
size_t n_descs,
type_desc const **descs,
uintptr_t n_obj_params) {
check_stack(task);
2010-06-23 21:03:09 -07:00
LOG_UPCALL_ENTRY(task);
LOG(task, cache, "upcall get_type_desc with size=%" PRIdPTR
", align=%" PRIdPTR ", %" PRIdPTR " descs", size, align,
n_descs);
rust_crate_cache *cache = task->get_crate_cache();
type_desc *td = cache->get_type_desc(size, align, n_descs, descs,
n_obj_params);
LOG(task, cache, "returning tydesc 0x%" PRIxPTR, td);
2010-06-23 21:03:09 -07:00
return td;
}
/**
* Resizes an interior vector that has been spilled to the heap.
*/
extern "C" CDECL void
upcall_ivec_resize_shared(rust_task *task,
rust_ivec *v,
size_t newsz) {
LOG_UPCALL_ENTRY(task);
scoped_lock with(task->sched->lock);
I(task->sched, !v->fill);
size_t new_alloc = next_power_of_two(newsz);
rust_ivec_heap *new_heap_part = (rust_ivec_heap *)
task->kernel->realloc(v->payload.ptr, new_alloc + sizeof(size_t));
new_heap_part->fill = newsz;
v->alloc = new_alloc;
v->payload.ptr = new_heap_part;
}
/**
* Spills an interior vector to the heap.
*/
extern "C" CDECL void
upcall_ivec_spill_shared(rust_task *task,
rust_ivec *v,
size_t newsz) {
LOG_UPCALL_ENTRY(task);
scoped_lock with(task->sched->lock);
size_t new_alloc = next_power_of_two(newsz);
rust_ivec_heap *heap_part = (rust_ivec_heap *)
task->kernel->malloc(new_alloc + sizeof(size_t),
"ivec spill shared");
heap_part->fill = newsz;
memcpy(&heap_part->data, v->payload.data, v->fill);
v->fill = 0;
v->alloc = new_alloc;
v->payload.ptr = heap_part;
}
extern "C" CDECL void
upcall_ivec_push(rust_task* task, rust_ivec* v, type_desc* elt_ty, void* x) {
LOG_UPCALL_ENTRY(task);
bool is_interior = v->fill || !v->payload.ptr;
size_t sz = elt_ty->size;
size_t old_fill = is_interior ? v->fill : v->payload.ptr->fill;
size_t new_sz = sz + old_fill;
if (new_sz > v->alloc) {
if (is_interior) {
upcall_ivec_spill_shared(task, v, new_sz);
is_interior = false;
} else {
upcall_ivec_resize_shared(task, v, new_sz);
}
} else {
if (is_interior) v->fill = new_sz;
else v->payload.ptr->fill = new_sz;
}
uint8_t* dataptr = is_interior ? &v->payload.data[0]
: &v->payload.ptr->data[0];
copy_elements(task, elt_ty, dataptr + old_fill, x, sz);
}
/**
* Returns a token that can be used to deallocate all of the allocated space
* space in the dynamic stack.
*/
extern "C" CDECL void *
upcall_dynastack_mark(rust_task *task) {
return task->dynastack.alloc(0);
}
/** Allocates space in the dynamic stack and returns it. */
extern "C" CDECL void *
upcall_dynastack_alloc(rust_task *task, size_t sz) {
return sz ? task->dynastack.alloc(sz) : NULL;
}
/** Frees space in the dynamic stack. */
extern "C" CDECL void
upcall_dynastack_free(rust_task *task, void *ptr) {
return task->dynastack.free(ptr);
}
2010-06-23 21:03:09 -07:00
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
2011-07-13 13:51:20 -07:00
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
2010-06-23 21:03:09 -07:00
// End:
//