New tests.
[mono.git] / mono / mini / method-to-ir.c
index def7a32bf98b6da179f4bd2233e0e1bb4ac9987b..5ae9f41d06e578a9fe5d7e56044e2352bf8707d1 100644 (file)
@@ -1713,8 +1713,6 @@ mini_emit_memset (MonoCompile *cfg, int destreg, int offset, int size, int val,
        }
 }
 
-#endif /* DISABLE_JIT */
-
 void 
 mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int soffset, int size, int align)
 {
@@ -1777,8 +1775,6 @@ mini_emit_memcpy (MonoCompile *cfg, int destreg, int doffset, int srcreg, int so
        }
 }
 
-#ifndef DISABLE_JIT
-
 static int
 ret_type_to_call_opcode (MonoType *type, int calli, int virt, MonoGenericSharingContext *gsctx)
 {
@@ -2972,11 +2968,42 @@ handle_unbox (MonoCompile *cfg, MonoClass *klass, MonoInst **sp, int context_use
  * Returns NULL and set the cfg exception on error.
  */
 static MonoInst*
-handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box)
+handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box, int context_used)
 {
        MonoInst *iargs [2];
        void *alloc_ftn;
 
+       if (context_used) {
+               MonoInst *data;
+               int rgctx_info;
+               MonoInst *iargs [2];
+
+               /*
+                 FIXME: we cannot get managed_alloc here because we can't get
+                 the class's vtable (because it's not a closed class)
+
+                 MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
+                 MonoMethod *managed_alloc = mono_gc_get_managed_allocator (vtable, for_box);
+               */
+
+               if (cfg->opt & MONO_OPT_SHARED)
+                       rgctx_info = MONO_RGCTX_INFO_KLASS;
+               else
+                       rgctx_info = MONO_RGCTX_INFO_VTABLE;
+               data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
+
+               if (cfg->opt & MONO_OPT_SHARED) {
+                       EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
+                       iargs [1] = data;
+                       alloc_ftn = mono_object_new;
+               } else {
+                       iargs [0] = data;
+                       alloc_ftn = mono_object_new_specific;
+               }
+
+               return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
+       }
+
        if (cfg->opt & MONO_OPT_SHARED) {
                EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
                EMIT_NEW_CLASSCONST (cfg, iargs [1], klass);
@@ -3020,54 +3047,32 @@ handle_alloc (MonoCompile *cfg, MonoClass *klass, gboolean for_box)
 
        return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
 }
-
-static MonoInst*
-handle_alloc_from_inst (MonoCompile *cfg, MonoClass *klass, MonoInst *data_inst,
-                                               gboolean for_box)
-{
-       MonoInst *iargs [2];
-       MonoMethod *managed_alloc = NULL;
-       void *alloc_ftn;
-
-       /*
-         FIXME: we cannot get managed_alloc here because we can't get
-         the class's vtable (because it's not a closed class)
-
-       MonoVTable *vtable = mono_class_vtable (cfg->domain, klass);
-       MonoMethod *managed_alloc = mono_gc_get_managed_allocator (vtable, for_box);
-       */
-
-       if (cfg->opt & MONO_OPT_SHARED) {
-               EMIT_NEW_DOMAINCONST (cfg, iargs [0]);
-               iargs [1] = data_inst;
-               alloc_ftn = mono_object_new;
-       } else {
-               if (managed_alloc) {
-                       iargs [0] = data_inst;
-                       return mono_emit_method_call (cfg, managed_alloc, iargs, NULL);
-               }
-
-               iargs [0] = data_inst;
-               alloc_ftn = mono_object_new_specific;
-       }
-
-       return mono_emit_jit_icall (cfg, alloc_ftn, iargs);
-}
        
 /*
  * Returns NULL and set the cfg exception on error.
  */    
 static MonoInst*
-handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass)
+handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used)
 {
        MonoInst *alloc, *ins;
 
        if (mono_class_is_nullable (klass)) {
                MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
-               return mono_emit_method_call (cfg, method, &val, NULL);
+
+               if (context_used) {
+                       /* FIXME: What if the class is shared?  We might not
+                          have to get the method address from the RGCTX. */
+                       MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
+                                                                                                       MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
+                       MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
+
+                       return mono_emit_rgctx_calli (cfg, mono_method_signature (method), &val, addr, rgctx);
+               } else {
+                       return mono_emit_method_call (cfg, method, &val, NULL);
+               }
        }
 
-       alloc = handle_alloc (cfg, klass, TRUE);
+       alloc = handle_alloc (cfg, klass, TRUE, context_used);
        if (!alloc)
                return NULL;
 
@@ -3076,29 +3081,6 @@ handle_box (MonoCompile *cfg, MonoInst *val, MonoClass *klass)
        return alloc;
 }
 
