2 * mini-gc.c: GC interface for the mono JIT
5 * Zoltan Varga (vargaz@gmail.com)
7 * Copyright 2009 Novell, Inc (http://www.novell.com)
15 #include <mono/metadata/gc-internal.h>
16 #include <mono/utils/mono-counters.h>
18 #define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
21 #define DEBUG(s) do { s; } while (0)
27 #define DEBUG_GC_MAP(s) do { s; fflush (stdout); } while (0)
29 #define DEBUG_GC_MAP(s)
32 #define GC_BITS_PER_WORD (sizeof (gsize) * 8)
35 * Per-thread data kept by this module. This is stored in the GC and passed to us as
36 * parameters, instead of being stored in a TLS variable, since during a collection,
37 * only the collection thread is active.
43 MonoJitTlsData *jit_tls;
47 * Contains information needed to mark a stack frame.
48 * FIXME: Optimize the memory usage.
51 /* The frame pointer register */
53 /* The offset of the local variable area in the stack frame relative to the frame pointer */
55 /* The size of the locals area. Can't use gc_refs->size as it includes padding */
58 * If this is set, then the frame contains references which we can't
62 /* A bitmap indicating which stack slots contain a GC ref */
63 /* If no stack slots contain GC refs, then this is NULL */
65 /* A pair of low pc offset-high pc offset for each 1 bit in gc_refs */
66 guint32 live_ranges [MONO_ZERO_LEN_ARRAY];
70 static guint32 gc_maps_size;
73 thread_attach_func (void)
75 return g_new0 (TlsData, 1);
79 thread_suspend_func (gpointer user_data, void *sigctx)
81 TlsData *tls = user_data;
83 tls->lmf = mono_get_lmf ();
85 mono_arch_sigctx_to_monoctx (sigctx, &tls->ctx);
86 tls->has_context = TRUE;
88 tls->has_context = FALSE;
90 tls->jit_tls = TlsGetValue (mono_jit_tls_id);
94 thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gboolean precise)
96 TlsData *tls = user_data;
98 MonoContext ctx, new_ctx;
99 MonoLMF *lmf = tls->lmf;
101 gboolean last = TRUE, managed;
103 guint8* fp, *locals_start, *locals_end;
106 if (mono_thread_current () == NULL) {
108 mono_gc_conservatively_scan_area (stack_start, stack_end);
112 /* FIXME: sgen-gc.c calls this multiple times for each major collection from pin_from_roots */
114 /* FIXME: Use real gc descriptors instead of bitmaps */
116 /* This is one past the last address which we have scanned */
117 stack_limit = stack_start;
119 //DEBUG (printf ("*** %s stack marking %p-%p ***\n", precise ? "Precise" : "Conservative", stack_start, stack_end));
121 if (!tls->has_context) {
122 memset (&new_ctx, 0, sizeof (ctx));
125 memcpy (&ctx, &new_ctx, sizeof (ctx));
127 g_assert ((guint64)stack_limit % sizeof (gpointer) == 0);
129 // FIXME: This doesn't work with appdomain transitions
130 ji = mono_find_jit_info (mono_domain_get (), tls->jit_tls, &res, NULL,
131 &ctx, &new_ctx, NULL, &lmf, NULL, &managed);
132 if (ji == (gpointer)-1)
135 /* The last frame can be in any state so mark conservatively */
141 /* These frames are returned by mono_find_jit_info () two times */
145 /* Scan the frame of this method */
148 * A frame contains the following:
153 * - localloc-ed memory
154 * Currently, only the locals are scanned precisely.
159 if (map->frame_reg == AMD64_RSP)
160 fp = (guint8*)ctx.rsp;
161 else if (map->frame_reg == AMD64_RBP)
162 fp = (guint8*)ctx.rbp;
164 g_assert_not_reached ();
167 g_assert_not_reached ();
170 locals_start = fp + map->locals_offset;
171 locals_end = locals_start + map->locals_size;
173 pc_offset = (guint8*)MONO_CONTEXT_GET_IP (&ctx) - (guint8*)ji->code_start;
174 g_assert (pc_offset >= 0);
176 DEBUG (char *fname = mono_method_full_name (ji->method, TRUE); printf ("Mark: %s offset: 0x%x limit: %p fp: %p locals: %p-%p%s\n", fname, pc_offset, stack_limit, fp, locals_start, locals_end, map->pin ? ", conservative" : ""); g_free (fname));
179 * FIXME: Add a function to mark using a bitmap, to avoid doing a
180 * call for each object.
183 /* Pinning needs to be done first, then the precise scan later */
186 g_assert (locals_start >= stack_limit);
188 if (locals_start > stack_limit) {
189 /* This scans the previously skipped frames as well */
191 DEBUG (printf ("\tConservative scan of %p-%p.\n", stack_limit, locals_start));
192 mono_gc_conservatively_scan_area (stack_limit, locals_start);
197 DEBUG (printf ("\tConservative scan of %p-%p.\n", locals_start, locals_end));
198 mono_gc_conservatively_scan_area (locals_start, locals_end);
201 stack_limit = locals_end;
203 if (!map->pin && map->gc_refs) {
206 for (i = 0; i < mono_bitset_size (map->gc_refs); ++i) {
207 if (mono_bitset_test_fast (map->gc_refs, i)) {
208 MonoObject **ptr = (MonoObject**)(locals_start + (i * sizeof (gpointer)));
209 MonoObject *obj = *ptr;
211 if (pc_offset >= map->live_ranges [loffset] && pc_offset < map->live_ranges [loffset + 1]) {
213 *ptr = mono_gc_scan_object (obj);
214 DEBUG (printf ("\tObjref at %p + 0x%x: %p -> %p.\n", locals_start, (int)(i * sizeof (gpointer)), obj, *ptr));
216 DEBUG (printf ("\tObjref at %p: %p.\n", ptr, obj));
219 DEBUG (printf ("\tDead Objref at %p.\n", ptr));
229 if (stack_limit < stack_end && !precise) {
230 DEBUG (printf ("\tConservative scan of %p-%p.\n", stack_limit, stack_end));
231 mono_gc_conservatively_scan_area (stack_limit, stack_end);
236 DEBUG (printf ("\tConservative scan of %p-%p.\n", stack_start, stack_end));
237 mono_gc_conservatively_scan_area (stack_start, stack_end);
241 //mono_gc_conservatively_scan_area (stack_start, stack_end);
245 mini_gc_create_gc_map (MonoCompile *cfg)
248 int i, nslots, alloc_size, loffset, min_offset, max_offset;
249 MonoBitSet *gc_refs = NULL;
250 gboolean pin = FALSE, norefs = FALSE;
251 guint32 *live_range_start, *live_range_end;
253 min_offset = ALIGN_TO (cfg->locals_min_stack_offset, sizeof (gpointer));
254 max_offset = cfg->locals_max_stack_offset;
256 for (i = cfg->locals_start; i < cfg->num_varinfo; i++) {
257 MonoInst *ins = cfg->varinfo [i];
258 MonoType *t = ins->inst_vtype;
260 if ((MONO_TYPE_ISSTRUCT (t) && ins->klass->has_references))
262 if (t->byref || t->type == MONO_TYPE_PTR)
264 if (ins && ins->opcode == OP_REGOFFSET && MONO_TYPE_IS_REFERENCE (ins->inst_vtype))
268 if (i == cfg->num_varinfo)
271 DEBUG_GC_MAP (printf ("GC Map for %s: 0x%x-0x%x\n", mono_method_full_name (cfg->method, TRUE), min_offset, max_offset));
273 nslots = (max_offset - min_offset) / sizeof (gpointer);
275 alloc_size = mono_bitset_alloc_size (nslots, 0);
276 gc_refs = mono_bitset_mem_new (mono_domain_alloc0 (cfg->domain, alloc_size), (max_offset - min_offset) / sizeof (gpointer), 0);
277 gc_maps_size += alloc_size;
279 live_range_start = g_new (guint32, nslots);
280 live_range_end = g_new (guint32, nslots);
283 for (i = 0; i < nslots; ++i) {
284 live_range_start [i] = (guint32)-1;
285 live_range_end [i] = 0;
288 for (i = cfg->locals_start; i < cfg->num_varinfo; i++) {
289 MonoInst *ins = cfg->varinfo [i];
290 MonoType *t = ins->inst_vtype;
293 vmv = MONO_VARINFO (cfg, i);
295 if ((MONO_TYPE_ISSTRUCT (t) && ins->klass->has_references)) {
299 mono_class_compute_gc_descriptor (ins->klass);
301 bitmap = mono_gc_get_bitmap_for_descr (ins->klass->gc_descr, &numbits);
304 int base_bit_offset = (ins->inst_offset - min_offset) / sizeof (gpointer);
305 for (j = 0; j < numbits; ++j) {
306 if (bitmap [j / GC_BITS_PER_WORD] & (1 << (j % GC_BITS_PER_WORD)))
307 /* The descriptor is for the boxed object */
308 mono_bitset_set_fast (gc_refs, base_bit_offset + j - (sizeof (MonoObject) / sizeof (gpointer)));
312 DEBUG_GC_MAP (printf ("\tVType: %s -> 0x%x\n", mono_type_full_name (ins->inst_vtype), (int)ins->inst_offset));
314 // FIXME: These have no live range
323 if (t->byref || t->type == MONO_TYPE_PTR || t->type == MONO_TYPE_I || t->type == MONO_TYPE_U)
325 if (vmv && !vmv->live_range_start)
330 if (ins && ins->opcode == OP_REGOFFSET && MONO_TYPE_IS_REFERENCE (ins->inst_vtype)) {
331 guint32 pos = (ins->inst_offset - min_offset) / sizeof (gpointer);
333 g_assert (ins->inst_offset % sizeof (gpointer) == 0);
334 g_assert (ins->inst_offset >= min_offset && ins->inst_offset < max_offset);
335 mono_bitset_set_fast (gc_refs, pos);
338 * If stack slots are shared, the live range will be the union of
339 * the live range of variables stored in it. This might cause some
340 * objects to outlive their live ranges.
342 live_range_start [pos] = MIN (live_range_start [pos], vmv->live_range_start);
343 live_range_end [pos] = MAX (live_range_end [pos], vmv->live_range_end);
345 DEBUG_GC_MAP (printf ("\tRef: %s -> 0x%x [0x%x - 0x%x]\n", mono_type_full_name (ins->inst_vtype), (int)ins->inst_offset, vmv->live_range_start, vmv->live_range_end));
349 alloc_size = sizeof (GCMap) + (norefs ? 0 : ((mono_bitset_count (gc_refs) - MONO_ZERO_LEN_ARRAY) * sizeof (guint32) * 2));
350 map = mono_domain_alloc0 (cfg->domain, alloc_size);
351 gc_maps_size += alloc_size;
353 map->frame_reg = cfg->frame_reg;
354 map->locals_offset = min_offset;
355 map->locals_size = ALIGN_TO (max_offset - min_offset, sizeof (gpointer));
356 map->gc_refs = gc_refs;
360 for (i = 0; i < mono_bitset_size (gc_refs); ++i) {
361 if (mono_bitset_test_fast (gc_refs, i)) {
362 map->live_ranges [loffset ++] = live_range_start [i];
363 map->live_ranges [loffset ++] = live_range_end [i];
370 static int precise_count;
373 if (getenv ("PRECISE_COUNT")) {
374 if (precise_count == atoi (getenv ("PRECISE_COUNT")))
375 printf ("LAST: %s\n", mono_method_full_name (cfg->method, TRUE));
376 if (precise_count > atoi (getenv ("PRECISE_COUNT")))
382 cfg->jit_info->gc_info = map;
384 g_free (live_range_start);
385 g_free (live_range_end);
393 memset (&cb, 0, sizeof (cb));
394 cb.thread_attach_func = thread_attach_func;
395 cb.thread_suspend_func = thread_suspend_func;
396 /* Comment this out to disable precise stack marking */
397 //cb.thread_mark_func = thread_mark_func;
398 mono_gc_set_gc_callbacks (&cb);
400 mono_counters_register ("GC Maps size",
401 MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_maps_size);
412 mini_gc_create_gc_map (MonoCompile *cfg)