[sdb] Add support for DebuggerNonUserCodeAttribute.
[mono.git] / mono / metadata / sgen-alloc.c
index 14486c273591d1fbe028fd6d97fc67fef632c0c7..02a1d45bd34dd834bb7c03717fe094fd9ca53bab 100644 (file)
@@ -8,25 +8,20 @@
  * Copyright 2005-2011 Novell, Inc (http://www.novell.com)
  * Copyright 2011 Xamarin Inc (http://www.xamarin.com)
  * Copyright 2011 Xamarin, Inc.
+ * Copyright (C) 2012 Xamarin Inc
  *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- * 
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- * 
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License 2.0 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License 2.0 along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
 /*
@@ -69,6 +64,8 @@ enum {
 
 #undef OPDEF
 
+static gboolean use_managed_allocator = TRUE;
+
 #ifdef HEAVY_STATISTICS
 static long long stat_objects_alloced = 0;
 static long long stat_bytes_alloced = 0;
@@ -132,7 +129,7 @@ alloc_degraded (MonoVTable *vtable, size_t size, gboolean for_mature)
                sgen_ensure_free_space (size);
        } else {
                if (sgen_need_major_collection (size))
-                       sgen_perform_collection (size, GENERATION_OLD, "mature allocation failure");
+                       sgen_perform_collection (size, GENERATION_OLD, "mature allocation failure", !for_mature);
        }
 
 
@@ -148,6 +145,34 @@ alloc_degraded (MonoVTable *vtable, size_t size, gboolean for_mature)
        return p;
 }
 
+static void
+zero_tlab_if_necessary (void *p, size_t size)
+{
+       if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
+               memset (p, 0, size);
+       } else {
+               /*
+                * This function is called for all allocations in
+                * TLABs.  TLABs originate from fragments, which are
+                * initialized to be faux arrays.  The remainder of
+                * the fragments are zeroed out at initialization for
+                * CLEAR_AT_GC, so here we just need to make sure that
+                * the array header is zeroed.  Since we don't know
+                * whether we're called for the start of a fragment or
+                * for somewhere in between, we zero in any case, just
+                * to make sure.
+                */
+
+               if (size >= sizeof (MonoArray))
+                       memset (p, 0, sizeof (MonoArray));
+               else {
+                       static guint8 zeros [sizeof (MonoArray)];
+
+                       SGEN_ASSERT (0, !memcmp (p, zeros, size), "TLAB segment must be zeroed out.");
+               }
+       }
+}
+
 /*
  * Provide a variant that takes just the vtable for small fixed-size objects.
  * The aligned size is already computed and stored in vt->gc_descr.
@@ -180,7 +205,7 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
 
                if (collect_before_allocs) {
                        if (((current_alloc % collect_before_allocs) == 0) && nursery_section) {
-                               sgen_perform_collection (0, GENERATION_NURSERY, "collect-before-alloc-triggered");
+                               sgen_perform_collection (0, GENERATION_NURSERY, "collect-before-alloc-triggered", TRUE);
                                if (!degraded_mode && sgen_can_alloc_size (size) && size <= SGEN_MAX_SMALL_OBJ_SIZE) {
                                        // FIXME:
                                        g_assert_not_reached ();
@@ -221,7 +246,7 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                         * visible before the vtable store.
                         */
 
-                       DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
+                       SGEN_LOG (6, "Allocated object %p, vtable: %p (%s), size: %zd", p, vtable, vtable->klass->name, size);
                        binary_protocol_alloc (p , vtable, size);
                        if (G_UNLIKELY (MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ()))
                                MONO_GC_NURSERY_OBJ_ALLOC ((mword)p, size, vtable->klass->name_space, vtable->klass->name);
@@ -276,13 +301,11 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                                        g_assert (0);
                                }
 
