Implement precise scanning of registers by scanning the stack slots where they are...
authorZoltan Varga <vargaz@gmail.com>
Thu, 16 Sep 2010 12:11:44 +0000 (14:11 +0200)
committerZoltan Varga <vargaz@gmail.com>
Mon, 3 Jan 2011 14:42:19 +0000 (15:42 +0100)
mono/mini/exceptions-amd64.c
mono/mini/gc-test.cs
mono/mini/liveness.c
mono/mini/method-to-ir.c
mono/mini/mini-amd64.c
mono/mini/mini-exceptions.c
mono/mini/mini-gc.c
mono/mini/mini-unwind.h
mono/mini/mini.h
mono/mini/unwind.c

index 862b61ed53e9e0dc742c3fd9809956eb9935bed2..e683bf2a11fcf6080ab66869911056d967764151 100644 (file)
@@ -537,7 +537,8 @@ mono_arch_get_throw_corlib_exception (MonoTrampInfo **info, gboolean aot)
 gboolean
 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, 
                                                         MonoJitInfo *ji, MonoContext *ctx, 
-                                                        MonoContext *new_ctx, MonoLMF **lmf, 
+                                                        MonoContext *new_ctx, MonoLMF **lmf,
+                                                        mgreg_t **save_locations,
                                                         StackFrameInfo *frame)
 {
        gpointer ip = MONO_CONTEXT_GET_IP (ctx);
@@ -563,6 +564,9 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
                        unwind_info = mono_aot_get_unwind_info (ji, &unwind_info_len);
                else
                        unwind_info = mono_get_cached_unwind_info (ji->used_regs, &unwind_info_len);
+
+               frame->unwind_info = unwind_info;
+               frame->unwind_info_len = unwind_info_len;
  
                regs [AMD64_RAX] = new_ctx->rax;
                regs [AMD64_RBX] = new_ctx->rbx;
@@ -580,7 +584,8 @@ mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls,
 
                mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start, 
                                                   (guint8*)ji->code_start + ji->code_size,
-                                                  ip, regs, MONO_MAX_IREGS + 1, &cfa);
+                                                  ip, regs, MONO_MAX_IREGS + 1, 
+                                                  save_locations, MONO_MAX_IREGS, &cfa);
 
                new_ctx->rax = regs [AMD64_RAX];
                new_ctx->rbx = regs [AMD64_RBX];
index 0e3130cc234f29564a671d66d8fdb0f003a1f9ed..a463a414a2fea16f6c91e2df6418cddeae30122d 100644 (file)
@@ -329,6 +329,11 @@ class Tests {
                return new object ();
        }
 
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static bool return_true () {
+               return true;
+       }
+
        [MethodImplAttribute (MethodImplOptions.NoInlining)]
        static bool return_false () {
                return false;
@@ -463,6 +468,26 @@ class Tests {
                return new object ();
        }
 
+       [MethodImplAttribute (MethodImplOptions.NoInlining)]
+       static void clobber_regs_and_gc () {
+               int sum = 0, i, j, k, l, m, n, s;
+               for (i = 0; i < 100; ++i)
+                       sum ++;
+               for (j = 0; j < 100; ++j)
+                       sum ++;
+               for (k = 0; k < 100; ++k)
+                       sum ++;
+               for (l = 0; l < 100; ++l)
+                       sum ++;
+               for (m = 0; m < 100; ++m)
+                       sum ++;
+               for (n = 0; n < 100; ++n)
+                       sum ++;
+               for (s = 0; s < 100; ++s)
+                       sum ++;
+               GC.Collect (1);
+       }
+
        [MethodImplAttribute (MethodImplOptions.NoInlining)]
        static void liveness_9_call1 (object o1, object o2, object o3) {
                o1.GetHashCode ();
@@ -472,10 +497,29 @@ class Tests {
 
        // Liveness + JIT temporaries
        public static int test_0_liveness_9 () {
-               // the result of alloc_obj () goes into a vreg, which gets converted to the
+               // the result of alloc_obj () goes into a vreg, which gets converted to a
                // JIT temporary because of the branching introduced by the cast
                // FIXME: This doesn't crash if MONO_TYPE_I is not treated as a GC ref
                liveness_9_call1 (alloc_obj (), (string)alloc_string (), alloc_obj_and_gc ());
                return 0;
        }
+
+       // Liveness for registers
+       public static int test_0_liveness_10 () {
+               // Make sure this goes into a register
+               object o = alloc_obj ();
+               o.GetHashCode ();
+               o.GetHashCode ();
+               o.GetHashCode ();
+               o.GetHashCode ();
+               o.GetHashCode ();
+               o.GetHashCode ();
+               // Break the bblock so o doesn't become a local vreg
+               if (return_true ())
+                       // Clobber it with a call and run a GC
+                       clobber_regs_and_gc ();
+               // Access it again
+               o.GetHashCode ();
+               return 0;
+       }
 }
\ No newline at end of file
index f8d073ff433ae5e08faa998024c0c3b6371b461e..d1ef6c81da783d2743a5e34948ee95a9039640e3 100644 (file)
@@ -958,6 +958,17 @@ update_liveness2_gc (MonoCompile *cfg, MonoInst *ins, gint32 *last_use, MonoMeth
 
 }
 
+static inline int
+get_vreg_from_var (MonoCompile *cfg, MonoInst *var)
+{
+       if (var->opcode == OP_REGVAR)
+               /* dreg contains a hreg, but inst_c0 still contains the var index */
+               return MONO_VARINFO (cfg, var->inst_c0)->vreg;
+       else
+               /* dreg still contains the vreg */
+               return var->dreg;
+}
+
 /*
  * mono_analyze_liveness_gc:
  *
@@ -1032,7 +1043,8 @@ mono_analyze_liveness_gc (MonoCompile *cfg)
                        k = (j * BITS_PER_CHUNK);       
                        while (bits_out) {
                                if ((bits_out & 1) && cfg->varinfo [k]->flags & MONO_INST_GC_TRACK) {
-                                       LIVENESS_DEBUG (printf ("Var R%d live at exit, last_use set to %x.\n", cfg->varinfo [j]->dreg, block_to));
+                                       int vreg = get_vreg_from_var (cfg, cfg->varinfo [k]);
+                                       LIVENESS_DEBUG (printf ("Var R%d live at exit, last_use set to %x.\n", vreg, block_to));
                                        last_use [k] = block_to;
                                }
                                bits_out >>= 1;
@@ -1063,22 +1075,26 @@ mono_analyze_liveness_gc (MonoCompile *cfg)
                        MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
 
                        if (last_use [idx] != 0 && cfg->varinfo [idx]->flags & MONO_INST_GC_TRACK) {
+                               int vreg = get_vreg_from_var (cfg, cfg->varinfo [idx]);
                                /* Live at exit, not written -> live on enter */
-                               LIVENESS_DEBUG (printf ("Var R%d live at enter, add range to R%d: [%x, %x)\n", cfg->varinfo [idx]->dreg, cfg->varinfo [idx]->dreg, block_from, last_use [idx]));
+                               LIVENESS_DEBUG (printf ("Var R%d live at enter, add range to R%d: [%x, %x)\n", vreg, vreg, block_from, last_use [idx]));
                                mono_linterval_add_range (cfg, vi->gc_interval, block_from, last_use [idx]);
                        }
                }
        }
 
