gc.rs 11.6 KB
Newer Older
1
/*! Precise garbage collector
E
Elliott Slaughter 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27

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.

*/

28 29 30 31
// NB: transitionary, de-mode-ing.
#[forbid(deprecated_mode)];
#[forbid(deprecated_pattern)];

32 33 34 35
use stackwalk::Word;
use libc::size_t;
use libc::uintptr_t;
use send_map::linear::LinearMap;
36

37 38 39 40
export Word;
export gc;
export cleanup_stack_for_failure;

41 42
// Mirrors rust_stack.h stk_seg
struct StackSegment {
43 44 45
    prev: *StackSegment,
    next: *StackSegment,
    end: uintptr_t,
46 47 48
    // And other fields which we don't care about...
}

49 50 51 52
extern mod rustrt {
    fn rust_annihilate_box(ptr: *Word);

    #[rust_stack]
53
    fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);
54 55

    #[rust_stack]
56 57 58 59 60
    fn rust_gc_metadata() -> *Word;

    fn rust_get_stack_segment() -> *StackSegment;
}

E
Elliott Slaughter 已提交
61
unsafe fn bump<T, U>(ptr: *T, count: uint) -> *U {
62
    return cast::reinterpret_cast(&ptr::offset(ptr, count));
E
Elliott Slaughter 已提交
63
}
64

E
Elliott Slaughter 已提交
65 66
unsafe fn align_to_pointer<T>(ptr: *T) -> *T {
    let align = sys::min_align_of::<*T>();
67
    let ptr: uint = cast::reinterpret_cast(&ptr);
E
Elliott Slaughter 已提交
68
    let ptr = (ptr + (align - 1)) & -align;
69
    return cast::reinterpret_cast(&ptr);
70 71
}

72 73 74 75 76
unsafe fn get_safe_point_count() -> uint {
    let module_meta = rustrt::rust_gc_metadata();
    return *module_meta;
}

77 78
type SafePoint = { sp_meta: *Word, fn_meta: *Word };

E
Elliott Slaughter 已提交
79 80
// Returns the safe point metadata for the given program counter, if
// any.
81 82
unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
    let module_meta = rustrt::rust_gc_metadata();
E
Elliott Slaughter 已提交
83 84
    let num_safe_points = *module_meta;
    let safe_points: *Word = bump(module_meta, 1);
85 86 87 88 89

    if ptr::is_null(pc) {
        return None;
    }

E
Elliott Slaughter 已提交
90
    // FIXME (#2997): Use binary rather than linear search.
E
Elliott Slaughter 已提交
91 92 93 94
    let mut spi = 0;
    while spi < num_safe_points {
        let sp: **Word = bump(safe_points, spi*3);
        let sp_loc = *sp;
95
        if sp_loc == pc {
E
Elliott Slaughter 已提交
96
            return Some({sp_meta: *bump(sp, 1), fn_meta: *bump(sp, 2)});
97
        }
E
Elliott Slaughter 已提交
98
        spi += 1;
99 100 101 102
    }
    return None;
}

103
type Visitor = fn(root: **Word, tydesc: *Word) -> bool;
104

E
Elliott Slaughter 已提交
105 106
// Walks the list of roots for the given safe point, and calls visitor
// on each root.
107
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
108 109
    let fp_bytes: *u8 = cast::reinterpret_cast(&fp);
    let sp_meta: *u32 = cast::reinterpret_cast(&sp.sp_meta);
110

E
Elliott Slaughter 已提交
111 112
    let num_stack_roots = *sp_meta as uint;
    let num_reg_roots = *ptr::offset(sp_meta, 1) as uint;
113

E
Elliott Slaughter 已提交
114 115 116 117
    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: ***Word = bump(addrspaces, num_stack_roots);
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132

    // 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()
            };
133
            if !visitor(root, tydesc) { return; }
134 135 136 137 138 139 140 141 142 143 144 145 146 147
        }
        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;
    }
}