-                               if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
-                                       memset (p, 0, size);
-                               }
+                               zero_tlab_if_necessary (p, size);
                        } else {
                                size_t alloc_size = 0;
                                if (TLAB_START)
-                                       DEBUG (3, fprintf (gc_debug_file, "Retire TLAB: %p-%p [%ld]\n", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size)));
+                                       SGEN_LOG (3, "Retire TLAB: %p-%p [%ld]", TLAB_START, TLAB_REAL_END, (long)(TLAB_REAL_END - TLAB_NEXT - size));
                                sgen_nursery_retire_region (p, available_in_tlab);
 
                                do {
@@ -307,9 +330,7 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                                TLAB_REAL_END = TLAB_START + alloc_size;
                                TLAB_TEMP_END = TLAB_START + MIN (SGEN_SCAN_START_SIZE, alloc_size);
 
-                               if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION) {
-                                       memset (TLAB_START, 0, alloc_size);
-                               }
+                               zero_tlab_if_necessary (TLAB_START, alloc_size);
 
                                /* Allocate from the TLAB */
                                p = (void*)TLAB_NEXT;
@@ -323,12 +344,12 @@ mono_gc_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                        sgen_set_nursery_scan_start ((char*)p);
                        /* we just bump tlab_temp_end as well */
                        TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SGEN_SCAN_START_SIZE);
-                       DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));
+                       SGEN_LOG (5, "Expanding local alloc: %p-%p", TLAB_NEXT, TLAB_TEMP_END);
                }
        }
 
        if (G_LIKELY (p)) {
-               DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
+               SGEN_LOG (6, "Allocated object %p, vtable: %p (%s), size: %zd", p, vtable, vtable->klass->name, size);
                binary_protocol_alloc (p, vtable, size);
                if (G_UNLIKELY (MONO_GC_MAJOR_OBJ_ALLOC_LARGE_ENABLED ()|| MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ())) {
                        if (size > SGEN_MAX_SMALL_OBJ_SIZE)
@@ -350,6 +371,7 @@ mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
        TLAB_ACCESS_INIT;
 
        size = ALIGN_UP (size);
+       SGEN_ASSERT (9, size >= sizeof (MonoObject), "Object too small");
 
        g_assert (vtable->gc_descr);
        if (size > SGEN_MAX_SMALL_OBJ_SIZE)
@@ -363,8 +385,7 @@ mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                sgen_set_nursery_scan_start ((char*)p);
 
                /*FIXME we should use weak memory ops here. Should help specially on x86. */
-               if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
-                       memset (p, 0, size);
+               zero_tlab_if_necessary (p, size);
        } else {
                int available_in_tlab;
                char *real_end;
@@ -385,7 +406,7 @@ mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                                sgen_set_nursery_scan_start (new_next);
                                /* we just bump tlab_temp_end as well */
                                TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SGEN_SCAN_START_SIZE);
-                               DEBUG (5, fprintf (gc_debug_file, "Expanding local alloc: %p-%p\n", TLAB_NEXT, TLAB_TEMP_END));         
+                               SGEN_LOG (5, "Expanding local alloc: %p-%p", TLAB_NEXT, TLAB_TEMP_END);
                        }
                } else if (available_in_tlab > SGEN_MAX_NURSERY_WASTE) {
                        /* Allocate directly from the nursery */
@@ -393,8 +414,7 @@ mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                        if (!p)
                                return NULL;
 
-                       if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
-                               memset (p, 0, size);                    
+                       zero_tlab_if_necessary (p, size);
                } else {
                        size_t alloc_size = 0;
 
@@ -410,8 +430,7 @@ mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
                        TLAB_TEMP_END = new_next + MIN (SGEN_SCAN_START_SIZE, alloc_size);
                        sgen_set_nursery_scan_start ((char*)p);
 
-                       if (nursery_clear_policy == CLEAR_AT_TLAB_CREATION)
-                               memset (new_next, 0, alloc_size);
+                       zero_tlab_if_necessary (new_next, alloc_size);
 
                        MONO_GC_NURSERY_TLAB_ALLOC ((mword)new_next, alloc_size);
                }
