2012-07-16 14:28:15 -05:00
|
|
|
import stackwalk::Word;
|
|
|
|
import libc::size_t;
|
2012-08-17 18:17:18 -05:00
|
|
|
import send_map::linear::LinearMap;
|
2012-07-16 14:28:15 -05:00
|
|
|
|
2012-08-23 14:14:47 -05:00
|
|
|
export Word;
|
|
|
|
export gc;
|
|
|
|
export cleanup_stack_for_failure;
|
|
|
|
|
2012-07-16 14:28:15 -05:00
|
|
|
extern mod rustrt {
|
|
|
|
fn rust_annihilate_box(ptr: *Word);
|
|
|
|
|
|
|
|
#[rust_stack]
|
|
|
|
fn rust_gc_metadata() -> *Word;
|
|
|
|
|
|
|
|
#[rust_stack]
|
|
|
|
fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);
|
|
|
|
}
|
|
|
|
|
|
|
|
type SafePoint = { sp_meta: *Word, fn_meta: *Word };
|
|
|
|
|
|
|
|
unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
|
|
|
|
let module_meta = rustrt::rust_gc_metadata();
|
|
|
|
let num_safe_points_ptr: *u32 = unsafe::reinterpret_cast(&module_meta);
|
|
|
|
let num_safe_points = *num_safe_points_ptr as Word;
|
|
|
|
let safe_points: *Word =
|
|
|
|
ptr::offset(unsafe::reinterpret_cast(&module_meta), 1);
|
|
|
|
|
|
|
|
if ptr::is_null(pc) {
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut sp = 0 as Word;
|
|
|
|
while sp < num_safe_points {
|
|
|
|
let sp_loc = *ptr::offset(safe_points, sp*3) as *Word;
|
|
|
|
if sp_loc == pc {
|
|
|
|
return Some(
|
|
|
|
{sp_meta: *ptr::offset(safe_points, sp*3 + 1) as *Word,
|
|
|
|
fn_meta: *ptr::offset(safe_points, sp*3 + 2) as *Word});
|
|
|
|
}
|
|
|
|
sp += 1;
|
|
|
|
}
|
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2012-08-17 18:17:18 -05:00
|
|
|
type Visitor = fn(root: **Word, tydesc: *Word) -> bool;
|
2012-07-16 14:28:15 -05:00
|
|
|
|
|
|
|
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
|
|
|
|
let fp_bytes: *u8 = unsafe::reinterpret_cast(&fp);
|
|
|
|
let sp_meta_u32s: *u32 = unsafe::reinterpret_cast(&sp.sp_meta);
|
|
|
|
|
|
|
|
let num_stack_roots = *sp_meta_u32s as uint;
|
|
|
|
let num_reg_roots = *ptr::offset(sp_meta_u32s, 1) as uint;
|
|
|
|
|
|
|
|
let stack_roots: *u32 =
|
|
|
|
unsafe::reinterpret_cast(&ptr::offset(sp_meta_u32s, 2));
|
|
|
|
let reg_roots: *u8 =
|
|
|
|
unsafe::reinterpret_cast(&ptr::offset(stack_roots, num_stack_roots));
|
|
|
|
let addrspaces: *Word =
|
|
|
|
unsafe::reinterpret_cast(&ptr::offset(reg_roots, num_reg_roots));
|
|
|
|
let tydescs: ***Word =
|
|
|
|
unsafe::reinterpret_cast(&ptr::offset(addrspaces, num_stack_roots));
|
|
|
|
|
|
|
|
// Stack roots
|
|
|
|
let mut sri = 0;
|
|
|
|
while sri < num_stack_roots {
|
|
|
|
if *ptr::offset(addrspaces, sri) >= 1 {
|
|
|
|
let root =
|
|
|
|
ptr::offset(fp_bytes, *ptr::offset(stack_roots, sri) as Word)
|
|
|
|
as **Word;
|
|
|
|
let tydescpp = ptr::offset(tydescs, sri);
|
|
|
|
let tydesc = if ptr::is_not_null(tydescpp) &&
|
|
|
|
ptr::is_not_null(*tydescpp) {
|
|
|
|
**tydescpp
|
|
|
|
} else {
|
|
|
|
ptr::null()
|
|
|
|
};
|
2012-08-17 18:17:18 -05:00
|
|
|
if !visitor(root, tydesc) { return; }
|
2012-07-16 14:28:15 -05:00
|
|
|
}
|
|
|
|
sri += 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Register roots
|
|
|
|
let mut rri = 0;
|
|
|
|
while rri < num_reg_roots {
|
|
|
|
if *ptr::offset(addrspaces, num_stack_roots + rri) == 1 {
|
|
|
|
// FIXME(#2997): Need to find callee saved registers on the stack.
|
|
|
|
}
|
|
|
|
rri += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type Memory = uint;
|
|
|
|
|
|
|
|
const task_local_heap: Memory = 1;
|
|
|
|
const exchange_heap: Memory = 2;
|
|
|
|
const stack: Memory = 4;
|
|
|
|
|
|
|
|
const need_cleanup: Memory = exchange_heap | stack;
|
|
|
|
|
2012-08-23 14:14:47 -05:00
|
|
|
unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
|
2012-07-16 14:28:15 -05:00
|
|
|
let mut last_ret: *Word = ptr::null();
|
2012-08-23 14:14:47 -05:00
|
|
|
// To avoid collecting memory used by the GC itself, skip stack
|
|
|
|
// frames until past the root GC stack frame. The root GC stack
|
|
|
|
// frame is marked by a sentinel, which is a box pointer stored on
|
|
|
|
// the stack.
|
|
|
|
let mut reached_sentinel = ptr::is_null(sentinel);
|
2012-08-17 18:17:18 -05:00
|
|
|
for stackwalk::walk_stack |frame| {
|
2012-07-16 14:28:15 -05:00
|
|
|
unsafe {
|
2012-08-23 14:14:47 -05:00
|
|
|
let mut delay_reached_sentinel = reached_sentinel;
|
2012-07-16 14:28:15 -05:00
|
|
|
if ptr::is_not_null(last_ret) {
|
|
|
|
let sp = is_safe_point(last_ret);
|
|
|
|
match sp {
|
|
|
|
Some(sp_info) => {
|
2012-08-17 18:17:18 -05:00
|
|
|
for walk_safe_point(frame.fp, sp_info) |root, tydesc| {
|
2012-08-23 14:14:47 -05:00
|
|
|
// Skip roots until we see the sentinel.
|
|
|
|
if !reached_sentinel {
|
|
|
|
if root == sentinel {
|
|
|
|
delay_reached_sentinel = true;
|
|
|
|
}
|
|
|
|
again;
|
|
|
|
}
|
|
|
|
|
2012-08-20 12:09:22 -05:00
|
|
|
// Skip null pointers, which can occur when a
|
|
|
|
// unique pointer has already been freed.
|
|
|
|
if ptr::is_null(*root) {
|
|
|
|
again;
|
|
|
|
}
|
|
|
|
|
2012-07-16 14:28:15 -05:00
|
|
|
if ptr::is_null(tydesc) {
|
|
|
|
// Root is a generic box.
|
|
|
|
let refcount = **root;
|
|
|
|
if mem | task_local_heap != 0 && refcount != -1 {
|
2012-08-17 18:17:18 -05:00
|
|
|
if !visitor(root, tydesc) { return; }
|
2012-08-22 18:55:48 -05:00
|
|
|
} else if mem | exchange_heap != 0
|
|
|
|
&& refcount == -1 {
|
2012-08-17 18:17:18 -05:00
|
|
|
if !visitor(root, tydesc) { return; }
|
2012-07-16 14:28:15 -05:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Root is a non-immediate.
|
|
|
|
if mem | stack != 0 {
|
2012-08-17 18:17:18 -05:00
|
|
|
if !visitor(root, tydesc) { return; }
|
2012-07-16 14:28:15 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
None => ()
|
|
|
|
}
|
|
|
|
}
|
2012-08-23 14:14:47 -05:00
|
|
|
reached_sentinel = delay_reached_sentinel;
|
2012-07-16 14:28:15 -05:00
|
|
|
last_ret = *ptr::offset(frame.fp, 1) as *Word;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gc() {
|
|
|
|
unsafe {
|
2012-08-23 14:14:47 -05:00
|
|
|
for walk_gc_roots(task_local_heap, ptr::null()) |_root, _tydesc| {
|
2012-07-16 14:28:15 -05:00
|
|
|
// FIXME(#2997): Walk roots and mark them.
|
|
|
|
io::stdout().write([46]); // .
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-17 18:17:18 -05:00
|
|
|
type RootSet = LinearMap<*Word,()>;
|
|
|
|
|
|
|
|
fn RootSet() -> RootSet {
|
|
|
|
LinearMap()
|
|
|
|
}
|
|
|
|
|
2012-08-28 13:00:24 -05:00
|
|
|
#[cfg(gc)]
|
|
|
|
fn expect_sentinel() -> bool { true }
|
|
|
|
|
|
|
|
#[cfg(nogc)]
|
|
|
|
fn expect_sentinel() -> bool { false }
|
|
|
|
|
2012-07-16 14:28:15 -05:00
|
|
|
// This should only be called from fail, as it will drop the roots
|
|
|
|
// which are *live* on the stack, rather than dropping those that are
|
|
|
|
// dead.
|
|
|
|
fn cleanup_stack_for_failure() {
|
|
|
|
unsafe {
|
2012-08-28 13:00:24 -05:00
|
|
|
// Leave a sentinel on the stack to mark the current frame. The
|
|
|
|
// stack walker will ignore any frames above the sentinel, thus
|
|
|
|
// avoiding collecting any memory being used by the stack walker
|
|
|
|
// itself.
|
|
|
|
//
|
|
|
|
// However, when core itself is not compiled with GC, then none of
|
|
|
|
// the functions in core will have GC metadata, which means we
|
|
|
|
// won't be able to find the sentinel root on the stack. In this
|
|
|
|
// case, we can safely skip the sentinel since we won't find our
|
|
|
|
// own stack roots on the stack anyway.
|
2012-08-23 14:14:47 -05:00
|
|
|
let sentinel_box = ~0;
|
2012-08-28 13:00:24 -05:00
|
|
|
let sentinel: **Word = if expect_sentinel() {
|
|
|
|
unsafe::reinterpret_cast(&ptr::addr_of(sentinel_box))
|
|
|
|
} else {
|
|
|
|
ptr::null()
|
|
|
|
};
|
2012-08-23 14:14:47 -05:00
|
|
|
|
2012-08-17 18:17:18 -05:00
|
|
|
let mut roots = ~RootSet();
|
2012-08-23 14:14:47 -05:00
|
|
|
for walk_gc_roots(need_cleanup, sentinel) |root, tydesc| {
|
2012-08-17 18:17:18 -05:00
|
|
|
// Track roots to avoid double frees.
|
|
|
|
if option::is_some(roots.find(&*root)) {
|
|
|
|
again;
|
|
|
|
}
|
|
|
|
roots.insert(*root, ());
|
|
|
|
|
2012-07-16 14:28:15 -05:00
|
|
|
if ptr::is_null(tydesc) {
|
|
|
|
rustrt::rust_annihilate_box(*root);
|
|
|
|
} else {
|
|
|
|
rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|