-       /* FIXME: Arguments, these are stored to in the prolog */
-
 #ifdef ENABLE_LIVENESS_DEBUG
        LIVENESS_DEBUG (printf ("\nGC LIVENESS RESULT:\n"));
        for (idx = 0; idx < max_vars; ++idx) {
                MonoMethodVar *vi = MONO_VARINFO (cfg, idx);
-
-               if (cfg->varinfo [idx]->opcode == OP_REGOFFSET && (cfg->varinfo [idx]->flags & MONO_INST_GC_TRACK)) {
-                       LIVENESS_DEBUG (printf ("\tR%d (%s+0x%x): ", cfg->varinfo [idx]->dreg, mono_arch_regname (cfg->varinfo [idx]->inst_basereg), (int)cfg->varinfo [idx]->inst_offset));
+               MonoInst *var = cfg->varinfo [idx];
+               int vreg = get_vreg_from_var (cfg, var);
+
+               if (var->flags & MONO_INST_GC_TRACK) {
+                       if (var->opcode == OP_REGOFFSET)
+                               LIVENESS_DEBUG (printf ("\tR%d (%s+0x%x): ", vreg, mono_arch_regname (var->inst_basereg), (int)var->inst_offset));
+                       else
+                               LIVENESS_DEBUG (printf ("\tR%d (%s): ", vreg, mono_arch_regname (var->dreg)));
                        LIVENESS_DEBUG (mono_linterval_print (vi->gc_interval));
                        LIVENESS_DEBUG (printf ("\n"));
                }
index 5a65d6c1c39f3bc55fca2df80b965dce7b1fd2af..f36eb513ab8aa5337b7923643d5d0b60a2102b71 100644 (file)
@@ -11464,7 +11464,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                        live_range_start_bb [dreg] = bb;
                                }
 
-                               if (cfg->compute_gc_maps && def_ins && var->opcode != OP_REGVAR && (var->flags & MONO_INST_GC_TRACK)) {
+                               if (cfg->compute_gc_maps && def_ins && (var->flags & MONO_INST_GC_TRACK)) {
                                        MonoInst *tmp;
 
                                        MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_DEF);
@@ -11493,6 +11493,16 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                //mono_inst_set_src_registers (ins, sregs);
                                                live_range_end [sreg] = use_ins;
                                                live_range_end_bb [sreg] = bb;
+
+                                               if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
+                                                       MonoInst *tmp;
+
+                                                       MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
+                                                       /* var->dreg is a hreg */
+                                                       tmp->inst_c1 = sreg;
+                                                       mono_bblock_insert_after_ins (bb, ins, tmp);
+                                               }
+
                                                continue;
                                        }
 
@@ -11574,8 +11584,7 @@ mono_spill_global_vars (MonoCompile *cfg, gboolean *need_local_opts)
                                                live_range_end_bb [var->dreg] = bb;
                                        }
 
-                                       // FIXME: Only for ref vars
-                                       if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && var->opcode != OP_REGVAR && (var->flags & MONO_INST_GC_TRACK)) {
+                                       if (cfg->compute_gc_maps && var->dreg < orig_next_vreg && (var->flags & MONO_INST_GC_TRACK)) {
                                                MonoInst *tmp;
 
                                                MONO_INST_NEW (cfg, tmp, OP_GC_LIVENESS_USE);
index 234beccbffbe5012fd4aa9dfacf03a971e6e4ca6..5645e69c2d28d2334842df2d31c00f62b9381bba 100644 (file)
@@ -5860,6 +5860,9 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                                offset += 8;
                                mono_emit_unwind_op_offset (cfg, code, i, - offset);
                                async_exc_point (code);
+
+                               /* These are handled automatically by the stack marking code */
+                               mini_gc_set_slot_type_from_cfa (cfg, - offset, SLOT_NOREF);
                        }
        }
 
@@ -6015,11 +6018,25 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        }
                }
 
+               /* These can't contain refs */
                mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, previous_lmf), SLOT_NOREF);
                mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, lmf_addr), SLOT_NOREF);
                mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, method), SLOT_NOREF);
                mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rip), SLOT_NOREF);
                mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rsp), SLOT_NOREF);
+
+               /* These are handled automatically by the stack marking code */
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rbx), SLOT_NOREF);
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rbp), SLOT_NOREF);
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r12), SLOT_NOREF);
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r13), SLOT_NOREF);
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r14), SLOT_NOREF);
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, r15), SLOT_NOREF);
+#ifdef HOST_WIN32
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rdi), SLOT_NOREF);
+               mini_gc_set_slot_type_from_fp (cfg, lmf_offset + G_STRUCT_OFFSET (MonoLMF, rsi), SLOT_NOREF);
+#endif
+
        }
 
        /* Save callee saved registers */
@@ -6033,6 +6050,10 @@ mono_arch_emit_prolog (MonoCompile *cfg)
                        if (AMD64_IS_CALLEE_SAVED_REG (i) && (cfg->used_int_regs & (1 << i))) {
                                amd64_mov_membase_reg (code, AMD64_RSP, save_area_offset, i, 8);
                                mono_emit_unwind_op_offset (cfg, code, i, - (cfa_offset - save_area_offset));
+
+                               /* These are handled automatically by the stack marking code */
+                               mini_gc_set_slot_type_from_cfa (cfg, - (cfa_offset - save_area_offset), SLOT_NOREF);
+
                                save_area_offset += 8;
                                async_exc_point (code);
                        }
index 23f4b6cef325a3abde760bfba0eeef3cf04c4f67..778ba2a8198eac9d3b40749c69a74ad61c6658c8 100644 (file)
@@ -212,7 +212,7 @@ find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *res, Mo
        if (managed)
                *managed = FALSE;
 
-       err = mono_arch_find_jit_info (domain, jit_tls, ji, ctx, new_ctx, lmf, &frame);
+       err = mono_arch_find_jit_info (domain, jit_tls, ji, ctx, new_ctx, lmf, NULL, &frame);
        if (!err)
                return (gpointer)-1;
 
@@ -320,15 +320,23 @@ mono_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, MonoJitInfo *re
  *
  *   A version of mono_find_jit_info which returns all data in the StackFrameInfo
  * structure.
