Adding public API for Pinned Object Heap allocations (#33526)
authorVladimir Sadov <vsadov@microsoft.com>
Wed, 18 Mar 2020 05:00:41 +0000 (22:00 -0700)
committerGitHub <noreply@github.com>
Wed, 18 Mar 2020 05:00:41 +0000 (05:00 +0000)
* Adding API for POH allocations and propagating flags all the way to Alloc.

* make `AllocateUninitializedArray` and `AllocateArray`  public

* Added NYI implementations to Mono

* moved tests to libraries

* Actually use POH and more tests.

* Disable tests for the new API on mono

* mop up remaining TODOs

* Fix build breaking whitespace.

* Mono tabs and mark heavier tests as [Outerloop]

* Mono  space before openning parens and braces

* Refactored AllocateArray

* PR feedback

* XML Doc comments

18 files changed:
src/coreclr/src/System.Private.CoreLib/ILLinkTrim.xml
src/coreclr/src/System.Private.CoreLib/src/System/GC.cs
src/coreclr/src/gc/gc.cpp
src/coreclr/src/gc/gcinterface.h
src/coreclr/src/vm/comutilnative.cpp
src/coreclr/src/vm/comutilnative.h
src/coreclr/src/vm/gchelpers.cpp
src/coreclr/src/vm/gchelpers.h
src/coreclr/src/vm/i386/jitinterfacex86.cpp
src/coreclr/src/vm/jithelpers.cpp
src/coreclr/src/vm/object.h
src/coreclr/src/vm/object.inl
src/coreclr/tests/issues.targets
src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.cs [deleted file]
src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.csproj [deleted file]
src/libraries/System.Runtime/ref/System.Runtime.cs
src/libraries/System.Runtime/tests/System/GCTests.cs
src/mono/netcore/System.Private.CoreLib/src/System/GC.Mono.cs

index 1dc8146..ff08dc6 100644 (file)
@@ -8,8 +8,6 @@
       <!-- Methods are used to register and unregister frozen segments. They are private and experimental. -->
       <method name="_RegisterFrozenSegment" />
       <method name="_UnregisterFrozenSegment" />
-      <!-- This is an internal API for now and is not yet used outside tests. -->
-      <method name="AllocateUninitializedArray" />
     </type>
     <!-- Properties and methods used by a debugger. -->
     <type fullname="System.Threading.Tasks.Task">
index c71747c..fc8a965 100644 (file)
@@ -84,8 +84,16 @@ namespace System
         [DllImport(RuntimeHelpers.QCall, CharSet = CharSet.Unicode)]
         internal static extern int _EndNoGCRegion();
 
+        // keep in sync with GC_ALLOC_FLAGS in gcinterface.h
+        internal enum GC_ALLOC_FLAGS
+        {
+            GC_ALLOC_NO_FLAGS = 0,
+            GC_ALLOC_ZEROING_OPTIONAL = 16,
+            GC_ALLOC_PINNED_OBJECT_HEAP = 64,
+        };
+
         [MethodImpl(MethodImplOptions.InternalCall)]
-        internal static extern Array AllocateNewArray(IntPtr typeHandle, int length, bool zeroingOptional);
+        internal static extern Array AllocateNewArray(IntPtr typeHandle, int length, GC_ALLOC_FLAGS flags);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern int GetGenerationWR(IntPtr handle);
@@ -651,31 +659,74 @@ namespace System
         }
 
         /// <summary>
-        /// Skips zero-initialization of the array if possible.
-        /// If T contains object references, the array is always zero-initialized.
+        /// Allocate an array while skipping zero-initialization if possible.
         /// </summary>
+        /// <typeparam name="T">Specifies the type of the array element.</typeparam>
+        /// <param name="length">Specifies the length of the array.</param>
+        /// <param name="pinned">Specifies whether the allocated array must be pinned.</param>
+        /// <remarks>
+        /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references.
+        /// </remarks>
         [MethodImpl(MethodImplOptions.AggressiveInlining)] // forced to ensure no perf drop for small memory buffers (hot path)
-        internal static T[] AllocateUninitializedArray<T>(int length)
+        public static T[] AllocateUninitializedArray<T>(int length, bool pinned = false)
         {
-            if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+            if (!pinned)
             {
-                return new T[length];
-            }
+                if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+                {
+                    return new T[length];
+                }
 
-            // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs
+                // for debug builds we always want to call AllocateNewArray to detect AllocateNewArray bugs
 #if !DEBUG
-            // small arrays are allocated using `new[]` as that is generally faster.
-            if (length < 2048 / Unsafe.SizeOf<T>())
+                // small arrays are allocated using `new[]` as that is generally faster.
+                if (length < 2048 / Unsafe.SizeOf<T>())
+                {
+                    return new T[length];
+                }
+#endif
+            }
+            else if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
             {
-                return new T[length];
+                ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
             }
-#endif
+
             // kept outside of the small arrays hot path to have inlining without big size growth
-            return AllocateNewUninitializedArray(length);
+            return AllocateNewUninitializedArray(length, pinned);
 
             // remove the local function when https://github.com/dotnet/coreclr/issues/5329 is implemented
-            static T[] AllocateNewUninitializedArray(int length)
-                => Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, zeroingOptional: true));
+            static T[] AllocateNewUninitializedArray(int length, bool pinned)
+            {
+                GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_ZEROING_OPTIONAL;
+                if (pinned)
+                    flags |= GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP;
+
+                return Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags));
+            }
+        }
+
+        /// <summary>
+        /// Allocate an array.
+        /// </summary>
+        /// <typeparam name="T">Specifies the type of the array element.</typeparam>
+        /// <param name="length">Specifies the length of the array.</param>
+        /// <param name="pinned">Specifies whether the allocated array must be pinned.</param>
+        /// <remarks>
+        /// If pinned is set to true, <typeparamref name="T"/> must not be a reference type or a type that contains object references.
+        /// </remarks>
+        public static T[] AllocateArray<T>(int length, bool pinned = false)
+        {
+            GC_ALLOC_FLAGS flags = GC_ALLOC_FLAGS.GC_ALLOC_NO_FLAGS;
+
+            if (pinned)
+            {
+                if (RuntimeHelpers.IsReferenceOrContainsReferences<T>())
+                    ThrowHelper.ThrowInvalidTypeWithPointersNotSupported(typeof(T));
+
+                flags = GC_ALLOC_FLAGS.GC_ALLOC_PINNED_OBJECT_HEAP;
+            }
+
+            return Unsafe.As<T[]>(AllocateNewArray(typeof(T[]).TypeHandle.Value, length, flags));
         }
     }
 }
