Remove one more compile-time dependency on sgen.
[mono.git] / mono / mini / method-to-ir.c
index 9aed6322d48aa41f2cf515f327eaeb9671fa3937..ae7cd780e3c7d4f819d36c29ed29d0f36dc6f4cb 100644 (file)
                        goto exception_exit;    \
                }                       \
        } while (0)
-
+#define OUT_OF_MEMORY_FAILURE do {     \
+               mono_cfg_set_exception (cfg, MONO_EXCEPTION_OUT_OF_MEMORY);             \
+               goto exception_exit;    \
+       } while (0)
 /* Determine whenever 'ins' represents a load of the 'this' argument */
 #define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
 
@@ -2596,19 +2599,26 @@ create_write_barrier_bitmap (MonoClass *klass, unsigned *wb_bitmap, int offset)
 static void
 emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value, int value_reg)
 {
-#ifdef HAVE_SGEN_GC
        int card_table_shift_bits;
        gpointer card_table_mask;
-       guint8 *card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
+       guint8 *card_table;
        MonoInst *dummy_use;
-
-#ifdef MONO_ARCH_HAVE_CARD_TABLE_WBARRIER
        int nursery_shift_bits;
        size_t nursery_size;
+       gboolean has_card_table_wb = FALSE;
+
+       if (!cfg->gen_write_barriers)
+               return;
+
+       card_table = mono_gc_get_card_table (&card_table_shift_bits, &card_table_mask);
 
        mono_gc_get_nursery (&nursery_shift_bits, &nursery_size);
 
-       if (!cfg->compile_aot && card_table && nursery_shift_bits > 0) {
+#ifdef MONO_ARCH_HAVE_CARD_TABLE_WBARRIER
+       has_card_table_wb = TRUE;
+#endif
+
+       if (has_card_table_wb && !cfg->compile_aot && card_table && nursery_shift_bits > 0) {
                MonoInst *wbarrier;
 
                MONO_INST_NEW (cfg, wbarrier, OP_CARD_TABLE_WBARRIER);
@@ -2618,9 +2628,7 @@ emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value, int value_
                else
                        wbarrier->sreg2 = value_reg;
                MONO_ADD_INS (cfg->cbb, wbarrier);
-       } else
-#endif
-       if (card_table) {
+       } else if (card_table) {
                int offset_reg = alloc_preg (cfg);
                int card_reg  = alloc_preg (cfg);
                MonoInst *ins;
@@ -2655,7 +2663,6 @@ emit_write_barrier (MonoCompile *cfg, MonoInst *ptr, MonoInst *value, int value_
                dummy_use->sreg1 = value_reg;
                MONO_ADD_INS (cfg->cbb, dummy_use);
        }
-#endif
 }
 
 static gboolean
@@ -4772,7 +4779,7 @@ check_inline_caller_method_name_limit (MonoMethod *caller_method)
 
 static int
 inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
-               guchar *ip, guint real_offset, GList *dont_inline, gboolean inline_allways)
+               guchar *ip, guint real_offset, GList *dont_inline, gboolean inline_always)
 {
        MonoInst *ins, *rvar = NULL;
        MonoMethodHeader *cheader;
@@ -4794,11 +4801,11 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
 
 #if (MONO_INLINE_CALLED_LIMITED_METHODS)
-       if ((! inline_allways) && ! check_inline_called_method_name_limit (cmethod))
+       if ((! inline_always) && ! check_inline_called_method_name_limit (cmethod))
                return 0;
 #endif
 #if (MONO_INLINE_CALLER_LIMITED_METHODS)
-       if ((! inline_allways) && ! check_inline_caller_method_name_limit (cfg->method))
+       if ((! inline_always) && ! check_inline_caller_method_name_limit (cfg->method))
                return 0;
 #endif
 
@@ -4814,8 +4821,13 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        cheader = mono_method_get_header (cmethod);
 
        if (cheader == NULL || mono_loader_get_last_error ()) {
+               MonoLoaderError *error = mono_loader_get_last_error ();
+
                if (cheader)
                        mono_metadata_free_mh (cheader);
+               if (inline_always && error)
+                       mono_cfg_set_exception (cfg, error->exception_type);
+
                mono_loader_clear_error ();
                return 0;
        }
@@ -4880,7 +4892,7 @@ inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig,
        cfg->ret_var_set = prev_ret_var_set;
        cfg->inline_depth --;
 
-       if ((costs >= 0 && costs < 60) || inline_allways) {
+       if ((costs >= 0 && costs < 60) || inline_always) {
                if (cfg->verbose_level > 2)
                        printf ("INLINE END %s -> %s\n", mono_method_full_name (cfg->method, TRUE), mono_method_full_name (cmethod, TRUE));
                
@@ -5430,6 +5442,60 @@ is_exception_class (MonoClass *class)
        return FALSE;
 }
 
+/*
+ * is_jit_optimizer_disabled:
+ *
+ *   Determine whenever M's assembly has a DebuggableAttribute with the
+ * IsJITOptimizerDisabled flag set.
+ */
+static gboolean
+is_jit_optimizer_disabled (MonoMethod *m)
+{
+       MonoAssembly *ass = m->klass->image->assembly;
+       MonoCustomAttrInfo* attrs;
+       static MonoClass *klass;
+       int i;
+       gboolean val = FALSE;
+
+       g_assert (ass);
+       if (ass->jit_optimizer_disabled_inited)
+               return ass->jit_optimizer_disabled;
+
+       klass = mono_class_from_name_cached (mono_defaults.corlib, "System.Diagnostics", "DebuggableAttribute");
+
+       attrs = mono_custom_attrs_from_assembly (ass);
+       if (attrs) {
+               for (i = 0; i < attrs->num_attrs; ++i) {
+                       MonoCustomAttrEntry *attr = &attrs->attrs [i];
+                       const gchar *p;
+                       int len;
+                       MonoMethodSignature *sig;
+
+                       if (!attr->ctor || attr->ctor->klass != klass)
+                               continue;
+                       /* Decode the attribute. See reflection.c */
+                       len = attr->data_size;
+                       p = (const char*)attr->data;
+                       g_assert (read16 (p) == 0x0001);
+                       p += 2;
+
+                       // FIXME: Support named parameters
+                       sig = mono_method_signature (attr->ctor);
+                       if (sig->param_count != 2 || sig->params [0]->type != MONO_TYPE_BOOLEAN || sig->params [1]->type != MONO_TYPE_BOOLEAN)
+                               continue;
+                       /* Two boolean arguments */
+                       p ++;
+                       val = *p;
+               }
+       }
+
+       ass->jit_optimizer_disabled = val;
+       mono_memory_barrier ();
+       ass->jit_optimizer_disabled_inited = TRUE;
+
+       return val;
+}
+
 /*
  * mono_method_to_ir:
  *
@@ -5468,6 +5534,9 @@ 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;
+
+       disable_inline = is_jit_optimizer_disabled (method);
 
        /* serialization and xdomain stuff may need access to private fields and methods */
        dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
@@ -5598,6 +5667,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        MonoExceptionClause *clause = &header->clauses [i];
                        GET_BBLOCK (cfg, try_bb, ip + clause->try_offset);
                        try_bb->real_offset = clause->try_offset;
+                       try_bb->try_start = TRUE;
+                       try_bb->region = ((i + 1) << 8) | clause->flags;
                        GET_BBLOCK (cfg, tblock, ip + clause->handler_offset);
                        tblock->real_offset = clause->handler_offset;
                        tblock->flags |= BB_EXCEPTION_HANDLER;
@@ -5610,6 +5681,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        if (clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY ||
                            clause->flags == MONO_EXCEPTION_CLAUSE_FILTER ||
                            clause->flags == MONO_EXCEPTION_CLAUSE_FAULT) {
+                               if (seq_points) {
+                                       NEW_SEQ_POINT (cfg, ins, clause->handler_offset, TRUE);
+                                       MONO_ADD_INS (tblock, ins);
+                               }
                                MONO_INST_NEW (cfg, ins, OP_START_HANDLER);
                                MONO_ADD_INS (tblock, ins);
 
@@ -6717,6 +6792,33 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                break;
                        }
 
+                       /*
+                        * Implement a workaround for the inherent races involved in locking:
+                        * Monitor.Enter ()
+                        * try {
+                        * } finally {
+                        *    Monitor.Exit ()
+                        * }
+                        * If a thread abort happens between the call to Monitor.Enter () and the start of the
+                        * try block, the Exit () won't be executed, see:
+                        * http://www.bluebytesoftware.com/blog/2007/01/30/MonitorEnterThreadAbortsAndOrphanedLocks.aspx
+                        * To work around this, we extend such try blocks to include the last x bytes
+                        * of the Monitor.Enter () call.
+                        */
+                       if (cmethod && cmethod->klass == mono_defaults.monitor_class && !strcmp (cmethod->name, "Enter") && mono_method_signature (cmethod)->param_count == 1) {
+                               MonoBasicBlock *tbb;
+
+                               GET_BBLOCK (cfg, tbb, ip + 5);
+                               /* 
+                                * Only extend try blocks with a finally, to avoid catching exceptions thrown
+                                * from Monitor.Enter like ArgumentNullException.
+                                */
+                               if (tbb->try_start && MONO_REGION_FLAGS(tbb->region) == MONO_EXCEPTION_CLAUSE_FINALLY) {
+                                       /* Mark this bblock as needing to be extended */
+                                       tbb->extend_try_block = TRUE;
+                               }
+                       }
+
                        /* Conversion to a JIT intrinsic */
                        if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_method (cfg, cmethod, fsig, sp))) {
                                bblock = cfg->cbb;
@@ -6736,20 +6838,20 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        /* Inlining */
                        if ((cfg->opt & MONO_OPT_INLINE) && cmethod &&
                                (!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
-                           mono_method_check_inlining (cfg, cmethod) &&
+                           !disable_inline && mono_method_check_inlining (cfg, cmethod) &&
                                 !g_list_find (dont_inline, cmethod)) {
                                int costs;
-                               gboolean allways = FALSE;
+                               gboolean always = FALSE;
 
                                if ((cmethod->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
                                        (cmethod->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
                                        /* Prevent inlining of methods that call wrappers */
                                        INLINE_FAILURE;
                                        cmethod = mono_marshal_get_native_wrapper (cmethod, check_for_pending_exc, FALSE);
-                                       allways = TRUE;
+                                       always = TRUE;
                                }
 
-                               if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, dont_inline, allways))) {
+                               if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, dont_inline, always))) {
                                        ip += 5;
                                        cfg->real_offset += 5;
                                        bblock = cfg->cbb;
@@ -7653,6 +7755,9 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                                NEW_PCONST (cfg, ins, NULL);
                                                ins->type = STACK_OBJ;
                                                ins->inst_p0 = mono_ldstr (cfg->domain, image, mono_metadata_token_index (n));
+                                               if (!ins->inst_p0)
+                                                       OUT_OF_MEMORY_FAILURE;
+
                                                *sp = ins;
                                                MONO_ADD_INS (bblock, ins);
                                        }