@@ -420,7 +439,7 @@ mono_gc_try_alloc_obj_nolock (MonoVTable *vtable, size_t size)
        HEAVY_STAT (++stat_objects_alloced);
        HEAVY_STAT (stat_bytes_alloced += size);
 
-       DEBUG (6, fprintf (gc_debug_file, "Allocated object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
+       SGEN_LOG (6, "Allocated object %p, vtable: %p (%s), size: %zd", p, vtable, vtable->klass->name, size);
        binary_protocol_alloc (p, vtable, size);
        if (G_UNLIKELY (MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ()))
                MONO_GC_NURSERY_OBJ_ALLOC ((mword)p, size, vtable->klass->name_space, vtable->klass->name);
@@ -435,8 +454,30 @@ void*
 mono_gc_alloc_obj (MonoVTable *vtable, size_t size)
 {
        void *res;
+
+       if (!SGEN_CAN_ALIGN_UP (size))
+               return NULL;
+
 #ifndef DISABLE_CRITICAL_REGION
        TLAB_ACCESS_INIT;
+
+       if (G_UNLIKELY (has_per_allocation_action)) {
+               static int alloc_count;
+               int current_alloc = InterlockedIncrement (&alloc_count);
+
+               if (verify_before_allocs) {
+                       if ((current_alloc % verify_before_allocs) == 0)
+                               sgen_check_whole_heap_stw ();
+               }
+               if (collect_before_allocs) {
+                       if (((current_alloc % collect_before_allocs) == 0) && nursery_section) {
+                               LOCK_GC;
+                               sgen_perform_collection (0, GENERATION_NURSERY, "collect-before-alloc-triggered", TRUE);
+                               UNLOCK_GC;
+                       }
+               }
+       }
+
        ENTER_CRITICAL_REGION;
        res = mono_gc_try_alloc_obj_nolock (vtable, size);
        if (res) {
@@ -457,6 +498,10 @@ void*
 mono_gc_alloc_vector (MonoVTable *vtable, size_t size, uintptr_t max_length)
 {
        MonoArray *arr;
+
+       if (!SGEN_CAN_ALIGN_UP (size))
+               return NULL;
+
 #ifndef DISABLE_CRITICAL_REGION
        TLAB_ACCESS_INIT;
        ENTER_CRITICAL_REGION;
@@ -491,6 +536,9 @@ mono_gc_alloc_array (MonoVTable *vtable, size_t size, uintptr_t max_length, uint
        MonoArray *arr;
        MonoArrayBounds *bounds;
 
+       if (!SGEN_CAN_ALIGN_UP (size))
+               return NULL;
+
 #ifndef DISABLE_CRITICAL_REGION
        TLAB_ACCESS_INIT;
        ENTER_CRITICAL_REGION;
@@ -529,6 +577,10 @@ void*
 mono_gc_alloc_string (MonoVTable *vtable, size_t size, gint32 len)
 {
        MonoString *str;
+
+       if (!SGEN_CAN_ALIGN_UP (size))
+               return NULL;
+
 #ifndef DISABLE_CRITICAL_REGION
        TLAB_ACCESS_INIT;
        ENTER_CRITICAL_REGION;
@@ -565,24 +617,27 @@ void*
 mono_gc_alloc_pinned_obj (MonoVTable *vtable, size_t size)
 {
        void **p;
+
+       if (!SGEN_CAN_ALIGN_UP (size))
+               return NULL;
        size = ALIGN_UP (size);
+
        LOCK_GC;
 
        if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
                /* large objects are always pinned anyway */
                p = sgen_los_alloc_large_inner (vtable, size);
        } else {
-               DEBUG (9, g_assert (vtable->klass->inited));
-               p = major_collector.alloc_small_pinned_obj (size, SGEN_VTABLE_HAS_REFERENCES (vtable));
+               SGEN_ASSERT (9, vtable->klass->inited, "class %s:%s is not initialized", vtable->klass->name_space, vtable->klass->name);
+               p = major_collector.alloc_small_pinned_obj (vtable, size, SGEN_VTABLE_HAS_REFERENCES (vtable));
        }
        if (G_LIKELY (p)) {
-               DEBUG (6, fprintf (gc_debug_file, "Allocated pinned object %p, vtable: %p (%s), size: %zd\n", p, vtable, vtable->klass->name, size));
+               SGEN_LOG (6, "Allocated pinned object %p, vtable: %p (%s), size: %zd", p, vtable, vtable->klass->name, size);
                if (size > SGEN_MAX_SMALL_OBJ_SIZE)
                        MONO_GC_MAJOR_OBJ_ALLOC_LARGE ((mword)p, size, vtable->klass->name_space, vtable->klass->name);
                else
                        MONO_GC_MAJOR_OBJ_ALLOC_PINNED ((mword)p, size, vtable->klass->name_space, vtable->klass->name);
                binary_protocol_alloc_pinned (p, vtable, size);
-               mono_atomic_store_seq (p, vtable);
        }
        UNLOCK_GC;
        return p;
@@ -592,10 +647,14 @@ void*
 mono_gc_alloc_mature (MonoVTable *vtable)
 {
        void **res;
-       size_t size = ALIGN_UP (vtable->klass->instance_size);
+       size_t size = vtable->klass->instance_size;
+
+       if (!SGEN_CAN_ALIGN_UP (size))
+               return NULL;
+       size = ALIGN_UP (size);
+
        LOCK_GC;
        res = alloc_degraded (vtable, size, TRUE);
-       mono_atomic_store_seq (res, vtable);
        UNLOCK_GC;
        if (G_UNLIKELY (vtable->klass->has_finalize))
                mono_object_register_finalizer ((MonoObject*)res);
@@ -690,6 +749,9 @@ create_allocator (int atype)
        MONO_THREAD_VAR_OFFSET (tlab_next_addr, tlab_next_addr_offset);
        MONO_THREAD_VAR_OFFSET (tlab_temp_end, tlab_temp_end_offset);
 
+       mono_tls_key_set_offset (TLS_KEY_SGEN_TLAB_NEXT_ADDR, tlab_next_addr_offset);
+       mono_tls_key_set_offset (TLS_KEY_SGEN_TLAB_TEMP_END, tlab_temp_end_offset);
+
        g_assert (tlab_next_addr_offset != -1);
        g_assert (tlab_temp_end_offset != -1);
 #endif
@@ -697,6 +759,7 @@ create_allocator (int atype)
        if (!registered) {
                mono_register_jit_icall (mono_gc_alloc_obj, "mono_gc_alloc_obj", mono_create_icall_signature ("object ptr int"), FALSE);
                mono_register_jit_icall (mono_gc_alloc_vector, "mono_gc_alloc_vector", mono_create_icall_signature ("object ptr int int"), FALSE);
+               mono_register_jit_icall (mono_gc_alloc_string, "mono_gc_alloc_string", mono_create_icall_signature ("object ptr int int32"), FALSE);
                registered = TRUE;
        }
 
@@ -709,17 +772,28 @@ create_allocator (int atype)
        } else if (atype == ATYPE_VECTOR) {
                num_params = 2;
                name = "AllocVector";
+       } else if (atype == ATYPE_STRING) {
+               num_params = 2;
+               name = "AllocString";
        } else {
                g_assert_not_reached ();
        }
 
        csig = mono_metadata_signature_alloc (mono_defaults.corlib, num_params);
-       csig->ret = &mono_defaults.object_class->byval_arg;
-       for (i = 0; i < num_params; ++i)
-               csig->params [i] = &mono_defaults.int_class->byval_arg;
+       if (atype == ATYPE_STRING) {
+               csig->ret = &mono_defaults.string_class->byval_arg;
+               csig->params [0] = &mono_defaults.int_class->byval_arg;
+               csig->params [1] = &mono_defaults.int32_class->byval_arg;
+       } else {
+               csig->ret = &mono_defaults.object_class->byval_arg;
+               for (i = 0; i < num_params; ++i)
+                       csig->params [i] = &mono_defaults.int_class->byval_arg;
+       }
 
        mb = mono_mb_new (mono_defaults.object_class, name, MONO_WRAPPER_ALLOC);
-       size_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
+
+#ifndef DISABLE_JIT
+       size_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
        if (atype == ATYPE_NORMAL || atype == ATYPE_SMALL) {
                /* size = vtable->klass->instance_size; */
                mono_mb_emit_ldarg (mb, 0);
@@ -730,18 +804,35 @@ create_allocator (int atype)
                mono_mb_emit_byte (mb, CEE_ADD);
                /* FIXME: assert instance_size stays a 4 byte integer */
                mono_mb_emit_byte (mb, CEE_LDIND_U4);
+               mono_mb_emit_byte (mb, CEE_CONV_I);
                mono_mb_emit_stloc (mb, size_var);
        } else if (atype == ATYPE_VECTOR) {
                MonoExceptionClause *clause;
-               int pos, pos_leave;
+               int pos, pos_leave, pos_error;
                MonoClass *oom_exc_class;
                MonoMethod *ctor;
 
-               /* n >  MONO_ARRAY_MAX_INDEX -> OverflowException */
+               /*
+                * n > MONO_ARRAY_MAX_INDEX => OutOfMemoryException
+                * n < 0                    => OverflowException
+                *
+                * We can do an unsigned comparison to catch both cases, then in the error
+                * case compare signed to distinguish between them.
+                */
                mono_mb_emit_ldarg (mb, 1);
                mono_mb_emit_icon (mb, MONO_ARRAY_MAX_INDEX);
+               mono_mb_emit_byte (mb, CEE_CONV_U);
                pos = mono_mb_emit_short_branch (mb, CEE_BLE_UN_S);
+
+               mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+               mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
+               mono_mb_emit_ldarg (mb, 1);
+               mono_mb_emit_icon (mb, 0);
+               pos_error = mono_mb_emit_short_branch (mb, CEE_BLT_S);
+               mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
+               mono_mb_patch_short_branch (mb, pos_error);
                mono_mb_emit_exception (mb, "OverflowException", NULL);
+
                mono_mb_patch_short_branch (mb, pos);
 
                clause = mono_image_alloc0 (mono_defaults.corlib, sizeof (MonoExceptionClause));
@@ -755,6 +846,7 @@ create_allocator (int atype)
                mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoClass, sizes.element_size));
                mono_mb_emit_byte (mb, CEE_ADD);
                mono_mb_emit_byte (mb, CEE_LDIND_U4);
+               mono_mb_emit_byte (mb, CEE_CONV_I);
 
                /* * n */
                mono_mb_emit_ldarg (mb, 1);
@@ -788,6 +880,39 @@ create_allocator (int atype)
                mono_mb_set_clauses (mb, 1, clause);
                mono_mb_patch_branch (mb, pos_leave);
                /* end catch */
+       } else if (atype == ATYPE_STRING) {
+               int pos;
+
+               /*
+                * a string allocator method takes the args: (vtable, len)
+                *
+                * bytes = sizeof (MonoString) + ((len + 1) * 2)
+                *
+                * condition:
+                *
+                * bytes <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
+                *
+                * therefore:
+                *
+                * sizeof (MonoString) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
+                * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - sizeof (MonoString)) / 2 - 1
+                */
+               mono_mb_emit_ldarg (mb, 1);
+               mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - sizeof (MonoString)) / 2 - 1);
+               pos = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
+
+               mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
+               mono_mb_emit_byte (mb, CEE_MONO_NOT_TAKEN);
+               mono_mb_emit_exception (mb, "OutOfMemoryException", NULL);
+               mono_mb_patch_short_branch (mb, pos);
+
+               mono_mb_emit_ldarg (mb, 1);
+               mono_mb_emit_icon (mb, 1);
+               mono_mb_emit_byte (mb, MONO_CEE_SHL);
+               //WE manually fold the above + 2 here
+               mono_mb_emit_icon (mb, sizeof (MonoString) + 2);
+               mono_mb_emit_byte (mb, CEE_ADD);
+               mono_mb_emit_stloc (mb, size_var);
        } else {
                g_assert_not_reached ();
        }
@@ -815,7 +940,7 @@ create_allocator (int atype)
 
        /* tlab_next_addr (local) = tlab_next_addr (TLS var) */
        tlab_next_addr_var = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
-       EMIT_TLS_ACCESS (mb, tlab_next_addr, tlab_next_addr_offset);
+       EMIT_TLS_ACCESS (mb, tlab_next_addr, TLS_KEY_SGEN_TLAB_NEXT_ADDR);
        mono_mb_emit_stloc (mb, tlab_next_addr_var);
 
        /* p = (void**)tlab_next; */
@@ -834,7 +959,7 @@ create_allocator (int atype)
 
        /* if (G_LIKELY (new_next < tlab_temp_end)) */
        mono_mb_emit_ldloc (mb, new_next_var);
-       EMIT_TLS_ACCESS (mb, tlab_temp_end, tlab_temp_end_offset);
+       EMIT_TLS_ACCESS (mb, tlab_temp_end, TLS_KEY_SGEN_TLAB_TEMP_END);
        slowpath_branch = mono_mb_emit_short_branch (mb, MONO_CEE_BLT_UN_S);
 
        /* Slowpath */
@@ -852,6 +977,9 @@ create_allocator (int atype)
        } else if (atype == ATYPE_VECTOR) {
                mono_mb_emit_ldarg (mb, 1);
                mono_mb_emit_icall (mb, mono_gc_alloc_vector);
+       } else if (atype == ATYPE_STRING) {
+               mono_mb_emit_ldarg (mb, 1);
+               mono_mb_emit_icall (mb, mono_gc_alloc_string);
        } else {
                g_assert_not_reached ();
        }
@@ -886,6 +1014,22 @@ create_allocator (int atype)
 #else
                mono_mb_emit_byte (mb, CEE_STIND_I4);
 #endif
+       } else  if (atype == ATYPE_STRING) {
+               /* need to set length and clear the last char */
+               /* s->length = len; */
+               mono_mb_emit_ldloc (mb, p_var);
+               mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoString, length));
+               mono_mb_emit_byte (mb, MONO_CEE_ADD);
+               mono_mb_emit_ldarg (mb, 1);
+               mono_mb_emit_byte (mb, MONO_CEE_STIND_I4);
+               /* s->chars [len] = 0; */
+               mono_mb_emit_ldloc (mb, p_var);
+               mono_mb_emit_ldloc (mb, size_var);
+               mono_mb_emit_icon (mb, 2);
+               mono_mb_emit_byte (mb, MONO_CEE_SUB);
+               mono_mb_emit_byte (mb, MONO_CEE_ADD);
+               mono_mb_emit_icon (mb, 0);
+               mono_mb_emit_byte (mb, MONO_CEE_STIND_I2);
        }
 
        /*
@@ -897,6 +1041,7 @@ create_allocator (int atype)
        /* return p */
        mono_mb_emit_ldloc (mb, p_var);
        mono_mb_emit_byte (mb, CEE_RET);