index e547def..e54a993 100644 (file)
@@ -12198,7 +12198,7 @@ void gc_heap::adjust_limit_clr (uint8_t* start, size_t limit_size, size_t size,
         uint8_t* obj_start = acontext->alloc_ptr;
         assert(start >= obj_start);
         uint8_t* obj_end = obj_start + size - plug_skew;
-        assert(obj_end > clear_start);
+        assert(obj_end >= clear_start);
 
         // if clearing at the object start, clear the syncblock.
         if(obj_start == start)
@@ -37204,7 +37204,9 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_
 #endif //_PREFAST_
 #endif //MULTIPLE_HEAPS
 
-    if (size >= loh_size_threshold || (flags & GC_ALLOC_LARGE_OBJECT_HEAP))
+    assert(size < loh_size_threshold || (flags & GC_ALLOC_LARGE_OBJECT_HEAP));
+
+    if (flags & GC_ALLOC_USER_OLD_HEAP)
     {
         // The LOH always guarantees at least 8-byte alignment, regardless of platform. Moreover it doesn't
         // support mis-aligned object headers so we can't support biased headers. Luckily for us
@@ -37213,7 +37215,8 @@ GCHeap::Alloc(gc_alloc_context* context, size_t size, uint32_t flags REQD_ALIGN_
         ASSERT((flags & GC_ALLOC_ALIGN8_BIAS) == 0);
         ASSERT(65536 < loh_size_threshold);
 
-        newAlloc = (Object*) hp->allocate_uoh_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), flags, loh_generation, acontext->alloc_bytes_uoh);
+        int gen_num = (flags & GC_ALLOC_PINNED_OBJECT_HEAP) ? poh_generation : loh_generation;
+        newAlloc = (Object*) hp->allocate_uoh_object (size + ComputeMaxStructAlignPadLarge(requiredAlignment), flags, gen_num, acontext->alloc_bytes_uoh);
         ASSERT(((size_t)newAlloc & 7) == 0);
 
 #ifdef FEATURE_STRUCTALIGN
index cd78561..de6456d 100644 (file)
@@ -890,7 +890,7 @@ void updateGCShadow(Object** ptr, Object* val);
 #define GC_CALL_INTERIOR            0x1
 #define GC_CALL_PINNED              0x2
 
-//flags for IGCHeapAlloc(...)
+// keep in sync with GC_ALLOC_FLAGS in GC.cs
 enum GC_ALLOC_FLAGS
 {
     GC_ALLOC_NO_FLAGS           = 0,
@@ -901,6 +901,7 @@ enum GC_ALLOC_FLAGS
     GC_ALLOC_ZEROING_OPTIONAL   = 16,
     GC_ALLOC_LARGE_OBJECT_HEAP  = 32,
     GC_ALLOC_PINNED_OBJECT_HEAP = 64,
+    GC_ALLOC_USER_OLD_HEAP      = GC_ALLOC_LARGE_OBJECT_HEAP | GC_ALLOC_PINNED_OBJECT_HEAP,
 };
 
 inline GC_ALLOC_FLAGS operator|(GC_ALLOC_FLAGS a, GC_ALLOC_FLAGS b)
index 7d0dea1..1f0e257 100644 (file)
@@ -1047,7 +1047,7 @@ FCIMPLEND
 **           zeroingOptional -> whether caller prefers to skip clearing the content of the array, if possible.
 **Exceptions: IDS_EE_ARRAY_DIMENSIONS_EXCEEDED when size is too large. OOM if can't allocate.
 ==============================================================================*/
-FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 length, CLR_BOOL zeroingOptional)
+FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 length, INT32 flags)
 {
     CONTRACTL {
         FCALL_CHECK;
@@ -1058,7 +1058,10 @@ FCIMPL3(Object*, GCInterface::AllocateNewArray, void* arrayTypeHandle, INT32 len
 
     HELPER_METHOD_FRAME_BEGIN_RET_0();
 
-    pRet = AllocateSzArray(arrayType, length, zeroingOptional ? GC_ALLOC_ZEROING_OPTIONAL : GC_ALLOC_NO_FLAGS);
+    //Only the following flags are used by GC.cs, so we'll just assert it here.
+    _ASSERTE((flags & ~(GC_ALLOC_ZEROING_OPTIONAL | GC_ALLOC_PINNED_OBJECT_HEAP)) == 0);
+
+    pRet = AllocateSzArray(arrayType, length, (GC_ALLOC_FLAGS)flags);
 
     HELPER_METHOD_FRAME_END();
 
index 56030c4..4493ec2 100644 (file)
@@ -128,7 +128,7 @@ public:
     static FCDECL0(INT64,    GetAllocatedBytesForCurrentThread);
     static FCDECL1(INT64,    GetTotalAllocatedBytes, CLR_BOOL precise);
 
-    static FCDECL3(Object*, AllocateNewArray, void* elementTypeHandle, INT32 length, CLR_BOOL zeroingOptional);
+    static FCDECL3(Object*, AllocateNewArray, void* elementTypeHandle, INT32 length, INT32 flags);
 
 #ifdef FEATURE_BASICFREEZE
     static
index e9352bd..5f375da 100644 (file)
 //
 //========================================================================
 
-#define ProfileTrackArrayAlloc(orObject) \
-            OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);\
-            GCPROTECT_BEGIN(objref);\
-            ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr());\
-            GCPROTECT_END();\
-            orObject = (ArrayBase *) OBJECTREFToObject(objref);
-
-
 inline gc_alloc_context* GetThreadAllocContext()
 {
     WRAPPER_NO_CONTRACT;
@@ -281,7 +273,7 @@ bool ToLogOrNotToLog(size_t size, const char *typeName)
 // this function is called on managed allocation path with unprotected Object*
 // as a result LogAlloc cannot call anything that would toggle the GC mode else
 // you'll introduce several GC holes!
-inline void LogAlloc(size_t size, MethodTable *pMT, Object* object)
+inline void LogAlloc(Object* object)
 {
     CONTRACTL
     {
@@ -292,6 +284,9 @@ inline void LogAlloc(size_t size, MethodTable *pMT, Object* object)
     CONTRACTL_END;
 
 #ifdef LOGGING
+    MethodTable* pMT = object->GetMethodTable();
+    size_t size      = object->GetSize();
+
     if (LoggingOn(LF_GCALLOC, LL_INFO10))
     {
         LogSpewAlways("Allocated %5d bytes for %s_TYPE" FMT_ADDR FMT_CLASS "\n",
@@ -311,9 +306,44 @@ inline void LogAlloc(size_t size, MethodTable *pMT, Object* object)
 #endif
 }
 #else
-#define LogAlloc(size, pMT, object)
+#define LogAlloc( object)
 #endif
 
+// signals completion of the object to GC and sends events if necessary
+template <class TObj>
+void PublishObjectAndNotify(TObj* &orObject, GC_ALLOC_FLAGS flags)
+{
+    _ASSERTE(orObject->HasEmptySyncBlockInfo());
+
+    if (flags & GC_ALLOC_USER_OLD_HEAP)
+    {
+        GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject);
+    }
+
+#ifdef  _LOGALLOC
+    LogAlloc(orObject);
+#endif // _LOGALLOC
+
+    // Notify the profiler of the allocation
+    // do this after initializing bounds so callback has size information
+    if (TrackAllocations() ||
+        (TrackLargeAllocations() && flags & GC_ALLOC_LARGE_OBJECT_HEAP))
+    {
+        OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
+        GCPROTECT_BEGIN(objref);
+        ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr());
+        GCPROTECT_END();
+        orObject = (TObj*) OBJECTREFToObject(objref);
+    }
+
+#ifdef FEATURE_EVENT_TRACE
+    // Send ETW event for allocation
+    if(ETW::TypeSystemLog::IsHeapAllocEventEnabled())
+    {
+        ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject);
+    }
+#endif // FEATURE_EVENT_TRACE
+}
 
 inline SIZE_T MaxArrayLength(SIZE_T componentSize)
 {
@@ -324,7 +354,7 @@ inline SIZE_T MaxArrayLength(SIZE_T componentSize)
     return (componentSize == 1) ? 0X7FFFFFC7 : 0X7FEFFFFF;
 }
 
-OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 cElements, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap)
+OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 cElements, GC_ALLOC_FLAGS flags)
 {
     CONTRACTL{
         THROWS;
@@ -334,10 +364,10 @@ OBJECTREF AllocateSzArray(TypeHandle arrayType, INT32 cElements, GC_ALLOC_FLAGS
 
     MethodTable* pArrayMT = arrayType.AsMethodTable();
 
-    return AllocateSzArray(pArrayMT, cElements, flags, bAllocateInLargeHeap);
+    return AllocateSzArray(pArrayMT, cElements, flags);
 }
 
-OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap)
+OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS flags)
 {
     CONTRACTL{
         THROWS;
@@ -345,6 +375,8 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
         MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
     } CONTRACTL_END;
 
+    // IBC Log MethodTable access
+    g_IBCLogger.LogMethodTableAccess(pArrayMT);
     SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT));
 
     _ASSERTE(pArrayMT->CheckInstanceActivated());