@@ -7853,12 +7958,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        }
 
                                        CHECK_CFG_EXCEPTION;
-                               } else
-
-
-
-                               if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
-                                   mono_method_check_inlining (cfg, cmethod) &&
+                               } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
+                                   !disable_inline && mono_method_check_inlining (cfg, cmethod) &&
                                    !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE) &&
                                    !g_list_find (dont_inline, cmethod)) {
                                        int costs;
@@ -7941,7 +8042,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                iargs [0] = sp [0];
                                
                                costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), 
-                                                          iargs, ip, cfg->real_offset, dont_inline, TRUE);                     
+                                                          iargs, ip, cfg->real_offset, dont_inline, TRUE);
+                               CHECK_CFG_EXCEPTION;
                                g_assert (costs > 0);
                                
                                ip += 5;
@@ -7999,7 +8101,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                iargs [0] = sp [0];
 
                                costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst), 
-                                                          iargs, ip, cfg->real_offset, dont_inline, TRUE);                     
+                                                          iargs, ip, cfg->real_offset, dont_inline, TRUE);
+                               CHECK_CFG_EXCEPTION;
                                g_assert (costs > 0);
                                
                                ip += 5;
@@ -8062,7 +8165,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
 
                                        costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass), 
                                                                                   iargs, ip, cfg->real_offset, dont_inline, TRUE);
-                       
+                                       CHECK_CFG_EXCEPTION;
                                        g_assert (costs > 0);
                                
                                        ip += 5;
@@ -8293,6 +8396,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
                                                costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper), 
                                                                       iargs, ip, cfg->real_offset, dont_inline, TRUE);
+                                               CHECK_CFG_EXCEPTION;
                                                g_assert (costs > 0);
                                                      
                                                cfg->real_offset += 5;
@@ -8339,6 +8443,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
                                        costs = inline_method (cfg, wrapper, mono_method_signature (wrapper), 
                                                                                   iargs, ip, cfg->real_offset, dont_inline, TRUE);
+                                       CHECK_CFG_EXCEPTION;
                                        bblock = cfg->cbb;
                                        g_assert (costs > 0);
                                                      
@@ -9312,7 +9417,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        NEW_BBLOCK (cfg, dont_throw);
 
                                        /*
-                                        * Currently, we allways rethrow the abort exception, despite the 
+                                        * Currently, we always rethrow the abort exception, despite the 
                                         * fact that this is not correct. See thread6.cs for an example. 
                                         * But propagating the abort exception is more important than 
                                         * getting the sematics right.