+<<<<<<< HEAD
  * A note about frames of type FRAME_TYPE_MANAGED_TO_NATIVE:
  * - These frames are used to mark managed-to-native transitions, so CTX will refer to native
  * code, and new_ctx will refer to the last managed frame. The caller should unwind once more
  * to obtain the last managed frame.
+=======
+ * If SAVE_LOCATIONS is not NULL, it should point to an array of size MONO_MAX_IREGS.
+ * On return, it will be filled with the locations where callee saved registers are saved
+ * by the current frame. This is returned outside of StackFrameInfo because it can be
+ * quite large on some platforms.
+>>>>>>> Implement precise scanning of registers by scanning the stack slots where they are saved.
  */
 gboolean
 mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
                                                MonoJitInfo *prev_ji, MonoContext *ctx,
                                                MonoContext *new_ctx, char **trace, MonoLMF **lmf,
+                                               mgreg_t **save_locations,
                                                StackFrameInfo *frame)
 {
        gboolean err;
@@ -348,7 +356,14 @@ mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls,
        if (!target_domain)
                target_domain = domain;
 
+<<<<<<< HEAD
        err = mono_arch_find_jit_info (target_domain, jit_tls, ji, ctx, new_ctx, lmf, frame);
+=======
+       if (save_locations)
+               memset (save_locations, 0, MONO_MAX_IREGS * sizeof (mgreg_t*));
+
+       err = mono_arch_find_jit_info_ext (target_domain, jit_tls, ji, ctx, new_ctx, lmf, save_locations, frame);
+>>>>>>> Implement precise scanning of registers by scanning the stack slots where they are saved.
        if (!err)
                return FALSE;
 
@@ -710,7 +725,12 @@ mono_walk_stack (MonoJitStackWalk func, MonoDomain *domain, MonoContext *start_c
 
        while (MONO_CONTEXT_GET_SP (&ctx) < jit_tls->end_of_stack) {
                frame.lmf = lmf;
+<<<<<<< HEAD
                res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, &frame);
+=======
+#ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
+               res = mono_find_jit_info_ext (domain, jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, &frame);
+>>>>>>> Implement precise scanning of registers by scanning the stack slots where they are saved.
                if (!res)
                        return;
 
index 306c1af154a85a5f935c197c46bf12daf51a257d..f950df14d89ef149df9fce1656803b3b7040f011 100644 (file)
@@ -41,6 +41,22 @@ typedef struct {
        GSList **live_intervals;
        /* Whenever the slot starts out as SLOT_PIN, then changes to SLOT_REF */
        gboolean *starts_pinned;
+       /* The number of registers in the map */
+       int nregs;
+       /*
+        * GC Type of registers.
+        * Registers might be shared between refs and non-refs, so we store a GC type
+        * for each live interval.
+        * FIXME: Do the same for slots too, i.e. make 'slots' a list.
+        * FIXME: Add a struct for the type + interval pair.
+        */
+       GSList **reg_types;
+       /* 
+        * Live intervals for registers.
+        * This has width MONO_MAX_IREGS.
+        * FIXME: Only store callee saved regs.
+        */
+       GSList **reg_live_intervals;
        /* Min and Max offsets of the stack frame relative to fp */
        int min_offset, max_offset;
        /* Same for the locals area */
@@ -86,10 +102,16 @@ typedef struct {
        int frame_offset;
        /* The number of stack slots */
        int nslots;
-       /* Thw width of the bitmap in bytes */
+       /* The number of registers in the map */
+       int nregs;
+       /* Thw width of the stack bitmap in bytes */
        int bitmap_width;
+       /* Thw width of the register bitmap in bytes */
+       int reg_bitmap_width;
        guint has_pin_slots : 1;
        guint has_ref_slots : 1;
+       guint has_ref_regs : 1;
+       guint has_pin_regs : 1;
        /* 
         * A bitmap whose width is equal to bitmap_width, and whose
         * height is equal to the number of possible PC offsets.
@@ -100,10 +122,22 @@ typedef struct {
         */
        guint8 *ref_bitmap;
        /*
-        * Same for SLOT_PIN. It is possible that the same bit is set in both bitmaps at different
-        * pc offsets, if the slot starts out as PIN, and later changes to REF.
+        * Same for SLOT_PIN. It is possible that the same bit is set in both bitmaps at
+     * different pc offsets, if the slot starts out as PIN, and later changes to REF.
         */
        guint8 *pin_bitmap;
+
+       /*
+        * Corresponding bitmaps for registers
+        * These have width MONO_MAX_IREGS in bits.
+        * FIXME: Merge these with the normal bitmaps, i.e. reserve the first x slots for them ?
+        */
+       guint8 *reg_pin_bitmap;
+       guint8 *reg_ref_bitmap;
+
+       /* The registers used by the method */
+       guint64 used_regs;
+
        /*
         * A bit array marking slots which contain refs.
         * This is used only for debugging.
@@ -148,6 +182,7 @@ typedef struct {
        int scanned;
        int scanned_precisely;
        int scanned_conservatively;
+       int scanned_registers;
 
        int all_slots;
        int noref_slots;
@@ -159,6 +194,40 @@ static JITGCStats stats;
 
 #define DEAD_REF ((gpointer)(gssize)0x2a2a2a2a2a2a2a2aULL)
 
+static inline void
+set_bit (guint8 *bitmap, int width, int y, int x)
+{
+       bitmap [(width * y) + (x / 8)] |= (1 << (x % 8));
+}
+
+static inline void
+clear_bit (guint8 *bitmap, int width, int y, int x)
+{
+       bitmap [(width * y) + (x / 8)] &= ~(1 << (x % 8));
+}
+
+static inline int
+get_bit (guint8 *bitmap, int width, int y, int x)
+{
+       return bitmap [(width * y) + (x / 8)] & (1 << (x % 8));
+}
+
+static const char*
+slot_type_to_string (StackSlotType type)
+{
+       switch (type) {
+       case SLOT_REF:
+               return "ref";
+       case SLOT_NOREF:
+               return "noref";
+       case SLOT_PIN:
+               return "pin";
+       default:
+               g_assert_not_reached ();
+               return NULL;
+       }
+}
+
 /*
  * thread_mark_func:
  *
@@ -178,9 +247,11 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
        GCMap *map;
        guint8* fp, *frame_start, *frame_end;
        int i, pc_offset;
-       int scanned = 0, scanned_precisely, scanned_conservatively;
+       int scanned = 0, scanned_precisely, scanned_conservatively, scanned_registers;
        gboolean res;
        StackFrameInfo frame;
+       mgreg_t *reg_locations [MONO_MAX_IREGS];
+       mgreg_t *new_reg_locations [MONO_MAX_IREGS];
 
        /* tls == NULL can happen during startup */
        if (mono_thread_internal_current () == NULL || !tls) {
@@ -200,6 +271,8 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
        scanned_precisely = 0;
        /* Number of bytes scanned conservatively based on GC map data */
        scanned_conservatively = 0;
+       /* Number of bytes scanned conservatively in register save areas */
+       scanned_registers = 0;
 
        /* This is one past the last address which we have scanned */
        stack_limit = stack_start;
@@ -211,13 +284,42 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
        else
                memcpy (&new_ctx, &tls->ctx, sizeof (MonoContext));
 
+       memset (reg_locations, 0, sizeof (reg_locations));
+       memset (new_reg_locations, 0, sizeof (new_reg_locations));
+
        while (TRUE) {
                memcpy (&ctx, &new_ctx, sizeof (ctx));
 
-               g_assert ((guint64)stack_limit % sizeof (gpointer) == 0);
+               for (i = 0; i < MONO_MAX_IREGS; ++i) {
+                       if (new_reg_locations [i]) {
+                               /*
+                                * If the current frame saves the register, it means it might modify its
+                                * value, thus the old location might not contain the same value, so
+                                * we have to mark it conservatively. If we have precise info about the
+                                * register, reg_locations [i] is already cleared.
+                                */
+                               if (!precise && reg_locations [i]) {
+                                       DEBUG (printf ("\tscan saved reg %s location %p.\n", mono_arch_regname (i), reg_locations [i]));
+                                       mono_gc_conservatively_scan_area (reg_locations [i], reg_locations [i] + sizeof (mgreg_t));
+                                       // FIXME: This is not correct because the location might be in a frame
+                                       // without a GC map
+                                       // Use a separate stat for now
+                                       //scanned_conservatively += sizeof (mgreg_t);
+                                       scanned_registers += sizeof (mgreg_t);
+                               }
+
+                               reg_locations [i] = new_reg_locations [i];
+
+                               if (!precise) {
+                                       DEBUG (printf ("\treg %s is at location %p.\n", mono_arch_regname (i), reg_locations [i]));
+                               }
+                       }
+               }
+
+               g_assert ((guint64)stack_limit % sizeof (mgreg_t) == 0);
 
 #ifdef MONO_ARCH_HAVE_FIND_JIT_INFO_EXT
-               res = mono_find_jit_info_ext (frame.domain ? frame.domain : mono_domain_get (), tls->jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, &frame);
+               res = mono_find_jit_info_ext (frame.domain ? frame.domain : mono_domain_get (), tls->jit_tls, NULL, &ctx, &new_ctx, NULL, &lmf, new_reg_locations, &frame);
                if (!res)
                        break;
 #else
@@ -253,6 +355,7 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
 
                if (!map) {
                        DEBUG (char *fname = mono_method_full_name (ji->method, TRUE); printf ("Mark(%d): No GC map for %s\n", precise, fname); g_free (fname));
+
                        continue;
                }
 
@@ -286,7 +389,7 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
 #endif
 
                frame_start = fp + map->frame_offset;
-               frame_end = frame_start + (map->nslots * sizeof (gpointer));
+               frame_end = frame_start + (map->nslots * sizeof (mgreg_t));
 
                pc_offset = (guint8*)MONO_CONTEXT_GET_IP (&ctx) - (guint8*)ji->code_start;
                g_assert (pc_offset >= 0);
@@ -298,8 +401,6 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
                 * call for each object.
                 */
 
-               scanned += frame_end - frame_start;
-
                /* Pinning needs to be done first, then the precise scan later */
 
                if (!precise) {
@@ -311,6 +412,7 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
                                mono_gc_conservatively_scan_area (stack_limit, frame_start);
                        }
 
+                       /* Mark stack slots */
                        if (map->has_pin_slots) {
                                guint8 *pin_bitmap = &map->pin_bitmap [(map->bitmap_width * pc_offset)];
                                guint8 *p;
@@ -321,25 +423,58 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
                                        pinned = pin_bitmap [i / 8] & (1 << (i % 8));
                                        if (pinned) {
                                                DEBUG (printf ("\tscan slot %s0x%x(fp)=%p.\n", (guint8*)p > (guint8*)fp ? "" : "-", ABS ((int)((gssize)p - (gssize)fp)), p));
-                                               mono_gc_conservatively_scan_area (p, p + sizeof (gpointer));
-                                               scanned_conservatively += sizeof (gpointer);
+                                               mono_gc_conservatively_scan_area (p, p + sizeof (mgreg_t));
+                                               scanned_conservatively += sizeof (mgreg_t);
                                        } else {
-                                               scanned_precisely += sizeof (gpointer);
+                                               scanned_precisely += sizeof (mgreg_t);
                                        }
-                                       p += sizeof (gpointer);
+                                       p += sizeof (mgreg_t);
                                }
                        } else {
-                               scanned_precisely += map->nslots * sizeof (gpointer);
+                               scanned_precisely += map->nslots * sizeof (mgreg_t);
                        }
 
+                       /* Mark registers */
+                       if (map->has_pin_regs) {
+                               guint8 *pin_bitmap = &map->reg_pin_bitmap [(map->reg_bitmap_width * pc_offset)];
+                               for (i = 0; i < map->nregs; ++i) {
+                                       if (!(map->used_regs & (1 << i)))
+                                               continue;
+
+                                       /* We treated the save slots as precise above */
+                                       scanned_precisely -= sizeof (mgreg_t);
+
+                                       if (!reg_locations [i])
+                                               continue;
+
+                                       if (!(pin_bitmap [i / 8] & (1 << (i % 8)))) {
+                                               /*
+                                                * The method uses this register, and we have precise info for it.
+                                                * This means the location will be scanned precisely.
+                                                * Tell the code at the beginning of the loop that this location is
+                                                * processed.
+                                                */
+                                               DEBUG (printf ("\treg %s at location %p is precise.\n", mono_arch_regname (i), reg_locations [i]));
+                                               reg_locations [i] = NULL;
+                                               scanned_precisely += sizeof (mgreg_t);
+                                       }
+                               }
+                       }
+
+                       scanned += frame_end - frame_start;
+
+                       /* Not == because registers are marked in a later frame */
+                       g_assert (scanned >= scanned_precisely + scanned_conservatively);
+
                        stack_limit = frame_end;
                } else {
+                       /* Mark stack slots */
                        if (map->has_ref_slots) {
                                guint8 *ref_bitmap = &map->ref_bitmap [(map->bitmap_width * pc_offset)];
                                gboolean live;
 
                                for (i = 0; i < map->nslots; ++i) {
-                                       MonoObject **ptr = (MonoObject**)(frame_start + (i * sizeof (gpointer)));
+                                       MonoObject **ptr = (MonoObject**)(frame_start + (i * sizeof (mgreg_t)));
 
                                        live = ref_bitmap [i / 8] & (1 << (i % 8));
 
@@ -354,7 +489,7 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
                                                }
                                        } else {
                                                if (map->ref_slots [i / 8] & (1 << (i % 8))) {
-                                                       DEBUG (printf ("\tref %s0x%x(fp)=%p: dead (%p)\n", (guint8*)ptr >= (guint8*)fp ? "" : "-", ABS ((int)((gssize)ptr - (gssize)fp)), ptr, obj));
+                                                       DEBUG (printf ("\tref %s0x%x(fp)=%p: dead (%p)\n", (guint8*)ptr >= (guint8*)fp ? "" : "-", ABS ((int)((gssize)ptr - (gssize)fp)), ptr, *ptr));
                                                        /*
                                                         * Fail fast if the live range is incorrect, and
                                                         * the JITted code tries to access this object
@@ -364,6 +499,58 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
                                        }
                                }
                        }
+
+                       /* Mark registers */
+
+                       /*
+                        * Registers are different from stack slots, they have no address where they
+                        * are stored. Instead, some frame below this frame in the stack saves them
+                        * in its prolog to the stack. We can mark this location precisely.
+                        */
+                       if (map->has_ref_regs) {
+                               guint8 *ref_bitmap = &map->reg_ref_bitmap [(map->reg_bitmap_width * pc_offset)];
+                               for (i = 0; i < map->nregs; ++i) {
+                                       if (!reg_locations [i])
+                                               continue;
+
+                                       if (ref_bitmap [i / 8] & (1 << (i % 8))) {
+                                               /*
+                                                * reg_locations [i] contains the address of the stack slot where
+                                                * i was last saved, so mark that slot.
+                                                */
+                                               MonoObject **ptr = (MonoObject**)reg_locations [i];
+                                               MonoObject *obj = *ptr;
+
+                                               if (obj) {
+                                                       DEBUG (printf ("\treg %s saved at %p: %p ->", mono_arch_regname (i), reg_locations [i], obj));
+                                                       *ptr = mono_gc_scan_object (obj);
+                                                       DEBUG (printf (" %p.\n", *ptr));
+                                               } else {
+                                                       DEBUG (printf ("\treg %s saved at %p: %p", mono_arch_regname (i), reg_locations [i], obj));
+                                               }
+                                       }
+
+                                       /* Mark the save slot as processed */
+                                       reg_locations [i] = NULL;
+                               }
+                       }       
+               }
+       }
+
+       if (!precise) {
+               /* Scan the remaining register save locations */
+               for (i = 0; i < MONO_MAX_IREGS; ++i) {
+                       if (reg_locations [i]) {
+                               DEBUG (printf ("\tscan saved reg location %p.\n", reg_locations [i]));
+                               mono_gc_conservatively_scan_area (reg_locations [i], reg_locations [i] + sizeof (mgreg_t));
+                               scanned_conservatively += sizeof (mgreg_t);
+                       }
+                       // FIXME: Is this needed ?
+                       if (new_reg_locations [i]) {
+                               DEBUG (printf ("\tscan saved reg location %p.\n", new_reg_locations [i]));
+                               mono_gc_conservatively_scan_area (new_reg_locations [i], new_reg_locations [i] + sizeof (mgreg_t));
+                               scanned_conservatively += sizeof (mgreg_t);
+                       }
                }
        }
 
@@ -379,6 +566,7 @@ thread_mark_func (gpointer user_data, guint8 *stack_start, guint8 *stack_end, gb
                stats.scanned += scanned;
                stats.scanned_precisely += scanned_precisely;
                stats.scanned_conservatively += scanned_conservatively;
+               stats.scanned_registers += scanned_registers;
        }
 
        //mono_gc_conservatively_scan_area (stack_start, stack_end);
@@ -562,7 +750,7 @@ process_other_slots (MonoCompile *cfg)
 
                if (cfg->verbose_level > 1) {
                        if (type == SLOT_NOREF)
-                               printf ("\tnoref at fp+0x%x (slot = %d) (cfa - 0x%x)\n", (int)(fp_slot * sizeof (mgreg_t)), fp_slot, (int)(slot * sizeof (mgreg_t)));
+                               printf ("\tnoref slot at fp+0x%x (slot = %d) (cfa - 0x%x)\n", (int)(fp_slot * sizeof (mgreg_t)), fp_slot, (int)(slot * sizeof (mgreg_t)));
                }
        }
 
@@ -581,7 +769,7 @@ process_other_slots (MonoCompile *cfg)
 
                if (cfg->verbose_level > 1) {
                        if (type == SLOT_REF)
-                               printf ("\tref at fp+0x%x (slot = %d)\n", offset, slot);
+                               printf ("\tref slot at fp+0x%x (slot = %d)\n", offset, slot);
                }
        }
 }
@@ -600,8 +788,8 @@ process_locals (MonoCompile *cfg)
        int locals_max_offset = gcfg->locals_max_offset;
 
        /* Slots for locals are NOREF by default */
-       locals_min_slot = (locals_min_offset - gcfg->min_offset) / sizeof (gpointer);
-       locals_max_slot = (locals_max_offset - gcfg->min_offset) / sizeof (gpointer);
+       locals_min_slot = (locals_min_offset - gcfg->min_offset) / sizeof (mgreg_t);
+       locals_max_slot = (locals_max_offset - gcfg->min_offset) / sizeof (mgreg_t);
        for (i = locals_min_slot; i < locals_max_slot; ++i)
                set_slot (gcfg, i, SLOT_NOREF);
 
@@ -627,13 +815,49 @@ process_locals (MonoCompile *cfg)
                MonoType *t = ins->inst_vtype;
                MonoMethodVar *vmv;
                guint32 pos;
+               gboolean byref = t->byref;
+               MonoLiveInterval *li;
 
                vmv = MONO_VARINFO (cfg, i);
 
+               if (ins->opcode == OP_REGVAR) {
+                       int hreg;
+                       StackSlotType slot_type;
+
+                       t = mini_type_get_underlying_type (NULL, t);
+
+                       hreg = ins->dreg;
+                       g_assert (hreg < MONO_MAX_IREGS);
+
+                       if (byref) {
+                               /* These have no live interval, be conservative */
+                               slot_type = SLOT_PIN;
+                               li = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoLiveInterval));
+                               mono_linterval_add_range (cfg, li, 0, cfg->code_size);
+                       } else if (!MONO_TYPE_IS_REFERENCE (t)) {
+                               // FIXME: These have no gc live interval
+                               continue;
+                       } else {
+                               slot_type = SLOT_REF;
+                               li = vmv->gc_interval;
+                       }
+
+                       gcfg->reg_types [hreg] = g_slist_prepend_mempool (cfg->mempool, gcfg->reg_types [hreg], GINT_TO_POINTER (slot_type));
+                       gcfg->reg_live_intervals [hreg] = g_slist_prepend_mempool (cfg->mempool, gcfg->reg_live_intervals [hreg], li);
+
+                       if (cfg->verbose_level > 1) {
+                               printf ("\t%s reg %s: ", slot_type_to_string (slot_type), mono_arch_regname (hreg));
+                               mono_linterval_print (li);
+                               printf ("\n");
+                       }
+
+                       continue;
+               }
+
                if (ins->opcode != OP_REGOFFSET)
                        continue;
 
