提交 3f0d207b 编写于 作者: E Elliott Slaughter

gc: Add stack walker for new garbage collector.

Safe points are exported in a per-module list via the crate map. A C
runtime call walks the crate map at startup and aggregates the list of
safe points for the program.

Currently the GC doesn't actually deallocate memory on malloc and
free. Adding the GC at this stage is primarily of testing value.

The GC does attempt to clean up exchange heap and stack-allocated
resource on failure.

A result of this patch is that the user now needs to be careful about
what code they write in destructors, because the GC and/or failure
cleanup may need to call destructors. Specifically, calls to malloc
are considered unsafe and may result in infinite loops or segfaults.
上级 fb8786fe
......@@ -60,7 +60,9 @@ RUNTIME_CS_$(1) := \
rt/rust_port.cpp \
rt/rust_upcall.cpp \
rt/rust_uv.cpp \
rt/rust_crate_map.cpp \
rt/rust_log.cpp \
rt/rust_gc_metadata.cpp \
rt/rust_port_selector.cpp \
rt/rust_util.cpp \
rt/circular_buffer.cpp \
......
......@@ -40,7 +40,7 @@ export uint, u8, u16, u32, u64;
export float, f32, f64;
export box, char, str, ptr, vec, at_vec, bool;
export either, option, result, iter;
export libc, os, io, run, rand, sys, unsafe, logging;
export gc, io, libc, os, run, rand, sys, unsafe, logging;
export comm, task, future, pipes;
export extfmt;
// The test harness links against core, so don't include runtime in tests.
......@@ -216,6 +216,7 @@ mod pipes;
// Runtime and language-primitive support
mod gc;
mod io;
mod libc;
mod os;
......
import stackwalk::Word;
import libc::size_t;
extern mod rustrt {
fn rust_annihilate_box(ptr: *Word);
#[rust_stack]
fn rust_gc_metadata() -> *Word;
#[rust_stack]
fn rust_call_tydesc_glue(root: *Word, tydesc: *Word, field: size_t);
}
type SafePoint = { sp_meta: *Word, fn_meta: *Word };
unsafe fn is_safe_point(pc: *Word) -> Option<SafePoint> {
let module_meta = rustrt::rust_gc_metadata();
let num_safe_points_ptr: *u32 = unsafe::reinterpret_cast(&module_meta);
let num_safe_points = *num_safe_points_ptr as Word;
let safe_points: *Word =
ptr::offset(unsafe::reinterpret_cast(&module_meta), 1);
if ptr::is_null(pc) {
return None;
}
let mut sp = 0 as Word;
while sp < num_safe_points {
let sp_loc = *ptr::offset(safe_points, sp*3) as *Word;
if sp_loc == pc {
return Some(
{sp_meta: *ptr::offset(safe_points, sp*3 + 1) as *Word,
fn_meta: *ptr::offset(safe_points, sp*3 + 2) as *Word});
}
sp += 1;
}
return None;
}
type Visitor = fn(root: **Word, tydesc: *Word);
unsafe fn walk_safe_point(fp: *Word, sp: SafePoint, visitor: Visitor) {
let fp_bytes: *u8 = unsafe::reinterpret_cast(&fp);
let sp_meta_u32s: *u32 = unsafe::reinterpret_cast(&sp.sp_meta);
let num_stack_roots = *sp_meta_u32s as uint;
let num_reg_roots = *ptr::offset(sp_meta_u32s, 1) as uint;
let stack_roots: *u32 =
unsafe::reinterpret_cast(&ptr::offset(sp_meta_u32s, 2));
let reg_roots: *u8 =
unsafe::reinterpret_cast(&ptr::offset(stack_roots, num_stack_roots));
let addrspaces: *Word =
unsafe::reinterpret_cast(&ptr::offset(reg_roots, num_reg_roots));
let tydescs: ***Word =
unsafe::reinterpret_cast(&ptr::offset(addrspaces, num_stack_roots));
// 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()
};
visitor(root, tydesc);
}
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;
}
}
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;
unsafe fn walk_gc_roots(mem: Memory, visitor: Visitor) {
let mut last_ret: *Word = ptr::null();
do stackwalk::walk_stack |frame| {
unsafe {
if ptr::is_not_null(last_ret) {
let sp = is_safe_point(last_ret);
match sp {
Some(sp_info) => {
do walk_safe_point(frame.fp, sp_info) |root, tydesc| {
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 {
visitor(root, tydesc);
}
} else {
// Root is a non-immediate.
if mem | stack != 0 {
visitor(root, tydesc);
}
}
}
}
None => ()
}
}
last_ret = *ptr::offset(frame.fp, 1) as *Word;
}
true
}
}
fn gc() {
unsafe {
let mut i = 0;
do walk_gc_roots(task_local_heap) |_root, _tydesc| {
// FIXME(#2997): Walk roots and mark them.
io::stdout().write([46]); // .
i += 1;
}
}
}
// 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 {
let mut i = 0;
do walk_gc_roots(need_cleanup) |root, tydesc| {
if ptr::is_null(tydesc) {
rustrt::rust_annihilate_box(*root);
} else {
rustrt::rust_call_tydesc_glue(*root, tydesc, 3 as size_t);
}
i += 1;
}
}
}
......@@ -8,6 +8,9 @@
use libc::size_t;
use libc::uintptr_t;
import gc::gc;
import gc::cleanup_stack_for_failure;
#[allow(non_camel_case_types)]
type rust_task = c_void;
......@@ -33,6 +36,7 @@
// gather_rust_rtcalls.
#[rt(fail)]
fn rt_fail(expr: *c_char, file: *c_char, line: size_t) {
cleanup_stack_for_failure();
rustrt::rust_upcall_fail(expr, file, line);
}
......
......@@ -7,6 +7,7 @@
#include "rust_kernel.h"
#include "rust_util.h"
#include "rust_scheduler.h"
#include "rust_gc_metadata.h"
// Creates a rust argument vector from the platform argument vector
struct
......@@ -85,6 +86,8 @@ rust_start(uintptr_t main_fn, int argc, char **argv, void* crate_map) {
// line as well.
rust_env *env = load_env();
update_gc_metadata(crate_map);
update_log_settings(crate_map, env->logspec);
// Maybe turn on typestate claim checking
......
#include "rust_crate_map.h"
void iter_module_map(const mod_entry* map,
void (*fn)(const mod_entry* entry, void *cookie),
void *cookie) {
for (const mod_entry* cur = map; cur->name; cur++) {
fn(cur, cookie);
}
}
void iter_crate_map(const cratemap* map,
void (*fn)(const mod_entry* map, void *cookie),
void *cookie) {
// First iterate this crate
iter_module_map(map->entries, fn, cookie);
// Then recurse on linked crates
// FIXME (#2673) this does double work in diamond-shaped deps. could
// keep a set of visited addresses, if it turns out to be actually
// slow
for (size_t i = 0; map->children[i]; i++) {
iter_crate_map(map->children[i], fn, cookie);
}
}
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
#ifndef RUST_CRATE_MAP_H
#define RUST_CRATE_MAP_H
#include "rust_log.h"
struct mod_entry {
const char* name;
uint32_t* state;
};
struct cratemap {
const mod_entry* entries;
const cratemap* children[1];
};
void iter_module_map(const mod_entry* map,
void (*fn)(const mod_entry* entry, void *cookie),
void *cookie);
void iter_crate_map(const cratemap* map,
void (*fn)(const mod_entry* entry, void *cookie),
void *cookie);
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
#endif /* RUST_CRATE_MAP_H */
#include "rust_gc_metadata.h"
#include "rust_crate_map.h"
#include "rust_globals.h"
#include <algorithm>
#include <vector>
struct safe_point {
size_t safe_point_loc;
size_t safe_point_meta;
size_t function_meta;
};
struct update_gc_entry_args {
std::vector<safe_point> *safe_points;
};
static void
update_gc_entry(const mod_entry* entry, void *cookie) {
update_gc_entry_args *args = (update_gc_entry_args *)cookie;
if (!strcmp(entry->name, "_gc_module_metadata")) {
size_t *next = entry->state;
uint32_t num_safe_points = *(uint32_t *)next;
next++;
for (uint32_t i = 0; i < num_safe_points; i++) {
safe_point sp = { next[0], next[1], next[2] };
next += 3;
args->safe_points->push_back(sp);
}
}
}
static bool
cmp_safe_point(safe_point a, safe_point b) {
return a.safe_point_loc < b.safe_point_loc;
}
size_t *global_safe_points = 0;
void
update_gc_metadata(const void* map) {
std::vector<safe_point> safe_points;
update_gc_entry_args args = { &safe_points };
// Extract list of safe points from each module.
iter_crate_map((const cratemap *)map, update_gc_entry, (void *)&args);
std::sort(safe_points.begin(), safe_points.end(), cmp_safe_point);
// Serialize safe point list into format expected by runtime.
global_safe_points =
(size_t *)malloc((safe_points.size()*3 + 1)*sizeof(size_t));
if (!global_safe_points) return;
size_t *next = global_safe_points;
*(uint32_t *)next = safe_points.size();
next++;
for (uint32_t i = 0; i < safe_points.size(); i++) {
next[0] = safe_points[i].safe_point_loc;
next[1] = safe_points[i].safe_point_meta;
next[2] = safe_points[i].function_meta;
next += 3;
}
}
extern "C" CDECL void *
rust_gc_metadata() {
return (void *)global_safe_points;
}
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
#ifndef RUST_GC_METADATA_H
#define RUST_GC_METADATA_H
void update_gc_metadata(const void* map);
//
// Local Variables:
// mode: C++
// fill-column: 78;
// indent-tabs-mode: nil
// c-basic-offset: 4
// buffer-file-coding-system: utf-8-unix
// End:
//
#endif /* RUST_GC_METADATA_H */
......@@ -4,6 +4,7 @@
#include "rust_log.h"
#include "rust_crate_map.h"
#include "util/array_list.h"
#include "rust_util.h"
#include "rust_task.h"
......@@ -160,16 +161,6 @@ rust_log::trace_ln(rust_task *task, uint32_t level, char *message) {
// Reading log directives and setting log level vars
struct mod_entry {
const char* name;
uint32_t* state;
};
struct cratemap {
const mod_entry* entries;
const cratemap* children[1];
};
struct log_directive {
char* name;
size_t level;
......@@ -212,33 +203,36 @@ size_t parse_logging_spec(char* spec, log_directive* dirs) {
return dir;
}
void update_module_map(const mod_entry* map, log_directive* dirs,
size_t n_dirs, size_t *n_matches) {
for (const mod_entry* cur = map; cur->name; cur++) {
size_t level = default_log_level, longest_match = 0;
for (size_t d = 0; d < n_dirs; d++) {
if (strstr(cur->name, dirs[d].name) == cur->name &&
strlen(dirs[d].name) > longest_match) {
longest_match = strlen(dirs[d].name);
level = dirs[d].level;
}
struct update_entry_args {
log_directive* dirs;
size_t n_dirs;
size_t *n_matches;
};
static void update_entry(const mod_entry* entry, void *cookie) {
update_entry_args *args = (update_entry_args *)cookie;
size_t level = default_log_level, longest_match = 0;
for (size_t d = 0; d < args->n_dirs; d++) {
if (strstr(entry->name, args->dirs[d].name) == entry->name &&
strlen(args->dirs[d].name) > longest_match) {
longest_match = strlen(args->dirs[d].name);
level = args->dirs[d].level;
}
*cur->state = level;
(*n_matches)++;
}
*entry->state = level;
(*args->n_matches)++;
}
void update_module_map(const mod_entry* map, log_directive* dirs,
size_t n_dirs, size_t *n_matches) {
update_entry_args args = { dirs, n_dirs, n_matches };
iter_module_map(map, update_entry, &args);
}
void update_crate_map(const cratemap* map, log_directive* dirs,
size_t n_dirs, size_t *n_matches) {
// First update log levels for this crate
update_module_map(map->entries, dirs, n_dirs, n_matches);
// Then recurse on linked crates
// FIXME (#2673) this does double work in diamond-shaped deps. could
// keep a set of visited addresses, if it turns out to be actually
// slow
for (size_t i = 0; map->children[i]; i++) {
update_crate_map(map->children[i], dirs, n_dirs, n_matches);
}
update_entry_args args = { dirs, n_dirs, n_matches };
iter_crate_map(map, update_entry, &args);
}
void print_crate_log_map(const cratemap* map) {
......
......@@ -1595,6 +1595,7 @@ fn trans_closure(ccx: @crate_ctxt, path: path, decl: ast::fn_decl,
do str::as_c_str("generic") |strategy| {
llvm::LLVMSetGC(fcx.llfn, strategy);
}
ccx.uses_gc = true;
}
// Create the first basic block in the function and keep a handle on it to
......@@ -2438,6 +2439,20 @@ fn gather_rtcalls(ccx: @crate_ctxt, crate: @ast::crate) {
}
}
fn decl_gc_metadata(ccx: @crate_ctxt, llmod_id: ~str) {
if !ccx.sess.opts.gc || !ccx.uses_gc {
return;
}
let gc_metadata_name = ~"_gc_module_metadata_" + llmod_id;
let gc_metadata = do str::as_c_str(gc_metadata_name) |buf| {
llvm::LLVMAddGlobal(ccx.llmod, T_i32(), buf)
};
llvm::LLVMSetGlobalConstant(gc_metadata, True);
lib::llvm::SetLinkage(gc_metadata, lib::llvm::ExternalLinkage);
ccx.module_data.insert(~"_gc_module_metadata", gc_metadata);
}
fn create_module_map(ccx: @crate_ctxt) -> ValueRef {
let elttype = T_struct(~[ccx.int_type, ccx.int_type]);
let maptype = T_array(elttype, ccx.module_data.size() + 1u);
......@@ -2679,6 +2694,7 @@ fn trans_crate(sess: session::session,
builder: BuilderRef_res(llvm::LLVMCreateBuilder()),
shape_cx: mk_ctxt(llmod),
crate_map: crate_map,
mut uses_gc: false,
dbg_cx: dbg_cx,
class_ctors: int_hash::<ast::def_id>(),
mut do_not_commit_warning_issued: false};
......@@ -2696,6 +2712,7 @@ fn trans_crate(sess: session::session,
trans_mod(ccx, crate.node.module);
}
decl_gc_metadata(ccx, llmod_id);
fill_crate_map(ccx, crate_map);
// NB: Must call force_declare_tydescs before emit_tydescs to break
// cyclical dependency with shape code! See shape.rs for details.
......
......@@ -162,6 +162,10 @@ fn BuilderRef_res(B: BuilderRef) -> BuilderRef_res {
builder: BuilderRef_res,
shape_cx: shape::ctxt,
crate_map: ValueRef,
// Set when at least one function uses GC. Needed so that
// decl_gc_metadata knows whether to link to the module metadata, which
// is not emitted by LLVM's GC pass when no functions use GC.
mut uses_gc: bool,
dbg_cx: Option<debuginfo::debug_ctxt>,
// Mapping from class constructors to parent class --
// used in base::trans_closure
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册