@@ -356,9 +388,6 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
     if (elemType == ELEMENT_TYPE_VOID)
         COMPlusThrow(kArgumentException);
 
-    // IBC Log MethodTable access
-    g_IBCLogger.LogMethodTableAccess(pArrayMT);
-
     if (cElements < 0)
         COMPlusThrow(kOverflowException);
 
@@ -385,22 +414,21 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
         ((DWORD)cElements >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold()))
     {
         STRESS_LOG2(LF_GC, LL_INFO10, "Allocating double MD array of size %d and length %d to large object heap\n", totalSize, cElements);
-        bAllocateInLargeHeap = TRUE;
+        flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
     }
 #endif
 
     if (totalSize >= g_pConfig->GetGCLOHThreshold())
-    {
-        bAllocateInLargeHeap = TRUE;
-    }
+        flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
 
-    flags |= (pArrayMT->ContainsPointers() ? GC_ALLOC_CONTAINS_REF : GC_ALLOC_NO_FLAGS);
+    if (pArrayMT->ContainsPointers())
+        flags |= GC_ALLOC_CONTAINS_REF;
 
     ArrayBase* orArray = NULL;
-    if (bAllocateInLargeHeap)
+    if (flags & GC_ALLOC_USER_OLD_HEAP)
     {
-        orArray = (ArrayBase*)Alloc(totalSize, flags | GC_ALLOC_LARGE_OBJECT_HEAP);
-        orArray->SetArrayMethodTableForLargeObject(pArrayMT);
+        orArray = (ArrayBase*)Alloc(totalSize, flags);
+        orArray->SetMethodTableForUOHObject(pArrayMT);
     }
     else
     {
@@ -457,40 +485,14 @@ OBJECTREF AllocateSzArray(MethodTable* pArrayMT, INT32 cElements, GC_ALLOC_FLAGS
 #endif
             orArray = (ArrayBase*)Alloc(totalSize, flags);
         }
-        orArray->SetArrayMethodTable(pArrayMT);
+        orArray->SetMethodTable(pArrayMT);
     }
 
     // Initialize Object
     orArray->m_NumComponents = cElements;
 
-    bool bProfilerNotifyLargeAllocation = false;
-
-    if (bAllocateInLargeHeap)
-    {
-        GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orArray);
-        bProfilerNotifyLargeAllocation = TrackLargeAllocations();
-    }
-
-#ifdef  _LOGALLOC
-    LogAlloc(totalSize, pArrayMT, orArray);
-#endif // _LOGALLOC
-
-    // Notify the profiler of the allocation
-    // do this after initializing bounds so callback has size information
-    if (TrackAllocations() || bProfilerNotifyLargeAllocation)
-    {
-        ProfileTrackArrayAlloc(orArray);
-    }
-
-#ifdef FEATURE_EVENT_TRACE
-    // Send ETW event for allocation
-    if(ETW::TypeSystemLog::IsHeapAllocEventEnabled())
-    {
-        ETW::TypeSystemLog::SendObjectAllocatedEvent(orArray);
-    }
-#endif // FEATURE_EVENT_TRACE
-
-    return ObjectToOBJECTREF((Object *) orArray);
+    PublishObjectAndNotify(orArray, flags);
+    return ObjectToOBJECTREF((Object*)orArray);
 }
 
 void ThrowOutOfMemoryDimensionsExceeded()
@@ -511,7 +513,7 @@ void ThrowOutOfMemoryDimensionsExceeded()
 //
 // This is wrapper overload to handle TypeHandle arrayType
 //
-OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap)
+OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags)
 {
     CONTRACTL
     {
@@ -520,7 +522,7 @@ OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, G
 
     MethodTable* pArrayMT = arrayType.AsMethodTable();
 
-    return AllocateArrayEx(pArrayMT, pArgs, dwNumArgs, flags, bAllocateInLargeHeap);
+    return AllocateArrayEx(pArrayMT, pArgs, dwNumArgs, flags);
 }
 
 //
@@ -530,7 +532,7 @@ OBJECTREF AllocateArrayEx(TypeHandle arrayType, INT32 *pArgs, DWORD dwNumArgs, G
 // allocate sub-arrays and fill them in.
 //
 // For arrays with lower bounds, pBounds is <lower bound 1>, <count 1>, <lower bound 2>, ...
-OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap)
+OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags)
 {
     CONTRACTL {
         THROWS;
@@ -540,8 +542,6 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
         PRECONDITION(dwNumArgs > 0);
     } CONTRACTL_END;
 
-    ArrayBase * orArray = NULL;
-
 #ifdef _DEBUG
     if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
     {
@@ -550,6 +550,15 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
     }
 #endif
 
+    // IBC Log MethodTable access
+    g_IBCLogger.LogMethodTableAccess(pArrayMT);
+    SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT));
+
+    // keep original flags in case the call is recursive (jugged array case)
+    // the aditional flags that we infer here, such as GC_ALLOC_CONTAINS_REF
+    // may not be applicable to inner arrays
+    GC_ALLOC_FLAGS flagsOriginal = flags;
+
    _ASSERTE(pArrayMT->CheckInstanceActivated());
     PREFIX_ASSUME(pArrayMT != NULL);
     CorElementType kind = pArrayMT->GetInternalCorElementType();
@@ -562,11 +571,6 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
 
     // Calculate the total number of elements in the array
     UINT32 cElements;
-
-    // IBC Log MethodTable access
-    g_IBCLogger.LogMethodTableAccess(pArrayMT);
-    SetTypeHandleOnThreadForAlloc(TypeHandle(pArrayMT));
-
     SIZE_T componentSize = pArrayMT->GetComponentSize();
     bool maxArrayDimensionLengthOverflow = false;
     bool providedLowerBounds = false;