-               if (ins->inst_offset % sizeof (gpointer) != 0)
+               if (ins->inst_offset % sizeof (mgreg_t) != 0)
                        continue;
 
                pos = fp_offset_to_slot (cfg, ins->inst_offset);
@@ -690,12 +914,12 @@ process_locals (MonoCompile *cfg)
                                        }
                                }
                        } else if (pin) {
-                               for (j = 0; j < size / sizeof (gpointer); ++j)
+                               for (j = 0; j < size / sizeof (mgreg_t); ++j)
                                        set_slot (gcfg, pos + j, SLOT_PIN);
                        }
 
                        if (!pin) {
-                               for (j = 0; j < size / sizeof (gpointer); ++j) {
+                               for (j = 0; j < size / sizeof (mgreg_t); ++j) {
                                        live_intervals [pos + j] = g_slist_prepend_mempool (cfg->mempool, live_intervals [pos + j], interval);
                                        starts_pinned [pos + j] = TRUE;
                                }
@@ -704,7 +928,7 @@ process_locals (MonoCompile *cfg)
                        g_free (bitmap);
 
                        if (cfg->verbose_level > 1) {
-                               printf ("\tvtype R%d at fp+0x%x-0x%x: %s ", vmv->vreg, (int)ins->inst_offset, (int)(ins->inst_offset + (size / sizeof (gpointer))), mono_type_full_name (ins->inst_vtype));
+                               printf ("\tvtype R%d at fp+0x%x-0x%x: %s ", vmv->vreg, (int)ins->inst_offset, (int)(ins->inst_offset + (size / sizeof (mgreg_t))), mono_type_full_name (ins->inst_vtype));
                                if (interval)
                                        mono_linterval_print (interval);
                                else
@@ -788,18 +1012,63 @@ process_arguments (MonoCompile *cfg)
 
        /*
         * Stack slots holding arguments are initialized in the prolog.
+        * This means we can treat them alive for the whole method.
         */
        for (i = 0; i < sig->param_count + sig->hasthis; ++i) {
                MonoInst *ins = cfg->args [i];
                MonoType *t = ins->inst_vtype;
                int pos;
+               gboolean byref = t->byref;
+
+               /* For some reason, 'this' is byref */
+               if (sig->hasthis && i == 0 && !cfg->method->klass->valuetype)
+                       t = &cfg->method->klass->byval_arg;
+
+               if (ins->opcode == OP_REGVAR) {
+                       int hreg;
+                       StackSlotType slot_type;
+
+                       t = mini_type_get_underlying_type (NULL, t);
+
+                       if (byref)
+                               slot_type = SLOT_PIN;
+                       else
+                               slot_type = MONO_TYPE_IS_REFERENCE (t) ? SLOT_REF : SLOT_NOREF;
+
+                       hreg = ins->dreg;
+                       g_assert (hreg < MONO_MAX_IREGS);
+
+                       if (gcfg->reg_live_intervals [hreg]) {
+                               /* 
+                                * FIXME: This argument shares a hreg with a local, we can't add the whole
+                                * method as a live interval, since it would overlap with the locals
+                                * live interval.
+                                */
+                               continue;
+                       }
+
+                       gcfg->reg_types [hreg] = g_slist_prepend_mempool (cfg->mempool, gcfg->reg_types [hreg], GINT_TO_POINTER (slot_type));
+
+                       /* Live for the whole method */
+                       li = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoLiveInterval));
+                       mono_linterval_add_range (cfg, li, 0, cfg->code_size);
+                       gcfg->reg_live_intervals [hreg] = g_slist_prepend_mempool (cfg->mempool, gcfg->reg_live_intervals [hreg], li);
+
+                       if (cfg->verbose_level > 1) {
+                               printf ("\t%s arg reg %s: ", slot_type_to_string (slot_type), mono_arch_regname (hreg));
+                               mono_linterval_print (li);
+                               printf ("\n");
+                       }
+
+                       continue;
+               }
 
                if (ins->opcode != OP_REGOFFSET)
                        continue;
 
-               pos = (ins->inst_offset - gcfg->min_offset) / sizeof (gpointer);
+               g_assert (ins->inst_offset % sizeof (mgreg_t) == 0);
 
-               g_assert (ins->inst_offset % sizeof (gpointer) == 0);
+               pos = (ins->inst_offset - gcfg->min_offset) / sizeof (mgreg_t);
 
                if (ins->inst_offset >= gcfg->max_offset)
                        /* In parent frame */
@@ -824,7 +1093,7 @@ process_arguments (MonoCompile *cfg)
                                size = mono_class_value_size (ins->klass, NULL);
 
                        if (!ins->klass->has_references) {
-                               for (j = 0; j < size / sizeof (gpointer); ++j)
+                               for (j = 0; j < size / sizeof (mgreg_t); ++j)
                                        set_slot (gcfg, pos + j, SLOT_NOREF);
                                continue;
                        }
@@ -832,10 +1101,6 @@ process_arguments (MonoCompile *cfg)
                        // FIXME:
                        continue;
                }
-               
-               /* For some reason, 'this' is byref */
-               if (sig->hasthis && i == 0 && !cfg->method->klass->valuetype)
-                       t = &cfg->method->klass->byval_arg;
 
                if (t->byref)
                        continue;
@@ -875,15 +1140,15 @@ compute_frame_size (MonoCompile *cfg)
 
        /* Locals */
 #ifdef TARGET_AMD64
-       locals_min_offset = ALIGN_TO (cfg->locals_min_stack_offset, sizeof (gpointer));
+       locals_min_offset = ALIGN_TO (cfg->locals_min_stack_offset, sizeof (mgreg_t));
        locals_max_offset = cfg->locals_max_stack_offset;
 #else
        /* min/max stack offset needs to be computed in mono_arch_allocate_vars () */
        NOT_IMPLEMENTED;
 #endif
 
-       locals_min_offset = ALIGN_TO (locals_min_offset, sizeof (gpointer));
-       locals_max_offset = ALIGN_TO (locals_max_offset, sizeof (gpointer));
+       locals_min_offset = ALIGN_TO (locals_min_offset, sizeof (mgreg_t));
+       locals_max_offset = ALIGN_TO (locals_max_offset, sizeof (mgreg_t));
 
        min_offset = locals_min_offset;
        max_offset = locals_max_offset;
@@ -921,13 +1186,13 @@ void
 mini_gc_create_gc_map (MonoCompile *cfg)
 {
        GCMap *map;
-       int i, nslots, alloc_size;
+       int i, nregs, nslots, alloc_size;
        int ntypes [16];
        StackSlotType *slots = NULL;
        GSList **live_intervals;
-       int bitmap_width, bitmap_size;
+       int bitmap_width, bitmap_size, reg_bitmap_width, reg_bitmap_size;
        gboolean *starts_pinned;
-       gboolean has_ref_slots, has_pin_slots;
+       gboolean has_ref_slots, has_pin_slots, has_ref_regs, has_pin_regs;
        MonoCompileGC *gcfg = cfg->gc_info;
 
        if (!cfg->compute_gc_maps)
@@ -983,7 +1248,8 @@ mini_gc_create_gc_map (MonoCompile *cfg)
        if (cfg->verbose_level > 1)
                printf ("GC Map for %s: 0x%x-0x%x\n", mono_method_full_name (cfg->method, TRUE), gcfg->min_offset, gcfg->max_offset);
 
-       nslots = (gcfg->max_offset - gcfg->min_offset) / sizeof (gpointer);
+       nslots = (gcfg->max_offset - gcfg->min_offset) / sizeof (mgreg_t);
+       nregs = MONO_MAX_IREGS;
        /* slot [i] == type of slot at fp - <min_offset> + i*4/8 */
        slots = g_new0 (StackSlotType, nslots);
        live_intervals = g_new0 (GSList*, nslots);
@@ -993,6 +1259,8 @@ mini_gc_create_gc_map (MonoCompile *cfg)
        gcfg->nslots = nslots;
        gcfg->live_intervals = live_intervals;
        gcfg->starts_pinned = starts_pinned;
+       gcfg->reg_types = mono_mempool_alloc0 (cfg->mempool, sizeof (GSList*) * nregs);
+       gcfg->reg_live_intervals = mono_mempool_alloc0 (cfg->mempool, sizeof (GSList*) * nregs);
 
        /* All slots start out as PIN */
        for (i = 0; i < nslots; ++i)
@@ -1015,12 +1283,22 @@ mini_gc_create_gc_map (MonoCompile *cfg)
                if (slots [i] == SLOT_PIN || (slots [i] == SLOT_REF && starts_pinned [i]))
                        has_pin_slots = TRUE;
        }
+       has_ref_regs = FALSE;
+       has_pin_regs = TRUE;
+       for (i = 0; i < nregs; ++i) {
+               GSList *l;
+               for (l = gcfg->reg_types [i]; l; l = l->next)
+                       if (GPOINTER_TO_UINT (l->data) == SLOT_REF)
+                               has_ref_regs = TRUE;
+       }
 
        if (cfg->verbose_level > 1)
                printf ("Slots: %d Refs: %d NoRefs: %d Pin: %d\n", nslots, ntypes [SLOT_REF], ntypes [SLOT_NOREF], ntypes [SLOT_PIN]);
 
        bitmap_width = ALIGN_TO (nslots, 8) / 8;
        bitmap_size = bitmap_width * cfg->code_len;
+       reg_bitmap_width = ALIGN_TO (nregs, 8) / 8;
+       reg_bitmap_size = reg_bitmap_width * cfg->code_len;
        alloc_size = sizeof (GCMap);
        map = mono_domain_alloc0 (cfg->domain, alloc_size);
        gc_maps_size += alloc_size;
@@ -1028,9 +1306,13 @@ mini_gc_create_gc_map (MonoCompile *cfg)
        map->frame_reg = cfg->frame_reg;
        map->frame_offset = gcfg->min_offset;
        map->nslots = nslots;
+       map->nregs = nregs;
        map->bitmap_width = bitmap_width;
+       map->reg_bitmap_width = reg_bitmap_width;
        map->has_ref_slots = has_ref_slots;
        map->has_pin_slots = has_pin_slots;
+       map->has_ref_regs = has_ref_regs;
+       map->has_pin_regs = has_pin_regs;
        map->ref_slots = mono_domain_alloc0 (cfg->domain, bitmap_width);
        gc_maps_size += bitmap_width;
 
@@ -1042,8 +1324,18 @@ mini_gc_create_gc_map (MonoCompile *cfg)
                map->pin_bitmap = mono_domain_alloc0 (cfg->domain, bitmap_size);
                gc_maps_size += bitmap_size;
        }