E
Elliott Slaughter 已提交
148 149
// Is fp contained in segment?
unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool {
150 151 152
    let begin: Word = cast::reinterpret_cast(&segment);
    let end: Word = cast::reinterpret_cast(&(*segment).end);
    let frame: Word = cast::reinterpret_cast(&fp);
153

E
Elliott Slaughter 已提交
154 155
    return begin <= frame && frame <= end;
}
156

E
Elliott Slaughter 已提交
157 158 159 160
// 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.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
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};
}

E
Elliott Slaughter 已提交
188 189 190 191 192 193 194 195
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;

E
Elliott Slaughter 已提交
196 197
// Walks stack, searching for roots of the requested type, and passes
// each root to the visitor.
198
unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
199
    let mut segment = rustrt::rust_get_stack_segment();
200
    let mut last_ret: *Word = ptr::null();
201 202 203 204 205
    // 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);
206
    for stackwalk::walk_stack |frame| {
207
        unsafe {
208 209 210 211
            let pc = last_ret;
            let {segment: next_segment, boundary: boundary} =
                find_segment_for_frame(frame.fp, segment);
            segment = next_segment;
E
Elliott Slaughter 已提交
212 213 214 215 216 217 218 219 220
            // 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.
221 222 223 224
            let ret_offset = if boundary { 4 } else { 1 };
            last_ret = *ptr::offset(frame.fp, ret_offset) as *Word;

            if ptr::is_null(pc) {
225
                loop;
226 227
            }

228
            let mut delay_reached_sentinel = reached_sentinel;
229 230 231 232 233 234 235 236
            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;
237
                        }
238
                        loop;
239
                    }
240

241 242 243
                    // Skip null pointers, which can occur when a
                    // unique pointer has already been freed.
                    if ptr::is_null(*root) {
244
                        loop;
245
                    }
246

247 248 249 250 251 252 253 254 255 256 257 258
                    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; }
259 260 261
                        }
                    }
                }
262 263
              }
              None => ()
264
            }
265
            reached_sentinel = delay_reached_sentinel;
266 267 268 269 270 271
        }
    }
}

fn gc() {
    unsafe {
272 273 274 275 276
        // Abort when GC is disabled.
        if get_safe_point_count() == 0 {
            return;
        }

277
        for walk_gc_roots(task_local_heap, ptr::null()) |_root, _tydesc| {
278 279 280 281 282 283
            // FIXME(#2997): Walk roots and mark them.
            io::stdout().write([46]); // .
        }
    }
}

284 285 286 287 288 289
type RootSet = LinearMap<*Word,()>;

fn RootSet() -> RootSet {
    LinearMap()
}

290 291 292 293 294 295
#[cfg(gc)]
fn expect_sentinel() -> bool { true }

#[cfg(nogc)]
fn expect_sentinel() -> bool { false }

E
Elliott Slaughter 已提交
296 297 298 299
// Entry point for GC-based cleanup. Walks stack looking for exchange
// heap and stack allocations requiring drop, and runs all
// destructors.
//
300 301 302 303 304
// 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 {
305 306 307 308 309
        // Abort when GC is disabled.
        if get_safe_point_count() == 0 {
            return;
        }

310 311 312 313 314 315 316 317 318 319
        // 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.
320
        let sentinel_box = ~0;
321
        let sentinel: **Word = if expect_sentinel() {
322
            cast::reinterpret_cast(&ptr::addr_of(sentinel_box))
323 324 325
        } else {
            ptr::null()
        };
326

327
        let mut roots = ~RootSet();
328
        for walk_gc_roots(need_cleanup, sentinel) |root, tydesc| {
329 330
            // Track roots to avoid double frees.
            if option::is_some(roots.find(&*root)) {
331
                loop;
332 333 334
            }
            roots.insert(*root, ());

335 336 337 338 339 340 341 342
            if ptr::is_null(tydesc) {
                rustrt::rust_annihilate_box(*root);
            } else {
                rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t);
            }
        }
    }
}