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
// Mirrors rust_stack.h stk_seg
struct StackSegment {
39 40 41
    prev: *StackSegment,
    next: *StackSegment,
    end: uintptr_t,
42 43 44
    // And other fields which we don't care about...
}

45
extern mod rustrt {
46
    #[legacy_exports];
47 48 49
    fn rust_annihilate_box(ptr: *Word);

    #[rust_stack]
50
    fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);
51 52

    #[rust_stack]
53 54 55 56 57
    fn rust_gc_metadata() -> *Word;

    fn rust_get_stack_segment() -> *StackSegment;
}

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

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

69 70 71 72 73
unsafe fn get_safe_point_count() -> uint {
    let module_meta = rustrt::rust_gc_metadata();
    return *module_meta;
}

74 75
type SafePoint = { sp_meta: *Word, fn_meta: *Word };

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

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

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

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

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

E
Elliott Slaughter 已提交
108 109
    let num_stack_roots = *sp_meta as uint;
    let num_reg_roots = *ptr::offset(sp_meta, 1) as uint;
110

E
Elliott Slaughter 已提交
111 112 113 114
    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);
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129

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

E
Elliott Slaughter 已提交
151 152
    return begin <= frame && frame <= end;
}
153

E
Elliott Slaughter 已提交
154 155 156 157
// 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.
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
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 已提交
185 186 187 188 189 190 191 192
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 已提交
193 194
// Walks stack, searching for roots of the requested type, and passes
// each root to the visitor.
195
unsafe fn walk_gc_roots(mem: Memory, sentinel: **Word, visitor: Visitor) {
196
    let mut segment = rustrt::rust_get_stack_segment();
197
    let mut last_ret: *Word = ptr::null();
198 199 200 201 202
    // 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);
203
    for stackwalk::walk_stack |frame| {
204
        unsafe {
205 206 207 208
            let pc = last_ret;
            let {segment: next_segment, boundary: boundary} =
                find_segment_for_frame(frame.fp, segment);
            segment = next_segment;
E
Elliott Slaughter 已提交
209 210 211 212 213 214 215 216 217
            // 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.
218 219 220 221
            let ret_offset = if boundary { 4 } else { 1 };
            last_ret = *ptr::offset(frame.fp, ret_offset) as *Word;

            if ptr::is_null(pc) {
222
                loop;
223 224
            }

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

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

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

267
pub fn gc() {
268
    unsafe {
269 270 271 272 273
        // Abort when GC is disabled.
        if get_safe_point_count() == 0 {
            return;
        }

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

281 282 283 284 285 286
type RootSet = LinearMap<*Word,()>;

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

287 288 289 290 291 292
#[cfg(gc)]
fn expect_sentinel() -> bool { true }

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

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

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

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

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