-static MonoInst *
-handle_box_from_inst (MonoCompile *cfg, MonoInst *val, MonoClass *klass, int context_used, MonoInst *data_inst)
-{
-       MonoInst *alloc, *ins;
-
-       if (mono_class_is_nullable (klass)) {
-               MonoMethod* method = mono_class_get_method_from_name (klass, "Box", 1);
-               /* FIXME: What if the class is shared?  We might not
-                  have to get the method address from the RGCTX. */
-               MonoInst *addr = emit_get_rgctx_method (cfg, context_used, method,
-                       MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
-               MonoInst *rgctx = emit_get_rgctx (cfg, cfg->current_method, context_used);
-
-               return mono_emit_rgctx_calli (cfg, mono_method_signature (method), &val, addr, rgctx);
-       } else {
-               alloc = handle_alloc_from_inst (cfg, klass, data_inst, TRUE);
-
-               EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, alloc->dreg, sizeof (MonoObject), val->dreg);
-
-               return alloc;
-       }
-}
-
 // FIXME: This doesn't work yet (class libs tests fail?)
 #define is_complex_isinst(klass) (TRUE || (klass->flags & TYPE_ATTRIBUTE_INTERFACE) || klass->rank || mono_class_is_nullable (klass) || klass->marshalbyref || (klass->flags & TYPE_ATTRIBUTE_SEALED) || mono_class_has_variant_generic_params (klass) || klass->byval_arg.type == MONO_TYPE_VAR || klass->byval_arg.type == MONO_TYPE_MVAR)
 
