[interp] Small fixes (#11667)
[mono-project.git] / mono / mini / mini-gc.c
blob754938c14fc2e2e55b44997bb2be1ed23df8d13f
1 /**
2 * \file
3 * GC interface for the mono JIT
5 * Author:
6 * Zoltan Varga (vargaz@gmail.com)
8 * Copyright 2009 Novell, Inc (http://www.novell.com)
9 * Copyright 2011 Xamarin, Inc (http://www.xamarin.com)
10 * Licensed under the MIT license. See LICENSE file in the project root for full license information.
13 #include "config.h"
14 #include "mini-gc.h"
15 #include "mini-runtime.h"
16 #include <mono/metadata/gc-internals.h>
18 static gboolean
19 get_provenance (StackFrameInfo *frame, MonoContext *ctx, gpointer data)
21 MonoJitInfo *ji = frame->ji;
22 MonoMethod *method;
23 if (!ji)
24 return FALSE;
25 method = jinfo_get_method (ji);
26 if (method->wrapper_type != MONO_WRAPPER_NONE)
27 return FALSE;
28 *(gpointer *)data = method;
29 return TRUE;
32 static gpointer
33 get_provenance_func (void)
35 gpointer provenance = NULL;
36 mono_walk_stack (get_provenance, MONO_UNWIND_DEFAULT, (gpointer)&provenance);
37 return provenance;
40 #if 0
41 //#if defined(MONO_ARCH_GC_MAPS_SUPPORTED)
43 #include <mono/metadata/sgen-conf.h>
44 #include <mono/metadata/gc-internals.h>
45 #include <mono/utils/mono-counters.h>
46 #include <mono/utils/unlocked.h>
48 #define SIZEOF_SLOT ((int)sizeof (mgreg_t))
50 #define GC_BITS_PER_WORD (sizeof (mword) * 8)
52 /* Contains state needed by the GC Map construction code */
53 typedef struct {
55 * This contains information about stack slots initialized in the prolog, encoded using
56 * (slot_index << 16) | slot_type. The slot_index is relative to the CFA, i.e. 0
57 * means cfa+0, 1 means cfa-4/8, etc.
59 GSList *stack_slots_from_cfa;
60 /* Same for stack slots relative to the frame pointer */
61 GSList *stack_slots_from_fp;
63 /* Number of slots in the map */
64 int nslots;
65 /* The number of registers in the map */
66 int nregs;
67 /* Min and Max offsets of the stack frame relative to fp */
68 int min_offset, max_offset;
69 /* Same for the locals area */
70 int locals_min_offset, locals_max_offset;
72 /* The call sites where this frame can be stopped during GC */
73 GCCallSite **callsites;
74 /* The number of call sites */
75 int ncallsites;
78 * The width of the stack bitmaps in bytes. This is not equal to the bitmap width at
79 * runtime, since it includes columns which are 0.
81 int stack_bitmap_width;
82 /*
83 * A bitmap whose width equals nslots, and whose height equals ncallsites.
84 * The bitmap contains a 1 if the corresponding stack slot has type SLOT_REF at the
85 * given callsite.
87 guint8 *stack_ref_bitmap;
88 /* Same for SLOT_PIN */
89 guint8 *stack_pin_bitmap;
92 * Similar bitmaps for registers. These have width MONO_MAX_IREGS in bits.
94 int reg_bitmap_width;
95 guint8 *reg_ref_bitmap;
96 guint8 *reg_pin_bitmap;
97 } MonoCompileGC;
99 #undef DEBUG
101 #if 0
102 /* We don't support debug levels, its all-or-nothing */
103 #define DEBUG(s) do { s; fflush (logfile); } while (0)
104 #define DEBUG_ENABLED 1
105 #else
106 #define DEBUG(s)
107 #endif
109 #ifdef DEBUG_ENABLED
110 //#if 1
111 #define DEBUG_PRECISE(s) do { s; } while (0)
112 #define DEBUG_PRECISE_ENABLED
113 #else
114 #define DEBUG_PRECISE(s)
115 #endif
118 * Contains information collected during the conservative stack marking pass,
119 * used during the precise pass. This helps to avoid doing a stack walk twice, which
120 * is expensive.
122 typedef struct {
123 guint8 *bitmap;
124 int nslots;
125 int frame_start_offset;
126 int nreg_locations;
127 /* Relative to stack_start */
128 int reg_locations [MONO_MAX_IREGS];
129 #ifdef DEBUG_PRECISE_ENABLED
130 MonoJitInfo *ji;
131 gpointer fp;
132 int regs [MONO_MAX_IREGS];
133 #endif
134 } FrameInfo;
136 /* Max number of frames stored in the TLS data */
137 #define MAX_FRAMES 50
140 * Per-thread data kept by this module. This is stored in the GC and passed to us as
141 * parameters, instead of being stored in a TLS variable, since during a collection,
142 * only the collection thread is active.
144 typedef struct {
145 MonoThreadUnwindState unwind_state;
146 MonoThreadInfo *info;
147 /* For debugging */
148 mgreg_t tid;
149 gpointer ref_to_track;
150 /* Number of frames collected during the !precise pass */
151 int nframes;
152 FrameInfo frames [MAX_FRAMES];
153 } TlsData;
155 /* These are constant so don't store them in the GC Maps */
156 /* Number of registers stored in gc maps */
157 #define NREGS MONO_MAX_IREGS
160 * The GC Map itself.
161 * Contains information needed to mark a stack frame.
162 * This is a transient structure, created from a compressed representation on-demand.
164 typedef struct {
166 * The offsets of the GC tracked area inside the stack frame relative to the frame pointer.
167 * This includes memory which is NOREF thus doesn't need GC maps.
169 int start_offset;
170 int end_offset;
172 * The offset relative to frame_offset where the the memory described by the GC maps
173 * begins.
175 int map_offset;
176 /* The number of stack slots in the map */
177 int nslots;
178 /* The frame pointer register */
179 guint8 frame_reg;
180 /* The size of each callsite table entry */
181 guint8 callsite_entry_size;
182 guint has_pin_slots : 1;
183 guint has_ref_slots : 1;
184 guint has_ref_regs : 1;
185 guint has_pin_regs : 1;
187 /* The offsets below are into an external bitmaps array */
190 * A bitmap whose width is equal to bitmap_width, and whose height is equal to ncallsites.
191 * The bitmap contains a 1 if the corresponding stack slot has type SLOT_REF at the
192 * given callsite.
194 guint32 stack_ref_bitmap_offset;
196 * Same for SLOT_PIN. It is possible that the same bit is set in both bitmaps at
197 * different callsites, if the slot starts out as PIN, and later changes to REF.
199 guint32 stack_pin_bitmap_offset;
202 * Corresponding bitmaps for registers
203 * These have width equal to the number of bits set in reg_ref_mask/reg_pin_mask.
204 * FIXME: Merge these with the normal bitmaps, i.e. reserve the first x slots for them ?
206 guint32 reg_pin_bitmap_offset;
207 guint32 reg_ref_bitmap_offset;
209 guint32 used_int_regs, reg_ref_mask, reg_pin_mask;
211 /* The number of bits set in the two masks above */
212 guint8 nref_regs, npin_regs;
215 * A bit array marking slots which contain refs.
216 * This is used only for debugging.
218 //guint8 *ref_slots;
220 /* Callsite offsets */
221 /* These can take up a lot of space, so encode them compactly */
222 union {
223 guint8 *offsets8;
224 guint16 *offsets16;
225 guint32 *offsets32;
226 } callsites;
227 int ncallsites;
228 } GCMap;
231 * A compressed version of GCMap. This is what gets stored in MonoJitInfo.
233 typedef struct {
234 //guint8 *ref_slots;
235 //guint8 encoded_size;
238 * The arrays below are embedded after the struct.
239 * Their address needs to be computed.
242 /* The fixed fields of the GCMap encoded using LEB128 */
243 guint8 encoded [MONO_ZERO_LEN_ARRAY];
245 /* An array of ncallsites entries, each entry is callsite_entry_size bytes long */
246 guint8 callsites [MONO_ZERO_LEN_ARRAY];
248 /* The GC bitmaps */
249 guint8 bitmaps [MONO_ZERO_LEN_ARRAY];
250 } GCEncodedMap;
252 static int precise_frame_count [2], precise_frame_limit = -1;
253 static gboolean precise_frame_limit_inited;
255 /* Stats */
256 typedef struct {
257 gint32 scanned_stacks;
258 gint32 scanned;
259 gint32 scanned_precisely;
260 gint32 scanned_conservatively;
261 gint32 scanned_registers;
262 gint32 scanned_native;
263 gint32 scanned_other;
265 gint32 all_slots;
266 gint32 noref_slots;
267 gint32 ref_slots;
268 gint32 pin_slots;
270 gint32 gc_maps_size;
271 gint32 gc_callsites_size;
272 gint32 gc_callsites8_size;
273 gint32 gc_callsites16_size;
274 gint32 gc_callsites32_size;
275 gint32 gc_bitmaps_size;
276 gint32 gc_map_struct_size;
277 gint32 tlsdata_size;
278 } JITGCStats;
280 static JITGCStats stats;
282 static FILE *logfile;
284 static gboolean enable_gc_maps_for_aot;
286 void
287 mini_gc_enable_gc_maps_for_aot (void)
289 enable_gc_maps_for_aot = TRUE;
292 // FIXME: Move these to a shared place
294 static inline void
295 encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf)
297 guint8 *p = buf;
299 do {
300 guint8 b = value & 0x7f;
301 value >>= 7;
302 if (value != 0) /* more bytes to come */
303 b |= 0x80;
304 *p ++ = b;
305 } while (value);
307 *endbuf = p;
310 static G_GNUC_UNUSED void
311 encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf)
313 gboolean more = 1;
314 gboolean negative = (value < 0);
315 guint32 size = 32;
316 guint8 byte;
317 guint8 *p = buf;
319 while (more) {
320 byte = value & 0x7f;
321 value >>= 7;
322 /* the following is unnecessary if the
323 * implementation of >>= uses an arithmetic rather
324 * than logical shift for a signed left operand
326 if (negative)
327 /* sign extend */
328 value |= - (1 <<(size - 7));
329 /* sign bit of byte is second high order bit (0x40) */
330 if ((value == 0 && !(byte & 0x40)) ||
331 (value == -1 && (byte & 0x40)))
332 more = 0;
333 else
334 byte |= 0x80;
335 *p ++= byte;
338 *endbuf = p;
341 static inline guint32
342 decode_uleb128 (guint8 *buf, guint8 **endbuf)
344 guint8 *p = buf;
345 guint32 res = 0;
346 int shift = 0;
348 while (TRUE) {
349 guint8 b = *p;
350 p ++;
352 res = res | (((int)(b & 0x7f)) << shift);
353 if (!(b & 0x80))
354 break;
355 shift += 7;
358 *endbuf = p;
360 return res;
363 static inline gint32
364 decode_sleb128 (guint8 *buf, guint8 **endbuf)
366 guint8 *p = buf;
367 gint32 res = 0;
368 int shift = 0;
370 while (TRUE) {
371 guint8 b = *p;
372 p ++;
374 res = res | (((int)(b & 0x7f)) << shift);
375 shift += 7;
376 if (!(b & 0x80)) {
377 if (shift < 32 && (b & 0x40))
378 res |= - (1 << shift);
379 break;
383 *endbuf = p;
385 return res;
388 static int
389 encode_frame_reg (int frame_reg)
391 #ifdef TARGET_AMD64
392 if (frame_reg == AMD64_RSP)
393 return 0;
394 else if (frame_reg == AMD64_RBP)
395 return 1;
396 #elif defined(TARGET_X86)
397 if (frame_reg == X86_EBP)
398 return 0;
399 else if (frame_reg == X86_ESP)
400 return 1;
401 #elif defined(TARGET_ARM)
402 if (frame_reg == ARMREG_SP)
403 return 0;
404 else if (frame_reg == ARMREG_FP)
405 return 1;
406 #elif defined(TARGET_S390X)
407 if (frame_reg == S390_SP)
408 return 0;
409 else if (frame_reg == S390_FP)
410 return 1;
411 #elif defined (TARGET_RISCV)
412 if (frame_reg == RISCV_SP)
413 return 0;
414 else if (frame_reg == RISCV_FP)
415 return 1;
416 #else
417 NOT_IMPLEMENTED;
418 #endif
419 g_assert_not_reached ();
420 return -1;
423 static int
424 decode_frame_reg (int encoded)
426 #ifdef TARGET_AMD64
427 if (encoded == 0)
428 return AMD64_RSP;
429 else if (encoded == 1)
430 return AMD64_RBP;
431 #elif defined(TARGET_X86)
432 if (encoded == 0)
433 return X86_EBP;
434 else if (encoded == 1)
435 return X86_ESP;
436 #elif defined(TARGET_ARM)
437 if (encoded == 0)
438 return ARMREG_SP;
439 else if (encoded == 1)
440 return ARMREG_FP;
441 #elif defined(TARGET_S390X)
442 if (encoded == 0)
443 return S390_SP;
444 else if (encoded == 1)
445 return S390_FP;
446 #elif defined (TARGET_RISCV)
447 if (encoded == 0)
448 return RISCV_SP;
449 else if (encoded == 1)
450 return RISCV_FP;
451 #else
452 NOT_IMPLEMENTED;
453 #endif
454 g_assert_not_reached ();
455 return -1;
458 #ifdef TARGET_AMD64
459 #ifdef HOST_WIN32
460 static int callee_saved_regs [] = { AMD64_RBP, AMD64_RBX, AMD64_R12, AMD64_R13, AMD64_R14, AMD64_R15, AMD64_RDI, AMD64_RSI };
461 #else
462 static int callee_saved_regs [] = { AMD64_RBP, AMD64_RBX, AMD64_R12, AMD64_R13, AMD64_R14, AMD64_R15 };
463 #endif
464 #elif defined(TARGET_X86)
465 static int callee_saved_regs [] = { X86_EBX, X86_ESI, X86_EDI };
466 #elif defined(TARGET_ARM)
467 static int callee_saved_regs [] = { ARMREG_V1, ARMREG_V2, ARMREG_V3, ARMREG_V4, ARMREG_V5, ARMREG_V7, ARMREG_FP };
468 #elif defined(TARGET_ARM64)
469 // FIXME:
470 static int callee_saved_regs [] = { };
471 #elif defined(TARGET_S390X)
472 static int callee_saved_regs [] = { s390_r6, s390_r7, s390_r8, s390_r9, s390_r10, s390_r11, s390_r12, s390_r13, s390_r14 };
473 #elif defined(TARGET_POWERPC64) && _CALL_ELF == 2
474 static int callee_saved_regs [] = {
475 ppc_r13, ppc_r14, ppc_r15, ppc_r16,
476 ppc_r17, ppc_r18, ppc_r19, ppc_r20,
477 ppc_r21, ppc_r22, ppc_r23, ppc_r24,
478 ppc_r25, ppc_r26, ppc_r27, ppc_r28,
479 ppc_r29, ppc_r30, ppc_r31 };
480 #elif defined(TARGET_POWERPC)
481 static int callee_saved_regs [] = { ppc_r6, ppc_r7, ppc_r8, ppc_r9, ppc_r10, ppc_r11, ppc_r12, ppc_r13, ppc_r14 };
482 #elif defined (TARGET_RISCV)
483 static int callee_saved_regs [] = {
484 RISCV_S0, RISCV_S1, RISCV_S2, RISCV_S3, RISCV_S4, RISCV_S5,
485 RISCV_S6, RISCV_S7, RISCV_S8, RISCV_S9, RISCV_S10, RISCV_S11,
487 #endif
489 static guint32
490 encode_regmask (guint32 regmask)
492 int i;
493 guint32 res;
495 res = 0;
496 for (i = 0; i < sizeof (callee_saved_regs) / sizeof (int); ++i) {
497 if (regmask & (1 << callee_saved_regs [i])) {
498 res |= (1 << i);
499 regmask -= (1 << callee_saved_regs [i]);
502 g_assert (regmask == 0);
503 return res;
506 static guint32
507 decode_regmask (guint32 regmask)
509 int i;
510 guint32 res;
512 res = 0;
513 for (i = 0; i < sizeof (callee_saved_regs) / sizeof (int); ++i)
514 if (regmask & (1 << i))
515 res |= (1 << callee_saved_regs [i]);
516 return res;
520 * encode_gc_map:
522 * Encode the fixed fields of MAP into a buffer pointed to by BUF.
524 static void
525 encode_gc_map (GCMap *map, guint8 *buf, guint8 **endbuf)
527 guint32 flags, freg;
529 encode_sleb128 (map->start_offset / SIZEOF_SLOT, buf, &buf);
530 encode_sleb128 (map->end_offset / SIZEOF_SLOT, buf, &buf);
531 encode_sleb128 (map->map_offset / SIZEOF_SLOT, buf, &buf);
532 encode_uleb128 (map->nslots, buf, &buf);
533 g_assert (map->callsite_entry_size <= 4);
534 freg = encode_frame_reg (map->frame_reg);
535 g_assert (freg < 2);
536 flags = (map->has_ref_slots ? 1 : 0) | (map->has_pin_slots ? 2 : 0) | (map->has_ref_regs ? 4 : 0) | (map->has_pin_regs ? 8 : 0) | ((map->callsite_entry_size - 1) << 4) | (freg << 6);
537 encode_uleb128 (flags, buf, &buf);
538 encode_uleb128 (encode_regmask (map->used_int_regs), buf, &buf);
539 if (map->has_ref_regs)
540 encode_uleb128 (encode_regmask (map->reg_ref_mask), buf, &buf);
541 if (map->has_pin_regs)
542 encode_uleb128 (encode_regmask (map->reg_pin_mask), buf, &buf);
543 encode_uleb128 (map->ncallsites, buf, &buf);
545 *endbuf = buf;
549 * decode_gc_map:
551 * Decode the encoded GC map representation in BUF and store the result into MAP.
553 static void
554 decode_gc_map (guint8 *buf, GCMap *map, guint8 **endbuf)
556 guint32 flags;
557 int stack_bitmap_size, reg_ref_bitmap_size, reg_pin_bitmap_size, offset, freg;
558 int i, n;
560 map->start_offset = decode_sleb128 (buf, &buf) * SIZEOF_SLOT;
561 map->end_offset = decode_sleb128 (buf, &buf) * SIZEOF_SLOT;
562 map->map_offset = decode_sleb128 (buf, &buf) * SIZEOF_SLOT;
563 map->nslots = decode_uleb128 (buf, &buf);
564 flags = decode_uleb128 (buf, &buf);
565 map->has_ref_slots = (flags & 1) ? 1 : 0;
566 map->has_pin_slots = (flags & 2) ? 1 : 0;
567 map->has_ref_regs = (flags & 4) ? 1 : 0;
568 map->has_pin_regs = (flags & 8) ? 1 : 0;
569 map->callsite_entry_size = ((flags >> 4) & 0x3) + 1;
570 freg = flags >> 6;
571 map->frame_reg = decode_frame_reg (freg);
572 map->used_int_regs = decode_regmask (decode_uleb128 (buf, &buf));
573 if (map->has_ref_regs) {
574 map->reg_ref_mask = decode_regmask (decode_uleb128 (buf, &buf));
575 n = 0;
576 for (i = 0; i < NREGS; ++i)
577 if (map->reg_ref_mask & (1 << i))
578 n ++;
579 map->nref_regs = n;
581 if (map->has_pin_regs) {
582 map->reg_pin_mask = decode_regmask (decode_uleb128 (buf, &buf));
583 n = 0;
584 for (i = 0; i < NREGS; ++i)
585 if (map->reg_pin_mask & (1 << i))
586 n ++;
587 map->npin_regs = n;
589 map->ncallsites = decode_uleb128 (buf, &buf);
591 stack_bitmap_size = (ALIGN_TO (map->nslots, 8) / 8) * map->ncallsites;
592 reg_ref_bitmap_size = (ALIGN_TO (map->nref_regs, 8) / 8) * map->ncallsites;
593 reg_pin_bitmap_size = (ALIGN_TO (map->npin_regs, 8) / 8) * map->ncallsites;
594 offset = 0;
595 map->stack_ref_bitmap_offset = offset;
596 if (map->has_ref_slots)
597 offset += stack_bitmap_size;
598 map->stack_pin_bitmap_offset = offset;
599 if (map->has_pin_slots)
600 offset += stack_bitmap_size;
601 map->reg_ref_bitmap_offset = offset;
602 if (map->has_ref_regs)
603 offset += reg_ref_bitmap_size;
604 map->reg_pin_bitmap_offset = offset;
605 if (map->has_pin_regs)
606 offset += reg_pin_bitmap_size;
608 *endbuf = buf;
611 static gpointer
612 thread_attach_func (void)
614 TlsData *tls;
616 tls = g_new0 (TlsData, 1);
617 tls->tid = mono_native_thread_id_get ();
618 tls->info = mono_thread_info_current ();
619 UnlockedAdd (&stats.tlsdata_size, sizeof (TlsData));
621 return tls;
624 static void
625 thread_detach_func (gpointer user_data)
627 TlsData *tls = user_data;
629 g_free (tls);
632 static void
633 thread_suspend_func (gpointer user_data, void *sigctx, MonoContext *ctx)
635 TlsData *tls = user_data;
637 if (!tls) {
638 /* Happens during startup */
639 return;
642 if (tls->tid != mono_native_thread_id_get ()) {
643 /* Happens on osx because threads are not suspended using signals */
644 #ifndef TARGET_WIN32
645 gboolean res;
646 #endif
648 g_assert (tls->info);
649 #ifdef TARGET_WIN32
650 return;
651 #else
652 res = mono_thread_state_init_from_handle (&tls->unwind_state, tls->info, NULL);
653 #endif
654 } else {
655 tls->unwind_state.unwind_data [MONO_UNWIND_DATA_LMF] = mono_get_lmf ();
656 if (sigctx) {
657 #ifdef MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX
658 mono_sigctx_to_monoctx (sigctx, &tls->unwind_state.ctx);
659 tls->unwind_state.valid = TRUE;
660 #else
661 tls->unwind_state.valid = FALSE;
662 #endif
663 } else if (ctx) {
664 memcpy (&tls->unwind_state.ctx, ctx, sizeof (MonoContext));
665 tls->unwind_state.valid = TRUE;
666 } else {
667 tls->unwind_state.valid = FALSE;
669 tls->unwind_state.unwind_data [MONO_UNWIND_DATA_JIT_TLS] = mono_tls_get_jit_tls ();
670 tls->unwind_state.unwind_data [MONO_UNWIND_DATA_DOMAIN] = mono_domain_get ();
673 if (!tls->unwind_state.unwind_data [MONO_UNWIND_DATA_DOMAIN]) {
674 /* Happens during startup */
675 tls->unwind_state.valid = FALSE;
676 return;
680 #define DEAD_REF ((gpointer)(gssize)0x2a2a2a2a2a2a2a2aULL)
682 static inline void
683 set_bit (guint8 *bitmap, int width, int y, int x)
685 bitmap [(width * y) + (x / 8)] |= (1 << (x % 8));
688 static inline void
689 clear_bit (guint8 *bitmap, int width, int y, int x)
691 bitmap [(width * y) + (x / 8)] &= ~(1 << (x % 8));
694 static inline int
695 get_bit (guint8 *bitmap, int width, int y, int x)
697 return bitmap [(width * y) + (x / 8)] & (1 << (x % 8));
700 static const char*
701 slot_type_to_string (GCSlotType type)
703 switch (type) {
704 case SLOT_REF:
705 return "ref";
706 case SLOT_NOREF:
707 return "noref";
708 case SLOT_PIN:
709 return "pin";
710 default:
711 g_assert_not_reached ();
712 return NULL;
716 static inline host_mgreg_t
717 get_frame_pointer (MonoContext *ctx, int frame_reg)
719 #if defined(TARGET_AMD64)
720 if (frame_reg == AMD64_RSP)
721 return ctx->rsp;
722 else if (frame_reg == AMD64_RBP)
723 return ctx->rbp;
724 #elif defined(TARGET_X86)
725 if (frame_reg == X86_ESP)
726 return ctx->esp;
727 else if (frame_reg == X86_EBP)
728 return ctx->ebp;
729 #elif defined(TARGET_ARM)
730 if (frame_reg == ARMREG_SP)
731 return (host_mgreg_t)MONO_CONTEXT_GET_SP (ctx);
732 else if (frame_reg == ARMREG_FP)
733 return (host_mgreg_t)MONO_CONTEXT_GET_BP (ctx);
734 #elif defined(TARGET_S390X)
735 if (frame_reg == S390_SP)
736 return (host_mgreg_t)MONO_CONTEXT_GET_SP (ctx);
737 else if (frame_reg == S390_FP)
738 return (host_mgreg_t)MONO_CONTEXT_GET_BP (ctx);
739 #elif defined (TARGET_RISCV)
740 if (frame_reg == RISCV_SP)
741 return MONO_CONTEXT_GET_SP (ctx);
742 else if (frame_reg == RISCV_FP)
743 return MONO_CONTEXT_GET_BP (ctx);
744 #endif
745 g_assert_not_reached ();
746 return 0;
750 * conservatively_pass:
752 * Mark a thread stack conservatively and collect information needed by the precise pass.
754 static void
755 conservative_pass (TlsData *tls, guint8 *stack_start, guint8 *stack_end)
757 MonoJitInfo *ji;
758 MonoMethod *method;
759 MonoContext ctx, new_ctx;
760 MonoLMF *lmf;
761 guint8 *stack_limit;
762 gboolean last = TRUE;
763 GCMap *map;
764 GCMap map_tmp;
765 GCEncodedMap *emap;
766 guint8* fp, *p, *real_frame_start, *frame_start, *frame_end;
767 int i, pc_offset, cindex, bitmap_width;
768 int scanned = 0, scanned_precisely, scanned_conservatively, scanned_registers;
769 gboolean res;
770 StackFrameInfo frame;
771 host_mgreg_t *reg_locations [MONO_MAX_IREGS];
772 host_mgreg_t *new_reg_locations [MONO_MAX_IREGS];
773 guint8 *bitmaps;
774 FrameInfo *fi;
775 guint32 precise_regmask;
777 if (tls) {
778 tls->nframes = 0;
779 tls->ref_to_track = NULL;
782 /* tls == NULL can happen during startup */
783 if (mono_thread_internal_current () == NULL || !tls) {
784 mono_gc_conservatively_scan_area (stack_start, stack_end);
785 UnlockedAdd (&stats.scanned_stacks, stack_end - stack_start);
786 return;
789 lmf = tls->unwind_state.unwind_data [MONO_UNWIND_DATA_LMF];
790 frame.domain = NULL;
792 /* Number of bytes scanned based on GC map data */
793 scanned = 0;
794 /* Number of bytes scanned precisely based on GC map data */
795 scanned_precisely = 0;
796 /* Number of bytes scanned conservatively based on GC map data */
797 scanned_conservatively = 0;
798 /* Number of bytes scanned conservatively in register save areas */
799 scanned_registers = 0;
801 /* This is one past the last address which we have scanned */
802 stack_limit = stack_start;
804 if (!tls->unwind_state.valid)
805 memset (&new_ctx, 0, sizeof (ctx));
806 else
807 memcpy (&new_ctx, &tls->unwind_state.ctx, sizeof (MonoContext));
809 memset (reg_locations, 0, sizeof (reg_locations));
810 memset (new_reg_locations, 0, sizeof (new_reg_locations));
812 while (TRUE) {
813 if (!tls->unwind_state.valid)
814 break;
816 memcpy (&ctx, &new_ctx, sizeof (ctx));
818 for (i = 0; i < MONO_MAX_IREGS; ++i) {
819 if (new_reg_locations [i]) {
821 * If the current frame saves the register, it means it might modify its
822 * value, thus the old location might not contain the same value, so
823 * we have to mark it conservatively.
825 if (reg_locations [i]) {
826 DEBUG (fprintf (logfile, "\tscan saved reg %s location %p.\n", mono_arch_regname (i), reg_locations [i]));
827 mono_gc_conservatively_scan_area (reg_locations [i], (char*)reg_locations [i] + SIZEOF_SLOT);
828 scanned_registers += SIZEOF_SLOT;
831 reg_locations [i] = new_reg_locations [i];
833 DEBUG (fprintf (logfile, "\treg %s is now at location %p.\n", mono_arch_regname (i), reg_locations [i]));
837 g_assert ((gsize)stack_limit % SIZEOF_SLOT == 0);
839 res = mono_find_jit_info_ext (frame.domain ? frame.domain : tls->unwind_state.unwind_data [MONO_UNWIND_DATA_DOMAIN], tls->unwind_state.unwind_data [MONO_UNWIND_DATA_JIT_TLS], NULL, &ctx, &new_ctx, NULL, &lmf, new_reg_locations, &frame);
840 if (!res)
841 break;
843 ji = frame.ji;
845 // FIXME: For skipped frames, scan the param area of the parent frame conservatively ?
846 // FIXME: trampolines
848 if (frame.type == FRAME_TYPE_MANAGED_TO_NATIVE) {
850 * These frames are problematic for several reasons:
851 * - they are unwound through an LMF, and we have no precise register tracking for those.
852 * - the LMF might not contain a precise ip, so we can't compute the call site.
853 * - the LMF only unwinds to the wrapper frame, so we get these methods twice.
855 DEBUG (fprintf (logfile, "Mark(0): <Managed-to-native transition>\n"));
856 for (i = 0; i < MONO_MAX_IREGS; ++i) {
857 if (reg_locations [i]) {
858 DEBUG (fprintf (logfile, "\tscan saved reg %s location %p.\n", mono_arch_regname (i), reg_locations [i]));
859 mono_gc_conservatively_scan_area (reg_locations [i], (char*)reg_locations [i] + SIZEOF_SLOT);
860 scanned_registers += SIZEOF_SLOT;
862 reg_locations [i] = NULL;
863 new_reg_locations [i] = NULL;
865 ctx = new_ctx;
866 continue;
869 if (ji)
870 method = jinfo_get_method (ji);
871 else
872 method = NULL;
874 /* The last frame can be in any state so mark conservatively */
875 if (last) {
876 if (ji) {
877 DEBUG (char *fname = mono_method_full_name (method, TRUE); fprintf (logfile, "Mark(0): %s+0x%x (%p)\n", fname, pc_offset, (gpointer)MONO_CONTEXT_GET_IP (&ctx)); g_free (fname));
879 DEBUG (fprintf (logfile, "\t <Last frame>\n"));
880 last = FALSE;
882 * new_reg_locations is not precise when a method is interrupted during its epilog, so clear it.
884 for (i = 0; i < MONO_MAX_IREGS; ++i) {
885 if (reg_locations [i]) {
886 DEBUG (fprintf (logfile, "\tscan saved reg %s location %p.\n", mono_arch_regname (i), reg_locations [i]));
887 mono_gc_conservatively_scan_area (reg_locations [i], (char*)reg_locations [i] + SIZEOF_SLOT);
888 scanned_registers += SIZEOF_SLOT;
890 if (new_reg_locations [i]) {
891 DEBUG (fprintf (logfile, "\tscan saved reg %s location %p.\n", mono_arch_regname (i), new_reg_locations [i]));
892 mono_gc_conservatively_scan_area (new_reg_locations [i], (char*)new_reg_locations [i] + SIZEOF_SLOT);
893 scanned_registers += SIZEOF_SLOT;
895 reg_locations [i] = NULL;
896 new_reg_locations [i] = NULL;
898 continue;
901 pc_offset = (guint8*)MONO_CONTEXT_GET_IP (&ctx) - (guint8*)ji->code_start;
903 /* These frames are very problematic */
904 if (method->wrapper_type == MONO_WRAPPER_MANAGED_TO_NATIVE) {
905 DEBUG (char *fname = mono_method_full_name (method, TRUE); fprintf (logfile, "Mark(0): %s+0x%x (%p)\n", fname, pc_offset, (gpointer)MONO_CONTEXT_GET_IP (&ctx)); g_free (fname));
906 DEBUG (fprintf (logfile, "\tSkip.\n"));
907 continue;
910 /* All the other frames are at a call site */
912 if (tls->nframes == MAX_FRAMES) {
914 * Can't save information since the array is full. So scan the rest of the
915 * stack conservatively.
917 DEBUG (fprintf (logfile, "Mark (0): Frame stack full.\n"));
918 break;
921 /* Scan the frame of this method */
924 * A frame contains the following:
925 * - saved registers
926 * - saved args
927 * - locals
928 * - spill area
929 * - localloc-ed memory
931 g_assert (pc_offset >= 0);
933 emap = ji->gc_info;
935 if (!emap) {
936 DEBUG (char *fname = mono_method_full_name (jinfo_get_method (ji), TRUE); fprintf (logfile, "Mark(0): %s+0x%x (%p)\n", fname, pc_offset, (gpointer)MONO_CONTEXT_GET_IP (&ctx)); g_free (fname));
937 DEBUG (fprintf (logfile, "\tNo GC Map.\n"));
938 continue;
941 /* The embedded callsite table requires this */
942 g_assert (((gsize)emap % 4) == 0);
945 * Debugging aid to control the number of frames scanned precisely
947 if (!precise_frame_limit_inited) {
948 char *mono_precise_count = g_getenv ("MONO_PRECISE_COUNT");
949 if (mono_precise_count) {
950 precise_frame_limit = atoi (mono_precise_count);
951 g_free (mono_precise_count);
953 precise_frame_limit_inited = TRUE;
956 if (precise_frame_limit != -1) {
957 if (precise_frame_count [FALSE] == precise_frame_limit)
958 printf ("LAST PRECISE FRAME: %s\n", mono_method_full_name (method, TRUE));
959 if (precise_frame_count [FALSE] > precise_frame_limit)
960 continue;
962 precise_frame_count [FALSE] ++;
964 /* Decode the encoded GC map */
965 map = &map_tmp;
966 memset (map, 0, sizeof (GCMap));
967 decode_gc_map (&emap->encoded [0], map, &p);
968 p = (guint8*)ALIGN_TO (p, map->callsite_entry_size);
969 map->callsites.offsets8 = p;
970 p += map->callsite_entry_size * map->ncallsites;
971 bitmaps = p;
973 fp = (guint8*)get_frame_pointer (&ctx, map->frame_reg);
975 real_frame_start = fp + map->start_offset;
976 frame_start = fp + map->start_offset + map->map_offset;
977 frame_end = fp + map->end_offset;
979 DEBUG (char *fname = mono_method_full_name (jinfo_get_method (ji), TRUE); fprintf (logfile, "Mark(0): %s+0x%x (%p) limit=%p fp=%p frame=%p-%p (%d)\n", fname, pc_offset, (gpointer)MONO_CONTEXT_GET_IP (&ctx), stack_limit, fp, frame_start, frame_end, (int)(frame_end - frame_start)); g_free (fname));
981 /* Find the callsite index */
982 if (map->callsite_entry_size == 1) {
983 for (i = 0; i < map->ncallsites; ++i)
984 /* ip points inside the call instruction */
985 if (map->callsites.offsets8 [i] == pc_offset + 1)
986 break;
987 } else if (map->callsite_entry_size == 2) {
988 // FIXME: Use a binary search
989 for (i = 0; i < map->ncallsites; ++i)
990 /* ip points inside the call instruction */
991 if (map->callsites.offsets16 [i] == pc_offset + 1)
992 break;
993 } else {
994 // FIXME: Use a binary search
995 for (i = 0; i < map->ncallsites; ++i)
996 /* ip points inside the call instruction */
997 if (map->callsites.offsets32 [i] == pc_offset + 1)
998 break;
1000 if (i == map->ncallsites) {
1001 printf ("Unable to find ip offset 0x%x in callsite list of %s.\n", pc_offset + 1, mono_method_full_name (method, TRUE));
1002 g_assert_not_reached ();
1004 cindex = i;
1007 * This is not neccessary true on x86 because frames have a different size at each
1008 * call site.
1010 //g_assert (real_frame_start >= stack_limit);
1012 if (real_frame_start > stack_limit) {
1013 /* This scans the previously skipped frames as well */
1014 DEBUG (fprintf (logfile, "\tscan area %p-%p (%d).\n", stack_limit, real_frame_start, (int)(real_frame_start - stack_limit)));
1015 mono_gc_conservatively_scan_area (stack_limit, real_frame_start);
1016 UnlockedAdd (&stats.scanned_other, real_frame_start - stack_limit);
1019 /* Mark stack slots */
1020 if (map->has_pin_slots) {
1021 int bitmap_width = ALIGN_TO (map->nslots, 8) / 8;
1022 guint8 *pin_bitmap = &bitmaps [map->stack_pin_bitmap_offset + (bitmap_width * cindex)];
1023 guint8 *p;
1024 gboolean pinned;
1026 p = frame_start;
1027 for (i = 0; i < map->nslots; ++i) {
1028 pinned = pin_bitmap [i / 8] & (1 << (i % 8));
1029 if (pinned) {
1030 DEBUG (fprintf (logfile, "\tscan slot %s0x%x(fp)=%p.\n", (guint8*)p > (guint8*)fp ? "" : "-", ABS ((int)((gssize)p - (gssize)fp)), p));
1031 mono_gc_conservatively_scan_area (p, p + SIZEOF_SLOT);
1032 scanned_conservatively += SIZEOF_SLOT;
1033 } else {
1034 scanned_precisely += SIZEOF_SLOT;
1036 p += SIZEOF_SLOT;
1038 } else {
1039 scanned_precisely += (map->nslots * SIZEOF_SLOT);
1042 /* The area outside of start-end is NOREF */
1043 scanned_precisely += (map->end_offset - map->start_offset) - (map->nslots * SIZEOF_SLOT);
1045 /* Mark registers */
1046 precise_regmask = map->used_int_regs | (1 << map->frame_reg);
1047 if (map->has_pin_regs) {
1048 int bitmap_width = ALIGN_TO (map->npin_regs, 8) / 8;
1049 guint8 *pin_bitmap = &bitmaps [map->reg_pin_bitmap_offset + (bitmap_width * cindex)];
1050 int bindex = 0;
1051 for (i = 0; i < NREGS; ++i) {
1052 if (!(map->used_int_regs & (1 << i)))
1053 continue;
1055 if (!(map->reg_pin_mask & (1 << i)))
1056 continue;
1058 if (pin_bitmap [bindex / 8] & (1 << (bindex % 8))) {
1059 DEBUG (fprintf (logfile, "\treg %s saved at 0x%p is pinning.\n", mono_arch_regname (i), reg_locations [i]));
1060 precise_regmask &= ~(1 << i);
1062 bindex ++;
1066 scanned += map->end_offset - map->start_offset;
1068 g_assert (scanned == scanned_precisely + scanned_conservatively);
1070 stack_limit = frame_end;
1072 /* Save information for the precise pass */
1073 fi = &tls->frames [tls->nframes];
1074 fi->nslots = map->nslots;
1075 bitmap_width = ALIGN_TO (map->nslots, 8) / 8;
1076 if (map->has_ref_slots)
1077 fi->bitmap = &bitmaps [map->stack_ref_bitmap_offset + (bitmap_width * cindex)];
1078 else
1079 fi->bitmap = NULL;
1080 fi->frame_start_offset = frame_start - stack_start;
1081 fi->nreg_locations = 0;
1082 DEBUG_PRECISE (fi->ji = ji);
1083 DEBUG_PRECISE (fi->fp = fp);
1085 if (map->has_ref_regs) {
1086 int bitmap_width = ALIGN_TO (map->nref_regs, 8) / 8;
1087 guint8 *ref_bitmap = &bitmaps [map->reg_ref_bitmap_offset + (bitmap_width * cindex)];
1088 int bindex = 0;
1089 for (i = 0; i < NREGS; ++i) {
1090 if (!(map->reg_ref_mask & (1 << i)))
1091 continue;
1093 if (reg_locations [i] && (ref_bitmap [bindex / 8] & (1 << (bindex % 8)))) {
1094 DEBUG_PRECISE (fi->regs [fi->nreg_locations] = i);
1095 DEBUG (fprintf (logfile, "\treg %s saved at 0x%p is ref.\n", mono_arch_regname (i), reg_locations [i]));
1096 fi->reg_locations [fi->nreg_locations] = (guint8*)reg_locations [i] - stack_start;
1097 fi->nreg_locations ++;
1099 bindex ++;
1104 * Clear locations of precisely tracked registers.
1106 if (precise_regmask) {
1107 for (i = 0; i < NREGS; ++i) {
1108 if (precise_regmask & (1 << i)) {
1110 * The method uses this register, and we have precise info for it.
1111 * This means the location will be scanned precisely.
1112 * Tell the code at the beginning of the loop that this location is
1113 * processed.
1115 if (reg_locations [i])
1116 DEBUG (fprintf (logfile, "\treg %s at location %p (==%p) is precise.\n", mono_arch_regname (i), reg_locations [i], (gpointer)*reg_locations [i]));
1117 reg_locations [i] = NULL;
1122 tls->nframes ++;
1125 /* Scan the remaining register save locations */
1126 for (i = 0; i < MONO_MAX_IREGS; ++i) {
1127 if (reg_locations [i]) {
1128 DEBUG (fprintf (logfile, "\tscan saved reg location %p.\n", reg_locations [i]));
1129 mono_gc_conservatively_scan_area (reg_locations [i], (char*)reg_locations [i] + SIZEOF_SLOT);
1130 scanned_registers += SIZEOF_SLOT;
1132 if (new_reg_locations [i]) {
1133 DEBUG (fprintf (logfile, "\tscan saved reg location %p.\n", new_reg_locations [i]));
1134 mono_gc_conservatively_scan_area (new_reg_locations [i], (char*)new_reg_locations [i] + SIZEOF_SLOT);
1135 scanned_registers += SIZEOF_SLOT;
1139 if (stack_limit < stack_end) {
1140 DEBUG (fprintf (logfile, "\tscan remaining stack %p-%p (%d).\n", stack_limit, stack_end, (int)(stack_end - stack_limit)));
1141 mono_gc_conservatively_scan_area (stack_limit, stack_end);
1142 UnlockedAdd (&stats.scanned_native, stack_end - stack_limit);
1145 DEBUG (fprintf (logfile, "Marked %d bytes, p=%d,c=%d out of %d.\n", scanned, scanned_precisely, scanned_conservatively, (int)(stack_end - stack_start)));
1147 UnlockedAdd (&stats.scanned_stacks, stack_end - stack_start);
1148 UnlockedAdd (&stats.scanned, scanned);
1149 UnlockedAdd (&stats.scanned_precisely, scanned_precisely);
1150 UnlockedAdd (&stats.scanned_conservatively, scanned_conservatively);
1151 UnlockedAdd (&stats.scanned_registers, scanned_registers);
1153 //mono_gc_conservatively_scan_area (stack_start, stack_end);
1157 * precise_pass:
1159 * Mark a thread stack precisely based on information saved during the conservative
1160 * pass.
1162 static void
1163 precise_pass (TlsData *tls, guint8 *stack_start, guint8 *stack_end, void *gc_data)
1165 int findex, i;
1166 FrameInfo *fi;
1167 guint8 *frame_start;
1169 if (!tls)
1170 return;
1172 if (!tls->unwind_state.valid)
1173 return;
1175 for (findex = 0; findex < tls->nframes; findex ++) {
1176 /* Load information saved by the !precise pass */
1177 fi = &tls->frames [findex];
1178 frame_start = stack_start + fi->frame_start_offset;
1180 DEBUG (char *fname = mono_method_full_name (jinfo_get_method (fi->ji), TRUE); fprintf (logfile, "Mark(1): %s\n", fname); g_free (fname));
1183 * FIXME: Add a function to mark using a bitmap, to avoid doing a
1184 * call for each object.
1187 /* Mark stack slots */
1188 if (fi->bitmap) {
1189 guint8 *ref_bitmap = fi->bitmap;
1190 gboolean live;
1192 for (i = 0; i < fi->nslots; ++i) {
1193 MonoObject **ptr = (MonoObject**)(frame_start + (i * SIZEOF_SLOT));
1195 live = ref_bitmap [i / 8] & (1 << (i % 8));
1197 if (live) {
1198 MonoObject *obj = *ptr;
1199 if (obj) {
1200 DEBUG (fprintf (logfile, "\tref %s0x%x(fp)=%p: %p ->", (guint8*)ptr >= (guint8*)fi->fp ? "" : "-", ABS ((int)((gssize)ptr - (gssize)fi->fp)), ptr, obj));
1201 *ptr = mono_gc_scan_object (obj, gc_data);
1202 DEBUG (fprintf (logfile, " %p.\n", *ptr));
1203 } else {
1204 DEBUG (fprintf (logfile, "\tref %s0x%x(fp)=%p: %p.\n", (guint8*)ptr >= (guint8*)fi->fp ? "" : "-", ABS ((int)((gssize)ptr - (gssize)fi->fp)), ptr, obj));
1206 } else {
1207 #if 0
1209 * This is disabled because the pointer takes up a lot of space.
1210 * Stack slots might be shared between ref and non-ref variables ?
1212 if (map->ref_slots [i / 8] & (1 << (i % 8))) {
1213 DEBUG (fprintf (logfile, "\tref %s0x%x(fp)=%p: dead (%p)\n", (guint8*)ptr >= (guint8*)fi->fp ? "" : "-", ABS ((int)((gssize)ptr - (gssize)fi->fp)), ptr, *ptr));
1215 * Fail fast if the live range is incorrect, and
1216 * the JITted code tries to access this object
1218 *ptr = DEAD_REF;
1220 #endif
1225 /* Mark registers */
1228 * Registers are different from stack slots, they have no address where they
1229 * are stored. Instead, some frame below this frame in the stack saves them
1230 * in its prolog to the stack. We can mark this location precisely.
1232 for (i = 0; i < fi->nreg_locations; ++i) {
1234 * reg_locations [i] contains the address of the stack slot where
1235 * a reg was last saved, so mark that slot.
1237 MonoObject **ptr = (MonoObject**)((guint8*)stack_start + fi->reg_locations [i]);
1238 MonoObject *obj = *ptr;
1240 if (obj) {
1241 DEBUG (fprintf (logfile, "\treg %s saved at %p: %p ->", mono_arch_regname (fi->regs [i]), ptr, obj));
1242 *ptr = mono_gc_scan_object (obj, gc_data);
1243 DEBUG (fprintf (logfile, " %p.\n", *ptr));
1244 } else {
1245 DEBUG (fprintf (logfile, "\treg %s saved at %p: %p\n", mono_arch_regname (fi->regs [i]), ptr, obj));
1251 * Debugging aid to check for missed refs.
1253 if (tls->ref_to_track) {
1254 gpointer *p;
1256 for (p = (gpointer*)stack_start; p < (gpointer*)stack_end; ++p)
1257 if (*p == tls->ref_to_track)
1258 printf ("REF AT %p.\n", p);
1263 * thread_mark_func:
1265 * This is called by the GC twice to mark a thread stack. PRECISE is FALSE at the first
1266 * call, and TRUE at the second. USER_DATA points to a TlsData
1267 * structure filled up by thread_suspend_func.
1269 static void
1270 thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gboolean precise, void *gc_data)
1272 TlsData *tls = user_data;
1274 DEBUG (fprintf (logfile, "****************************************\n"));
1275 DEBUG (fprintf (logfile, "*** %s stack marking for thread %p (%p-%p) ***\n", precise ? "Precise" : "Conservative", tls ? GUINT_TO_POINTER (tls->tid) : NULL, stack_start, stack_end));
1276 DEBUG (fprintf (logfile, "****************************************\n"));
1278 if (!precise)
1279 conservative_pass (tls, stack_start, stack_end);
1280 else
1281 precise_pass (tls, stack_start, stack_end, gc_data);
1284 #ifndef DISABLE_JIT
1286 static void
1287 mini_gc_init_gc_map (MonoCompile *cfg)
1289 if (COMPILE_LLVM (cfg))
1290 return;
1292 if (!mono_gc_is_moving ())
1293 return;
1295 if (cfg->compile_aot) {
1296 if (!enable_gc_maps_for_aot)
1297 return;
1298 } else if (!mono_gc_precise_stack_mark_enabled ())
1299 return;
1301 #if 1
1302 /* Debugging support */
1304 static int precise_count;
1306 precise_count ++;
1307 char *mono_gcmap_count = g_getenv ("MONO_GCMAP_COUNT");
1308 if (mono_gcmap_count) {
1309 int count = atoi (mono_gcmap_count);
1310 g_free (mono_gcmap_count);
1311 if (precise_count == count)
1312 printf ("LAST: %s\n", mono_method_full_name (cfg->method, TRUE));
1313 if (precise_count > count)
1314 return;
1317 #endif
1319 cfg->compute_gc_maps = TRUE;
1321 cfg->gc_info = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoCompileGC));
1325 * mini_gc_set_slot_type_from_fp:
1327 * Set the GC slot type of the stack slot identified by SLOT_OFFSET, which should be
1328 * relative to the frame pointer. By default, all stack slots are type PIN, so there is no
1329 * need to call this function for those slots.
1331 void
1332 mini_gc_set_slot_type_from_fp (MonoCompile *cfg, int slot_offset, GCSlotType type)
1334 MonoCompileGC *gcfg = (MonoCompileGC*)cfg->gc_info;
1336 if (!cfg->compute_gc_maps)
1337 return;
1339 g_assert (slot_offset % SIZEOF_SLOT == 0);
1341 gcfg->stack_slots_from_fp = g_slist_prepend_mempool (cfg->mempool, gcfg->stack_slots_from_fp, GINT_TO_POINTER (((slot_offset) << 16) | type));
1345 * mini_gc_set_slot_type_from_cfa:
1347 * Set the GC slot type of the stack slot identified by SLOT_OFFSET, which should be
1348 * relative to the DWARF CFA value. This should be called from mono_arch_emit_prolog ().
1349 * If type is STACK_REF, the slot is assumed to be live from the end of the prolog until
1350 * the end of the method. By default, all stack slots are type PIN, so there is no need to
1351 * call this function for those slots.
1353 void
1354 mini_gc_set_slot_type_from_cfa (MonoCompile *cfg, int slot_offset, GCSlotType type)
1356 MonoCompileGC *gcfg = (MonoCompileGC*)cfg->gc_info;
1357 int slot = - (slot_offset / SIZEOF_SLOT);
1359 if (!cfg->compute_gc_maps)
1360 return;
1362 g_assert (slot_offset <= 0);
1363 g_assert (slot_offset % SIZEOF_SLOT == 0);
1365 gcfg->stack_slots_from_cfa = g_slist_prepend_mempool (cfg->mempool, gcfg->stack_slots_from_cfa, GUINT_TO_POINTER (((slot) << 16) | type));
1368 static inline int
1369 fp_offset_to_slot (MonoCompile *cfg, int offset)
1371 MonoCompileGC *gcfg = cfg->gc_info;
1373 return (offset - gcfg->min_offset) / SIZEOF_SLOT;
1376 static inline int
1377 slot_to_fp_offset (MonoCompile *cfg, int slot)
1379 MonoCompileGC *gcfg = cfg->gc_info;
1381 return (slot * SIZEOF_SLOT) + gcfg->min_offset;
1384 static inline MONO_ALWAYS_INLINE void
1385 set_slot (MonoCompileGC *gcfg, int slot, int callsite_index, GCSlotType type)
1387 g_assert (slot >= 0 && slot < gcfg->nslots);
1389 if (type == SLOT_PIN) {
1390 clear_bit (gcfg->stack_ref_bitmap, gcfg->stack_bitmap_width, slot, callsite_index);
1391 set_bit (gcfg->stack_pin_bitmap, gcfg->stack_bitmap_width, slot, callsite_index);
1392 } else if (type == SLOT_REF) {
1393 set_bit (gcfg->stack_ref_bitmap, gcfg->stack_bitmap_width, slot, callsite_index);
1394 clear_bit (gcfg->stack_pin_bitmap, gcfg->stack_bitmap_width, slot, callsite_index);
1395 } else if (type == SLOT_NOREF) {
1396 clear_bit (gcfg->stack_ref_bitmap, gcfg->stack_bitmap_width, slot, callsite_index);
1397 clear_bit (gcfg->stack_pin_bitmap, gcfg->stack_bitmap_width, slot, callsite_index);
1401 static inline void
1402 set_slot_everywhere (MonoCompileGC *gcfg, int slot, GCSlotType type)
1404 int width, pos;
1405 guint8 *ref_bitmap, *pin_bitmap;
1408 int cindex;
1410 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex)
1411 set_slot (gcfg, slot, cindex, type);
1413 ref_bitmap = gcfg->stack_ref_bitmap;
1414 pin_bitmap = gcfg->stack_pin_bitmap;
1415 width = gcfg->stack_bitmap_width;
1416 pos = width * slot;
1418 if (type == SLOT_PIN) {
1419 memset (ref_bitmap + pos, 0, width);
1420 memset (pin_bitmap + pos, 0xff, width);
1421 } else if (type == SLOT_REF) {
1422 memset (ref_bitmap + pos, 0xff, width);
1423 memset (pin_bitmap + pos, 0, width);
1424 } else if (type == SLOT_NOREF) {
1425 memset (ref_bitmap + pos, 0, width);
1426 memset (pin_bitmap + pos, 0, width);
1430 static inline void
1431 set_slot_in_range (MonoCompileGC *gcfg, int slot, int from, int to, GCSlotType type)
1433 int cindex;
1435 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex) {
1436 int callsite_offset = gcfg->callsites [cindex]->pc_offset;
1437 if (callsite_offset >= from && callsite_offset < to)
1438 set_slot (gcfg, slot, cindex, type);
1442 static inline void
1443 set_reg_slot (MonoCompileGC *gcfg, int slot, int callsite_index, GCSlotType type)
1445 g_assert (slot >= 0 && slot < gcfg->nregs);
1447 if (type == SLOT_PIN) {
1448 clear_bit (gcfg->reg_ref_bitmap, gcfg->reg_bitmap_width, slot, callsite_index);
1449 set_bit (gcfg->reg_pin_bitmap, gcfg->reg_bitmap_width, slot, callsite_index);
1450 } else if (type == SLOT_REF) {
1451 set_bit (gcfg->reg_ref_bitmap, gcfg->reg_bitmap_width, slot, callsite_index);
1452 clear_bit (gcfg->reg_pin_bitmap, gcfg->reg_bitmap_width, slot, callsite_index);
1453 } else if (type == SLOT_NOREF) {
1454 clear_bit (gcfg->reg_ref_bitmap, gcfg->reg_bitmap_width, slot, callsite_index);
1455 clear_bit (gcfg->reg_pin_bitmap, gcfg->reg_bitmap_width, slot, callsite_index);
1459 static inline void
1460 set_reg_slot_everywhere (MonoCompileGC *gcfg, int slot, GCSlotType type)
1462 int cindex;
1464 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex)
1465 set_reg_slot (gcfg, slot, cindex, type);
1468 static inline void
1469 set_reg_slot_in_range (MonoCompileGC *gcfg, int slot, int from, int to, GCSlotType type)
1471 int cindex;
1473 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex) {
1474 int callsite_offset = gcfg->callsites [cindex]->pc_offset;
1475 if (callsite_offset >= from && callsite_offset < to)
1476 set_reg_slot (gcfg, slot, cindex, type);
1480 static void
1481 process_spill_slots (MonoCompile *cfg)
1483 MonoCompileGC *gcfg = cfg->gc_info;
1484 MonoBasicBlock *bb;
1485 GSList *l;
1486 int i;
1488 /* Mark all ref/pin spill slots as NOREF by default outside of their live range */
1489 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
1490 for (l = bb->spill_slot_defs; l; l = l->next) {
1491 MonoInst *def = l->data;
1492 int spill_slot = def->inst_c0;
1493 int bank = def->inst_c1;
1494 int offset = cfg->spill_info [bank][spill_slot].offset;
1495 int slot = fp_offset_to_slot (cfg, offset);
1497 if (bank == MONO_REG_INT_MP || bank == MONO_REG_INT_REF)
1498 set_slot_everywhere (gcfg, slot, SLOT_NOREF);
1502 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
1503 for (l = bb->spill_slot_defs; l; l = l->next) {
1504 MonoInst *def = l->data;
1505 int spill_slot = def->inst_c0;
1506 int bank = def->inst_c1;
1507 int offset = cfg->spill_info [bank][spill_slot].offset;
1508 int slot = fp_offset_to_slot (cfg, offset);
1509 GCSlotType type;
1511 if (bank == MONO_REG_INT_MP)
1512 type = SLOT_PIN;
1513 else
1514 type = SLOT_REF;
1517 * Extend the live interval for the GC tracked spill slots
1518 * defined in this bblock.
1519 * FIXME: This is not needed.
1521 set_slot_in_range (gcfg, slot, def->backend.pc_offset, bb->native_offset + bb->native_length, type);
1523 if (cfg->verbose_level > 1)
1524 printf ("\t%s spill slot at %s0x%x(fp) (slot = %d)\n", slot_type_to_string (type), offset >= 0 ? "" : "-", ABS (offset), slot);
1528 /* Set fp spill slots to NOREF */
1529 for (i = 0; i < cfg->spill_info_len [MONO_REG_DOUBLE]; ++i) {
1530 int offset = cfg->spill_info [MONO_REG_DOUBLE][i].offset;
1531 int slot;
1533 if (offset == -1)
1534 continue;
1536 slot = fp_offset_to_slot (cfg, offset);
1538 set_slot_everywhere (gcfg, slot, SLOT_NOREF);
1539 /* FIXME: 32 bit */
1540 if (cfg->verbose_level > 1)
1541 printf ("\tfp spill slot at %s0x%x(fp) (slot = %d)\n", offset >= 0 ? "" : "-", ABS (offset), slot);
1544 /* Set int spill slots to NOREF */
1545 for (i = 0; i < cfg->spill_info_len [MONO_REG_INT]; ++i) {
1546 int offset = cfg->spill_info [MONO_REG_INT][i].offset;
1547 int slot;
1549 if (offset == -1)
1550 continue;
1552 slot = fp_offset_to_slot (cfg, offset);
1554 set_slot_everywhere (gcfg, slot, SLOT_NOREF);
1555 if (cfg->verbose_level > 1)
1556 printf ("\tint spill slot at %s0x%x(fp) (slot = %d)\n", offset >= 0 ? "" : "-", ABS (offset), slot);
1561 * process_other_slots:
1563 * Process stack slots registered using mini_gc_set_slot_type_... ().
1565 static void
1566 process_other_slots (MonoCompile *cfg)
1568 MonoCompileGC *gcfg = cfg->gc_info;
1569 GSList *l;
1571 /* Relative to the CFA */
1572 for (l = gcfg->stack_slots_from_cfa; l; l = l->next) {
1573 guint data = GPOINTER_TO_UINT (l->data);
1574 int cfa_slot = data >> 16;
1575 GCSlotType type = data & 0xff;
1576 int slot;
1579 * Map the cfa relative slot to an fp relative slot.
1580 * slot_addr == cfa - <cfa_slot>*4/8
1581 * fp + cfa_offset == cfa
1582 * -> slot_addr == fp + (cfa_offset - <cfa_slot>*4/8)
1584 slot = (cfg->cfa_offset / SIZEOF_SLOT) - cfa_slot - (gcfg->min_offset / SIZEOF_SLOT);
1586 set_slot_everywhere (gcfg, slot, type);
1588 if (cfg->verbose_level > 1) {
1589 int fp_offset = slot_to_fp_offset (cfg, slot);
1590 if (type == SLOT_NOREF)
1591 printf ("\tnoref slot at %s0x%x(fp) (slot = %d) (cfa - 0x%x)\n", fp_offset >= 0 ? "" : "-", ABS (fp_offset), slot, (int)(cfa_slot * SIZEOF_SLOT));
1595 /* Relative to the FP */
1596 for (l = gcfg->stack_slots_from_fp; l; l = l->next) {
1597 gint data = GPOINTER_TO_INT (l->data);
1598 int offset = data >> 16;
1599 GCSlotType type = data & 0xff;
1600 int slot;
1602 slot = fp_offset_to_slot (cfg, offset);
1604 set_slot_everywhere (gcfg, slot, type);
1606 /* Liveness for these slots is handled by process_spill_slots () */
1608 if (cfg->verbose_level > 1) {
1609 if (type == SLOT_REF)
1610 printf ("\tref slot at fp+0x%x (slot = %d)\n", offset, slot);
1611 else if (type == SLOT_NOREF)
1612 printf ("\tnoref slot at 0x%x(fp) (slot = %d)\n", offset, slot);
1617 static gsize*
1618 get_vtype_bitmap (MonoType *t, int *numbits)
1620 MonoClass *klass = mono_class_from_mono_type_internal (t);
1622 if (klass->generic_container || mono_class_is_open_constructed_type (t)) {
1623 /* FIXME: Generic sharing */
1624 return NULL;
1625 } else {
1626 mono_class_compute_gc_descriptor (klass);
1628 return mono_gc_get_bitmap_for_descr (klass->gc_descr, numbits);
1632 static inline const char*
1633 get_offset_sign (int offset)
1635 return offset < 0 ? "-" : "+";
1638 static inline int
1639 get_offset_val (int offset)
1641 return offset < 0 ? (- offset) : offset;
1644 static void
1645 process_variables (MonoCompile *cfg)
1647 MonoCompileGC *gcfg = cfg->gc_info;
1648 MonoMethodSignature *sig = mono_method_signature_internal (cfg->method);
1649 int i, locals_min_slot, locals_max_slot, cindex;
1650 MonoBasicBlock *bb;
1651 MonoInst *tmp;
1652 int *pc_offsets;
1653 int locals_min_offset = gcfg->locals_min_offset;
1654 int locals_max_offset = gcfg->locals_max_offset;
1656 /* Slots for locals are NOREF by default */
1657 locals_min_slot = (locals_min_offset - gcfg->min_offset) / SIZEOF_SLOT;
1658 locals_max_slot = (locals_max_offset - gcfg->min_offset) / SIZEOF_SLOT;
1659 for (i = locals_min_slot; i < locals_max_slot; ++i) {
1660 set_slot_everywhere (gcfg, i, SLOT_NOREF);
1664 * Compute the offset where variables are initialized in the first bblock, if any.
1666 pc_offsets = g_new0 (int, cfg->next_vreg);
1668 bb = cfg->bb_entry->next_bb;
1669 MONO_BB_FOR_EACH_INS (bb, tmp) {
1670 if (tmp->opcode == OP_GC_LIVENESS_DEF) {
1671 int vreg = tmp->inst_c1;
1672 if (pc_offsets [vreg] == 0) {
1673 g_assert (tmp->backend.pc_offset > 0);
1674 pc_offsets [vreg] = tmp->backend.pc_offset;
1680 * Stack slots holding arguments are initialized in the prolog.
1681 * This means we can treat them alive for the whole method.
1683 for (i = 0; i < cfg->num_varinfo; i++) {
1684 MonoInst *ins = cfg->varinfo [i];
1685 MonoType *t = ins->inst_vtype;
1686 MonoMethodVar *vmv;
1687 guint32 pos;
1688 gboolean byref, is_this = FALSE;
1689 gboolean is_arg = i < cfg->locals_start;
1691 if (ins == cfg->ret) {
1692 if (!(ins->opcode == OP_REGOFFSET && MONO_TYPE_ISSTRUCT (t)))
1693 continue;
1696 vmv = MONO_VARINFO (cfg, i);
1698 /* For some reason, 'this' is byref */
1699 if (sig->hasthis && ins == cfg->args [0] && !cfg->method->klass->valuetype) {
1700 t = m_class_get_byval_arg (cfg->method->klass);
1701 is_this = TRUE;
1704 byref = t->byref;
1706 if (ins->opcode == OP_REGVAR) {
1707 int hreg;
1708 GCSlotType slot_type;
1710 t = mini_get_underlying_type (t);
1712 hreg = ins->dreg;
1713 g_assert (hreg < MONO_MAX_IREGS);
1715 if (byref)
1716 slot_type = SLOT_PIN;
1717 else
1718 slot_type = mini_type_is_reference (t) ? SLOT_REF : SLOT_NOREF;
1720 if (slot_type == SLOT_PIN) {
1721 /* These have no live interval, be conservative */
1722 set_reg_slot_everywhere (gcfg, hreg, slot_type);
1723 } else {
1725 * Unlike variables allocated to the stack, we generate liveness info
1726 * for noref vars in registers in mono_spill_global_vars (), because
1727 * knowing that a register doesn't contain a ref allows us to mark its save
1728 * locations precisely.
1730 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex)
1731 if (gcfg->callsites [cindex]->liveness [i / 8] & (1 << (i % 8)))
1732 set_reg_slot (gcfg, hreg, cindex, slot_type);
1735 if (cfg->verbose_level > 1) {
1736 printf ("\t%s %sreg %s(R%d)\n", slot_type_to_string (slot_type), is_arg ? "arg " : "", mono_arch_regname (hreg), vmv->vreg);
1739 continue;
1742 if (ins->opcode != OP_REGOFFSET)
1743 continue;
1745 if (ins->inst_offset % SIZEOF_SLOT != 0)
1746 continue;
1748 pos = fp_offset_to_slot (cfg, ins->inst_offset);
1750 if (is_arg && ins->flags & MONO_INST_IS_DEAD) {
1751 /* These do not get stored in the prolog */
1752 set_slot_everywhere (gcfg, pos, SLOT_NOREF);
1754 if (cfg->verbose_level > 1) {
1755 printf ("\tdead arg at fp%s0x%x (slot = %d): %s\n", get_offset_sign (ins->inst_offset), get_offset_val (ins->inst_offset), pos, mono_type_full_name (ins->inst_vtype));
1757 continue;
1760 if (MONO_TYPE_ISSTRUCT (t)) {
1761 int numbits = 0, j;
1762 gsize *bitmap = NULL;
1763 gboolean pin = FALSE;
1764 int size;
1765 int size_in_slots;
1767 if (ins->backend.is_pinvoke)
1768 size = mono_class_native_size (ins->klass, NULL);
1769 else
1770 size = mono_class_value_size (ins->klass, NULL);
1771 size_in_slots = ALIGN_TO (size, SIZEOF_SLOT) / SIZEOF_SLOT;
1773 if (cfg->verbose_level > 1)
1774 printf ("\tvtype R%d at %s0x%x(fp)-%s0x%x(fp) (slot %d-%d): %s\n", vmv->vreg, get_offset_sign (ins->inst_offset), get_offset_val (ins->inst_offset), get_offset_sign (ins->inst_offset), get_offset_val (ins->inst_offset + (size_in_slots * SIZEOF_SLOT)), pos, pos + size_in_slots, mono_type_full_name (ins->inst_vtype));
1776 if (!ins->klass->has_references) {
1777 if (is_arg) {
1778 for (j = 0; j < size_in_slots; ++j)
1779 set_slot_everywhere (gcfg, pos + j, SLOT_NOREF);
1781 continue;
1784 bitmap = get_vtype_bitmap (t, &numbits);
1785 if (!bitmap)
1786 pin = TRUE;
1789 * Most vtypes are marked volatile because of the LDADDR instructions,
1790 * and they have no liveness information since they are decomposed
1791 * before the liveness pass. We emit OP_GC_LIVENESS_DEF instructions for
1792 * them during VZERO decomposition.
1794 if (!is_arg) {
1795 if (!pc_offsets [vmv->vreg])
1796 pin = TRUE;
1798 if (ins->backend.is_pinvoke)
1799 pin = TRUE;
1802 if (bitmap) {
1803 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex) {
1804 if (gcfg->callsites [cindex]->pc_offset > pc_offsets [vmv->vreg]) {
1805 for (j = 0; j < numbits; ++j) {
1806 if (bitmap [j / GC_BITS_PER_WORD] & ((gsize)1 << (j % GC_BITS_PER_WORD))) {
1807 /* The descriptor is for the boxed object */
1808 set_slot (gcfg, (pos + j - (MONO_ABI_SIZEOF (MonoObject) / SIZEOF_SLOT)), cindex, pin ? SLOT_PIN : SLOT_REF);
1814 if (cfg->verbose_level > 1) {
1815 for (j = 0; j < numbits; ++j) {
1816 if (bitmap [j / GC_BITS_PER_WORD] & ((gsize)1 << (j % GC_BITS_PER_WORD)))
1817 printf ("\t\t%s slot at 0x%x(fp) (slot = %d)\n", pin ? "pin" : "ref", (int)(ins->inst_offset + (j * SIZEOF_SLOT)), (int)(pos + j - (MONO_ABI_SIZEOF (MonoObject) / SIZEOF_SLOT)));
1820 } else {
1821 if (cfg->verbose_level > 1)
1822 printf ("\t\tpinned\n");
1823 for (j = 0; j < size_in_slots; ++j) {
1824 set_slot_everywhere (gcfg, pos + j, SLOT_PIN);
1828 g_free (bitmap);
1830 continue;
1833 if (!is_arg && (ins->inst_offset < gcfg->min_offset || ins->inst_offset >= gcfg->max_offset))
1834 /* Vret addr etc. */
1835 continue;
1837 if (t->byref) {
1838 if (is_arg) {
1839 set_slot_everywhere (gcfg, pos, SLOT_PIN);
1840 } else {
1841 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex)
1842 if (gcfg->callsites [cindex]->liveness [i / 8] & (1 << (i % 8)))
1843 set_slot (gcfg, pos, cindex, SLOT_PIN);
1845 if (cfg->verbose_level > 1)
1846 printf ("\tbyref at %s0x%x(fp) (R%d, slot = %d): %s\n", ins->inst_offset < 0 ? "-" : "", (ins->inst_offset < 0) ? -(int)ins->inst_offset : (int)ins->inst_offset, vmv->vreg, pos, mono_type_full_name (ins->inst_vtype));
1847 continue;
1851 * This is currently disabled, but could be enabled to debug crashes.
1853 #if 0
1854 if (t->type == MONO_TYPE_I) {
1856 * Variables created in mono_handle_global_vregs have type I, but they
1857 * could hold GC refs since the vregs they were created from might not been
1858 * marked as holding a GC ref. So be conservative.
1860 set_slot_everywhere (gcfg, pos, SLOT_PIN);
1861 continue;
1863 #endif
1865 t = mini_get_underlying_type (t);
1867 if (!mini_type_is_reference (t)) {
1868 set_slot_everywhere (gcfg, pos, SLOT_NOREF);
1869 if (cfg->verbose_level > 1)
1870 printf ("\tnoref%s at %s0x%x(fp) (R%d, slot = %d): %s\n", (is_arg ? " arg" : ""), ins->inst_offset < 0 ? "-" : "", (ins->inst_offset < 0) ? -(int)ins->inst_offset : (int)ins->inst_offset, vmv->vreg, pos, mono_type_full_name (ins->inst_vtype));
1871 if (!t->byref && sizeof (mgreg_t) == 4 && (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8 || t->type == MONO_TYPE_R8)) {
1872 set_slot_everywhere (gcfg, pos + 1, SLOT_NOREF);
1873 if (cfg->verbose_level > 1)
1874 printf ("\tnoref at %s0x%x(fp) (R%d, slot = %d): %s\n", ins->inst_offset < 0 ? "-" : "", (ins->inst_offset < 0) ? -(int)(ins->inst_offset + 4) : (int)ins->inst_offset + 4, vmv->vreg, pos + 1, mono_type_full_name (ins->inst_vtype));
1876 continue;
1879 /* 'this' is marked INDIRECT for gshared methods */
1880 if (ins->flags & (MONO_INST_VOLATILE | MONO_INST_INDIRECT) && !is_this) {
1882 * For volatile variables, treat them alive from the point they are
1883 * initialized in the first bblock until the end of the method.
1885 if (is_arg) {
1886 set_slot_everywhere (gcfg, pos, SLOT_REF);
1887 } else if (pc_offsets [vmv->vreg]) {
1888 set_slot_in_range (gcfg, pos, 0, pc_offsets [vmv->vreg], SLOT_PIN);
1889 set_slot_in_range (gcfg, pos, pc_offsets [vmv->vreg], cfg->code_size, SLOT_REF);
1890 } else {
1891 set_slot_everywhere (gcfg, pos, SLOT_PIN);
1893 if (cfg->verbose_level > 1)
1894 printf ("\tvolatile ref at %s0x%x(fp) (R%d, slot = %d): %s\n", ins->inst_offset < 0 ? "-" : "", (ins->inst_offset < 0) ? -(int)ins->inst_offset : (int)ins->inst_offset, vmv->vreg, pos, mono_type_full_name (ins->inst_vtype));
1895 continue;
1898 if (is_arg) {
1899 /* Live for the whole method */
1900 set_slot_everywhere (gcfg, pos, SLOT_REF);
1901 } else {
1902 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex)
1903 if (gcfg->callsites [cindex]->liveness [i / 8] & (1 << (i % 8)))
1904 set_slot (gcfg, pos, cindex, SLOT_REF);
1907 if (cfg->verbose_level > 1) {
1908 printf ("\tref%s at %s0x%x(fp) (R%d, slot = %d): %s\n", (is_arg ? " arg" : ""), ins->inst_offset < 0 ? "-" : "", (ins->inst_offset < 0) ? -(int)ins->inst_offset : (int)ins->inst_offset, vmv->vreg, pos, mono_type_full_name (ins->inst_vtype));
1912 g_free (pc_offsets);
1915 static int
1916 sp_offset_to_fp_offset (MonoCompile *cfg, int sp_offset)
1919 * Convert a sp relative offset to a slot index. This is
1920 * platform specific.
1922 #ifdef TARGET_AMD64
1923 /* fp = sp + offset */
1924 g_assert (cfg->frame_reg == AMD64_RBP);
1925 return (- cfg->arch.sp_fp_offset + sp_offset);
1926 #elif defined(TARGET_X86)
1927 /* The offset is computed from the sp at the start of the call sequence */
1928 g_assert (cfg->frame_reg == X86_EBP);
1929 #ifdef MONO_X86_NO_PUSHES
1930 return (- cfg->arch.sp_fp_offset + sp_offset);
1931 #else
1932 return (- cfg->arch.sp_fp_offset - sp_offset);
1933 #endif
1934 #else
1935 NOT_IMPLEMENTED;
1936 return -1;
1937 #endif
1940 static void
1941 process_param_area_slots (MonoCompile *cfg)
1943 MonoCompileGC *gcfg = cfg->gc_info;
1944 int cindex, i;
1945 gboolean *is_param;
1948 * These slots are used for passing parameters during calls. They are sp relative, not
1949 * fp relative, so they are harder to handle.
1951 if (cfg->flags & MONO_CFG_HAS_ALLOCA)
1952 /* The distance between fp and sp is not constant */
1953 return;
1955 is_param = mono_mempool_alloc0 (cfg->mempool, gcfg->nslots * sizeof (gboolean));
1957 for (cindex = 0; cindex < gcfg->ncallsites; ++cindex) {
1958 GCCallSite *callsite = gcfg->callsites [cindex];
1959 GSList *l;
1961 for (l = callsite->param_slots; l; l = l->next) {
1962 MonoInst *def = l->data;
1963 MonoType *t = def->inst_vtype;
1964 int sp_offset = def->inst_offset;
1965 int fp_offset = sp_offset_to_fp_offset (cfg, sp_offset);
1966 int slot = fp_offset_to_slot (cfg, fp_offset);
1967 guint32 align;
1968 guint32 size;
1970 if (MONO_TYPE_ISSTRUCT (t)) {
1971 size = mini_type_stack_size_full (t, &align, FALSE);
1972 } else {
1973 size = sizeof (mgreg_t);
1976 for (i = 0; i < size / sizeof (mgreg_t); ++i) {
1977 g_assert (slot + i >= 0 && slot + i < gcfg->nslots);
1978 is_param [slot + i] = TRUE;
1983 /* All param area slots are noref by default */
1984 for (i = 0; i < gcfg->nslots; ++i) {
1985 if (is_param [i])
1986 set_slot_everywhere (gcfg, i, SLOT_NOREF);
1990 * We treat param area slots as being part of the callee's frame, to be able to handle tailcalls which overwrite
1991 * the argument area of the caller.
1995 static void
1996 process_finally_clauses (MonoCompile *cfg)
1998 MonoCompileGC *gcfg = cfg->gc_info;
1999 GCCallSite **callsites;
2000 int ncallsites;
2001 gboolean has_finally;
2002 int i, j, nslots, nregs;
2004 ncallsites = gcfg->ncallsites;
2005 nslots = gcfg->nslots;
2006 nregs = gcfg->nregs;
2007 callsites = gcfg->callsites;
2010 * The calls to the finally clauses don't show up in the cfg. See
2011 * test_0_liveness_8 ().
2012 * Variables accessed inside the finally clause are already marked VOLATILE by
2013 * mono_liveness_handle_exception_clauses (). Variables not accessed inside the finally clause have
2014 * correct liveness outside the finally clause. So mark them PIN inside the finally clauses.
2016 has_finally = FALSE;
2017 for (i = 0; i < cfg->header->num_clauses; ++i) {
2018 MonoExceptionClause *clause = &cfg->header->clauses [i];
2020 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
2021 has_finally = TRUE;
2024 if (has_finally) {
2025 if (cfg->verbose_level > 1)
2026 printf ("\tMethod has finally clauses, pessimizing live ranges.\n");
2027 for (j = 0; j < ncallsites; ++j) {
2028 MonoBasicBlock *bb = callsites [j]->bb;
2029 MonoExceptionClause *clause;
2030 gboolean is_in_finally = FALSE;
2032 for (i = 0; i < cfg->header->num_clauses; ++i) {
2033 clause = &cfg->header->clauses [i];
2035 if (MONO_OFFSET_IN_HANDLER (clause, bb->real_offset)) {
2036 if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY) {
2037 is_in_finally = TRUE;
2038 break;
2043 if (is_in_finally) {
2044 for (i = 0; i < nslots; ++i)
2045 set_slot (gcfg, i, j, SLOT_PIN);
2046 for (i = 0; i < nregs; ++i)
2047 set_reg_slot (gcfg, i, j, SLOT_PIN);
2053 static void
2054 compute_frame_size (MonoCompile *cfg)
2056 int i, locals_min_offset, locals_max_offset, cfa_min_offset, cfa_max_offset;
2057 int min_offset, max_offset;
2058 MonoCompileGC *gcfg = cfg->gc_info;
2059 MonoMethodSignature *sig = mono_method_signature_internal (cfg->method);
2060 GSList *l;
2062 /* Compute min/max offsets from the fp */
2064 /* Locals */
2065 #if defined(TARGET_AMD64) || defined(TARGET_X86) || defined(TARGET_ARM) || defined(TARGET_S390X)
2066 locals_min_offset = ALIGN_TO (cfg->locals_min_stack_offset, SIZEOF_SLOT);
2067 locals_max_offset = cfg->locals_max_stack_offset;
2068 #else
2069 /* min/max stack offset needs to be computed in mono_arch_allocate_vars () */
2070 NOT_IMPLEMENTED;
2071 #endif
2073 locals_min_offset = ALIGN_TO (locals_min_offset, SIZEOF_SLOT);
2074 locals_max_offset = ALIGN_TO (locals_max_offset, SIZEOF_SLOT);
2076 min_offset = locals_min_offset;
2077 max_offset = locals_max_offset;
2079 /* Arguments */
2080 for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
2081 MonoInst *ins = cfg->args [i];
2083 if (ins->opcode == OP_REGOFFSET) {
2084 int size, size_in_slots;
2085 size = mini_type_stack_size_full (ins->inst_vtype, NULL, ins->backend.is_pinvoke);
2086 size_in_slots = ALIGN_TO (size, SIZEOF_SLOT) / SIZEOF_SLOT;
2088 min_offset = MIN (min_offset, ins->inst_offset);
2089 max_offset = MAX ((int)max_offset, (int)(ins->inst_offset + (size_in_slots * SIZEOF_SLOT)));
2093 /* Cfa slots */
2094 g_assert (cfg->frame_reg == cfg->cfa_reg);
2095 g_assert (cfg->cfa_offset > 0);
2096 cfa_min_offset = 0;
2097 cfa_max_offset = cfg->cfa_offset;
2099 min_offset = MIN (min_offset, cfa_min_offset);
2100 max_offset = MAX (max_offset, cfa_max_offset);
2102 /* Fp relative slots */
2103 for (l = gcfg->stack_slots_from_fp; l; l = l->next) {
2104 gint data = GPOINTER_TO_INT (l->data);
2105 int offset = data >> 16;
2107 min_offset = MIN (min_offset, offset);
2110 /* Spill slots */
2111 if (!(cfg->flags & MONO_CFG_HAS_SPILLUP)) {
2112 int stack_offset = ALIGN_TO (cfg->stack_offset, SIZEOF_SLOT);
2113 min_offset = MIN (min_offset, (-stack_offset));
2116 /* Param area slots */
2117 #ifdef TARGET_AMD64
2118 min_offset = MIN (min_offset, -cfg->arch.sp_fp_offset);
2119 #elif defined(TARGET_X86)
2120 #ifdef MONO_X86_NO_PUSHES
2121 min_offset = MIN (min_offset, -cfg->arch.sp_fp_offset);
2122 #else
2123 min_offset = MIN (min_offset, - (cfg->arch.sp_fp_offset + cfg->arch.param_area_size));
2124 #endif
2125 #elif defined(TARGET_ARM)
2126 // FIXME:
2127 #elif defined(TARGET_s390X)
2128 // FIXME:
2129 #else
2130 NOT_IMPLEMENTED;
2131 #endif
2133 gcfg->min_offset = min_offset;
2134 gcfg->max_offset = max_offset;
2135 gcfg->locals_min_offset = locals_min_offset;
2136 gcfg->locals_max_offset = locals_max_offset;
2139 static void
2140 init_gcfg (MonoCompile *cfg)
2142 int i, nregs, nslots;
2143 MonoCompileGC *gcfg = cfg->gc_info;
2144 GCCallSite **callsites;
2145 int ncallsites;
2146 MonoBasicBlock *bb;
2147 GSList *l;
2150 * Collect callsites
2152 ncallsites = 0;
2153 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
2154 ncallsites += g_slist_length (bb->gc_callsites);
2156 callsites = mono_mempool_alloc0 (cfg->mempool, ncallsites * sizeof (GCCallSite*));
2157 i = 0;
2158 for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
2159 for (l = bb->gc_callsites; l; l = l->next)
2160 callsites [i++] = l->data;
2163 /* The callsites should already be ordered by pc offset */
2164 for (i = 1; i < ncallsites; ++i)
2165 g_assert (callsites [i - 1]->pc_offset < callsites [i]->pc_offset);
2168 * The stack frame looks like this:
2170 * <fp + max_offset> == cfa -> <end of previous frame>
2171 * <other stack slots>
2172 * <locals>
2173 * <other stack slots>
2174 * fp + min_offset ->
2175 * ...
2176 * fp ->
2179 if (cfg->verbose_level > 1)
2180 printf ("GC Map for %s: 0x%x-0x%x\n", mono_method_full_name (cfg->method, TRUE), gcfg->min_offset, gcfg->max_offset);
2182 nslots = (gcfg->max_offset - gcfg->min_offset) / SIZEOF_SLOT;
2183 nregs = NREGS;
2185 gcfg->nslots = nslots;
2186 gcfg->nregs = nregs;
2187 gcfg->callsites = callsites;
2188 gcfg->ncallsites = ncallsites;
2189 gcfg->stack_bitmap_width = ALIGN_TO (ncallsites, 8) / 8;
2190 gcfg->reg_bitmap_width = ALIGN_TO (ncallsites, 8) / 8;
2191 gcfg->stack_ref_bitmap = mono_mempool_alloc0 (cfg->mempool, gcfg->stack_bitmap_width * nslots);
2192 gcfg->stack_pin_bitmap = mono_mempool_alloc0 (cfg->mempool, gcfg->stack_bitmap_width * nslots);
2193 gcfg->reg_ref_bitmap = mono_mempool_alloc0 (cfg->mempool, gcfg->reg_bitmap_width * nregs);
2194 gcfg->reg_pin_bitmap = mono_mempool_alloc0 (cfg->mempool, gcfg->reg_bitmap_width * nregs);
2196 /* All slots start out as PIN */
2197 memset (gcfg->stack_pin_bitmap, 0xff, gcfg->stack_bitmap_width * nregs);
2198 for (i = 0; i < nregs; ++i) {
2200 * By default, registers are NOREF.
2201 * It is possible for a callee to save them before being defined in this method,
2202 * but the saved value is dead too, so it doesn't need to be marked.
2204 if ((cfg->used_int_regs & (1 << i)))
2205 set_reg_slot_everywhere (gcfg, i, SLOT_NOREF);
2209 static inline gboolean
2210 has_bit_set (guint8 *bitmap, int width, int slot)
2212 int i;
2213 int pos = width * slot;
2215 for (i = 0; i < width; ++i) {
2216 if (bitmap [pos + i])
2217 break;
2219 return i < width;
2222 static void
2223 create_map (MonoCompile *cfg)
2225 GCMap *map;
2226 int i, j, nregs, nslots, nref_regs, npin_regs, alloc_size, bitmaps_size, bitmaps_offset;
2227 int ntypes [16];
2228 int stack_bitmap_width, stack_bitmap_size, reg_ref_bitmap_width, reg_ref_bitmap_size;
2229 int reg_pin_bitmap_width, reg_pin_bitmap_size, bindex;
2230 int start, end;
2231 gboolean has_ref_slots, has_pin_slots, has_ref_regs, has_pin_regs;
2232 MonoCompileGC *gcfg = cfg->gc_info;
2233 GCCallSite **callsites;
2234 int ncallsites;
2235 guint8 *bitmap, *bitmaps;
2236 guint32 reg_ref_mask, reg_pin_mask;
2238 ncallsites = gcfg->ncallsites;
2239 nslots = gcfg->nslots;
2240 nregs = gcfg->nregs;
2241 callsites = gcfg->callsites;
2244 * Compute the real size of the bitmap i.e. ignore NOREF columns at the beginning and at
2245 * the end. Also, compute whenever the map needs ref/pin bitmaps, and collect stats.
2247 has_ref_slots = FALSE;
2248 has_pin_slots = FALSE;
2249 start = -1;
2250 end = -1;
2251 memset (ntypes, 0, sizeof (ntypes));
2252 for (i = 0; i < nslots; ++i) {
2253 gboolean has_ref = FALSE;
2254 gboolean has_pin = FALSE;
2256 if (has_bit_set (gcfg->stack_pin_bitmap, gcfg->stack_bitmap_width, i))
2257 has_pin = TRUE;
2258 if (has_bit_set (gcfg->stack_ref_bitmap, gcfg->stack_bitmap_width, i))
2259 has_ref = TRUE;
2261 if (has_ref)
2262 has_ref_slots = TRUE;
2263 if (has_pin)
2264 has_pin_slots = TRUE;
2266 if (has_ref)
2267 ntypes [SLOT_REF] ++;
2268 else if (has_pin)
2269 ntypes [SLOT_PIN] ++;
2270 else
2271 ntypes [SLOT_NOREF] ++;
2273 if (has_ref || has_pin) {
2274 if (start == -1)
2275 start = i;
2276 end = i + 1;
2279 if (start == -1) {
2280 start = end = nslots;
2281 } else {
2282 g_assert (start != -1);
2283 g_assert (start < end);
2286 has_ref_regs = FALSE;
2287 has_pin_regs = FALSE;
2288 reg_ref_mask = 0;
2289 reg_pin_mask = 0;
2290 nref_regs = 0;
2291 npin_regs = 0;
2292 for (i = 0; i < nregs; ++i) {
2293 gboolean has_ref = FALSE;
2294 gboolean has_pin = FALSE;
2296 if (!(cfg->used_int_regs & (1 << i)))
2297 continue;
2299 if (has_bit_set (gcfg->reg_pin_bitmap, gcfg->reg_bitmap_width, i))
2300 has_pin = TRUE;
2301 if (has_bit_set (gcfg->reg_ref_bitmap, gcfg->reg_bitmap_width, i))
2302 has_ref = TRUE;
2304 if (has_ref) {
2305 reg_ref_mask |= (1 << i);
2306 has_ref_regs = TRUE;
2307 nref_regs ++;
2309 if (has_pin) {
2310 reg_pin_mask |= (1 << i);
2311 has_pin_regs = TRUE;
2312 npin_regs ++;
2316 if (cfg->verbose_level > 1)
2317 printf ("Slots: %d Start: %d End: %d Refs: %d NoRefs: %d Pin: %d Callsites: %d\n", nslots, start, end, ntypes [SLOT_REF], ntypes [SLOT_NOREF], ntypes [SLOT_PIN], ncallsites);
2319 /* Create the GC Map */
2321 /* The work bitmaps have one row for each slot, since this is how we access them during construction */
2322 stack_bitmap_width = ALIGN_TO (end - start, 8) / 8;
2323 stack_bitmap_size = stack_bitmap_width * ncallsites;
2324 reg_ref_bitmap_width = ALIGN_TO (nref_regs, 8) / 8;
2325 reg_ref_bitmap_size = reg_ref_bitmap_width * ncallsites;
2326 reg_pin_bitmap_width = ALIGN_TO (npin_regs, 8) / 8;
2327 reg_pin_bitmap_size = reg_pin_bitmap_width * ncallsites;
2328 bitmaps_size = (has_ref_slots ? stack_bitmap_size : 0) + (has_pin_slots ? stack_bitmap_size : 0) + (has_ref_regs ? reg_ref_bitmap_size : 0) + (has_pin_regs ? reg_pin_bitmap_size : 0);
2330 map = mono_mempool_alloc0 (cfg->mempool, sizeof (GCMap));
2332 map->frame_reg = cfg->frame_reg;
2333 map->start_offset = gcfg->min_offset;
2334 map->end_offset = gcfg->min_offset + (nslots * SIZEOF_SLOT);
2335 map->map_offset = start * SIZEOF_SLOT;
2336 map->nslots = end - start;
2337 map->has_ref_slots = has_ref_slots;
2338 map->has_pin_slots = has_pin_slots;
2339 map->has_ref_regs = has_ref_regs;
2340 map->has_pin_regs = has_pin_regs;
2341 g_assert (nregs < 32);
2342 map->used_int_regs = cfg->used_int_regs;
2343 map->reg_ref_mask = reg_ref_mask;
2344 map->reg_pin_mask = reg_pin_mask;
2345 map->nref_regs = nref_regs;
2346 map->npin_regs = npin_regs;
2348 bitmaps = mono_mempool_alloc0 (cfg->mempool, bitmaps_size);
2350 bitmaps_offset = 0;
2351 if (has_ref_slots) {
2352 map->stack_ref_bitmap_offset = bitmaps_offset;
2353 bitmaps_offset += stack_bitmap_size;
2355 bitmap = &bitmaps [map->stack_ref_bitmap_offset];
2356 for (i = 0; i < nslots; ++i) {
2357 for (j = 0; j < ncallsites; ++j) {
2358 if (get_bit (gcfg->stack_ref_bitmap, gcfg->stack_bitmap_width, i, j))
2359 set_bit (bitmap, stack_bitmap_width, j, i - start);
2363 if (has_pin_slots) {
2364 map->stack_pin_bitmap_offset = bitmaps_offset;
2365 bitmaps_offset += stack_bitmap_size;
2367 bitmap = &bitmaps [map->stack_pin_bitmap_offset];
2368 for (i = 0; i < nslots; ++i) {
2369 for (j = 0; j < ncallsites; ++j) {
2370 if (get_bit (gcfg->stack_pin_bitmap, gcfg->stack_bitmap_width, i, j))
2371 set_bit (bitmap, stack_bitmap_width, j, i - start);
2375 if (has_ref_regs) {
2376 map->reg_ref_bitmap_offset = bitmaps_offset;
2377 bitmaps_offset += reg_ref_bitmap_size;
2379 bitmap = &bitmaps [map->reg_ref_bitmap_offset];
2380 bindex = 0;
2381 for (i = 0; i < nregs; ++i) {
2382 if (reg_ref_mask & (1 << i)) {
2383 for (j = 0; j < ncallsites; ++j) {
2384 if (get_bit (gcfg->reg_ref_bitmap, gcfg->reg_bitmap_width, i, j))
2385 set_bit (bitmap, reg_ref_bitmap_width, j, bindex);
2387 bindex ++;
2391 if (has_pin_regs) {
2392 map->reg_pin_bitmap_offset = bitmaps_offset;
2393 bitmaps_offset += reg_pin_bitmap_size;
2395 bitmap = &bitmaps [map->reg_pin_bitmap_offset];
2396 bindex = 0;
2397 for (i = 0; i < nregs; ++i) {
2398 if (reg_pin_mask & (1 << i)) {
2399 for (j = 0; j < ncallsites; ++j) {
2400 if (get_bit (gcfg->reg_pin_bitmap, gcfg->reg_bitmap_width, i, j))
2401 set_bit (bitmap, reg_pin_bitmap_width, j, bindex);
2403 bindex ++;
2408 /* Call sites */
2409 map->ncallsites = ncallsites;
2410 if (cfg->code_len < 256)
2411 map->callsite_entry_size = 1;
2412 else if (cfg->code_len < 65536)
2413 map->callsite_entry_size = 2;
2414 else
2415 map->callsite_entry_size = 4;
2417 /* Encode the GC Map */
2419 guint8 buf [256];
2420 guint8 *endbuf;
2421 GCEncodedMap *emap;
2422 int encoded_size;
2423 guint8 *p;
2425 encode_gc_map (map, buf, &endbuf);
2426 g_assert (endbuf - buf < 256);
2428 encoded_size = endbuf - buf;
2429 alloc_size = sizeof (GCEncodedMap) + ALIGN_TO (encoded_size, map->callsite_entry_size) + (map->callsite_entry_size * map->ncallsites) + bitmaps_size;
2431 emap = mono_domain_alloc0 (cfg->domain, alloc_size);
2432 //emap->ref_slots = map->ref_slots;
2434 /* Encoded fixed fields */
2435 p = &emap->encoded [0];
2436 //emap->encoded_size = encoded_size;
2437 memcpy (p, buf, encoded_size);
2438 p += encoded_size;
2440 /* Callsite table */
2441 p = (guint8*)ALIGN_TO ((gsize)p, map->callsite_entry_size);
2442 if (map->callsite_entry_size == 1) {
2443 guint8 *offsets = p;
2444 for (i = 0; i < ncallsites; ++i)
2445 offsets [i] = callsites [i]->pc_offset;
2446 UnlockedAdd (&stats.gc_callsites8_size, ncallsites * sizeof (guint8));
2447 } else if (map->callsite_entry_size == 2) {
2448 guint16 *offsets = (guint16*)p;
2449 for (i = 0; i < ncallsites; ++i)
2450 offsets [i] = callsites [i]->pc_offset;
2451 UnlockedAdd (&stats.gc_callsites16_size, ncallsites * sizeof (guint16));
2452 } else {
2453 guint32 *offsets = (guint32*)p;
2454 for (i = 0; i < ncallsites; ++i)
2455 offsets [i] = callsites [i]->pc_offset;
2456 UnlockedAdd (&stats.gc_callsites32_size, ncallsites * sizeof (guint32));
2458 p += ncallsites * map->callsite_entry_size;
2460 /* Bitmaps */
2461 memcpy (p, bitmaps, bitmaps_size);
2462 p += bitmaps_size;
2464 g_assert ((guint8*)p - (guint8*)emap <= alloc_size);
2466 UnlockedAdd (&stats.gc_maps_size, alloc_size);
2467 UnlockedAdd (&stats.gc_callsites_size, ncallsites * map->callsite_entry_size);
2468 UnlockedAdd (&stats.gc_bitmaps_size, bitmaps_size);
2469 UnlockedAdd (&stats.gc_map_struct_size, sizeof (GCEncodedMap) + encoded_size);
2471 cfg->jit_info->gc_info = emap;
2473 cfg->gc_map = (guint8*)emap;
2474 cfg->gc_map_size = alloc_size;
2477 UnlockedAdd (&stats.all_slots, nslots);
2478 UnlockedAdd (&stats.ref_slots, ntypes [SLOT_REF]);
2479 UnlockedAdd (&stats.noref_slots, ntypes [SLOT_NOREF]);
2480 UnlockedAdd (&stats.pin_slots, ntypes [SLOT_PIN]);
2483 void
2484 mini_gc_create_gc_map (MonoCompile *cfg)
2486 if (!cfg->compute_gc_maps)
2487 return;
2490 * During marking, all frames except the top frame are at a call site, and we mark the
2491 * top frame conservatively. This means that we only need to compute and record
2492 * GC maps for call sites.
2495 if (!(cfg->comp_done & MONO_COMP_LIVENESS))
2496 /* Without liveness info, the live ranges are not precise enough */
2497 return;
2499 mono_analyze_liveness_gc (cfg);
2501 compute_frame_size (cfg);
2503 init_gcfg (cfg);
2505 process_spill_slots (cfg);
2506 process_other_slots (cfg);
2507 process_param_area_slots (cfg);
2508 process_variables (cfg);
2509 process_finally_clauses (cfg);
2511 create_map (cfg);
2514 #endif /* DISABLE_JIT */
2516 static void
2517 parse_debug_options (void)
2519 char **opts, **ptr;
2520 const char *env;
2522 env = g_getenv ("MONO_GCMAP_DEBUG");
2523 if (!env)
2524 return;
2526 opts = g_strsplit (env, ",", -1);
2527 for (ptr = opts; ptr && *ptr; ptr ++) {
2528 /* No options yet */
2529 fprintf (stderr, "Invalid format for the MONO_GCMAP_DEBUG env variable: '%s'\n", env);
2530 exit (1);
2532 g_strfreev (opts);
2533 g_free (env);
2536 void
2537 mini_gc_init (void)
2539 MonoGCCallbacks cb;
2541 memset (&cb, 0, sizeof (cb));
2542 cb.thread_attach_func = thread_attach_func;
2543 cb.thread_detach_func = thread_detach_func;
2544 cb.thread_suspend_func = thread_suspend_func;
2545 /* Comment this out to disable precise stack marking */
2546 cb.thread_mark_func = thread_mark_func;
2547 cb.get_provenance_func = get_provenance_func;
2548 mono_gc_set_gc_callbacks (&cb);
2550 logfile = mono_gc_get_logfile ();
2552 parse_debug_options ();
2554 mono_counters_register ("GC Maps size",
2555 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.gc_maps_size);
2556 mono_counters_register ("GC Call Sites size",
2557 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.gc_callsites_size);
2558 mono_counters_register ("GC Bitmaps size",
2559 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.gc_bitmaps_size);
2560 mono_counters_register ("GC Map struct size",
2561 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.gc_map_struct_size);
2562 mono_counters_register ("GC Call Sites encoded using 8 bits",
2563 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.gc_callsites8_size);
2564 mono_counters_register ("GC Call Sites encoded using 16 bits",
2565 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.gc_callsites16_size);
2566 mono_counters_register ("GC Call Sites encoded using 32 bits",
2567 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.gc_callsites32_size);
2569 mono_counters_register ("GC Map slots (all)",
2570 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.all_slots);
2571 mono_counters_register ("GC Map slots (ref)",
2572 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.ref_slots);
2573 mono_counters_register ("GC Map slots (noref)",
2574 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.noref_slots);
2575 mono_counters_register ("GC Map slots (pin)",
2576 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.pin_slots);
2578 mono_counters_register ("GC TLS Data size",
2579 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.tlsdata_size);
2581 mono_counters_register ("Stack space scanned (all)",
2582 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_stacks);
2583 mono_counters_register ("Stack space scanned (native)",
2584 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_native);
2585 mono_counters_register ("Stack space scanned (other)",
2586 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_other);
2587 mono_counters_register ("Stack space scanned (using GC Maps)",
2588 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned);
2589 mono_counters_register ("Stack space scanned (precise)",
2590 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_precisely);
2591 mono_counters_register ("Stack space scanned (pin)",
2592 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_conservatively);
2593 mono_counters_register ("Stack space scanned (pin registers)",
2594 MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_registers);
2597 #else
2599 void
2600 mini_gc_enable_gc_maps_for_aot (void)
2604 void
2605 mini_gc_init (void)
2607 MonoGCCallbacks cb;
2608 memset (&cb, 0, sizeof (cb));
2609 cb.get_provenance_func = get_provenance_func;
2610 mono_gc_set_gc_callbacks (&cb);
2613 #ifndef DISABLE_JIT
2615 static void
2616 mini_gc_init_gc_map (MonoCompile *cfg)
2620 void
2621 mini_gc_create_gc_map (MonoCompile *cfg)
2625 void
2626 mini_gc_set_slot_type_from_fp (MonoCompile *cfg, int slot_offset, GCSlotType type)
2630 void
2631 mini_gc_set_slot_type_from_cfa (MonoCompile *cfg, int slot_offset, GCSlotType type)
2635 #endif /* DISABLE_JIT */
2637 #endif
2639 #ifndef DISABLE_JIT
2642 * mini_gc_init_cfg:
2644 * Set GC specific options in CFG.
2646 void
2647 mini_gc_init_cfg (MonoCompile *cfg)
2649 if (mono_gc_is_moving ()) {
2650 cfg->disable_ref_noref_stack_slot_share = TRUE;
2651 cfg->gen_write_barriers = TRUE;
2654 mini_gc_init_gc_map (cfg);
2657 #endif /* DISABLE_JIT */
2660 * Problems with the current code:
2661 * - the stack walk is slow
2662 * - vtypes/refs used in EH regions are treated conservatively
2663 * - if the code is finished, less pinning will be done, causing problems because
2664 * we promote all surviving objects to old-gen.
2665 * - the unwind code can't handle a method stopped inside a finally region, it thinks the caller is
2666 * another method, but in reality it is either the exception handling code or the CALL_HANDLER opcode.
2667 * This manifests in "Unable to find ip offset x in callsite list" assertions.
2668 * - the unwind code also can't handle frames which are in the epilog, since the unwind info is not
2669 * precise there.
2673 * Ideas for creating smaller GC maps:
2674 * - remove empty columns from the bitmaps. This requires adding a mask bit array for
2675 * each bitmap.
2676 * - merge reg and stack slot bitmaps, so the unused bits at the end of the reg bitmap are
2677 * not wasted.
2678 * - if the bitmap width is not a multiple of 8, the remaining bits are wasted.
2679 * - group ref and non-ref stack slots together in mono_allocate_stack_slots ().
2680 * - add an index for the callsite table so that each entry can be encoded as a 1 byte difference
2681 * from an index entry.