auto merge of #8218 : brson/rust/nogc, r=brson

These are both obsoleted by the forthcoming new GC.
This commit is contained in:
bors 2013-08-04 16:25:54 -07:00
commit 77bc6c5955
4 changed files with 0 additions and 444 deletions

View File

@ -1,358 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[doc(hidden)];
#[allow(non_uppercase_statics)];
/*! Precise garbage collector
The precise GC exposes two functions, gc and
cleanup_stack_for_failure. The gc function is the entry point to the
garbage collector itself. The cleanup_stack_for_failure is the entry
point for GC-based cleanup.
Precise GC depends on changes to LLVM's GC which add support for
automatic rooting and addrspace-based metadata marking. Rather than
explicitly rooting pointers with LLVM's gcroot intrinsic, the GC
merely creates allocas for pointers, and allows an LLVM pass to
automatically infer roots based on the allocas present in a function
(and live at a given location). The compiler communicates the type of
the pointer to LLVM by setting the addrspace of the pointer type. The
compiler then emits a map from addrspace to tydesc, which LLVM then
uses to match pointers with their tydesc. The GC reads the metadata
table produced by LLVM, and uses it to determine which glue functions
to call to free objects on their respective heaps.
GC-based cleanup is a replacement for landing pads which relies on the
GC infrastructure to find pointers on the stack to cleanup. Whereas
the normal GC needs to walk task-local heap allocations, the cleanup
code needs to walk exchange heap allocations and stack-allocations
with destructors.
*/
use cast;
use container::{Set, MutableSet};
use io;
use libc::{uintptr_t};
use option::{None, Option, Some};
use ptr;
use hashmap::HashSet;
use stackwalk::walk_stack;
use sys;
use unstable::intrinsics::{TyDesc};
pub use stackwalk::Word;
// Mirrors rust_stack.h stk_seg
pub struct StackSegment {
prev: *StackSegment,
next: *StackSegment,
end: uintptr_t,
// And other fields which we don't care about...
}
pub mod rustrt {
use stackwalk::Word;
use super::StackSegment;
#[link_name = "rustrt"]
extern {
#[rust_stack]
pub fn rust_gc_metadata() -> *Word;
pub fn rust_get_stack_segment() -> *StackSegment;
pub fn rust_get_c_stack() -> *StackSegment;
}
}
unsafe fn bump<T, U>(ptr: *T, count: uint) -> *U {
return ptr::offset(ptr, count as int) as *U;
}
unsafe fn align_to_pointer<T>(ptr: *T) -> *T {
let align = sys::min_align_of::<*T>();
let ptr = ptr as uint;
let ptr = (ptr + (align - 1)) & -align;
return ptr as *T;
}
unsafe fn get_safe_point_count() -> uint {
let module_meta = rustrt::rust_gc_metadata();
return *module_meta;
}
struct SafePoint {
sp_meta: *Word,
fn_meta: *Word,
}
// Returns the safe point metadata for the given program counter, if
// any.
unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
let module_meta = rustrt::rust_gc_metadata();
let num_safe_points = *module_meta;
let safe_points: *Word = bump(module_meta, 1);
if ptr::is_null(pc) {
return None;
}
// FIXME (#2997): Use binary rather than linear search.
let mut spi = 0;
while spi < num_safe_points {
let sp: **Word = bump(safe_points, spi*3);
let sp_loc = *sp;
if sp_loc == pc {
return Some(SafePoint {
sp_meta: *bump(sp, 1),
fn_meta: *bump(sp, 2),
});
}
spi += 1;
}
return None;
}
type Visitor<'self> = &'self fn(root: **Word, tydesc: *TyDesc);
// Walks the list of roots for the given safe point, and calls visitor
// on each root.
unsafe fn _walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
let fp_bytes = fp as *u8;
let sp_meta = sp.sp_meta as *u32;
let num_stack_roots = *sp_meta as uint;
let num_reg_roots = *ptr::offset(sp_meta, 1) as uint;
let stack_roots: *u32 = bump(sp_meta, 2);
let reg_roots: *u8 = bump(stack_roots, num_stack_roots);
let addrspaces: *Word = align_to_pointer(bump(reg_roots, num_reg_roots));
let tydescs: ***TyDesc = bump(addrspaces, num_stack_roots);
// Stack roots
let mut sri = 0;
while sri < num_stack_roots {
if *ptr::offset(addrspaces, sri as int) >= 1 {
let root =
ptr::offset(fp_bytes, *ptr::offset(stack_roots, sri as int) as int)
as **Word;
let tydescpp = ptr::offset(tydescs, sri as int);
let tydesc = if ptr::is_not_null(tydescpp) &&
ptr::is_not_null(*tydescpp) {
**tydescpp
} else {
ptr::null()
};
visitor(root, tydesc);
}
sri += 1;
}
// Register roots
let mut rri = 0;
while rri < num_reg_roots {
if *ptr::offset(addrspaces, (num_stack_roots + rri) as int) == 1 {
// FIXME(#2997): Need to find callee saved registers on the stack.
}
rri += 1;
}
}
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
_walk_safe_point(fp, sp, visitor)
}
// Is fp contained in segment?
unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool {
let begin = segment as Word;
let end = (*segment).end as Word;
let frame = fp as Word;
return begin <= frame && frame <= end;
}
struct Segment { segment: *StackSegment, boundary: bool }
// Find and return the segment containing the given frame pointer. At
// stack segment boundaries, returns true for boundary, so that the
// caller can do any special handling to identify where the correct
// return address is in the stack frame.
unsafe fn find_segment_for_frame(fp: *Word, segment: *StackSegment)
-> Segment {
// 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: segment, boundary: false};
}
// If frame is in previous frame, then we're at a boundary.
if !in_segment && in_prev_segment {
return Segment {segment: (*segment).prev, boundary: true};
}
// Otherwise, we're somewhere on the inside of the frame.
return Segment {segment: segment, boundary: false};
}
type Memory = uint;
static task_local_heap: Memory = 1;
static exchange_heap: Memory = 2;
static stack: Memory = 4;
static need_cleanup: Memory = exchange_heap | stack;
// Walks stack, searching for roots of the requested type, and passes
// each root to the visitor.
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
// frame is marked by a sentinel, which is a box pointer stored on
// the stack.
let mut reached_sentinel = ptr::is_null(sentinel);
do walk_stack |frame| {
let pc = last_ret;
let Segment {segment: next_segment, boundary: boundary} =
find_segment_for_frame(frame.fp, segment);
segment = next_segment;
// Each stack segment is bounded by a morestack frame. The
// morestack frame includes two return addresses, one for
// morestack itself, at the normal offset from the frame
// pointer, and then a second return address for the
// function prologue (which called morestack after
// determining that it had hit the end of the stack).
// Since morestack itself takes two parameters, the offset
// for this second return address is 3 greater than the
// return address for morestack.
let ret_offset = if boundary { 4 } else { 1 };
last_ret = *ptr::offset(frame.fp, ret_offset as int) as *Word;
if !ptr::is_null(pc) {
let mut delay_reached_sentinel = reached_sentinel;
let sp = is_safe_point(pc);
match sp {
Some(sp_info) => {
do walk_safe_point(frame.fp, sp_info) |root, tydesc| {
// Skip roots until we see the sentinel.
if !reached_sentinel && root == sentinel {
delay_reached_sentinel = true;
}
// Skip null pointers, which can occur when a
// unique pointer has already been freed.
if reached_sentinel && !ptr::is_null(*root) {
if ptr::is_null(tydesc) {
// Root is a generic box.
let refcount = **root;
if mem | task_local_heap != 0 && refcount != -1 {
visitor(root, tydesc);
} else if mem | exchange_heap != 0 && refcount == -1 {
visitor(root, tydesc);
}
} else {
// Root is a non-immediate.
if mem | stack != 0 {
visitor(root, tydesc);
}
}
}
}
}
None => ()
}
reached_sentinel = delay_reached_sentinel;
}
}
}
unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
_walk_gc_roots(mem, sentinel, visitor)
}
pub fn gc() {
unsafe {
// Abort when GC is disabled.
if get_safe_point_count() == 0 {
return;
}
do walk_gc_roots(task_local_heap, ptr::null()) |_root, _tydesc| {
// FIXME(#2997): Walk roots and mark them.
io::stdout().write([46]); // .
}
}
}
#[cfg(gc)]
fn expect_sentinel() -> bool { true }
#[cfg(nogc)]
fn expect_sentinel() -> bool { false }
// Entry point for GC-based cleanup. Walks stack looking for exchange
// heap and stack allocations requiring drop, and runs all
// destructors.
//
// 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.
pub fn cleanup_stack_for_failure() {
unsafe {
// Abort when GC is disabled.
if get_safe_point_count() == 0 {
return;
}
// 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.
let sentinel_box = ~0;
let sentinel: **Word = if expect_sentinel() {
cast::transmute(&sentinel_box)
} else {
ptr::null()
};
let mut roots = HashSet::new();
do walk_gc_roots(need_cleanup, sentinel) |root, tydesc| {
// Track roots to avoid double frees.
if !roots.contains(&*root) {
roots.insert(*root);
if ptr::is_null(tydesc) {
// FIXME #4420: Destroy this box
// FIXME #4330: Destroy this box
} else {
((*tydesc).drop_glue)(*root as *i8);
}
}
}
}
}

