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
extern mod rustrt {
50
    #[legacy_exports];
51 52 53
    fn rust_annihilate_box(ptr: *Word);

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

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

    fn rust_get_stack_segment() -> *StackSegment;
}

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

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

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

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

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

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

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

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

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

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

E
Elliott Slaughter 已提交
115 116 117 118
    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);
119 120 121 122 123 124 125 126 127 128 129 130 131 132 133

    // 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()
            };
134
            if !visitor(root, tydesc) { return; }
135 136 137 138 139 140 141 142 143 144 145 146 147 148
        }
        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 已提交
149 150
// Is fp contained in segment?
unsafe fn is_frame_in_segment(fp: *Word, segment: *StackSegment) -> bool {
151 152 153
    let begin: Word = cast::reinterpret_cast(&segment);
    let end: Word = cast::reinterpret_cast(&(*segment).end);
    let frame: Word = cast::reinterpret_cast(&fp);
154

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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