rust/src/rt/rust_gc.cpp
2011-09-01 11:47:58 -07:00

198 lines
4.4 KiB
C++

// Rust garbage collection.
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
#include <stdint.h>
#include "rust_abi.h"
#include "rust_gc.h"
#include "rust_internal.h"
#include "rust_shape.h"
#ifdef __WIN32__
#include <windows.h>
#else
#include <dlfcn.h>
#endif
#define END_OF_STACK_RA (void (*)())0xdeadbeef
namespace gc {
weak_symbol<const uintptr_t> safe_point_data("rust_gc_safe_points");
struct frame {
uint8_t *bp; // The frame pointer.
void (*ra)(); // The return address.
frame(void *in_bp, void (*in_ra)()) : bp((uint8_t *)in_bp), ra(in_ra) {}
inline void next() {
ra = *(void (**)())(bp + sizeof(void *));
bp = *(uint8_t **)bp;
}
};
struct root_info {
intptr_t frame_offset;
uintptr_t dynamic; // 0 = static, 1 = dynamic
type_desc *tydesc;
};
struct root {
type_desc *tydesc;
uint8_t *data;
root(const root_info &info, const frame &frame)
: tydesc(info.tydesc),
data((uint8_t *)frame.bp + info.frame_offset) {}
};
struct safe_point {
uintptr_t n_roots;
root_info roots[0];
};
struct safe_point_index_entry {
void (*ra)(); // The return address.
const struct safe_point *safe_point; // The safe point.
struct cmp {
bool operator()(const safe_point_index_entry &entry, void (*ra)())
const {
return entry.ra < ra;
}
bool operator()(void (*ra)(), const safe_point_index_entry &entry)
const {
return ra < entry.ra;
}
};
};
class safe_point_map {
uintptr_t n_safe_points;
const safe_point_index_entry *index;
const safe_point *safe_points;
public:
safe_point_map() {
const uintptr_t *data = *safe_point_data;
n_safe_points = *data++;
index = (const safe_point_index_entry *)data;
data += n_safe_points * 2;
safe_points = (const safe_point *)data;
}
const safe_point *get_safe_point(void (*addr)());
};
class gc {
private:
rust_task *task;
void mark(std::vector<root> &roots);
void sweep();
public:
gc(rust_task *in_task) : task(in_task) {}
void run();
std::vector<frame> backtrace();
};
const safe_point *
safe_point_map::get_safe_point(void (*addr)()) {
safe_point_index_entry::cmp cmp;
const safe_point_index_entry *entry =
std::lower_bound(index, index + n_safe_points, addr, cmp);
return (entry && entry->ra == addr) ? entry->safe_point : NULL;
}
void
gc::mark(std::vector<root> &roots) {
std::vector<root>::iterator ri = roots.begin(), rend = roots.end();
while (ri < rend) {
DPRINT("root: %p\n", ri->data);
shape::arena arena;
shape::type_param *params = shape::type_param::from_tydesc(ri->tydesc,
arena);
shape::log log(task, true, ri->tydesc->shape, params,
ri->tydesc->shape_tables, ri->data, std::cerr);
log.walk();
DPRINT("\n");
++ri;
}
// TODO
}
void
gc::sweep() {
// TODO
}
std::vector<frame>
gc::backtrace() {
std::vector<frame> frames;
// Ideally we would use the current value of EIP here, but there's no
// portable way to get that and there are never any GC roots in our C++
// frames anyhow.
frame f(__builtin_frame_address(0), (void (*)())NULL);
while (f.ra != END_OF_STACK_RA) {
frames.push_back(f);
f.next();
}
return frames;
}
void
gc::run() {
safe_point_map map;
// Find roots.
std::vector<root> roots;
std::vector<frame> call_stack = backtrace();
for (unsigned i = 0; i < call_stack.size(); i++) {
frame f = call_stack[i];
const safe_point *sp = map.get_safe_point(f.ra);
if (!sp)
continue;
DPRINT("%u: ra %p, ebp %p\n", i, call_stack[i].ra, call_stack[i].bp);
for (unsigned j = 0; j < sp->n_roots; j++) {
root r(sp->roots[j], f);
roots.push_back(r);
}
}
// Mark and sweep.
mark(roots);
sweep();
}
void
maybe_gc(rust_task *task) {
if (*safe_point_data == NULL)
return;
// FIXME: We ought to lock this.
static int zeal = -1;
if (zeal == -1) {
char *ev = getenv("RUST_GC_ZEAL");
zeal = ev && ev[0] != '\0' && ev[0] != '0';
}
if (zeal) {
gc gc(task);
gc.run();
}
}
}