@@ -3493,7 +3475,7 @@ handle_delegate_ctor (MonoCompile *cfg, MonoClass *klass, MonoInst *target, Mono
        MonoDomain *domain;
        guint8 **code_slot;
 
-       obj = handle_alloc (cfg, klass, FALSE);
+       obj = handle_alloc (cfg, klass, FALSE, 0);
        if (!obj)
                return NULL;
 
@@ -3731,7 +3713,7 @@ mini_field_access_needs_cctor_run (MonoCompile *cfg, MonoMethod *method, MonoVTa
 }
 
 static MonoInst*
-mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index)
+mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, MonoInst *index, gboolean bcheck)
 {
        MonoInst *ins;
        guint32 size;
@@ -3762,7 +3744,8 @@ mini_emit_ldelema_1_ins (MonoCompile *cfg, MonoClass *klass, MonoInst *arr, Mono
        }
 #endif
 
-       MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
+       if (bcheck)
+               MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index2_reg);
 
 #if defined(TARGET_X86) || defined(TARGET_AMD64)
        if (size == 1 || size == 2 || size == 4 || size == 8) {
@@ -3856,7 +3839,7 @@ mini_emit_ldelema_ins (MonoCompile *cfg, MonoMethod *cmethod, MonoInst **sp, uns
        rank = mono_method_signature (cmethod)->param_count - (is_set? 1: 0);
 
        if (rank == 1)
-               return mini_emit_ldelema_1_ins (cfg, cmethod->klass->element_class, sp [0], sp [1]);
+               return mini_emit_ldelema_1_ins (cfg, cmethod->klass->element_class, sp [0], sp [1], TRUE);
 
 #ifndef MONO_ARCH_EMULATE_MUL_DIV
        /* emit_ldelema_2 depends on OP_LMUL */
@@ -3919,6 +3902,25 @@ should_insert_brekpoint (MonoMethod *method) {
        }
 }
 
+/* optimize the simple GetGenericValueImpl/SetGenericValueImpl generic icalls */
+static MonoInst*
+emit_array_generic_access (MonoCompile *cfg, MonoMethodSignature *fsig, MonoInst **args, int is_set)
+{
+       MonoInst *addr, *store, *load;
+       MonoClass *eklass = mono_class_from_mono_type (fsig->params [2]);
+
+       /* the bounds check is already done by the callers */
+       addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1], FALSE);
+       if (is_set) {
+               EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, args [2]->dreg, 0);
+               EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, addr->dreg, 0, load->dreg);
+       } else {
+               EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
+               EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
+       }
+       return store;
+}
+
 static MonoInst*
 mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **args)
 {
@@ -4007,6 +4009,8 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
                } else
                        return NULL;
        } else if (cmethod->klass == mono_defaults.array_class) {
+               if (strcmp (cmethod->name + 1, "etGenericValueImpl") == 0)
+                       return emit_array_generic_access (cfg, fsig, args, *cmethod->name == 'S');
                if (cmethod->name [0] != 'g')
                        return NULL;
 
@@ -4097,15 +4101,6 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
 
                return (MonoInst*)mono_emit_method_call (cfg, fast_method, args, NULL);
 #endif
-       } else if (mini_class_is_system_array (cmethod->klass) &&
-                       strcmp (cmethod->name, "GetGenericValueImpl") == 0) {
-               MonoInst *addr, *store, *load;
-               MonoClass *eklass = mono_class_from_mono_type (fsig->params [1]);
-
-               addr = mini_emit_ldelema_1_ins (cfg, eklass, args [0], args [1]);
-               EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, load, &eklass->byval_arg, addr->dreg, 0);
-               EMIT_NEW_STORE_MEMBASE_TYPE (cfg, store, &eklass->byval_arg, args [2]->dreg, 0, load->dreg);
-               return store;
        } else if (cmethod->klass->image == mono_defaults.corlib &&
                           (strcmp (cmethod->klass->name_space, "System.Threading") == 0) &&
                           (strcmp (cmethod->klass->name, "Interlocked") == 0)) {
@@ -4252,7 +4247,7 @@ mini_emit_inst_for_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSign
                                size = 4;
                        else if (is_ref || fsig->params [1]->type == MONO_TYPE_I)
                                size = sizeof (gpointer);
-                       else if (sizeof (gpointer) == 8 && fsig->params [1]->type == MONO_TYPE_I4)
+                       else if (sizeof (gpointer) == 8 && fsig->params [1]->type == MONO_TYPE_I8)
                                size = 8;
                        if (size == 4) {
                                MONO_INST_NEW (cfg, ins, OP_ATOMIC_CAS_I4);
@@ -6058,6 +6053,19 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        /* MS.NET seems to silently convert this to a callvirt */
                                        virtual = 1;
 
+                               {
+                                       /*
+                                        * MS.NET accepts non virtual calls to virtual final methods of transparent proxy classes and
+                                        * converts to a callvirt.
+                                        *
+                                        * tests/bug-515884.il is an example of this behavior
+                                        */
+                                       const int test_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL | METHOD_ATTRIBUTE_STATIC;
+                                       const int expected_flags = METHOD_ATTRIBUTE_VIRTUAL | METHOD_ATTRIBUTE_FINAL;
+                                       if (!virtual && cmethod->klass->marshalbyref && (cmethod->flags & test_flags) == expected_flags && cfg->method->wrapper_type == MONO_WRAPPER_NONE)
+                                               virtual = 1;
+                               }
+
                                if (!cmethod->klass->inited)
                                        if (!mono_class_init (cmethod->klass))
                                                goto load_error;
@@ -6121,7 +6129,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                         */
                                        EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &constrained_call->byval_arg, sp [0]->dreg, 0);
                                        ins->klass = constrained_call;
-                                       sp [0] = handle_box (cfg, ins, constrained_call);
+                                       sp [0] = handle_box (cfg, ins, constrained_call, mono_class_check_context_used (constrained_call));
                                        CHECK_CFG_EXCEPTION;
                                } else if (!constrained_call->valuetype) {
                                        int dreg = alloc_preg (cfg);
@@ -7457,16 +7465,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                         * will be transformed into a normal call there.
                                         */
                                } else if (context_used) {
-                                       MonoInst *data;
-                                       int rgctx_info;
-
-                                       if (cfg->opt & MONO_OPT_SHARED)
-                                               rgctx_info = MONO_RGCTX_INFO_KLASS;
-                                       else
-                                               rgctx_info = MONO_RGCTX_INFO_VTABLE;
-                                       data = emit_get_rgctx_klass (cfg, context_used, cmethod->klass, rgctx_info);
-
-                                       alloc = handle_alloc_from_inst (cfg, cmethod->klass, data, FALSE);
+                                       alloc = handle_alloc (cfg, cmethod->klass, FALSE, context_used);
                                        *sp = alloc;
                                } else {
                                        MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
@@ -7485,7 +7484,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                                class_inits = g_slist_prepend (class_inits, vtable);
                                        }
 
