gc.rs 11.6 KB
Newer Older
E
Elliott Slaughter 已提交
1 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
/*! 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.

*/

28 29
import stackwalk::Word;
import libc::size_t;
30
import libc::uintptr_t;
31
import send_map::linear::LinearMap;
32

33 34 35 36
export Word;
export gc;
export cleanup_stack_for_failure;

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 46 47 48
extern mod rustrt {
    fn rust_annihilate_box(ptr: *Word);

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

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

    fn rust_get_stack_segment() -> *StackSegment;
}

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

E
Elliott Slaughter 已提交
61 62 63 64 65
unsafe fn align_to_pointer<T>(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);
66 67
}

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

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

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

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

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

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

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

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

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

    // 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()
            };
129
            if !visitor(root, tydesc) { return; }
130 131 132 133 134 135 136 137 138 139 140 141 142 143
        }
        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 已提交
144 145 146 147 148
// Is fp contained in segment?
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);
149

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

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

            if ptr::is_null(pc) {
                again;
            }

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

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

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

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

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

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

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

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

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

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

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

323
        let mut roots = ~RootSet();
324
        for walk_gc_roots(need_cleanup, sentinel) |root, tydesc| {
325 326 327 328 329 330
            // Track roots to avoid double frees.
            if option::is_some(roots.find(&*root)) {
                again;
            }
            roots.insert(*root, ());

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