Files
rust/src/rt/rust_upcall.cpp
T
Marijn Haverbeke c9c5ee252a Implement non-internal ivecs
Vectors are now similar to our old, pre-internal vectors, except that
they are uniquely owned, not refcounted.

Their name should probably change too, then. I've renamed them to vec
in the runtime, will do so throughout the compiler later.
2011-08-29 09:07:53 +02:00

408 lines
12 KiB
C++

#include "rust_gc.h"
#include "rust_internal.h"
#include "rust_upcall.h"
// Upcalls.
extern "C" CDECL char const *
str_buf(rust_task *task, rust_str *s);
#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
extern "C" void
upcall_grow_task(rust_task *task, size_t n_frame_bytes) {
I(task->sched, false);
LOG_UPCALL_ENTRY(task);
task->grow(n_frame_bytes);
}
extern "C" CDECL
void upcall_log_int(rust_task *task, uint32_t level, int32_t i) {
LOG_UPCALL_ENTRY(task);
if (task->sched->log_lvl >= level)
task->sched->log(task, level, "rust: %" PRId32 " (0x%" PRIx32 ")",
i, i);
}
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);
}
}
extern "C" CDECL void
upcall_yield(rust_task *task) {
LOG_UPCALL_ENTRY(task);
LOG(task, comm, "upcall yield()");
task->yield(1);
}
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);
}
extern "C" CDECL void
upcall_fail(rust_task *task,
char const *expr,
char const *file,
size_t line) {
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);
}
/**
* Called whenever a task's ref count drops to zero.
*/
extern "C" CDECL void
upcall_kill(rust_task *task, rust_task_id tid) {
LOG_UPCALL_ENTRY(task);
rust_task *target = task->kernel->get_task_by_id(tid);
target->kill();
target->deref();
}
/**
* Called by the exit glue when the task terminates.
*/
extern "C" CDECL void
upcall_exit(rust_task *task) {
LOG_UPCALL_ENTRY(task);
task->die();
task->notify_tasks_waiting_to_join();
task->yield(1);
}
extern "C" CDECL uintptr_t
upcall_malloc(rust_task *task, size_t nbytes, type_desc *td) {
LOG_UPCALL_ENTRY(task);
LOG(task, mem,
"upcall malloc(%" PRIdPTR ", 0x%" PRIxPTR ")"
" with gc-chain head = 0x%" PRIxPTR,
nbytes, td, task->gc_alloc_chain);
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);
return (uintptr_t) p;
}
/**
* Called whenever an object's ref count drops to zero.
*/
extern "C" CDECL void
upcall_free(rust_task *task, void* ptr, uintptr_t is_gc) {
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) {
size_t alloc = next_power_of_two(sizeof(rust_str) + fill);
void *mem = task->malloc(alloc, "rust_str (make_str)");
if (!mem) {
task->fail();
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);
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);
}
static rust_evec *
vec_grow(rust_task *task,
rust_evec *v,
size_t n_bytes,
uintptr_t *need_copy,
type_desc *td)
{
rust_scheduler *sched = task->sched;
LOG(task, mem,
"vec_grow(0x%" PRIxPTR ", %" PRIdPTR
"), 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;
size_t alloc = next_power_of_two(sizeof(rust_evec) + v->fill + n_bytes);
if (v->ref_count == 1) {
// Fastest path: already large enough.
if (v->alloc >= alloc) {
LOG(task, mem, "no-growth path");
return v;
}
// Second-fastest path: can at least realloc.
LOG(task, mem, "realloc path");
v = (rust_evec*) task->realloc(v, alloc, td->is_stateful);
if (!v) {
task->fail();
return NULL;
}
v->alloc = alloc;
} else {
/**
* Slowest path: make a new vec.
*
* 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");
void *mem = task->malloc(alloc, "rust_evec (vec_grow)", td);
if (!mem) {
task->fail();
return NULL;
}
if (v->ref_count != CONST_REFCOUNT)
v->deref();
v = new (mem) rust_evec(alloc, 0, NULL);
*need_copy = 1;
}
I(sched, sizeof(rust_evec) + v->fill <= v->alloc);
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
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) {
take_glue(NULL, task, NULL, tydescs, p);
}
}
}
extern "C" CDECL void
upcall_evec_append(rust_task *task, type_desc *t, type_desc *elem_t,
rust_evec **dst_ptr, rust_evec *src, bool skip_null)
{
LOG_UPCALL_ENTRY(task);
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;
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,
rust_evec **dst_ptr, rust_evec *src, bool skip_null) {
upcall_evec_append(task, t, elem_t, dst_ptr, src, skip_null);
}
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);
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);
return td;
}
extern "C" CDECL void
upcall_vec_grow(rust_task* task, rust_vec** vp, size_t new_sz) {
LOG_UPCALL_ENTRY(task);
// FIXME factor this into a utility function
if (new_sz > (*vp)->alloc) {
size_t new_alloc = next_power_of_two(new_sz);
*vp = (rust_vec*)task->kernel->realloc(*vp, new_alloc +
sizeof(rust_vec));
(*vp)->alloc = new_alloc;
}
(*vp)->fill = new_sz;
}
extern "C" CDECL void
upcall_vec_push(rust_task* task, rust_vec** vp, type_desc* elt_ty,
void* elt) {
LOG_UPCALL_ENTRY(task);
rust_vec* v = *vp;
size_t new_sz = v->fill + elt_ty->size;
if (new_sz > v->alloc) {
size_t new_alloc = next_power_of_two(new_sz);
*vp = v = (rust_vec*)task->kernel->realloc(v, new_alloc +
sizeof(rust_vec));
v->alloc = new_alloc;
}
copy_elements(task, elt_ty, &v->data[0] + v->fill, elt, elt_ty->size);
v->fill += elt_ty->size;
}
/**
* 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);
}
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// compile-command: "make -k -C $RBUILD 2>&1 | sed -e 's/\\/x\\//x:\\//g'";
// End:
//