From 961bd48724a3b9e75685d23d8d0e52a20f6ccf68 Mon Sep 17 00:00:00 2001 From: Elliott Slaughter Date: Fri, 24 Aug 2012 10:43:57 -0700 Subject: [PATCH] gc: Fix for GC missing stack frames across segment boundaries. --- src/libcore/gc.rs | 135 ++++++++++++++++++++++++++---------- src/rt/rust_builtin.cpp | 5 ++ src/rt/rust_gc_metadata.cpp | 14 ++-- src/rt/rustrt.def.in | 1 + 4 files changed, 113 insertions(+), 42 deletions(-) diff --git a/src/libcore/gc.rs b/src/libcore/gc.rs index b075c87320290..bf3c7df91fbc1 100644 --- a/src/libcore/gc.rs +++ b/src/libcore/gc.rs @@ -1,19 +1,38 @@ import stackwalk::Word; import libc::size_t; +import libc::uintptr_t; import send_map::linear::LinearMap; export Word; export gc; export cleanup_stack_for_failure; +// Mirrors rust_stack.h stk_seg +struct StackSegment { + let prev: *StackSegment; + let next: *StackSegment; + let end: uintptr_t; + // And other fields which we don't care about... +} + extern mod rustrt { fn rust_annihilate_box(ptr: *Word); #[rust_stack] - fn rust_gc_metadata() -> *Word; + fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t); #[rust_stack] - fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t); + fn rust_gc_metadata() -> *Word; + + fn rust_get_stack_segment() -> *StackSegment; +} + +unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool { + let begin: Word = unsafe::reinterpret_cast(&segment); + let end: Word = unsafe::reinterpret_cast(&(*segment).end); + let frame: Word = unsafe::reinterpret_cast(&fp); + + return begin <= frame && frame <= end; } type SafePoint = { sp_meta: *Word, fn_meta: *Word }; @@ -44,6 +63,17 @@ unsafe fn is_safe_point(pc: *Word) -> Option { type Visitor = fn(root: **Word, tydesc: *Word) -> bool; +unsafe fn bump(ptr: *T, count: uint) -> *U { + return unsafe::reinterpret_cast(&ptr::offset(ptr, count)); +} + +unsafe fn align_to_pointer(ptr: *T) -> *T { + let align = sys::min_align_of::<*T>(); + let ptr: uint = unsafe::reinterpret_cast(&ptr); + let ptr = (ptr + (align - 1)) & -align; + return unsafe::reinterpret_cast(&ptr); +} + 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); @@ -97,7 +127,35 @@ const stack: Memory = 4; const need_cleanup: Memory = exchange_heap | stack; +unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment) + -> {segment: *StackSegment, boundary: bool} { + // Check if frame is in either current frame or previous frame. + let in_segment = is_frame_in_segment(fp, segment); + let in_prev_segment = ptr::is_not_null((*segment).prev) && + is_frame_in_segment(fp, (*segment).prev); + + // If frame is not in either segment, walk down segment list until + // we find the segment containing this frame. + if !in_segment && !in_prev_segment { + let mut segment = segment; + while ptr::is_not_null((*segment).next) && + is_frame_in_segment(fp, (*segment).next) { + segment = (*segment).next; + } + return {segment: segment, boundary: false}; + } + + // If frame is in previous frame, then we're at a boundary. + if !in_segment && in_prev_segment { + return {segment: (*segment).prev, boundary: true}; + } + + // Otherwise, we're somewhere on the inside of the frame. + return {segment: segment, boundary: false}; +} + unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) { + let mut segment = rustrt::rust_get_stack_segment(); let mut last_ret: *Word = ptr::null(); // To avoid collecting memory used by the GC itself, skip stack // frames until past the root GC stack frame. The root GC stack @@ -106,48 +164,55 @@ unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) { let mut reached_sentinel = ptr::is_null(sentinel); for stackwalk::walk_stack |frame| { unsafe { + let pc = last_ret; + let {segment: next_segment, boundary: boundary} = + find_segment_for_frame(frame.fp, segment); + segment = next_segment; + let ret_offset = if boundary { 4 } else { 1 }; + last_ret = *ptr::offset(frame.fp, ret_offset) as *Word; + + if ptr::is_null(pc) { + again; + } + let mut delay_reached_sentinel = reached_sentinel; - if ptr::is_not_null(last_ret) { - let sp = is_safe_point(last_ret); - match sp { - Some(sp_info) => { - for walk_safe_point(frame.fp, sp_info) |root, tydesc| { - // Skip roots until we see the sentinel. - if !reached_sentinel { - if root == sentinel { - delay_reached_sentinel = true; - } - again; + let sp = is_safe_point(pc); + match sp { + Some(sp_info) => { + for walk_safe_point(frame.fp, sp_info) |root, tydesc| { + // Skip roots until we see the sentinel. + if !reached_sentinel { + if root == sentinel { + delay_reached_sentinel = true; } + again; + } - // Skip null pointers, which can occur when a - // unique pointer has already been freed. - if ptr::is_null(*root) { - again; - } + // Skip null pointers, which can occur when a + // unique pointer has already been freed. + if ptr::is_null(*root) { + again; + } - if ptr::is_null(tydesc) { - // Root is a generic box. - let refcount = **root; - if mem | task_local_heap != 0 && refcount != -1 { - if !visitor(root, tydesc) { return; } - } else if mem | exchange_heap != 0 - && refcount == -1 { - if !visitor(root, tydesc) { return; } - } - } else { - // Root is a non-immediate. - if mem | stack != 0 { - if !visitor(root, tydesc) { return; } - } + if ptr::is_null(tydesc) { + // Root is a generic box. + let refcount = **root; + if mem | task_local_heap != 0 && refcount != -1 { + if !visitor(root, tydesc) { return; } + } else if mem | exchange_heap != 0 && refcount == -1 { + if !visitor(root, tydesc) { return; } + } + } else { + // Root is a non-immediate. + if mem | stack != 0 { + if !visitor(root, tydesc) { return; } } } - } - None => () } + } + None => () } reached_sentinel = delay_reached_sentinel; - last_ret = *ptr::offset(frame.fp, 1) as *Word; } } } diff --git a/src/rt/rust_builtin.cpp b/src/rt/rust_builtin.cpp index e1fbabda50fc8..b72fdd15e3c0a 100644 --- a/src/rt/rust_builtin.cpp +++ b/src/rt/rust_builtin.cpp @@ -610,6 +610,11 @@ rust_get_task() { return rust_get_current_task(); } +extern "C" CDECL stk_seg * +rust_get_stack_segment() { + return rust_get_current_task()->stk; +} + extern "C" CDECL void start_task(rust_task *target, fn_env_pair *f) { target->start(f->f, f->env, NULL); diff --git a/src/rt/rust_gc_metadata.cpp b/src/rt/rust_gc_metadata.cpp index a4da879daa0bf..baefe99ceee0b 100644 --- a/src/rt/rust_gc_metadata.cpp +++ b/src/rt/rust_gc_metadata.cpp @@ -6,9 +6,9 @@ #include struct safe_point { - size_t safe_point_loc; - size_t safe_point_meta; - size_t function_meta; + uintptr_t safe_point_loc; + uintptr_t safe_point_meta; + uintptr_t function_meta; }; struct update_gc_entry_args { @@ -19,7 +19,7 @@ static void update_gc_entry(const mod_entry* entry, void *cookie) { update_gc_entry_args *args = (update_gc_entry_args *)cookie; if (!strcmp(entry->name, "_gc_module_metadata")) { - size_t *next = entry->state; + uintptr_t *next = entry->state; uint32_t num_safe_points = *(uint32_t *)next; next++; @@ -37,7 +37,7 @@ cmp_safe_point(safe_point a, safe_point b) { return a.safe_point_loc < b.safe_point_loc; } -size_t *global_safe_points = 0; +uintptr_t *global_safe_points = 0; void update_gc_metadata(const void* map) { @@ -50,10 +50,10 @@ update_gc_metadata(const void* map) { // Serialize safe point list into format expected by runtime. global_safe_points = - (size_t *)malloc((safe_points.size()*3 + 1)*sizeof(size_t)); + (uintptr_t *)malloc((safe_points.size()*3 + 1)*sizeof(uintptr_t)); if (!global_safe_points) return; - size_t *next = global_safe_points; + uintptr_t *next = global_safe_points; *(uint32_t *)next = safe_points.size(); next++; for (uint32_t i = 0; i < safe_points.size(); i++) { diff --git a/src/rt/rustrt.def.in b/src/rt/rustrt.def.in index 3965651b54825..e9f2890893f9e 100644 --- a/src/rt/rustrt.def.in +++ b/src/rt/rustrt.def.in @@ -54,6 +54,7 @@ rust_env_pairs rust_task_yield rust_task_is_unwinding rust_get_task +rust_get_stack_segment rust_task_weaken rust_task_unweaken sched_threads