+#endif
 
        res = mono_mb_create_method (mb, csig, 8);
        mono_mb_free (mb);
@@ -917,10 +1062,9 @@ create_allocator (int atype)
  *     object allocate (MonoVTable *vtable)
  */
 MonoMethod*
-mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
+mono_gc_get_managed_allocator (MonoClass *klass, gboolean for_box)
 {
 #ifdef MANAGED_ALLOCATION
-       MonoClass *klass = vtable->klass;
 
 #ifdef HAVE_KW_THREAD
        int tlab_next_offset = -1;
@@ -931,21 +1075,21 @@ mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
        if (tlab_next_offset == -1 || tlab_temp_end_offset == -1)
                return NULL;
 #endif
-
+       if (collect_before_allocs)
+               return NULL;
        if (!mono_runtime_has_tls_get ())
                return NULL;
        if (klass->instance_size > tlab_size)
                return NULL;
-       if (klass->has_finalize || klass->marshalbyref || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
+
+       if (klass->has_finalize || mono_class_is_marshalbyref (klass) || (mono_profiler_get_events () & MONO_PROFILE_ALLOCATIONS))
                return NULL;
        if (klass->rank)
                return NULL;
        if (klass->byval_arg.type == MONO_TYPE_STRING)
-               return NULL;
-       if (collect_before_allocs)
-               return NULL;
-
-       if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE)
+               return mono_gc_get_managed_allocator_by_type (ATYPE_STRING);
+       /* Generic classes have dynamic field and can go above MAX_SMALL_OBJ_SIZE. */
+       if (ALIGN_TO (klass->instance_size, ALLOC_ALIGN) < MAX_SMALL_OBJ_SIZE && !mono_class_is_open_constructed_type (&klass->byval_arg))
                return mono_gc_get_managed_allocator_by_type (ATYPE_SMALL);
        else
                return mono_gc_get_managed_allocator_by_type (ATYPE_NORMAL);
@@ -955,11 +1099,9 @@ mono_gc_get_managed_allocator (MonoVTable *vtable, gboolean for_box)
 }
 
 MonoMethod*
-mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
+mono_gc_get_managed_array_allocator (MonoClass *klass)
 {
 #ifdef MANAGED_ALLOCATION
-       MonoClass *klass = vtable->klass;
-
 #ifdef HAVE_KW_THREAD
        int tlab_next_offset = -1;
        int tlab_temp_end_offset = -1;
@@ -970,7 +1112,7 @@ mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
                return NULL;
 #endif
 
-       if (rank != 1)
+       if (klass->rank != 1)
                return NULL;
        if (!mono_runtime_has_tls_get ())
                return NULL;
@@ -978,7 +1120,7 @@ mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
                return NULL;
        if (has_per_allocation_action)
                return NULL;
-       g_assert (!mono_class_has_finalizer (klass) && !klass->marshalbyref);
+       g_assert (!mono_class_has_finalizer (klass) && !mono_class_is_marshalbyref (klass));
 
        return mono_gc_get_managed_allocator_by_type (ATYPE_VECTOR);
 #else
@@ -986,20 +1128,39 @@ mono_gc_get_managed_array_allocator (MonoVTable *vtable, int rank)
 #endif
 }
 
+void
+sgen_set_use_managed_allocator (gboolean flag)
+{
+       use_managed_allocator = flag;
+}
+
 MonoMethod*
 mono_gc_get_managed_allocator_by_type (int atype)
 {
 #ifdef MANAGED_ALLOCATION
        MonoMethod *res;
 
+       if (!use_managed_allocator)
+               return NULL;
+
        if (!mono_runtime_has_tls_get ())
                return NULL;
 
-       mono_loader_lock ();
        res = alloc_method_cache [atype];
-       if (!res)
-               res = alloc_method_cache [atype] = create_allocator (atype);
-       mono_loader_unlock ();
+       if (res)
+               return res;
+
+       res = create_allocator (atype);
+       LOCK_GC;
+       if (alloc_method_cache [atype]) {
+               mono_free_method (res);
+               res = alloc_method_cache [atype];
+       } else {
+               mono_memory_barrier ();
+               alloc_method_cache [atype] = res;
+       }
+       UNLOCK_GC;
+
        return res;
 #else
        return NULL;