diff options
| author | Patrick Walton <pcwalton@mimiga.net> | 2011-09-21 18:13:46 -0700 |
|---|---|---|
| committer | Patrick Walton <pcwalton@mimiga.net> | 2011-09-21 18:14:23 -0700 |
| commit | ad3b9c41b47090b94237fd7d4429d1431ecb8181 (patch) | |
| tree | 569bdda0d918153948674314d88fd60258d62e74 /src/rt/rust_cc.cpp | |
| parent | a993621e43eeb6d4303d1f78faaf54cc881d49ab (diff) | |
| download | rust-ad3b9c41b47090b94237fd7d4429d1431ecb8181.tar.gz rust-ad3b9c41b47090b94237fd7d4429d1431ecb8181.zip | |
rt: Implement cycle collection marking. Simple cycles can now be detected.
Diffstat (limited to 'src/rt/rust_cc.cpp')
| -rw-r--r-- | src/rt/rust_cc.cpp | 250 |
1 files changed, 244 insertions, 6 deletions
diff --git a/src/rt/rust_cc.cpp b/src/rt/rust_cc.cpp index 4f9d93e59cd..11f8332bcd7 100644 --- a/src/rt/rust_cc.cpp +++ b/src/rt/rust_cc.cpp @@ -5,9 +5,11 @@ #include "rust_internal.h" #include "rust_shape.h" #include "rust_task.h" +#include <cassert> #include <cstdio> #include <cstdlib> #include <map> +#include <set> #include <vector> #include <stdint.h> @@ -23,7 +25,7 @@ typedef std::map<void *,uintptr_t> irc_map; class irc : public shape::data<irc,shape::ptr> { friend class shape::data<irc,shape::ptr>; - irc_map ircs; + irc_map &ircs; irc(const irc &other, const shape::ptr &in_dp) : shape::data<irc,shape::ptr>(other.task, other.align, other.sp, @@ -114,10 +116,13 @@ class irc : public shape::data<irc,shape::ptr> { return; // Bump the internal reference count of the box. - if (ircs.find((void *)dp) == ircs.end()) - ircs[(void *)dp] = 1; - else - ++ircs[(void *)dp]; + if (ircs.find((void *)ref_count_dp) == ircs.end()) { + //DPRINT("setting internal reference count for %p\n", + // (void *)ref_count_dp); + ircs[(void *)ref_count_dp] = 1; + } else { + ++ircs[(void *)ref_count_dp]; + } // Do not traverse the contents of this box; it's in the allocation // somewhere, so we're guaranteed to come back to it (if we haven't @@ -167,7 +172,8 @@ irc::compute_ircs(rust_task *task, irc_map &ircs) { type_desc *tydesc = begin->second; - DPRINT("determining internal ref counts: %p, tydesc=%p\n", p, tydesc); + //DPRINT("determining internal ref counts: %p, tydesc=%p\n", p, + //tydesc); shape::arena arena; shape::type_param *params = @@ -187,10 +193,242 @@ irc::compute_ircs(rust_task *task, irc_map &ircs) { } +// Root finding + +void +find_roots(rust_task *task, irc_map &ircs, std::vector<void *> &roots) { + std::map<void *,type_desc *>::iterator begin(task->local_allocs.begin()), + end(task->local_allocs.end()); + while (begin != end) { + void *alloc = begin->first; + uintptr_t *ref_count_ptr = reinterpret_cast<uintptr_t *>(alloc); + uintptr_t ref_count = *ref_count_ptr; + + uintptr_t irc; + if (ircs.find(alloc) != ircs.end()) + irc = ircs[alloc]; + else + irc = 0; + + if (irc < ref_count) { + // This allocation must be a root, because the internal reference + // count is smaller than the total reference count. + //DPRINT("root found: %p, irc %lu, ref count %lu\n", alloc, irc, + // ref_count); + roots.push_back(alloc); + } else { + //DPRINT("nonroot found: %p, ref count %lu\n", alloc, ref_count); + /*assert(irc == ref_count && "Internal reference count must be " + "less than or equal to the total reference count!");*/ + } + + ++begin; + } +} + + +// Marking + +class mark : public shape::data<mark,shape::ptr> { + friend class shape::data<mark,shape::ptr>; + + std::set<void *> &marked; + + mark(const mark &other, const shape::ptr &in_dp) + : shape::data<mark,shape::ptr>(other.task, other.align, other.sp, + other.params, other.tables, in_dp), + marked(other.marked) {} + + mark(const mark &other, + const uint8_t *in_sp, + const shape::type_param *in_params, + const rust_shape_tables *in_tables = NULL) + : shape::data<mark,shape::ptr>(other.task, + other.align, + in_sp, + in_params, + in_tables ? in_tables : other.tables, + other.dp), + marked(other.marked) {} + + mark(const mark &other, + const uint8_t *in_sp, + const shape::type_param *in_params, + const rust_shape_tables *in_tables, + shape::ptr in_dp) + : shape::data<mark,shape::ptr>(other.task, + other.align, + in_sp, + in_params, + in_tables, + in_dp), + marked(other.marked) {} + + mark(rust_task *in_task, + bool in_align, + const uint8_t *in_sp, + const shape::type_param *in_params, + const rust_shape_tables *in_tables, + uint8_t *in_data, + std::set<void *> &in_marked) + : shape::data<mark,shape::ptr>(in_task, in_align, in_sp, in_params, + in_tables, in_data), + marked(in_marked) {} + + void walk_vec(bool is_pod, uint16_t sp_size) { + if (is_pod || shape::get_dp<void *>(dp) == NULL) + return; // There can't be any outbound pointers from this. + + std::pair<uint8_t *,uint8_t *> data_range(get_vec_data_range(dp)); + if (data_range.second - data_range.first > 100000) + abort(); // FIXME: Temporary sanity check. + + mark sub(*this, data_range.first); + shape::ptr data_end = sub.end_dp = data_range.second; + while (sub.dp < data_end) { + sub.walk_reset(); + align = true; + } + } + + void walk_tag(shape::tag_info &tinfo, uint32_t tag_variant) { + shape::data<mark,shape::ptr>::walk_variant(tinfo, tag_variant); + } + + void walk_box() { + shape::data<mark,shape::ptr>::walk_box_contents(); + } + + void walk_fn() { + shape::data<mark,shape::ptr>::walk_fn_contents(dp); + } + + void walk_obj() { + shape::data<mark,shape::ptr>::walk_obj_contents(dp); + } + + void walk_res(const shape::rust_fn *dtor, unsigned n_params, + const shape::type_param *params, const uint8_t *end_sp, + bool live) { + while (this->sp != end_sp) { + this->walk(); + align = true; + } + } + + void walk_subcontext(mark &sub) { sub.walk(); } + + void walk_box_contents(mark &sub, shape::ptr &ref_count_dp) { + if (!ref_count_dp) + return; + + if (marked.find((void *)ref_count_dp) != marked.end()) + return; // Skip to avoid chasing cycles. + + marked.insert((void *)ref_count_dp); + sub.walk(); + } + + void walk_struct(const uint8_t *end_sp) { + while (this->sp != end_sp) { + this->walk(); + align = true; + } + } + + void walk_variant(shape::tag_info &tinfo, uint32_t variant_id, + const std::pair<const uint8_t *,const uint8_t *> + variant_ptr_and_end); + + template<typename T> + inline void walk_number() { /* no-op */ } + +public: + static void do_mark(rust_task *task, const std::vector<void *> &roots, + std::set<void *> &marked); +}; + +void +mark::walk_variant(shape::tag_info &tinfo, uint32_t variant_id, + const std::pair<const uint8_t *,const uint8_t *> + variant_ptr_and_end) { + mark sub(*this, variant_ptr_and_end.first, tinfo.params); + + assert(variant_id < 256); // FIXME: Temporary sanity check. + + const uint8_t *variant_end = variant_ptr_and_end.second; + while (sub.sp < variant_end) { + sub.walk(); + align = true; + } +} + +void +mark::do_mark(rust_task *task, const std::vector<void *> &roots, + std::set<void *> &marked) { + std::vector<void *>::const_iterator begin(roots.begin()), + end(roots.end()); + while (begin != end) { + void *alloc = *begin; + if (marked.find(alloc) == marked.end()) { + marked.insert(alloc); + + uint8_t *p = reinterpret_cast<uint8_t *>(alloc); + p += sizeof(uintptr_t); // Skip over the reference count. + + type_desc *tydesc = task->local_allocs[*begin]; + + //DPRINT("marking: %p, tydesc=%p\n", p, tydesc); + + shape::arena arena; + shape::type_param *params = + shape::type_param::from_tydesc(tydesc, arena); + +#if 0 + shape::log log(task, true, tydesc->shape, params, + tydesc->shape_tables, p, std::cerr); + log.walk(); + DPRINT("\n"); +#endif + + mark mark(task, true, tydesc->shape, params, tydesc->shape_tables, + p, marked); + mark.walk(); + } + + ++begin; + } +} + + +void +sweep(rust_task *task, const std::set<void *> &marked) { + std::map<void *,type_desc *>::iterator begin(task->local_allocs.begin()), + end(task->local_allocs.end()); + while (begin != end) { + void *alloc = begin->first; + if (marked.find(alloc) == marked.end()) { + DPRINT("object is part of a cycle: %p\n", alloc); + } + ++begin; + } +} + + void do_cc(rust_task *task) { + DPRINT("cc; n allocs = %lu\n", task->local_allocs.size()); + irc_map ircs; irc::compute_ircs(task, ircs); + + std::vector<void *> roots; + find_roots(task, ircs, roots); + + std::set<void *> marked; + mark::do_mark(task, roots, marked); + + sweep(task, marked); } void |