+       if (has_ref_regs) {
+               map->reg_ref_bitmap = mono_domain_alloc0 (cfg->domain, reg_bitmap_size);
+               gc_maps_size += reg_bitmap_size;
+       }
+       if (has_pin_regs) {
+               map->reg_pin_bitmap = mono_domain_alloc0 (cfg->domain, reg_bitmap_size);
+               gc_maps_size += reg_bitmap_size;
+       }
 
        /* Create liveness bitmaps */
+
+       /* Stack slots */
        for (i = 0; i < nslots; ++i) {
                int pc_offset;
 
@@ -1061,19 +1353,74 @@ mini_gc_create_gc_map (MonoCompile *cfg)
 
                                iv = live_intervals [i]->data;
                                for (pc_offset = 0; pc_offset < iv->range->from; ++pc_offset)
-                                       map->pin_bitmap [(map->bitmap_width * pc_offset) + i / 8] |= (1 << (i % 8));                                    
+                                       set_bit (map->pin_bitmap, bitmap_width, pc_offset, i);
                        }
 
                        for (l = live_intervals [i]; l; l = l->next) {
                                iv = l->data;
                                for (r = iv->range; r; r = r->next) {
                                        for (pc_offset = r->from; pc_offset < r->to; ++pc_offset)
-                                               map->ref_bitmap [(map->bitmap_width * pc_offset) + i / 8] |= (1 << (i % 8));
+                                               set_bit (map->ref_bitmap, bitmap_width, pc_offset, i);
                                }
                        }
                } else if (slots [i] == SLOT_PIN) {
                        for (pc_offset = 0; pc_offset < cfg->code_len; ++pc_offset)
-                               map->pin_bitmap [(map->bitmap_width * pc_offset) + i / 8] |= (1 << (i % 8));
+                               set_bit (map->pin_bitmap, bitmap_width, pc_offset, i);
+               }
+       }
+
+       /* Registers */
+       for (i = 0; i < nregs; ++i) {
+               MonoLiveInterval *iv;
+               GSList *l, *l2;
+               MonoLiveRange2 *r;
+               int pc_offset;
+               StackSlotType type;
+
+               if (!(cfg->used_int_regs & (1 << i))) {
+                       g_assert (!gcfg->reg_types [i]);
+                       continue;
+               }
+
+               g_assert (i < 64);
+
+               map->used_regs |= (1 << i);
+
+               /*
+                * By default, registers are PIN.
+                * This is because we don't know their type outside their live range, since
+                * they could have the same value as in the caller, or a value set by the
+                * current method etc.
+                */
+               for (pc_offset = 0; pc_offset < cfg->code_len; ++pc_offset)
+                       set_bit (map->reg_pin_bitmap, reg_bitmap_width, pc_offset, i);
+
+               l2 = gcfg->reg_types [i];
+               for (l = gcfg->reg_live_intervals [i]; l; l = l->next) {
+                       iv = l->data;
+                       type = GPOINTER_TO_UINT (l2->data);
+
+                       if (type == SLOT_REF) {
+                               for (r = iv->range; r; r = r->next) {
+                                       for (pc_offset = r->from; pc_offset < r->to; ++pc_offset) {
+                                               set_bit (map->reg_ref_bitmap, reg_bitmap_width, pc_offset, i);
+                                       }
+                               }
+                       } else if (type == SLOT_PIN) {
+                               for (r = iv->range; r; r = r->next) {
+                                       for (pc_offset = r->from; pc_offset < r->to; ++pc_offset) {
+                                               set_bit (map->reg_pin_bitmap, reg_bitmap_width, pc_offset, i);
+                                       }
+                               }
+                       } else if (type == SLOT_NOREF) {
+                               for (r = iv->range; r; r = r->next) {
+                                       for (pc_offset = r->from; pc_offset < r->to; ++pc_offset) {
+                                               clear_bit (map->reg_pin_bitmap, reg_bitmap_width, pc_offset, i);
+                                       }
+                               }
+                       }
+
+                       l2 = l2->next;
                }
        }
 