@@ -580,7 +584,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
         if (rank == 1 && (dwNumArgs == 1 || pArgs[0] == 0))
         {
             TypeHandle szArrayType = ClassLoader::LoadArrayTypeThrowing(pArrayMT->GetArrayElementTypeHandle(), ELEMENT_TYPE_SZARRAY, 1);
-            return AllocateSzArray(szArrayType, pArgs[dwNumArgs - 1], flags, bAllocateInLargeHeap);
+            return AllocateSzArray(szArrayType, pArgs[dwNumArgs - 1], flags);
         }
 
         providedLowerBounds = (dwNumArgs == 2*rank);
@@ -642,21 +646,21 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
         (cElements >= g_pConfig->GetDoubleArrayToLargeObjectHeapThreshold()))
     {
         STRESS_LOG2(LF_GC, LL_INFO10, "Allocating double MD array of size %d and length %d to large object heap\n", totalSize, cElements);
-        bAllocateInLargeHeap = TRUE;
+        flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
     }
 #endif
 
     if (totalSize >= g_pConfig->GetGCLOHThreshold())
-    {
-        bAllocateInLargeHeap = TRUE;
-    }
+        flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
 
-    flags |= (pArrayMT->ContainsPointers() ? GC_ALLOC_CONTAINS_REF : GC_ALLOC_NO_FLAGS);
+    if (pArrayMT->ContainsPointers())
+        flags |= GC_ALLOC_CONTAINS_REF;
 
-    if (bAllocateInLargeHeap)
+    ArrayBase* orArray = NULL;
+    if (flags & GC_ALLOC_USER_OLD_HEAP)
     {
-        orArray = (ArrayBase *) Alloc(totalSize, flags | GC_ALLOC_LARGE_OBJECT_HEAP);
-        orArray->SetArrayMethodTableForLargeObject(pArrayMT);
+        orArray = (ArrayBase*)Alloc(totalSize, flags);
+        orArray->SetMethodTableForUOHObject(pArrayMT);
     }
     else
     {
@@ -674,24 +678,11 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
         }
 #endif
         orArray = (ArrayBase*)Alloc(totalSize, flags);
-        orArray->SetArrayMethodTable(pArrayMT);
+        orArray->SetMethodTable(pArrayMT);
     }
 
     // Initialize Object
     orArray->m_NumComponents = cElements;
-
-    bool bProfilerNotifyLargeAllocation = false;
-
-    if (bAllocateInLargeHeap)
-    {
-        GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orArray);
-        bProfilerNotifyLargeAllocation = TrackLargeAllocations();
-    }
-
-#ifdef  _LOGALLOC
-    LogAlloc(totalSize, pArrayMT, orArray);
-#endif // _LOGALLOC
-
     if (kind == ELEMENT_TYPE_ARRAY)
     {
         INT32 *pCountsPtr      = (INT32 *) orArray->GetBoundsPtr();
@@ -704,20 +695,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
         }
     }
 
-    // Notify the profiler of the allocation
-    // do this after initializing bounds so callback has size information
-    if (TrackAllocations() || bProfilerNotifyLargeAllocation)
-    {
-        ProfileTrackArrayAlloc(orArray);
-    }
-
-#ifdef FEATURE_EVENT_TRACE
-    // Send ETW event for allocation
-    if(ETW::TypeSystemLog::IsHeapAllocEventEnabled())
-    {
-        ETW::TypeSystemLog::SendObjectAllocatedEvent(orArray);
-    }
-#endif // FEATURE_EVENT_TRACE
+    PublishObjectAndNotify(orArray, flags);
 
     if (kind != ELEMENT_TYPE_ARRAY)
     {
@@ -741,7 +719,7 @@ OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs,
                     TypeHandle subArrayType = pArrayMT->GetArrayElementTypeHandle();
                     for (UINT32 i = 0; i < cElements; i++)
                     {
-                        OBJECTREF obj = AllocateArrayEx(subArrayType, &pArgs[1], dwNumArgs-1, flags, bAllocateInLargeHeap);
+                        OBJECTREF obj = AllocateArrayEx(subArrayType, &pArgs[1], dwNumArgs-1, flagsOriginal);
                         outerArray->SetAt(i, obj);
                     }
 
@@ -848,7 +826,8 @@ OBJECTREF AllocateObjectArray(DWORD cElements, TypeHandle elementType, BOOL bAll
     _ASSERTE(arrayType.GetInternalCorElementType() == ELEMENT_TYPE_SZARRAY);
 #endif //_DEBUG
 
-    return AllocateSzArray(arrayType, (INT32) cElements, GC_ALLOC_NO_FLAGS, bAllocateInLargeHeap);
+    GC_ALLOC_FLAGS flags = bAllocateInLargeHeap ? GC_ALLOC_LARGE_OBJECT_HEAP : GC_ALLOC_NO_FLAGS;
+    return AllocateSzArray(arrayType, (INT32) cElements, flags);
 }
 
 STRINGREF AllocateString( DWORD cchStringLength )
@@ -859,8 +838,6 @@ STRINGREF AllocateString( DWORD cchStringLength )
         MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
     } CONTRACTL_END;
 
-    StringObject    *orObject  = NULL;
-
 #ifdef _DEBUG
     if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
     {
@@ -876,50 +853,23 @@ STRINGREF AllocateString( DWORD cchStringLength )
     if (cchStringLength > 0x3FFFFFDF)
         ThrowOutOfMemory();
 
-    SIZE_T ObjectSize = PtrAlign(StringObject::GetSize(cchStringLength));
-    _ASSERTE(ObjectSize > cchStringLength);
+    SIZE_T totalSize = PtrAlign(StringObject::GetSize(cchStringLength));
+    _ASSERTE(totalSize > cchStringLength);
 
     SetTypeHandleOnThreadForAlloc(TypeHandle(g_pStringClass));
 
-    orObject = (StringObject *)Alloc( ObjectSize, GC_ALLOC_NO_FLAGS);
+    GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS;
+    if (totalSize >= g_pConfig->GetGCLOHThreshold())
+        flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
 
-    // Object is zero-init already
-    _ASSERTE( orObject->HasEmptySyncBlockInfo() );
+    StringObject* orString = (StringObject*)Alloc(totalSize, flags);
 
     // Initialize Object
-    //<TODO>@TODO need to build a LARGE g_pStringMethodTable before</TODO>
-    orObject->SetMethodTable( g_pStringClass );
-    orObject->SetStringLength( cchStringLength );
-
-    bool bProfilerNotifyLargeAllocation = false;
-    if (ObjectSize >= g_pConfig->GetGCLOHThreshold())
-    {
-        bProfilerNotifyLargeAllocation = TrackLargeAllocations();
-        GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject);
-    }
+    orString->SetMethodTable(g_pStringClass);
+    orString->SetStringLength(cchStringLength);
 
-    // Notify the profiler of the allocation
-    if (TrackAllocations() || bProfilerNotifyLargeAllocation)
-    {
-        OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
-        GCPROTECT_BEGIN(objref);
-        ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr());
-        GCPROTECT_END();
-
-        orObject = (StringObject *) OBJECTREFToObject(objref);
-    }
-
-#ifdef FEATURE_EVENT_TRACE
-    // Send ETW event for allocation
-    if(ETW::TypeSystemLog::IsHeapAllocEventEnabled())
-    {
-        ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject);
-    }
-#endif // FEATURE_EVENT_TRACE
-
-    LogAlloc(ObjectSize, g_pStringClass, orObject);
-
-    return( ObjectToSTRINGREF(orObject) );
+    PublishObjectAndNotify(orString, flags);
+    return ObjectToSTRINGREF(orString);
 }
 
 #ifdef FEATURE_UTF8STRING
