Skip to content

Commit ad3b9c4

Browse files
committed
rt: Implement cycle collection marking. Simple cycles can now be detected.
1 parent a993621 commit ad3b9c4

File tree

1 file changed

+244
-6
lines changed

1 file changed

+244
-6
lines changed

src/rt/rust_cc.cpp

Lines changed: 244 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55
#include "rust_internal.h"
66
#include "rust_shape.h"
77
#include "rust_task.h"
8+
#include <cassert>
89
#include <cstdio>
910
#include <cstdlib>
1011
#include <map>
12+
#include <set>
1113
#include <vector>
1214
#include <stdint.h>
1315

@@ -23,7 +25,7 @@ typedef std::map<void *,uintptr_t> irc_map;
2325
class irc : public shape::data<irc,shape::ptr> {
2426
friend class shape::data<irc,shape::ptr>;
2527

26-
irc_map ircs;
28+
irc_map &ircs;
2729

2830
irc(const irc &other, const shape::ptr &in_dp)
2931
: shape::data<irc,shape::ptr>(other.task, other.align, other.sp,
@@ -114,10 +116,13 @@ class irc : public shape::data<irc,shape::ptr> {
114116
return;
115117

116118
// Bump the internal reference count of the box.
117-
if (ircs.find((void *)dp) == ircs.end())
118-
ircs[(void *)dp] = 1;
119-
else
120-
++ircs[(void *)dp];
119+
if (ircs.find((void *)ref_count_dp) == ircs.end()) {
120+
//DPRINT("setting internal reference count for %p\n",
121+
// (void *)ref_count_dp);
122+
ircs[(void *)ref_count_dp] = 1;
123+
} else {
124+
++ircs[(void *)ref_count_dp];
125+
}
121126

122127
// Do not traverse the contents of this box; it's in the allocation
123128
// 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) {
167172

168173
type_desc *tydesc = begin->second;
169174

170-
DPRINT("determining internal ref counts: %p, tydesc=%p\n", p, tydesc);
175+
//DPRINT("determining internal ref counts: %p, tydesc=%p\n", p,
176+
//tydesc);
171177

172178
shape::arena arena;
173179
shape::type_param *params =
@@ -187,10 +193,242 @@ irc::compute_ircs(rust_task *task, irc_map &ircs) {
187193
}
188194

189195

196+
// Root finding
197+
198+
void
199+
find_roots(rust_task *task, irc_map &ircs, std::vector<void *> &roots) {
200+
std::map<void *,type_desc *>::iterator begin(task->local_allocs.begin()),
201+
end(task->local_allocs.end());
202+
while (begin != end) {
203+
void *alloc = begin->first;
204+
uintptr_t *ref_count_ptr = reinterpret_cast<uintptr_t *>(alloc);
205+
uintptr_t ref_count = *ref_count_ptr;
206+
207+
uintptr_t irc;
208+
if (ircs.find(alloc) != ircs.end())
209+
irc = ircs[alloc];
210+
else
211+
irc = 0;
212+
213+
if (irc < ref_count) {
214+
// This allocation must be a root, because the internal reference
215+
// count is smaller than the total reference count.
216+
//DPRINT("root found: %p, irc %lu, ref count %lu\n", alloc, irc,
217+
// ref_count);
218+
roots.push_back(alloc);
219+
} else {
220+
//DPRINT("nonroot found: %p, ref count %lu\n", alloc, ref_count);
221+
/*assert(irc == ref_count && "Internal reference count must be "
222+
"less than or equal to the total reference count!");*/
223+
}
224+
225+
++begin;
226+
}
227+
}
228+
229+
230+
// Marking
231+
232+
class mark : public shape::data<mark,shape::ptr> {
233+
friend class shape::data<mark,shape::ptr>;
234+
235+
std::set<void *> &marked;
236+
237+
mark(const mark &other, const shape::ptr &in_dp)
238+
: shape::data<mark,shape::ptr>(other.task, other.align, other.sp,
239+
other.params, other.tables, in_dp),
240+
marked(other.marked) {}
241+
242+
mark(const mark &other,
243+
const uint8_t *in_sp,
244+
const shape::type_param *in_params,
245+
const rust_shape_tables *in_tables = NULL)
246+
: shape::data<mark,shape::ptr>(other.task,
247+
other.align,
248+
in_sp,
249+
in_params,
250+
in_tables ? in_tables : other.tables,
251+
other.dp),
252+
marked(other.marked) {}
253+
254+
mark(const mark &other,
255+
const uint8_t *in_sp,
256+
const shape::type_param *in_params,
257+
const rust_shape_tables *in_tables,
258+
shape::ptr in_dp)
259+
: shape::data<mark,shape::ptr>(other.task,
260+
other.align,
261+
in_sp,
262+
in_params,
263+
in_tables,
264+
in_dp),
265+
marked(other.marked) {}
266+
267+
mark(rust_task *in_task,
268+
bool in_align,
269+
const uint8_t *in_sp,
270+
const shape::type_param *in_params,
271+
const rust_shape_tables *in_tables,
272+
uint8_t *in_data,
273+
std::set<void *> &in_marked)
274+
: shape::data<mark,shape::ptr>(in_task, in_align, in_sp, in_params,
275+
in_tables, in_data),
276+
marked(in_marked) {}
277+
278+
void walk_vec(bool is_pod, uint16_t sp_size) {
279+
if (is_pod || shape::get_dp<void *>(dp) == NULL)
280+
return; // There can't be any outbound pointers from this.
281+
282+
std::pair<uint8_t *,uint8_t *> data_range(get_vec_data_range(dp));
283+
if (data_range.second - data_range.first > 100000)
284+
abort(); // FIXME: Temporary sanity check.
285+
286+
mark sub(*this, data_range.first);
287+
shape::ptr data_end = sub.end_dp = data_range.second;
288+
while (sub.dp < data_end) {
289+
sub.walk_reset();
290+
align = true;
291+
}
292+
}
293+
294+
void walk_tag(shape::tag_info &tinfo, uint32_t tag_variant) {
295+
shape::data<mark,shape::ptr>::walk_variant(tinfo, tag_variant);
296+
}
297+
298+
void walk_box() {
299+
shape::data<mark,shape::ptr>::walk_box_contents();
300+
}
301+
302+
void walk_fn() {
303+
shape::data<mark,shape::ptr>::walk_fn_contents(dp);
304+
}
305+
306+
void walk_obj() {
307+
shape::data<mark,shape::ptr>::walk_obj_contents(dp);
308+
}
309+
310+
void walk_res(const shape::rust_fn *dtor, unsigned n_params,
311+
const shape::type_param *params, const uint8_t *end_sp,
312+
bool live) {
313+
while (this->sp != end_sp) {
314+
this->walk();
315+
align = true;
316+
}
317+
}
318+
319+
void walk_subcontext(mark &sub) { sub.walk(); }
320+
321+
void walk_box_contents(mark &sub, shape::ptr &ref_count_dp) {
322+
if (!ref_count_dp)
323+
return;
324+
325+
if (marked.find((void *)ref_count_dp) != marked.end())
326+
return; // Skip to avoid chasing cycles.
327+
328+
marked.insert((void *)ref_count_dp);
329+
sub.walk();
330+
}
331+
332+
void walk_struct(const uint8_t *end_sp) {
333+
while (this->sp != end_sp) {
334+
this->walk();
335+
align = true;
336+
}
337+
}
338+
339+
void walk_variant(shape::tag_info &tinfo, uint32_t variant_id,
340+
const std::pair<const uint8_t *,const uint8_t *>
341+
variant_ptr_and_end);
342+
343+
template<typename T>
344+
inline void walk_number() { /* no-op */ }
345+
346+
public:
347+
static void do_mark(rust_task *task, const std::vector<void *> &roots,
348+
std::set<void *> &marked);
349+
};
350+
351+
void
352+
mark::walk_variant(shape::tag_info &tinfo, uint32_t variant_id,
353+
const std::pair<const uint8_t *,const uint8_t *>
354+
variant_ptr_and_end) {
355+
mark sub(*this, variant_ptr_and_end.first, tinfo.params);
356+
357+
assert(variant_id < 256); // FIXME: Temporary sanity check.
358+
359+
const uint8_t *variant_end = variant_ptr_and_end.second;
360+
while (sub.sp < variant_end) {
361+
sub.walk();
362+
align = true;
363+
}
364+
}
365+
366+
void
367+
mark::do_mark(rust_task *task, const std::vector<void *> &roots,
368+
std::set<void *> &marked) {
369+
std::vector<void *>::const_iterator begin(roots.begin()),
370+
end(roots.end());
371+
while (begin != end) {
372+
void *alloc = *begin;
373+
if (marked.find(alloc) == marked.end()) {
374+
marked.insert(alloc);
375+
376+
uint8_t *p = reinterpret_cast<uint8_t *>(alloc);
377+
p += sizeof(uintptr_t); // Skip over the reference count.
378+
379+
type_desc *tydesc = task->local_allocs[*begin];
380+
381+
//DPRINT("marking: %p, tydesc=%p\n", p, tydesc);
382+
383+
shape::arena arena;
384+
shape::type_param *params =
385+
shape::type_param::from_tydesc(tydesc, arena);
386+
387+
#if 0
388+
shape::log log(task, true, tydesc->shape, params,
389+
tydesc->shape_tables, p, std::cerr);
390+
log.walk();
391+
DPRINT("\n");
392+
#endif
393+
394+
mark mark(task, true, tydesc->shape, params, tydesc->shape_tables,
395+
p, marked);
396+
mark.walk();
397+
}
398+
399+
++begin;
400+
}
401+
}
402+
403+
404+
void
405+
sweep(rust_task *task, const std::set<void *> &marked) {
406+
std::map<void *,type_desc *>::iterator begin(task->local_allocs.begin()),
407+
end(task->local_allocs.end());
408+
while (begin != end) {
409+
void *alloc = begin->first;
410+
if (marked.find(alloc) == marked.end()) {
411+
DPRINT("object is part of a cycle: %p\n", alloc);
412+
}
413+
++begin;
414+
}
415+
}
416+
417+
190418
void
191419
do_cc(rust_task *task) {
420+
DPRINT("cc; n allocs = %lu\n", task->local_allocs.size());
421+
192422
irc_map ircs;
193423
irc::compute_ircs(task, ircs);
424+
425+
std::vector<void *> roots;
426+
find_roots(task, ircs, roots);
427+
428+
std::set<void *> marked;
429+
mark::do_mark(task, roots, marked);
430+
431+
sweep(task, marked);
194432
}
195433

196434
void

0 commit comments

Comments
 (0)