Moved ProviderCollectionTest.cs from System assembly to System.Configuration.
[mono.git] / mono / mini / tramp-arm.c
index 2e07508400b9855bfa393ef42dba9d0bc05e32f0..985593c82a18ff485da549405ba7ec5480765aa3 100644 (file)
@@ -110,7 +110,7 @@ mono_arch_patch_plt_entry (guint8 *code, guint8 *addr)
 {
        /* Patch the jump table entry used by the plt entry */
        guint32 offset = ((guint32*)code)[3];
-       guint8 *jump_entry = code + offset + 16;
+       guint8 *jump_entry = code + offset + 12;
 
        *(guint8**)jump_entry = addr;
 }
@@ -118,7 +118,7 @@ mono_arch_patch_plt_entry (guint8 *code, guint8 *addr)
 void
 mono_arch_nullify_class_init_trampoline (guint8 *code, gssize *regs)
 {
-       return;
+       mono_arch_patch_callsite (NULL, code, nullified_class_init_trampoline);
 }
 
 void
@@ -184,10 +184,14 @@ mono_arch_create_trampoline_code_full (MonoTrampolineType tramp_type, guint32 *c
                 * The trampoline contains a pc-relative offset to the got slot where the
                 * value is stored. The offset can be found at [lr + 4].
                 */
+               g_assert (tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
                ARM_LDR_IMM (buf, ARMREG_V2, ARMREG_LR, 4);
                ARM_LDR_REG_REG (buf, ARMREG_V2, ARMREG_V2, ARMREG_LR);
        } else {
-               ARM_LDR_IMM (buf, ARMREG_V2, ARMREG_LR, 0);
+               if (tramp_type != MONO_TRAMPOLINE_GENERIC_CLASS_INIT)
+                       ARM_LDR_IMM (buf, ARMREG_V2, ARMREG_LR, 0);
+               else
+                       ARM_MOV_REG_REG (buf, ARMREG_V2, MONO_ARCH_VTABLE_REG);
        }
        ARM_LDR_IMM (buf, ARMREG_V3, ARMREG_SP, LR_OFFSET);
 
@@ -318,9 +322,11 @@ mono_arch_create_trampoline_code_full (MonoTrampolineType tramp_type, guint32 *c
         */
        ARM_ADD_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, sizeof (MonoLMF) - sizeof (guint) * 14);
        ARM_POP_NWB (buf, 0x5fff);
+       if (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH)
+               ARM_MOV_REG_REG (buf, ARMREG_R0, ARMREG_IP);
        /* do we need to set sp? */
        ARM_ADD_REG_IMM8 (buf, ARMREG_SP, ARMREG_SP, (14 * 4));
-       if (tramp_type == MONO_TRAMPOLINE_CLASS_INIT)
+       if ((tramp_type == MONO_TRAMPOLINE_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_GENERIC_CLASS_INIT) || (tramp_type == MONO_TRAMPOLINE_RGCTX_LAZY_FETCH))
                ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_LR);
        else
                ARM_MOV_REG_REG (buf, ARMREG_PC, ARMREG_IP);
@@ -368,7 +374,7 @@ mono_arch_get_nullified_class_init_trampoline (guint32 *code_len)
 
        *code_len = buf - code;
 
-       return buf;
+       return code;
 }
 
 #define SPEC_TRAMP_SIZE 24
@@ -431,10 +437,155 @@ mono_arch_create_specific_trampoline (gpointer arg1, MonoTrampolineType tramp_ty
        return code;
 }
 
+#define arm_is_imm12(v) ((int)(v) > -4096 && (int)(v) < 4096)
+
 gpointer
-mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 encoded_offset)
+mono_arch_create_rgctx_lazy_fetch_trampoline (guint32 slot)
 {
-       /* FIXME: implement! */
-       g_assert_not_reached ();
-       return NULL;
+       guint8 *tramp;
+       guint8 *code, *buf;
+       int tramp_size;
+       guint32 code_len;
+       guint8 **rgctx_null_jumps;
+       int depth, index;
+       int i, njumps;
+       gboolean mrgctx;
+
+       mrgctx = MONO_RGCTX_SLOT_IS_MRGCTX (slot);
+       index = MONO_RGCTX_SLOT_INDEX (slot);
+       if (mrgctx)
+               index += sizeof (MonoMethodRuntimeGenericContext) / sizeof (gpointer);
+       for (depth = 0; ; ++depth) {
+               int size = mono_class_rgctx_get_array_size (depth, mrgctx);
+
+               if (index < size - 1)
+                       break;
+               index -= size - 1;
+       }
+
+       tramp_size = 64 + 16 * depth;
+
+       code = buf = mono_global_codeman_reserve (tramp_size);
+
+       rgctx_null_jumps = g_malloc (sizeof (guint8*) * (depth + 2));
+       njumps = 0;
+
+       /* The vtable/mrgctx is in R0 */
+       g_assert (MONO_ARCH_VTABLE_REG == ARMREG_R0);
+
+       if (mrgctx) {
+               /* get mrgctx ptr */
+               ARM_MOV_REG_REG (code, ARMREG_R1, ARMREG_R0);
+       } else {
+               /* load rgctx ptr from vtable */
+               g_assert (arm_is_imm12 (G_STRUCT_OFFSET (MonoVTable, runtime_generic_context)));
+               ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R0, G_STRUCT_OFFSET (MonoVTable, runtime_generic_context));
+               /* is the rgctx ptr null? */
+               ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
+               /* if yes, jump to actual trampoline */
+               rgctx_null_jumps [njumps ++] = code;
+               ARM_B_COND (code, ARMCOND_EQ, 0);
+       }
+
+       for (i = 0; i < depth; ++i) {
+               /* load ptr to next array */
+               if (mrgctx && i == 0) {
+                       g_assert (arm_is_imm12 (sizeof (MonoMethodRuntimeGenericContext)));
+                       ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, sizeof (MonoMethodRuntimeGenericContext));
+               } else {
+                       ARM_LDR_IMM (code, ARMREG_R1, ARMREG_R1, 0);
+               }
+               /* is the ptr null? */
+               ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
+               /* if yes, jump to actual trampoline */
+               rgctx_null_jumps [njumps ++] = code;
+               ARM_B_COND (code, ARMCOND_EQ, 0);
+       }
+
+       /* fetch slot */
+       code = mono_arm_emit_load_imm (code, ARMREG_R2, sizeof (gpointer) * (index + 1));
+       ARM_LDR_REG_REG (code, ARMREG_R1, ARMREG_R1, ARMREG_R2);
+       /* is the slot null? */
+       ARM_CMP_REG_IMM (code, ARMREG_R1, 0, 0);
+       /* if yes, jump to actual trampoline */
+       rgctx_null_jumps [njumps ++] = code;
+       ARM_B_COND (code, ARMCOND_EQ, 0);
+       /* otherwise return, result is in R1 */
+       ARM_MOV_REG_REG (code, ARMREG_R0, ARMREG_R1);
+       ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);
+
+       g_assert (njumps <= depth + 2);
+       for (i = 0; i < njumps; ++i)
+               arm_patch (rgctx_null_jumps [i], code);
+
+       g_free (rgctx_null_jumps);
+
+       /* Slowpath */
+
+       /* The vtable/mrgctx is still in R0 */
+
+       tramp = mono_arch_create_specific_trampoline (GUINT_TO_POINTER (slot), MONO_TRAMPOLINE_RGCTX_LAZY_FETCH, mono_get_root_domain (), &code_len);
+
+       /* Jump to the actual trampoline */
+       ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
+       ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);
+       *(guint32*)code = tramp;
+       code += 4;
+
+       mono_arch_flush_icache (buf, code - buf);
+
+       g_assert (code - buf <= tramp_size);
+
+       return buf;
+}
+
+#define arm_is_imm8(v) ((v) > -256 && (v) < 256)
+
+gpointer
+mono_arch_create_generic_class_init_trampoline (void)
+{
+       guint8 *tramp;
+       guint8 *code, *buf;
+       static int byte_offset = -1;
+       static guint8 bitmask;
+       guint8 *jump;
+       int tramp_size;
+       guint32 code_len, imm8;
+       gint rot_amount;
+
+       tramp_size = 64;
+
+       code = buf = mono_global_codeman_reserve (tramp_size);
+
+       if (byte_offset < 0)
+               mono_marshal_find_bitfield_offset (MonoVTable, initialized, &byte_offset, &bitmask);
+
+       g_assert (arm_is_imm8 (byte_offset));
+       ARM_LDRSB_IMM (code, ARMREG_IP, MONO_ARCH_VTABLE_REG, byte_offset);
+       imm8 = mono_arm_is_rotated_imm8 (bitmask, &rot_amount);
+       g_assert (imm8 >= 0);
+       ARM_AND_REG_IMM (code, ARMREG_IP, ARMREG_IP, imm8, rot_amount);
+       ARM_CMP_REG_IMM (code, ARMREG_IP, 0, 0);
+       jump = code;
+       ARM_B_COND (code, ARMCOND_EQ, 0);
+
+       /* Initialized case */
+       ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_LR);   
+
+       /* Uninitialized case */
+       arm_patch (jump, code);
+
+       tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_GENERIC_CLASS_INIT, mono_get_root_domain (), &code_len);
+
+       /* Jump to the actual trampoline */
+       ARM_LDR_IMM (code, ARMREG_R1, ARMREG_PC, 0); /* temp reg */
+       ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_R1);
+       *(guint32*)code = tramp;
+       code += 4;
+
+       mono_arch_flush_icache (buf, code - buf);
+
+       g_assert (code - buf <= tramp_size);
+
+       return buf;
 }