-                                       alloc = handle_alloc (cfg, cmethod->klass, FALSE);
+                                       alloc = handle_alloc (cfg, cmethod->klass, FALSE, 0);
                                        *sp = alloc;
                                }
                                CHECK_CFG_EXCEPTION; /*for handle_alloc*/
@@ -7791,19 +7790,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                break;
                        }
 
-                       if (context_used) {
-                               MonoInst *data;
-                               int rgctx_info;
-
-                               if (cfg->opt & MONO_OPT_SHARED)
-                                       rgctx_info = MONO_RGCTX_INFO_KLASS;
-                               else
-                                       rgctx_info = MONO_RGCTX_INFO_VTABLE;
-                               data = emit_get_rgctx_klass (cfg, context_used, klass, rgctx_info);
-                               *sp++ = handle_box_from_inst (cfg, val, klass, context_used, data);
-                       } else {
-                               *sp++ = handle_box (cfg, val, klass);
-                       }
+                       *sp++ = handle_box (cfg, val, klass, context_used);
 
                        CHECK_CFG_EXCEPTION;
                        ip += 5;
@@ -8260,21 +8247,21 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                                EMIT_NEW_ICONST (cfg, *sp, *((guint32 *)addr));
                                                sp++;
                                                break;
-#ifndef HAVE_MOVING_COLLECTOR
                                        case MONO_TYPE_I:
                                        case MONO_TYPE_U:
+                                       case MONO_TYPE_PTR:
+                                       case MONO_TYPE_FNPTR:
+#ifndef HAVE_MOVING_COLLECTOR
                                        case MONO_TYPE_STRING:
                                        case MONO_TYPE_OBJECT:
                                        case MONO_TYPE_CLASS:
                                        case MONO_TYPE_SZARRAY:
-                                       case MONO_TYPE_PTR:
-                                       case MONO_TYPE_FNPTR:
                                        case MONO_TYPE_ARRAY:
+#endif
                                                EMIT_NEW_PCONST (cfg, *sp, *((gpointer *)addr));
                                                type_to_eval_stack_type ((cfg), field->type, *sp);
                                                sp++;
                                                break;
-#endif
                                        case MONO_TYPE_I8:
                                        case MONO_TYPE_U8:
                                                EMIT_NEW_I8CONST (cfg, *sp, *((gint64 *)addr));
@@ -8355,7 +8342,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                   allocator because we can't get the
                                   open generic class's vtable.  We
                                   have the same problem in
-                                  handle_alloc_from_inst().  This
+                                  handle_alloc().  This
                                   needs to be solved so that we can
                                   have managed allocs of shared
                                   generic classes. */
@@ -8476,7 +8463,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        }
 
                        readonly = FALSE;
-                       ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1]);
+                       ins = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
                        *sp++ = ins;
                        ip += 5;
                        break;
@@ -8520,7 +8507,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
                                EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset);
                        } else {
-                               addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1]);
+                               addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
                                EMIT_NEW_LOAD_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0);
                        }
                        *sp++ = ins;
@@ -8584,7 +8571,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                                        MONO_EMIT_BOUNDS_CHECK (cfg, array_reg, MonoArray, max_length, index_reg);
                                        EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, array_reg, offset, sp [2]->dreg);
                                } else {
-                                       addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1]);
+                                       addr = mini_emit_ldelema_1_ins (cfg, klass, sp [0], sp [1], TRUE);
                                        EMIT_NEW_STORE_MEMBASE_TYPE (cfg, ins, &klass->byval_arg, addr->dreg, 0, sp [2]->dreg);
                                }
                        }
@@ -10326,14 +10313,15 @@ mono_op_to_op_imm_noemul (int opcode)
        case OP_LSHR:
        case OP_LSHL:
        case OP_LSHR_UN:
+               return -1;
 #endif
 #if defined(MONO_ARCH_EMULATE_MUL_DIV) || defined(MONO_ARCH_EMULATE_DIV)
        case OP_IDIV:
        case OP_IDIV_UN:
        case OP_IREM:
        case OP_IREM_UN:
-#endif
                return -1;
+#endif
        default:
                return mono_op_to_op_imm (opcode);
        }