View File

@ -1,80 +0,0 @@
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#[allow(missing_doc)];
use cast::transmute;
use unstable::intrinsics;
pub type Word = uint;
pub struct Frame {
fp: *Word
}
pub fn Frame(fp: *Word) -> Frame {
Frame {
fp: fp
}
}
pub fn walk_stack(visit: &fn(Frame)) {
debug!("beginning stack walk");
do frame_address |frame_pointer| {
let mut frame_address: *Word = unsafe {
transmute(frame_pointer)
};
loop {
let fr = Frame(frame_address);
debug!("frame: %x", unsafe { transmute(fr.fp) });
visit(fr);
unsafe {
let next_fp: **Word = transmute(frame_address);
frame_address = *next_fp;
if *frame_address == 0u {
debug!("encountered task_start_wrapper. ending walk");
// This is the task_start_wrapper_frame. There is
// no stack beneath it and it is a foreign frame.
break;
}
}
}
}
}
#[test]
fn test_simple() {
do walk_stack |_frame| {
}
}
#[test]
fn test_simple_deep() {
fn run(i: int) {
if i == 0 { return }
do walk_stack |_frame| {
// Would be nice to test something here...
}
run(i - 1);
}
run(10);
}
fn frame_address(f: &fn(x: *u8)) {
unsafe {
intrinsics::frame_address(f)
}
}

View File

@ -170,7 +170,6 @@ pub mod local_data;
/* Runtime and platform support */
pub mod gc;
pub mod libc;
pub mod os;
pub mod path;
@ -196,7 +195,6 @@ pub mod unstable;
mod unicode;
#[path = "num/cmath.rs"]
mod cmath;
mod stackwalk;
// XXX: This shouldn't be pub, and it should be reexported under 'unstable'
// but name resolution doesn't work without it being pub.

View File

@ -13,7 +13,6 @@
#[allow(missing_doc)];
use cast;
use gc;
use io;
use libc;
use libc::{c_char, size_t};
@ -147,7 +146,6 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
match context {
OldTaskContext => {
unsafe {
gc::cleanup_stack_for_failure();
rustrt::rust_upcall_fail(msg, file, line);
cast::transmute(())
}
@ -180,8 +178,6 @@ pub fn begin_unwind_(msg: *c_char, file: *c_char, line: size_t) -> ! {
msg, file, line as int);
}
gc::cleanup_stack_for_failure();
let task = Local::unsafe_borrow::<Task>();
if (*task).unwinder.unwinding {
rtabort!("unwinding again");