@@ -931,8 +881,6 @@ UTF8STRINGREF AllocateUtf8String(DWORD cchStringLength)
         MODE_COOPERATIVE; // returns an objref without pinning it => cooperative
     } CONTRACTL_END;
 
-    Utf8StringObject    *orObject = NULL;
-
 #ifdef _DEBUG
     if (g_pConfig->ShouldInjectFault(INJECTFAULT_GCHEAP))
     {
@@ -953,50 +901,23 @@ UTF8STRINGREF AllocateUtf8String(DWORD cchStringLength)
     if (cchStringLength > 0x7FFFFFBF)
         ThrowOutOfMemory();
 
-    SIZE_T ObjectSize = PtrAlign(Utf8StringObject::GetSize(cchStringLength));
-    _ASSERTE(ObjectSize > cchStringLength);
+    SIZE_T totalSize = PtrAlign(Utf8StringObject::GetSize(cchStringLength));
+    _ASSERTE(totalSize > cchStringLength);
 
     SetTypeHandleOnThreadForAlloc(TypeHandle(g_pUtf8StringClass));
 
-    orObject = (Utf8StringObject *)Alloc(ObjectSize, GC_ALLOC_NO_FLAGS);
+    GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS;
+    if (totalSize >= g_pConfig->GetGCLOHThreshold())
+        flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
 
-    // Object is zero-init already
-    _ASSERTE(orObject->HasEmptySyncBlockInfo());
+    Utf8StringObject* orString = (Utf8StringObject*)Alloc(totalSize, flags);
 
     // Initialize Object
-    orObject->SetMethodTable(g_pUtf8StringClass);
-    orObject->SetLength(cchStringLength);
-
-    bool bProfilerNotifyLargeAllocation = false;
-
-    if (ObjectSize >= g_pConfig->GetGCLOHThreshold())
-    {
-        GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject);
-        bProfilerNotifyLargeAllocation = TrackLargeAllocations();
-    }
-
-    // Notify the profiler of the allocation
-    if (TrackAllocations() || bProfilerNotifyLargeAllocation)
-    {
-        OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
-        GCPROTECT_BEGIN(objref);
-        ProfilerObjectAllocatedCallback(objref, (ClassID)orObject->GetTypeHandle().AsPtr());
-        GCPROTECT_END();
-
-        orObject = (Utf8StringObject *)OBJECTREFToObject(objref);
-    }
+    orString->SetMethodTable(g_pUtf8StringClass);
+    orString->SetLength(cchStringLength);
 
-#ifdef FEATURE_EVENT_TRACE
-    // Send ETW event for allocation
-    if (ETW::TypeSystemLog::IsHeapAllocEventEnabled())
-    {
-        ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject);
-    }
-#endif // FEATURE_EVENT_TRACE
-
-    LogAlloc(ObjectSize, g_pUtf8StringClass, orObject);
-
-    return( ObjectToUTF8STRINGREF(orObject) );
+    PublishObjectAndNotify(orString, flags);
+    return ObjectToUTF8STRINGREF(orString);
 }
 #endif // FEATURE_UTF8STRING
 
@@ -1073,9 +994,16 @@ OBJECTREF AllocateObject(MethodTable *pMT
 #endif // FEATURE_COMINTEROP_UNMANAGED_ACTIVATION
 #endif // FEATURE_COMINTEROP
     {
-        DWORD baseSize = pMT->GetBaseSize();
-        GC_ALLOC_FLAGS flags = ((pMT->ContainsPointers() ? GC_ALLOC_CONTAINS_REF : GC_ALLOC_NO_FLAGS) |
-                                (pMT->HasFinalizer() ? GC_ALLOC_FINALIZE : GC_ALLOC_NO_FLAGS));
+        GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS;
+        if (pMT->ContainsPointers())
+            flags |= GC_ALLOC_CONTAINS_REF;
+
+        if (pMT->HasFinalizer())
+            flags |= GC_ALLOC_FINALIZE;
+
+        DWORD totalSize = pMT->GetBaseSize();
+        if (totalSize >= g_pConfig->GetGCLOHThreshold())
+            flags |= GC_ALLOC_LARGE_OBJECT_HEAP;
 
 #ifdef FEATURE_64BIT_ALIGNMENT
         if (pMT->RequiresAlign8())
@@ -1089,50 +1017,22 @@ OBJECTREF AllocateObject(MethodTable *pMT
             _ASSERTE(sizeof(Object) == 4);
             flags |= GC_ALLOC_ALIGN8;
             if (pMT->IsValueType())
-            {
                 flags |= GC_ALLOC_ALIGN8_BIAS;
-            }
         }
 #endif // FEATURE_64BIT_ALIGNMENT
 
-        Object* orObject = (Object*)Alloc(baseSize, flags);
-
-        // verify zero'd memory (at least for sync block)
-        _ASSERTE( orObject->HasEmptySyncBlockInfo() );
+        Object* orObject = (Object*)Alloc(totalSize, flags);
 
-        bool bProfilerNotifyLargeAllocation = false;
-        if ((baseSize >= g_pConfig->GetGCLOHThreshold()))
+        if (flags & GC_ALLOC_USER_OLD_HEAP)
         {
-            orObject->SetMethodTableForLargeObject(pMT);
-            bProfilerNotifyLargeAllocation = TrackLargeAllocations();
-            GCHeapUtilities::GetGCHeap()->PublishObject((BYTE*)orObject);
+            orObject->SetMethodTableForUOHObject(pMT);
         }
         else
         {
             orObject->SetMethodTable(pMT);
         }
 
-        // Notify the profiler of the allocation
-        if (TrackAllocations() || bProfilerNotifyLargeAllocation)
-        {
-            OBJECTREF objref = ObjectToOBJECTREF((Object*)orObject);
-            GCPROTECT_BEGIN(objref);
-            ProfilerObjectAllocatedCallback(objref, (ClassID) orObject->GetTypeHandle().AsPtr());
-            GCPROTECT_END();
-
-            orObject = (Object *) OBJECTREFToObject(objref);
-        }
-
-#ifdef FEATURE_EVENT_TRACE
-        // Send ETW event for allocation
-        if(ETW::TypeSystemLog::IsHeapAllocEventEnabled())
-        {
-            ETW::TypeSystemLog::SendObjectAllocatedEvent(orObject);
-        }
-#endif // FEATURE_EVENT_TRACE
-
-        LogAlloc(pMT->GetBaseSize(), pMT, orObject);
-
+        PublishObjectAndNotify(orObject, flags);
         oref = OBJECTREF_TO_UNCHECKED_OBJECTREF(orObject);
     }
 
@@ -1466,7 +1366,7 @@ void ErectWriteBarrierForMT(MethodTable **dst, MethodTable *ref)
 
 #endif // FEATURE_USE_SOFTWARE_WRITE_WATCH_FOR_GC_HEAP
 
-        BYTE *refObject = *(BYTE **)((MethodTable*)ref)->GetLoaderAllocatorObjectHandle();
+        BYTE *refObject = *(BYTE **)ref->GetLoaderAllocatorObjectHandle();
         if((BYTE*) refObject >= g_ephemeral_low && (BYTE*) refObject < g_ephemeral_high)
         {
             // VolatileLoadWithoutBarrier() is used here to prevent fetch of g_card_table from being reordered
index 5fe51ce..9cf70b1 100644 (file)
 //========================================================================
 
 // Allocate single-dimensional array given array type
-OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE);
-OBJECTREF AllocateSzArray(TypeHandle  arrayType, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE);
+OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);
+OBJECTREF AllocateSzArray(TypeHandle  arrayType, INT32 length, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);
 
 // The main Array allocation routine, can do multi-dimensional
-OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE);
-OBJECTREF AllocateArrayEx(TypeHandle  arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS, BOOL bAllocateInLargeHeap = FALSE);
+OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);
+OBJECTREF AllocateArrayEx(TypeHandle  arrayType, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags = GC_ALLOC_NO_FLAGS);
 
 // Create a SD array of primitive types given an element type
 OBJECTREF AllocatePrimitiveArray(CorElementType type, DWORD cElements);
index 5666687..edc3fa1 100644 (file)
@@ -717,7 +717,7 @@ void *JIT_TrialAlloc::GenAllocString(Flags flags)
 
     // we need to load the method table for string from the global
 
-    // mov ecx, [g_pStringMethodTable]
+    // mov ecx, [g_pStringClass]
     sl.Emit16(0x0d8b);
     sl.Emit32((int)(size_t)&g_pStringClass);
 
index 0f3d4f3..8d64b26 100644 (file)
@@ -2609,7 +2609,7 @@ HCIMPL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, IN
 
         _ASSERTE(allocPtr != nullptr);
         ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
-        array->SetArrayMethodTable(pArrayMT);
+        array->SetMethodTable(pArrayMT);
         _ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
         array->m_NumComponents = static_cast<DWORD>(componentCount);
 
@@ -2668,7 +2668,7 @@ HCIMPL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, I
 
         _ASSERTE(allocPtr != nullptr);
         ArrayBase *array = reinterpret_cast<ArrayBase *>(allocPtr);
-        array->SetArrayMethodTable(pArrayMT);
+        array->SetMethodTable(pArrayMT);
         _ASSERTE(static_cast<DWORD>(componentCount) == componentCount);
         array->m_NumComponents = static_cast<DWORD>(componentCount);
 
index a7d68c1..8a09151 100644 (file)
@@ -149,33 +149,17 @@ class Object
         m_pMethTab = pMT;
     }
 
-    VOID SetMethodTable(MethodTable *pMT
-                        DEBUG_ARG(BOOL bAllowArray = FALSE))
+    VOID SetMethodTable(MethodTable *pMT)
     {
-        LIMITED_METHOD_CONTRACT;
-        m_pMethTab = pMT;
-
-#ifdef _DEBUG
-        if (!bAllowArray)
-        {
-            AssertNotArray();
-        }
-#endif // _DEBUG
+        WRAPPER_NO_CONTRACT;
+        RawSetMethodTable(pMT);
     }
 
-    VOID SetMethodTableForLargeObject(MethodTable *pMT
-                                      DEBUG_ARG(BOOL bAllowArray = FALSE))
+    VOID SetMethodTableForUOHObject(MethodTable *pMT)
     {
-        // This function must be used if the allocation occurs on the large object heap, and the method table might be a collectible type
         WRAPPER_NO_CONTRACT;
+        // This function must be used if the allocation occurs on a UOH heap, and the method table might be a collectible type
         ErectWriteBarrierForMT(&m_pMethTab, pMT);
-
-#ifdef _DEBUG
-        if (!bAllowArray)
-        {
-            AssertNotArray();
-        }
-#endif // _DEBUG
     }
 #endif //!DACCESS_COMPILE
 
@@ -477,16 +461,6 @@ class Object
 
  private:
     VOID ValidateInner(BOOL bDeep, BOOL bVerifyNextHeader, BOOL bVerifySyncBlock);
