Merge pull request #444 from knocte/xbuild_improvements
[mono.git] / mono / mini / method-to-ir.c
index a3d2e7ea8c0cec19049fa5826e9613957b578439..c4d731d20969ffa66a82f67d41a4075a393caa80 100644 (file)
@@ -52,6 +52,7 @@
 #include <mono/metadata/monitor.h>
 #include <mono/metadata/profiler-private.h>
 #include <mono/metadata/profiler.h>
+#include <mono/metadata/debug-mono-symfile.h>
 #include <mono/utils/mono-compiler.h>
 #include <mono/utils/mono-memory-model.h>
 #include <mono/metadata/mono-basic-block.h>
@@ -347,6 +348,8 @@ mono_create_helper_signatures (void)
 
 #define LOAD_ERROR do { if (mini_get_debug_options ()->break_on_unverified) G_BREAKPOINT (); else goto load_error; } while (0)
 
+#define TYPE_LOAD_ERROR(klass) do { if (mini_get_debug_options ()->break_on_unverified) G_BREAKPOINT (); else { cfg->exception_ptr = klass; goto load_error; } } while (0)
+
 #define GET_BBLOCK(cfg,tblock,ip) do { \
                (tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
                if (!(tblock)) {        \
@@ -3063,6 +3066,17 @@ emit_generic_class_init (MonoCompile *cfg, MonoClass *klass)
 #endif
 }
 
+static void
+emit_seq_point (MonoCompile *cfg, MonoMethod *method, guint8* ip, gboolean intr_loc)
+{
+       MonoInst *ins;
+
+       if (cfg->gen_seq_points && cfg->method == method) {
+               NEW_SEQ_POINT (cfg, ins, ip - cfg->header->code, intr_loc);
+               MONO_ADD_INS (cfg->cbb, ins);
+       }
+}
+
 static void
 save_cast_details (MonoCompile *cfg, MonoClass *klass, int obj_reg)
 {
@@ -5633,14 +5647,8 @@ is_supported_tail_call (MonoCompile *cfg, MonoMethod *method, MonoMethod *cmetho
        /* Debugging support */
 #if 0
        if (supported_tail_call) {
-               static int count = 0;
-               count ++;
-               if (getenv ("COUNT")) {
-                       if (count == atoi (getenv ("COUNT")))
-                               printf ("LAST: %s\n", mono_method_full_name (cmethod, TRUE));
-                       if (count > atoi (getenv ("COUNT")))
-                               supported_tail_call = FALSE;
-               }
+               if (!mono_debug_count ())
+                       supported_tail_call = FALSE;
        }
 #endif
 
@@ -5764,8 +5772,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
        gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
        int context_used;
        gboolean init_locals, seq_points, skip_dead_blocks;
-       gboolean disable_inline;
+       gboolean disable_inline, sym_seq_points = FALSE;
        MonoInst *cached_tls_addr = NULL;
+       MonoDebugMethodInfo *minfo;
+       MonoBitSet *seq_point_locs = NULL;
 
        disable_inline = is_jit_optimizer_disabled (method);
 
@@ -5809,6 +5819,23 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
        seq_points = cfg->gen_seq_points && cfg->method == method;
 
+       if (cfg->gen_seq_points && cfg->method == method) {
+               minfo = mono_debug_lookup_method (method);
+               if (minfo) {
+                       int i, n_il_offsets;
+                       int *il_offsets;
+                       int *line_numbers;
+
+                       mono_debug_symfile_get_line_numbers_full (minfo, NULL, NULL, &n_il_offsets, &il_offsets, &line_numbers, NULL, NULL);
+                       seq_point_locs = mono_bitset_mem_new (mono_mempool_alloc0 (cfg->mempool, mono_bitset_alloc_size (header->code_size, 0)), header->code_size, 0);
+                       sym_seq_points = TRUE;
+                       for (i = 0; i < n_il_offsets; ++i) {
+                               if (il_offsets [i] < header->code_size)
+                                       mono_bitset_set_fast (seq_point_locs, il_offsets [i]);
+                       }
+               }
+       }
+
        /* 
         * Methods without init_locals set could cause asserts in various passes
         * (#497220).
@@ -6264,7 +6291,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                 * Currently, we generate these automatically at points where the IL
                 * stack is empty.
                 */
-               if (seq_points && sp == stack_start) {
+               if (seq_points && ((sp == stack_start) || (sym_seq_points && mono_bitset_test_fast (seq_point_locs, ip - header->code)))) {
                        /*
                         * Make methods interruptable at the beginning, and at the targets of
                         * backward branches.
@@ -6305,7 +6332,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                switch (*ip) {
                case CEE_NOP:
-                       if (seq_points && sp != stack_start) {
+                       if (seq_points && !sym_seq_points && sp != stack_start) {
                                /*
                                 * The C# compiler uses these nops to notify the JIT that it should
                                 * insert seq points.
@@ -6663,6 +6690,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        MonoInst *vtable_arg = NULL;
                        gboolean check_this = FALSE;
                        gboolean supported_tail_call = FALSE;
+                       gboolean need_seq_point = FALSE;
 
                        CHECK_OPSIZE (5);
                        token = read32 (ip + 1);
@@ -6727,7 +6755,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        cmethod = mini_get_method (cfg, method, token, NULL, generic_context);
                                        cil_method = cmethod;
                                }
-
+                                       
                                if (!cmethod || mono_loader_get_last_error ())
                                        LOAD_ERROR;
                                if (!dont_verify && !cfg->skip_visibility) {
@@ -6762,7 +6790,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                if (!cmethod->klass->inited)
                                        if (!mono_class_init (cmethod->klass))
-                                               LOAD_ERROR;
+                                               TYPE_LOAD_ERROR (cmethod->klass);
 
                                if (cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL &&
                                    mini_class_is_system_array (cmethod->klass)) {
@@ -6787,6 +6815,21 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                mono_save_token_info (cfg, image, token, cil_method);
 
+                               if (!MONO_TYPE_IS_VOID (fsig->ret) && !sym_seq_points) {
+                                       /*
+                                        * Need to emit an implicit seq point after every non-void call so single stepping through nested calls like
+                                        * foo (bar (), baz ())
+                                        * works correctly. MS does this also:
+                                        * http://stackoverflow.com/questions/6937198/making-your-net-language-step-correctly-in-the-debugger
+                                        * The problem with this approach is that the debugger will stop after all calls returning a value,
+                                        * even for simple cases, like:
+                                        * int i = foo ();
+                                        */
+                                       /* Special case a few common successor opcodes */
+                                       if (!(ip + 5 < end && ip [5] == CEE_POP))
+                                               need_seq_point = TRUE;
+                               }
+
                                n = fsig->param_count + fsig->hasthis;
 
                                if (mono_security_get_mode () == MONO_SECURITY_MODE_CAS) {
@@ -7010,6 +7053,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                ip += 5;
                                ins_flag = 0;
+                               if (need_seq_point)
+                                       emit_seq_point (cfg, method, ip, FALSE);
                                break;
                        }
 
@@ -7053,6 +7098,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                ip += 5;
                                ins_flag = 0;
+                               if (need_seq_point)
+                                       emit_seq_point (cfg, method, ip, FALSE);
                                break;
                        }
 
@@ -7077,12 +7124,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        cfg->real_offset += 5;
                                        bblock = cfg->cbb;
 
-                                       if (!MONO_TYPE_IS_VOID (fsig->ret))
+                                       if (!MONO_TYPE_IS_VOID (fsig->ret)) {
                                                /* *sp is already set by inline_method */
                                                sp++;
+                                       }
 
                                        inline_costs += costs;
                                        ins_flag = 0;
+                                       if (need_seq_point)
+                                               emit_seq_point (cfg, method, ip, FALSE);
                                        break;
                                }
                        }
@@ -7187,6 +7237,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                ip += 5;
                                ins_flag = 0;
+                               if (need_seq_point)
+                                       emit_seq_point (cfg, method, ip, FALSE);
                                break;
                        }
                                        
