diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Unix.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Unix.cs deleted file mode 100644 index f49cf74467d767..00000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Unix.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace Internal.Runtime -{ - internal unsafe partial class FrozenObjectHeapManager - { - private static void* ClrVirtualReserve(nuint size) - { - // The shim will return null for failure - return (void*)Interop.Sys.MMap( - 0, - size, - Interop.Sys.MemoryMappedProtections.PROT_NONE, - Interop.Sys.MemoryMappedFlags.MAP_PRIVATE | Interop.Sys.MemoryMappedFlags.MAP_ANONYMOUS, - -1, - 0); - - } - - private static void* ClrVirtualCommit(void* pBase, nuint size) - { - int result = Interop.Sys.MProtect( - (nint)pBase, - size, - Interop.Sys.MemoryMappedProtections.PROT_READ | Interop.Sys.MemoryMappedProtections.PROT_WRITE); - - return result == 0 ? pBase : null; - } - - private static void ClrVirtualFree(void* pBase, nuint size) - { - Debug.Assert(size != 0); - Interop.Sys.MUnmap((nint)pBase, size); - } - } -} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Windows.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Windows.cs deleted file mode 100644 index 9cf171e61c3f50..00000000000000 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.Windows.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Diagnostics; - -namespace Internal.Runtime -{ - internal unsafe partial class FrozenObjectHeapManager - { - private static void* ClrVirtualReserve(nuint size) - { - return Interop.Kernel32.VirtualAlloc(null, size, Interop.Kernel32.MemOptions.MEM_RESERVE, Interop.Kernel32.PageOptions.PAGE_READWRITE); - } - - private static void* ClrVirtualCommit(void* pBase, nuint size) - { - return Interop.Kernel32.VirtualAlloc(pBase, size, Interop.Kernel32.MemOptions.MEM_COMMIT, Interop.Kernel32.PageOptions.PAGE_READWRITE); - } - - private static void ClrVirtualFree(void* pBase, nuint size) - { - // We require the size parameter for Unix implementation sake. - // The Win32 API ignores this parameter because we must pass zero. - // If the caller passed zero, this is going to be broken on Unix - // so let's at least assert that. - Debug.Assert(size != 0); - - Interop.Kernel32.VirtualFree(pBase, 0, Interop.Kernel32.MemOptions.MEM_RELEASE); - } - } -} diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.cs b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.cs index e9b41c447b84e5..c261e9d7514b4b 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.cs +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/Internal/Runtime/FrozenObjectHeapManager.cs @@ -4,25 +4,21 @@ using System; using System.Runtime; using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Threading; using Debug = System.Diagnostics.Debug; -// Rewrite of src\coreclr\vm\frozenobjectheap.cpp in C# - namespace Internal.Runtime { - internal unsafe partial class FrozenObjectHeapManager + internal unsafe class FrozenObjectHeapManager { public static readonly FrozenObjectHeapManager Instance = new FrozenObjectHeapManager(); private readonly Lock m_Crst = new Lock(useTrivialWaits: true); private FrozenObjectSegment m_CurrentSegment; - // Default size to reserve for a frozen segment - private const nuint FOH_SEGMENT_DEFAULT_SIZE = 4 * 1024 * 1024; - // Size to commit on demand in that reserved space - private const nuint FOH_COMMIT_SIZE = 64 * 1024; + private const nuint FOH_SEGMENT_DEFAULT_SIZE = 4 * 1024; public T? TryAllocateObject() where T : class { @@ -37,8 +33,6 @@ internal unsafe partial class FrozenObjectHeapManager using (m_Crst.EnterScope()) { Debug.Assert(type != null); - // _ASSERT(FOH_COMMIT_SIZE >= MIN_OBJECT_SIZE); - // Currently we don't support frozen objects with special alignment requirements // TODO: We should also give up on arrays of doubles on 32-bit platforms. // (we currently never allocate them on frozen segments) @@ -51,12 +45,10 @@ internal unsafe partial class FrozenObjectHeapManager #endif // NOTE: objectSize is expected be the full size including header - // _ASSERT(objectSize >= MIN_OBJECT_SIZE); - if (objectSize > FOH_COMMIT_SIZE) + if (objectSize > FOH_SEGMENT_DEFAULT_SIZE - 2 * (nuint)sizeof(ObjHeader)) { - // The current design doesn't allow objects larger than FOH_COMMIT_SIZE and - // since FrozenObjectHeap is just an optimization, let's not fill it with huge objects. + // Leave room for the first and next object headers in a default-sized segment. return null; } @@ -67,7 +59,7 @@ internal unsafe partial class FrozenObjectHeapManager nuint newSegmentSize = FOH_SEGMENT_DEFAULT_SIZE; if (m_CurrentSegment != null) { - // Double the reserved size to reduce the number of frozen segments in apps with lots of frozen objects + // Double the size to reduce the number of frozen segments in apps with lots of frozen objects // Use the same size in case if prevSegmentSize*2 operation overflows. nuint prevSegmentSize = m_CurrentSegment.m_Size; newSegmentSize = Math.Max(prevSegmentSize, prevSegmentSize * 2); @@ -90,21 +82,14 @@ internal unsafe partial class FrozenObjectHeapManager private class FrozenObjectSegment { - // Start of the reserved memory, the first object starts at "m_pStart + sizeof(ObjHeader)" (its pMT) + // Start of the allocated memory, the first object starts at "m_pStart + sizeof(ObjHeader)" (its pMT) private byte* m_pStart; // Pointer to the end of the current segment, ready to be used as a pMT for a new object // meaning that "m_pCurrent - sizeof(ObjHeader)" is the actual start of the new object (header). - // - // m_pCurrent <= m_SizeCommitted public byte* m_pCurrent; - // Memory committed in the current segment - // - // m_SizeCommitted <= m_pStart + FOH_SIZE_RESERVED - public nuint m_SizeCommitted; - - // Total memory reserved for the current segment + // Total memory allocated for the current segment public nuint m_Size; private IntPtr m_SegmentHandle; @@ -113,53 +98,23 @@ public FrozenObjectSegment(nuint sizeHint) { m_Size = sizeHint; - Debug.Assert(m_Size > FOH_COMMIT_SIZE); - Debug.Assert(m_Size % FOH_COMMIT_SIZE == 0); + Debug.Assert(m_Size >= FOH_SEGMENT_DEFAULT_SIZE); - void* alloc = ClrVirtualReserve(m_Size); - if (alloc == null) - { - // Try again with the default FOH size - if (m_Size > FOH_SEGMENT_DEFAULT_SIZE) - { - m_Size = FOH_SEGMENT_DEFAULT_SIZE; - Debug.Assert(m_Size > FOH_COMMIT_SIZE); - Debug.Assert(m_Size % FOH_COMMIT_SIZE == 0); - alloc = ClrVirtualReserve(m_Size); - } - - if (alloc == null) - { - throw new OutOfMemoryException(); - } - } - - // Commit a chunk in advance - m_pStart = (byte*)ClrVirtualCommit(alloc, FOH_COMMIT_SIZE); - if (m_pStart == null) - { - ClrVirtualFree(alloc, m_Size); - throw new OutOfMemoryException(); - } + m_pStart = (byte*)NativeMemory.AllocZeroed(m_Size); m_pCurrent = m_pStart + sizeof(ObjHeader); - m_SegmentHandle = RuntimeImports.RhRegisterFrozenSegment(m_pStart, (nuint)m_pCurrent - (nuint)m_pStart, FOH_COMMIT_SIZE, m_Size); + m_SegmentHandle = RuntimeImports.RhRegisterFrozenSegment(m_pStart, (nuint)m_pCurrent - (nuint)m_pStart, m_Size, m_Size); if (m_SegmentHandle == IntPtr.Zero) { - ClrVirtualFree(alloc, m_Size); + NativeMemory.Free(m_pStart); throw new OutOfMemoryException(); } - - m_SizeCommitted = FOH_COMMIT_SIZE; } public HalfBakedObject* TryAllocateObject(MethodTable* type, nuint objectSize) { Debug.Assert((m_pStart != null) && (m_Size > 0)); - //_ASSERT(IS_ALIGNED(m_pCurrent, DATA_ALIGNMENT)); - //_ASSERT(IS_ALIGNED(objectSize, DATA_ALIGNMENT)); - Debug.Assert(objectSize <= FOH_COMMIT_SIZE); Debug.Assert(m_pCurrent >= m_pStart + sizeof(ObjHeader)); nuint spaceUsed = (nuint)(m_pCurrent - m_pStart); @@ -174,25 +129,12 @@ public FrozenObjectSegment(nuint sizeHint) return null; } - // Check if we need to commit a new chunk - if (spaceUsed + objectSize + (nuint)sizeof(ObjHeader) > m_SizeCommitted) - { - // Make sure we don't go out of bounds during this commit - Debug.Assert(m_SizeCommitted + FOH_COMMIT_SIZE <= m_Size); - - if (ClrVirtualCommit(m_pStart + m_SizeCommitted, FOH_COMMIT_SIZE) == null) - { - throw new OutOfMemoryException(); - } - m_SizeCommitted += FOH_COMMIT_SIZE; - } - HalfBakedObject* obj = (HalfBakedObject*)m_pCurrent; obj->SetMethodTable(type); m_pCurrent += objectSize; - RuntimeImports.RhUpdateFrozenSegment(m_SegmentHandle, m_pCurrent, m_pStart + m_SizeCommitted); + RuntimeImports.RhUpdateFrozenSegment(m_SegmentHandle, m_pCurrent, m_pStart + m_Size); return obj; } diff --git a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj index 1319a74a9667c8..6a1b1aed8614ca 100644 --- a/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj +++ b/src/coreclr/nativeaot/System.Private.CoreLib/src/System.Private.CoreLib.csproj @@ -267,7 +267,6 @@ Common\Interop\Windows\Kernel32\Interop.ExitProcess.cs - System\Runtime\InteropServices\BuiltInVariantExtensions.cs @@ -301,7 +300,6 @@ - @@ -311,9 +309,6 @@ Common\Interop\Unix\System.Native\Interop.Exit.cs - - Common\Interop\Unix\System.Native\Interop.MProtect.cs - diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MProtect.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MProtect.cs deleted file mode 100644 index 482848da93cf08..00000000000000 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.MProtect.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Runtime.InteropServices; -using Microsoft.Win32.SafeHandles; - -internal static partial class Interop -{ - internal static partial class Sys - { - [LibraryImport(Libraries.SystemNative, EntryPoint = "SystemNative_MProtect", SetLastError = true)] - internal static partial int MProtect(IntPtr addr, ulong len, MemoryMappedProtections prot); - } -} diff --git a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs index 7e1423bd1b87b4..7922e6436c0c60 100644 --- a/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs +++ b/src/tests/nativeaot/SmokeTests/Reflection/Reflection.cs @@ -102,10 +102,42 @@ private static int Main() TestActivatingThingsInSignature.Run(); TestArrayInitialize.Run(); TestDelegateInvokeFromEvent.Run(); + TestFrozenRuntimeTypes.Run(); return 100; } + class TestFrozenRuntimeTypes + { + public static void Run() + { + const int MaxRank = 32; + Type[] types = new Type[MaxRank * MaxRank]; + for (int outerRank = 1; outerRank <= MaxRank; outerRank++) + { + for (int innerRank = 1; innerRank <= MaxRank; innerRank++) + { + Type type = typeof(TestFrozenRuntimeTypes).MakeArrayType(innerRank).MakeArrayType(outerRank); + types[(outerRank - 1) * MaxRank + innerRank - 1] = type; + } + } + + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true, compacting: true); + + for (int outerRank = 1; outerRank <= MaxRank; outerRank++) + { + for (int innerRank = 1; innerRank <= MaxRank; innerRank++) + { + Type type = types[(outerRank - 1) * MaxRank + innerRank - 1]; + Assert.Equal(outerRank, type.GetArrayRank()); + Assert.Equal(innerRank, type.GetElementType().GetArrayRank()); + Assert.Same(typeof(TestFrozenRuntimeTypes), type.GetElementType().GetElementType()); + Assert.Same(typeof(TestFrozenRuntimeTypes).MakeArrayType(innerRank).MakeArrayType(outerRank), type); + } + } + } + } + class TestReflectionInvoke { internal class InvokeTests