-
-#ifdef _DEBUG
-    void AssertNotArray()
-    {
-        if (m_pMethTab->IsArray())
-        {
-            _ASSERTE(!"ArrayBase::SetArrayMethodTable/ArrayBase::SetArrayMethodTableForLargeObject should be used for arrays");
-        }
-    }
-#endif // _DEBUG
 };
 
 /*
@@ -547,8 +521,8 @@ class ArrayBase : public Object
     friend class GCHeap;
     friend class CObjectHeader;
     friend class Object;
-    friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap);
-    friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags, BOOL bAllocateInLargeHeap);
+    friend OBJECTREF AllocateSzArray(MethodTable *pArrayMT, INT32 length, GC_ALLOC_FLAGS flags);
+    friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, GC_ALLOC_FLAGS flags);
     friend FCDECL2(Object*, JIT_NewArr1VC_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
     friend FCDECL2(Object*, JIT_NewArr1OBJ_MP_FastPortable, CORINFO_CLASS_HANDLE arrayMT, INT_PTR size);
     friend class JIT_TrialAlloc;
@@ -575,20 +549,15 @@ public:
     // type is stored in the array or not
     inline TypeHandle GetArrayElementTypeHandle() const;
 
-        // Get the CorElementType for the elements in the array.  Avoids creating a TypeHandle
+    // Get the CorElementType for the elements in the array.  Avoids creating a TypeHandle
     inline CorElementType GetArrayElementType() const;
 
     inline unsigned GetRank() const;
 
-        // Total element count for the array
+    // Total element count for the array
     inline DWORD GetNumComponents() const;
 
-#ifndef DACCESS_COMPILE
-    inline void SetArrayMethodTable(MethodTable *pArrayMT);
-    inline void SetArrayMethodTableForLargeObject(MethodTable *pArrayMT);
-#endif // !DACCESS_COMPILE
-
-        // Get pointer to elements, handles any number of dimensions
+    // Get pointer to elements, handles any number of dimensions
     PTR_BYTE GetDataPtr(BOOL inGC = FALSE) const {
         LIMITED_METHOD_CONTRACT;
         SUPPORTS_DAC;
@@ -699,7 +668,7 @@ class PtrArray : public ArrayBase
 {
     friend class GCHeap;
     friend class ClrDataAccess;
-    friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, DWORD flags, BOOL bAllocateInLargeHeap);
+    friend OBJECTREF AllocateArrayEx(MethodTable *pArrayMT, INT32 *pArgs, DWORD dwNumArgs, DWORD flags);
     friend class JIT_TrialAlloc;
     friend class CheckAsmOffsets;
 
index 8ed202d..cac1582 100644 (file)
@@ -175,24 +175,6 @@ inline DWORD ArrayBase::GetNumComponents() const
     return m_NumComponents;
 }
 
-#ifndef DACCESS_COMPILE
-inline void ArrayBase::SetArrayMethodTable(MethodTable *pArrayMT)
-{
-    LIMITED_METHOD_CONTRACT;
-
-    SetMethodTable(pArrayMT
-                   DEBUG_ARG(TRUE));
-}
-
-inline void ArrayBase::SetArrayMethodTableForLargeObject(MethodTable *pArrayMT)
-{
-    LIMITED_METHOD_CONTRACT;
-
-    SetMethodTableForLargeObject(pArrayMT
-                                 DEBUG_ARG(TRUE));
-}
-#endif // !DACCESS_COMPILE
-
 inline /* static */ unsigned ArrayBase::GetDataPtrOffset(MethodTable* pMT)
 {
     LIMITED_METHOD_CONTRACT;
index 47b7d78..b5c8ba6 100644 (file)
         <ExcludeList Include="$(XunitTestBinBase)/GC/API/GC/AddUsageTest/**">
             <Issue>needs triage</Issue>
         </ExcludeList>
-        <ExcludeList Include="$(XunitTestBinBase)/GC/API/GC/AllocateUninitializedArray/**">
-            <Issue>needs triage</Issue>
-        </ExcludeList>
         <ExcludeList Include="$(XunitTestBinBase)/GC/API/GC/Collect1/**">
             <Issue>needs triage</Issue>
         </ExcludeList>
diff --git a/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.cs b/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.cs
deleted file mode 100644 (file)
index 28dcd96..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-// See the LICENSE file in the project root for more information.
-// Tests GC.Collect()
-
-using System;
-
-public class Test {
-
-
-    public static int Main() {
-        // allocate a bunch of SOH byte arrays and touch them.
-        var r = new Random(1234);
-        for (int i = 0; i < 10000; i++)
-        {
-            int size = r.Next(10000);
-            var arr = AllocUninitialized<byte>.Call(size);
-
-            if (size > 1)
-            {
-                arr[0] = 5;
-                arr[size - 1] = 17;
-                if (arr[0] != 5 || arr[size - 1] != 17)
-                {
-                    Console.WriteLine("Scenario 1 for GC.AllocUninitialized() failed!");
-                    return 1;
-                }
-            }
-        }
-
-        // allocate a bunch of LOH int arrays and touch them.
-        for (int i = 0; i < 1000; i++)
-        {
-            int size = r.Next(100000, 1000000);
-            var arr = AllocUninitialized<int>.Call(size);
-
-            arr[0] = 5;
-            arr[size - 1] = 17;
-            if (arr[0] != 5 || arr[size - 1] != 17)
-            {
-                Console.WriteLine("Scenario 2 for GC.AllocUninitialized() failed!");
-                return 1;
-            }
-        }
-
-        // allocate a string array
-        {
-            int i = 100;
-            var arr = AllocUninitialized<string>.Call(i);
-
-            arr[0] = "5";
-            arr[i - 1] = "17";
-            if (arr[0] != "5" || arr[i - 1] != "17")
-            {
-                Console.WriteLine("Scenario 3 for GC.AllocUninitialized() failed!");
-                return 1;
-            }
-        }
-
-        // allocate max size byte array
-        {
-            if (IntPtr.Size == 8)
-            {
-                int i = 0x7FFFFFC7;
-                var arr = AllocUninitialized<byte>.Call(i);
-
-                arr[0] = 5;
-                arr[i - 1] = 17;
-                if (arr[0] != 5 || arr[i - 1] != 17)
-                {
-                    Console.WriteLine("Scenario 4 for GC.AllocUninitialized() failed!");
-                    return 1;
-                }
-            }
-        }
-
-        // negative size
-        {
-            int GetNegativeValue() => -1;
-            int negativeSize = GetNegativeValue();
-            Type expectedExceptionType = null;
-
-            try
-            {
-                GC.KeepAlive(new byte[negativeSize]);
-
-                Console.WriteLine("Scenario 5 Expected exception (new operator)!");
-                return 1;
-            }
-            catch (Exception newOperatorEx)
-            {
-                expectedExceptionType = newOperatorEx.GetType();
-            }
-
-            try
-            {
-                var arr = AllocUninitialized<byte>.Call(-1);
-
-                Console.WriteLine("Scenario 5 Expected exception (GC.AllocateUninitializedArray)!");
-                return 1;
-            }
-            catch (Exception allocUninitializedEx) when (allocUninitializedEx.GetType() == expectedExceptionType)
-            {
-                // OK
-            }
-            catch (Exception other)
-            {
-                Console.WriteLine($"Scenario 5 Expected exception type mismatch: expected {expectedExceptionType}, but got {other.GetType()}!");
-                return 1;
-            }
-        }
-
-        // too large
-        {
-            try
-            {
-                var arr = AllocUninitialized<double>.Call(int.MaxValue);
-
-                Console.WriteLine("Scenario 6 Expected exception!");
-                return 1;
-            }
-            catch (OutOfMemoryException)
-            {
-            }
-        }
-
-
-        Console.WriteLine("Test for GC.Collect() passed!");
-        return 100;
-    }
-
-    //TODO: This should be removed once the API is public.
-    static class AllocUninitialized<T>
-    {
-        public static Func<int, T[]> Call = (i) =>
-        {
-            // replace the stub with actual impl.
-            Call = (Func<int, T[]>)typeof(System.GC).
-            GetMethod("AllocateUninitializedArray",
-                bindingAttr: System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static,
-                binder: null,
-                new Type[] { typeof(int) },
-                modifiers: new System.Reflection.ParameterModifier[0]).
-            MakeGenericMethod(new Type[] { typeof(T) }).
-            CreateDelegate(typeof(Func<int, T[]>));
-
-            // call the impl.
-            return Call(i);
-        };
-    }
-}
diff --git a/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.csproj b/src/coreclr/tests/src/GC/API/GC/AllocateUninitializedArray.csproj
deleted file mode 100644 (file)
index 41499bc..0000000
+++ /dev/null
@@ -1,13 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <OutputType>Exe</OutputType>
-    <CLRTestPriority>0</CLRTestPriority>
-  </PropertyGroup>
-  <PropertyGroup>
-    <!-- Set to 'Full' if the Debug? column is marked in the spreadsheet. Leave blank otherwise. -->
-    <DebugType>PdbOnly</DebugType>
-  </PropertyGroup>
-  <ItemGroup>
-    <Compile Include="AllocateUninitializedArray.cs" />
-  </ItemGroup>
-</Project>
index da3de89..d09b7e3 100644 (file)
@@ -2029,6 +2029,8 @@ namespace System
     {
         public static int MaxGeneration { get { throw null; } }
         public static void AddMemoryPressure(long bytesAllocated) { }
+        public static T[] AllocateArray<T>(int length, bool pinned = false) { throw null; }
+        public static T[] AllocateUninitializedArray<T>(int length, bool pinned = false) { throw null; }
         public static void CancelFullGCNotification() { }
         public static void Collect() { }
         public static void Collect(int generation) { }
index 4f65939..b7e3070 100644 (file)
@@ -3,6 +3,7 @@
 // See the LICENSE file in the project root for more information.
 
 using System;
+using System.Collections.Generic;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Diagnostics;
@@ -424,7 +425,8 @@ namespace System.Tests
         public static void LatencyRoundtrips_LowLatency(GCLatencyMode value) => LatencyRoundtrips(value);
     }
 
-    public class GCExtendedTests    {
+    public class GCExtendedTests
+    {
         private const int TimeoutMilliseconds = 10 * 30 * 1000; //if full GC is triggered it may take a while
 
         /// <summary>
@@ -892,5 +894,178 @@ namespace System.Tests
                 previous = CallGetTotalAllocatedBytes(previous);
             }
         }
+
+        [Fact]
+        [OuterLoop]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+        private static void AllocateUninitializedArray()
+        {
+            // allocate a bunch of SOH byte arrays and touch them.
+            var r = new Random(1234);
+            for (int i = 0; i < 10000; i++)
+            {
+                int size = r.Next(10000);
+                var arr = GC.AllocateUninitializedArray<byte>(size, pinned: i % 2 == 1);
+
+                if (size > 1)
+                {
+                    arr[0] = 5;
+                    arr[size - 1] = 17;
+                    Assert.True(arr[0] == 5 && arr[size - 1] == 17);
+                }
+            }
+
+            // allocate a bunch of LOH int arrays and touch them.
+            for (int i = 0; i < 1000; i++)
+            {
+                int size = r.Next(100000, 1000000);
+                var arr = GC.AllocateUninitializedArray<int>(size, pinned: i % 2 == 1);
+
+                arr[0] = 5;
+                arr[size - 1] = 17;
+                Assert.True(arr[0] == 5 && arr[size - 1] == 17);
+            }
+
+            // allocate a string array
+            {
+                int i = 100;
+                var arr = GC.AllocateUninitializedArray<string>(i);
+
+                arr[0] = "5";
+                arr[i - 1] = "17";
+                Assert.True(arr[0] == "5" && arr[i - 1] == "17");
+            }
+
+            // allocate max size byte array
+            {
+                if (IntPtr.Size == 8)
+                {
+                    int i = 0x7FFFFFC7;
+                    var arr = GC.AllocateUninitializedArray<byte>(i);
+
+                    arr[0] = 5;
+                    arr[i - 1] = 17;
+                    Assert.True(arr[0] == 5 && arr[i - 1] == 17);
+                }
+            }
+        }
+
+        [Fact]
+        [OuterLoop]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+        private static void AllocateArray()
+        {
+            // allocate a bunch of SOH byte arrays and touch them.
+            var r = new Random(1234);
+            for (int i = 0; i < 10000; i++)
+            {
+                int size = r.Next(10000);
+                var arr = GC.AllocateArray<byte>(size, pinned: i % 2 == 1);
+
+                if (size > 1)
+                {
+                    arr[0] = 5;
+                    arr[size - 1] = 17;
+                    Assert.True(arr[0] == 5 && arr[size - 1] == 17);
+                }
+            }
+
+            // allocate a bunch of LOH int arrays and touch them.
+            for (int i = 0; i < 1000; i++)
+            {
+                int size = r.Next(100000, 1000000);
+                var arr = GC.AllocateArray<int>(size, pinned: i % 2 == 1);
+
+                arr[0] = 5;
+                arr[size - 1] = 17;
+                Assert.True(arr[0] == 5 && arr[size - 1] == 17);
+            }
+
+            // allocate a string array
+            {
+                int i = 100;
+                var arr = GC.AllocateArray<string>(i);
+
+                arr[0] = "5";
+                arr[i - 1] = "17";
+                Assert.True(arr[0] == "5" && arr[i - 1] == "17");
+            }
+
+            // allocate max size byte array
+            {
+                if (IntPtr.Size == 8)
+                {
+                    int i = 0x7FFFFFC7;
+                    var arr = GC.AllocateArray<byte>(i);
+
+                    arr[0] = 5;
+                    arr[i - 1] = 17;
+                    Assert.True(arr[0] == 5 && arr[i - 1] == 17);
+                }
+            }
+        }
+
+        [Theory]
+        [InlineData(-1)]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+        private static void AllocateArrayNegativeSize(int negValue)
+        {
+            Assert.Throws<OverflowException>(() => GC.AllocateUninitializedArray<byte>(-1));
+            Assert.Throws<OverflowException>(() => GC.AllocateUninitializedArray<byte>(negValue));
+            Assert.Throws<OverflowException>(() => GC.AllocateUninitializedArray<byte>(-1, pinned: true));
+            Assert.Throws<OverflowException>(() => GC.AllocateUninitializedArray<byte>(negValue, pinned: true));
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+        private static void AllocateArrayTooLarge()
+        {
+            Assert.Throws<OutOfMemoryException>(() => GC.AllocateUninitializedArray<double>(int.MaxValue));
+            Assert.Throws<OutOfMemoryException>(() => GC.AllocateUninitializedArray<double>(int.MaxValue, pinned: true));
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+        private static void AllocateArrayRefType()
+        {
+            GC.AllocateUninitializedArray<string>(100);
+            Assert.Throws<ArgumentException>(() => GC.AllocateUninitializedArray<string>(100, pinned: true));
+        }
+
+        [Fact]
+        [ActiveIssue("https://github.com/dotnet/runtime/issues/33583", TestRuntimes.Mono)]
+        private unsafe static void AllocateArrayCheckPinning()
+        {
+            var list = new List<long[]>();
+
+            var r = new Random(1234);
+            for (int i = 0; i < 10000; i++)
+            {
+                int size = r.Next(2, 100);
+                var arr = i % 2 == 1 ?
+                    GC.AllocateArray<long>(size, pinned: true) :
+                    GC.AllocateUninitializedArray<long>(size, pinned: true) ;
+
+                fixed (long* pElem = &arr[0])
+                {
+                    *pElem = (long)pElem;
+                }
+
+                if (r.Next(100) % 2 == 0)
+                {
+                    list.Add(arr);
+                }
+            }
+
+            GC.Collect();
+
+            foreach (var arr in list)
+            {
+                fixed (long* pElem = &arr[0])
+                {
+                    Assert.Equal(*pElem, (long)pElem);
+                }
+            }
+        }
     }
 }
index e5efe52..5894af3 100644 (file)
@@ -246,17 +246,20 @@ namespace System
 
                public static GCMemoryInfo GetGCMemoryInfo ()
                {
-                       _GetGCMemoryInfo(out long highMemoryLoadThresholdBytes,
+                       _GetGCMemoryInfo (out long highMemoryLoadThresholdBytes,
                                                         out long memoryLoadBytes,
                                                         out long totalAvailableMemoryBytes,
                                                         out long heapSizeBytes,
                                                         out long fragmentedBytes );
                        
-                       return new GCMemoryInfo(highMemoryLoadThresholdBytes, memoryLoadBytes, totalAvailableMemoryBytes, heapSizeBytes, fragmentedBytes);
+                       return new GCMemoryInfo (highMemoryLoadThresholdBytes, memoryLoadBytes, totalAvailableMemoryBytes, heapSizeBytes, fragmentedBytes);
                }
 
-               internal static T[] AllocateUninitializedArray<T> (int length)
+               public static T[] AllocateUninitializedArray<T> (int length, bool pinned = false)
                {
+                       if (pinned)
+                               throw new NotImplementedException ();
+
                        // Mono only does explicit zeroning if the array is to big for the nursery, but less than 1 Mb - 4 kb.
                        // If it is bigger than that, we grab memoroy directly from the OS which comes pre-zeroed.
                        // Experimentation shows that if we just skip the zeroing in this case, we do not save a measurable
@@ -264,5 +267,13 @@ namespace System
                        // Revist if we change LOS implementation.
                        return new T [length];
                }
+
+               public static T[] AllocateArray<T> (int length, bool pinned = false)
+               {
+                       if (pinned)
+                               throw new NotImplementedException ();
+
+                       return new T [length];
+               }
        }
 }