@@ -7232,6 +7284,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                ip += 5;
                                ins_flag = 0;
+                               emit_seq_point (cfg, method, ip, FALSE);
                                break;
                        }
 
@@ -7244,6 +7297,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                ip += 5;
                                ins_flag = 0;
+                               if (need_seq_point)
+                                       emit_seq_point (cfg, method, ip, FALSE);
                                break;
                        }
 
@@ -7309,6 +7364,18 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                break;
                        }
 
+                       /* 
+                        * Synchronized wrappers.
+                        * Its hard to determine where to replace a method with its synchronized
+                        * wrapper without causing an infinite recursion. The current solution is
+                        * to add the synchronized wrapper in the trampolines, and to
+                        * change the called method to a dummy wrapper, and resolve that wrapper
+                        * to the real method in mono_jit_compile_method ().
+                        */
+                       if (cfg->method->wrapper_type == MONO_WRAPPER_SYNCHRONIZED && mono_marshal_method_from_wrapper (cfg->method) == cmethod) {
+                               cmethod = mono_marshal_get_synchronized_inner_wrapper (cmethod);
+                       }
+
                        /* Common call */
                        INLINE_FAILURE;
                        ins = mono_emit_method_call_full (cfg, cmethod, fsig, sp, virtual ? sp [0] : NULL,
@@ -7321,6 +7388,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                        ip += 5;
                        ins_flag = 0;
+                       if (need_seq_point)
+                               emit_seq_point (cfg, method, ip, FALSE);
                        break;
                }
                case CEE_RET:
@@ -7333,9 +7402,15 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                 * (test case: test_0_inline_throw ()).
                                 */
                                if (return_var && cfg->cbb->in_count) {
+                                       MonoType *ret_type = mono_method_signature (method)->ret;
+
                                        MonoInst *store;
                                        CHECK_STACK (1);
                                        --sp;
+
+                                       if ((method->wrapper_type == MONO_WRAPPER_DYNAMIC_METHOD || method->wrapper_type == MONO_WRAPPER_NONE) && target_type_is_incompatible (cfg, ret_type, *sp))
+                                               UNVERIFIED;
+
                                        //g_assert (returnvar != -1);
                                        EMIT_NEW_TEMPSTORE (cfg, store, return_var->inst_c0, *sp);
                                        cfg->ret_var_set = TRUE;
@@ -7344,7 +7419,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if (cfg->ret) {
                                        MonoType *ret_type = mono_method_signature (method)->ret;
 
-                                       if (seq_points) {
+                                       if (seq_points && !sym_seq_points) {
                                                /* 
                                                 * Place a seq point here too even through the IL stack is not
                                                 * empty, so a step over on
@@ -8080,7 +8155,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        mono_save_token_info (cfg, image, token, cmethod);
 
                        if (!mono_class_init (cmethod->klass))
-                               LOAD_ERROR;
+                               TYPE_LOAD_ERROR (cmethod->klass);
 
                        if (cfg->generic_sharing_context)
                                context_used = mono_method_check_context_used (cmethod);
@@ -8877,7 +8952,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                        ftype = mono_field_get_type (field);
 
-                       g_assert (!(ftype->attrs & FIELD_ATTRIBUTE_LITERAL));
+                       if (ftype->attrs & FIELD_ATTRIBUTE_LITERAL)
+                               UNVERIFIED;
 
                        /* The special_static_fields field is init'd in mono_class_vtable, so it needs
                         * to be called here.
@@ -9234,7 +9310,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        ins->sreg1 = sp [0]->dreg;
                                        ins->inst_newa_class = klass;
                                        ins->type = STACK_OBJ;
-                                       ins->klass = klass;
+                                       ins->klass = array_type;
                                        MONO_ADD_INS (cfg->cbb, ins);
                                        cfg->flags |= MONO_CFG_HAS_ARRAY_ACCESS;
                                        cfg->cbb->has_array_access = TRUE;
@@ -10097,6 +10173,64 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                ip += 5;
                                break;
                        }
+                       case CEE_MONO_JIT_ATTACH: {
+                               MonoInst *args [16];
+                               MonoInst *ad_ins, *lmf_ins;
+                               MonoBasicBlock *next_bb = NULL;
+
+                               cfg->orig_domain_var = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_LOCAL);
+
+                               EMIT_NEW_PCONST (cfg, ins, NULL);
+                               MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
+
+#if TARGET_WIN32
+                               ad_ins = NULL;
+                               lmf_ins = NULL;
+#else
+                               ad_ins = mono_get_domain_intrinsic (cfg);
+                               lmf_ins = mono_get_lmf_intrinsic (cfg);
+#endif
+
+#ifdef MONO_ARCH_HAVE_TLS_GET
+                               if (MONO_ARCH_HAVE_TLS_GET && ad_ins && lmf_ins) {
+                                       NEW_BBLOCK (cfg, next_bb);
+
+                                       MONO_ADD_INS (cfg->cbb, ad_ins);
+                                       MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, ad_ins->dreg, 0);
+                                       MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, next_bb);
+
+                                       MONO_ADD_INS (cfg->cbb, lmf_ins);
+                                       MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, lmf_ins->dreg, 0);
+                                       MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBNE_UN, next_bb);
+                               }
+#endif
+
+                               if (cfg->compile_aot) {
+                                       /* AOT code is only used in the root domain */
+                                       EMIT_NEW_PCONST (cfg, args [0], NULL);
+                               } else {
+                                       EMIT_NEW_PCONST (cfg, args [0], cfg->domain);
+                               }
+                               ins = mono_emit_jit_icall (cfg, mono_jit_thread_attach, args);
+                               MONO_EMIT_NEW_UNALU (cfg, OP_MOVE, cfg->orig_domain_var->dreg, ins->dreg);
+
+                               if (next_bb) {
+                                       MONO_START_BB (cfg, next_bb);
+                                       bblock = cfg->cbb;
+                               }
+                               ip += 2;
+                               break;
+                       }
+                       case CEE_MONO_JIT_DETACH: {
+                               MonoInst *args [16];
+
+                               /* Restore the original domain */
+                               dreg = alloc_ireg (cfg);
+                               EMIT_NEW_UNALU (cfg, args [0], OP_MOVE, dreg, cfg->orig_domain_var->dreg);
+                               mono_emit_jit_icall (cfg, mono_jit_set_domain, args);
+                               ip += 2;
+                               break;
+                       }
                        default:
                                g_error ("opcode 0x%02x 0x%02x not handled", MONO_CUSTOM_PREFIX, ip [1]);
                                break;