+/*
+ * A compressed version of GCMap. This is what gets stored in MonoJitInfo.
+ */
+typedef struct {
+ //guint8 *ref_slots;
+ //guint8 encoded_size;
+
+ /*
+ * The arrays below are embedded after the struct.
+ * Their address needs to be computed.
+ */
+
+ /* The fixed fields of the GCMap encoded using LEB128 */
+ guint8 encoded [MONO_ZERO_LEN_ARRAY];
+
+ /* An array of ncallsites entries, each entry is callsite_entry_size bytes long */
+ guint8 callsites [MONO_ZERO_LEN_ARRAY];
+
+ /* The GC bitmaps */
+ guint8 bitmaps [MONO_ZERO_LEN_ARRAY];
+} GCEncodedMap;
+
+static int precise_frame_count [2], precise_frame_limit = -1;
+static gboolean precise_frame_limit_inited;
+
+/* Stats */
+typedef struct {
+ int scanned_stacks;
+ int scanned;
+ int scanned_precisely;
+ int scanned_conservatively;
+ int scanned_registers;
+ int scanned_native;
+ int scanned_other;
+
+ int all_slots;
+ int noref_slots;
+ int ref_slots;
+ int pin_slots;
+
+ int gc_maps_size;
+ int gc_callsites_size;
+ int gc_callsites8_size;
+ int gc_callsites16_size;
+ int gc_callsites32_size;
+ int gc_bitmaps_size;
+ int gc_map_struct_size;
+ int tlsdata_size;
+} JITGCStats;
+
+static JITGCStats stats;
+
+static FILE *logfile;
+
+// FIXME: Move these to a shared place
+
+static inline void
+encode_uleb128 (guint32 value, guint8 *buf, guint8 **endbuf)
+{
+ guint8 *p = buf;
+
+ do {
+ guint8 b = value & 0x7f;
+ value >>= 7;
+ if (value != 0) /* more bytes to come */
+ b |= 0x80;
+ *p ++ = b;
+ } while (value);
+
+ *endbuf = p;
+}
+
+static G_GNUC_UNUSED void
+encode_sleb128 (gint32 value, guint8 *buf, guint8 **endbuf)
+{
+ gboolean more = 1;
+ gboolean negative = (value < 0);
+ guint32 size = 32;
+ guint8 byte;
+ guint8 *p = buf;
+
+ while (more) {
+ byte = value & 0x7f;
+ value >>= 7;
+ /* the following is unnecessary if the
+ * implementation of >>= uses an arithmetic rather
+ * than logical shift for a signed left operand
+ */
+ if (negative)
+ /* sign extend */
+ value |= - (1 <<(size - 7));
+ /* sign bit of byte is second high order bit (0x40) */
+ if ((value == 0 && !(byte & 0x40)) ||
+ (value == -1 && (byte & 0x40)))
+ more = 0;
+ else
+ byte |= 0x80;
+ *p ++= byte;
+ }
+
+ *endbuf = p;
+}
+
+static inline guint32
+decode_uleb128 (guint8 *buf, guint8 **endbuf)
+{
+ guint8 *p = buf;
+ guint32 res = 0;
+ int shift = 0;
+
+ while (TRUE) {
+ guint8 b = *p;
+ p ++;
+
+ res = res | (((int)(b & 0x7f)) << shift);
+ if (!(b & 0x80))
+ break;
+ shift += 7;
+ }
+
+ *endbuf = p;
+
+ return res;
+}
+
+static inline gint32
+decode_sleb128 (guint8 *buf, guint8 **endbuf)
+{
+ guint8 *p = buf;
+ gint32 res = 0;
+ int shift = 0;
+
+ while (TRUE) {
+ guint8 b = *p;
+ p ++;
+
+ res = res | (((int)(b & 0x7f)) << shift);
+ shift += 7;
+ if (!(b & 0x80)) {
+ if (shift < 32 && (b & 0x40))
+ res |= - (1 << shift);
+ break;
+ }
+ }
+
+ *endbuf = p;
+
+ return res;
+}
+
+static int
+encode_frame_reg (int frame_reg)
+{
+#ifdef TARGET_AMD64
+ if (frame_reg == AMD64_RSP)
+ return 0;
+ else if (frame_reg == AMD64_RBP)
+ return 1;
+#elif defined(TARGET_X86)
+ if (frame_reg == X86_EBP)
+ return 0;
+ else if (frame_reg == X86_ESP)
+ return 1;
+#else
+ NOT_IMPLEMENTED;
+#endif
+ g_assert_not_reached ();
+ return -1;
+}
+
+static int
+decode_frame_reg (int encoded)
+{
+#ifdef TARGET_AMD64
+ if (encoded == 0)
+ return AMD64_RSP;
+ else if (encoded == 1)
+ return AMD64_RBP;
+#elif defined(TARGET_X86)
+ if (encoded == 0)
+ return X86_EBP;
+ else if (encoded == 1)
+ return X86_ESP;
+#else
+ NOT_IMPLEMENTED;
+#endif
+ g_assert_not_reached ();
+ return -1;
+}
+
+#ifdef TARGET_AMD64
+#ifdef HOST_WIN32
+static int callee_saved_regs [] = { AMD64_RBP, AMD64_RBX, AMD64_R12, AMD64_R13, AMD64_R14, AMD64_R15, AMD64_RDI, AMD64_RSI };
+#else
+static int callee_saved_regs [] = { AMD64_RBP, AMD64_RBX, AMD64_R12, AMD64_R13, AMD64_R14, AMD64_R15 };
+#endif
+#elif defined(TARGET_X86)
+static int callee_saved_regs [] = { X86_EBX, X86_ESI, X86_EDI };
+#endif
+
+static guint32
+encode_regmask (guint32 regmask)
+{
+ int i;
+ guint32 res;
+
+ res = 0;
+ for (i = 0; i < sizeof (callee_saved_regs) / sizeof (int); ++i) {
+ if (regmask & (1 << callee_saved_regs [i])) {
+ res |= (1 << i);
+ regmask -= (1 << callee_saved_regs [i]);
+ }
+ }
+ g_assert (regmask == 0);
+ return res;
+}
+
+static guint32
+decode_regmask (guint32 regmask)
+{
+ int i;
+ guint32 res;
+
+ res = 0;
+ for (i = 0; i < sizeof (callee_saved_regs) / sizeof (int); ++i)
+ if (regmask & (1 << i))
+ res |= (1 << callee_saved_regs [i]);
+ return res;
+}
+
+/*
+ * encode_gc_map:
+ *
+ * Encode the fixed fields of MAP into a buffer pointed to by BUF.
+ */
+static void
+encode_gc_map (GCMap *map, guint8 *buf, guint8 **endbuf)
+{
+ guint32 flags, freg;
+
+ encode_sleb128 (map->start_offset / sizeof (mgreg_t), buf, &buf);
+ encode_sleb128 (map->end_offset / sizeof (mgreg_t), buf, &buf);
+ encode_sleb128 (map->map_offset / sizeof (mgreg_t), buf, &buf);
+ encode_uleb128 (map->nslots, buf, &buf);
+ g_assert (map->callsite_entry_size <= 4);
+ freg = encode_frame_reg (map->frame_reg);
+ g_assert (freg < 2);
+ 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);
+ encode_uleb128 (flags, buf, &buf);
+ encode_uleb128 (encode_regmask (map->used_int_regs), buf, &buf);
+ if (map->has_ref_regs)
+ encode_uleb128 (encode_regmask (map->reg_ref_mask), buf, &buf);
+ if (map->has_pin_regs)
+ encode_uleb128 (encode_regmask (map->reg_pin_mask), buf, &buf);
+ encode_uleb128 (map->ncallsites, buf, &buf);
+
+ *endbuf = buf;
+}
+
+/*
+ * decode_gc_map:
+ *
+ * Decode the encoded GC map representation in BUF and store the result into MAP.
+ */
+static void
+decode_gc_map (guint8 *buf, GCMap *map, guint8 **endbuf)
+{
+ guint32 flags;
+ int stack_bitmap_size, reg_ref_bitmap_size, reg_pin_bitmap_size, offset, freg;
+ int i, n;
+
+ map->start_offset = decode_sleb128 (buf, &buf) * sizeof (mgreg_t);
+ map->end_offset = decode_sleb128 (buf, &buf) * sizeof (mgreg_t);
+ map->map_offset = decode_sleb128 (buf, &buf) * sizeof (mgreg_t);
+ map->nslots = decode_uleb128 (buf, &buf);
+ flags = decode_uleb128 (buf, &buf);
+ map->has_ref_slots = (flags & 1) ? 1 : 0;
+ map->has_pin_slots = (flags & 2) ? 1 : 0;
+ map->has_ref_regs = (flags & 4) ? 1 : 0;
+ map->has_pin_regs = (flags & 8) ? 1 : 0;
+ map->callsite_entry_size = ((flags >> 4) & 0x3) + 1;
+ freg = flags >> 6;
+ map->frame_reg = decode_frame_reg (freg);
+ map->used_int_regs = decode_regmask (decode_uleb128 (buf, &buf));
+ if (map->has_ref_regs) {
+ map->reg_ref_mask = decode_regmask (decode_uleb128 (buf, &buf));
+ n = 0;
+ for (i = 0; i < NREGS; ++i)
+ if (map->reg_ref_mask & (1 << i))
+ n ++;
+ map->nref_regs = n;
+ }
+ if (map->has_pin_regs) {
+ map->reg_pin_mask = decode_regmask (decode_uleb128 (buf, &buf));
+ n = 0;
+ for (i = 0; i < NREGS; ++i)
+ if (map->reg_pin_mask & (1 << i))
+ n ++;
+ map->npin_regs = n;
+ }
+ map->ncallsites = decode_uleb128 (buf, &buf);
+
+ stack_bitmap_size = (ALIGN_TO (map->nslots, 8) / 8) * map->ncallsites;
+ reg_ref_bitmap_size = (ALIGN_TO (map->nref_regs, 8) / 8) * map->ncallsites;
+ reg_pin_bitmap_size = (ALIGN_TO (map->npin_regs, 8) / 8) * map->ncallsites;
+ offset = 0;
+ map->stack_ref_bitmap_offset = offset;
+ if (map->has_ref_slots)
+ offset += stack_bitmap_size;
+ map->stack_pin_bitmap_offset = offset;
+ if (map->has_pin_slots)
+ offset += stack_bitmap_size;
+ map->reg_ref_bitmap_offset = offset;
+ if (map->has_ref_regs)
+ offset += reg_ref_bitmap_size;
+ map->reg_pin_bitmap_offset = offset;
+ if (map->has_pin_regs)
+ offset += reg_pin_bitmap_size;
+
+ *endbuf = buf;
+}