@@ -1124,8 +1471,10 @@ mini_gc_init (void)
                                                        MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned);
        mono_counters_register ("Stack space scanned (precise)",
                                                        MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_precisely);
-       mono_counters_register ("Stack space scanned (conservative)",
+       mono_counters_register ("Stack space scanned (pin)",
                                                        MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_conservatively);
+       mono_counters_register ("Stack space scanned (pin registers)",
+                                                       MONO_COUNTER_GC | MONO_COUNTER_INT, &stats.scanned_registers);
 }
 
 #else
index 378d7e13f01991909531a9ab523545ca6859244f..18cd299cd81e6086d7bbdc518487ddd380d16fc1 100644 (file)
@@ -103,8 +103,9 @@ mono_unwind_ops_encode (GSList *unwind_ops, guint32 *out_len) MONO_INTERNAL;
 
 void
 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
-                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, 
-                                  int nregs, guint8 **out_cfa) MONO_INTERNAL;
+                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, int nregs,
+                                  mgreg_t **save_locations, int save_locations_len,
+                                  guint8 **out_cfa) MONO_INTERNAL;
 
 void mono_unwind_init (void) MONO_INTERNAL;
 
index 17cbb017daa198277bd9127aa6ecc6400761c6a6..6e30af22c532d186dd288c99e736ab3604fea3d3 100644 (file)
@@ -238,6 +238,8 @@ typedef struct {
        int native_offset;
        int il_offset;
        gpointer lmf;
+       guint32 unwind_info_len;
+       guint8 *unwind_info;
 } StackFrameInfo;
 
 typedef struct {
@@ -1902,7 +1904,8 @@ void     mono_arch_setup_resume_sighandler_ctx  (MonoContext *ctx, gpointer func
 gboolean
 mono_arch_find_jit_info (MonoDomain *domain, MonoJitTlsData *jit_tls, 
                                                 MonoJitInfo *ji, MonoContext *ctx, 
-                                                MonoContext *new_ctx, MonoLMF **lmf, 
+                                                MonoContext *new_ctx, MonoLMF **lmf,
+                                                mgreg_t **save_locations,
                                                 StackFrameInfo *frame_info) MONO_INTERNAL;
 gpointer  mono_arch_get_throw_exception_by_name (void) MONO_INTERNAL;
 gpointer mono_arch_get_call_filter              (MonoTrampInfo **info, gboolean aot) MONO_INTERNAL;
@@ -1979,6 +1982,7 @@ gboolean
 mono_find_jit_info_ext (MonoDomain *domain, MonoJitTlsData *jit_tls, 
                                                MonoJitInfo *prev_ji, MonoContext *ctx,
                                                MonoContext *new_ctx, char **trace, MonoLMF **lmf,
+                                               mgreg_t **save_locations,
                                                StackFrameInfo *frame) MONO_INTERNAL;
 
 gpointer mono_get_throw_exception               (void) MONO_INTERNAL;
index df3058114d47876f3da0b236e7f5e0e223f5164c..754081febd8de041ca59dc9c9c177f6af9473162 100644 (file)
@@ -319,12 +319,16 @@ print_dwarf_state (int cfa_reg, int cfa_offset, int ip, int nregs, Loc *location
  * Given the state of the current frame as stored in REGS, execute the unwind 
  * operations in unwind_info until the location counter reaches POS. The result is 
  * stored back into REGS. OUT_CFA will receive the value of the CFA.
+ * If SAVE_LOCATIONS is non-NULL, it should point to an array of size SAVE_LOCATIONS_LEN.
+ * On return, the nth entry will point to the address of the stack slot where register
+ * N was saved, or NULL, if it was not saved by this frame.
  * This function is signal safe.
  */
 void
 mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len, 
-                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, 
-                                  int nregs, guint8 **out_cfa)
+                                  guint8 *start_ip, guint8 *end_ip, guint8 *ip, mgreg_t *regs, int nregs,
+                                  mgreg_t **save_locations, int save_locations_len,
+                                  guint8 **out_cfa)
 {
        Loc locations [NUM_REGS];
        int i, pos, reg, cfa_reg, cfa_offset;
@@ -386,12 +390,17 @@ mono_unwind_frame (guint8 *unwind_info, guint32 unwind_info_len,
                }
        }
 
+       if (save_locations)
+               memset (save_locations, 0, save_locations_len * sizeof (mgreg_t*));
+
        cfa_val = (guint8*)regs [mono_dwarf_reg_to_hw_reg (cfa_reg)] + cfa_offset;
        for (i = 0; i < NUM_REGS; ++i) {
                if (locations [i].loc_type == LOC_OFFSET) {
                        int hreg = mono_dwarf_reg_to_hw_reg (i);
                        g_assert (hreg < nregs);
                        regs [hreg] = *(mgreg_t*)(cfa_val + locations [i].offset);
+                       if (save_locations && hreg < save_locations_len)
+                               save_locations [hreg] = (mgreg_t*)(cfa_val + locations [i].offset);
                }
        }