diff --git a/ARMeilleure/Common/Counter.cs b/ARMeilleure/Common/Counter.cs index 66ae1c70e..1ceba1762 100644 --- a/ARMeilleure/Common/Counter.cs +++ b/ARMeilleure/Common/Counter.cs @@ -18,10 +18,22 @@ namespace ARMeilleure.Common /// /// instance /// Index in the - private Counter(EntryTable countTable, int index) + /// is + /// is unsupported + public Counter(EntryTable countTable) { - _countTable = countTable; - _index = index; + if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) && + typeof(T) != typeof(short) && typeof(T) != typeof(ushort) && + typeof(T) != typeof(int) && typeof(T) != typeof(uint) && + typeof(T) != typeof(long) && typeof(T) != typeof(ulong) && + typeof(T) != typeof(nint) && typeof(T) != typeof(nuint) && + typeof(T) != typeof(float) && typeof(T) != typeof(double)) + { + throw new ArgumentException("Counter does not support the specified type."); + } + + _countTable = countTable ?? throw new ArgumentNullException(nameof(countTable)); + _index = countTable.Allocate(); } /// @@ -67,48 +79,11 @@ namespace ARMeilleure.Common } /// - /// Frees resources used by instance. + /// Frees resources used by the instance. /// ~Counter() { Dispose(false); } - - /// - /// Tries to create a instance from the specified instance. - /// - /// from which to create the - /// instance if success; otherwise - /// if success; otherwise - /// is - /// is unsupported - public static bool TryCreate(EntryTable countTable, out Counter counter) - { - if (countTable == null) - { - throw new ArgumentNullException(nameof(countTable)); - } - - if (typeof(T) != typeof(byte) && typeof(T) != typeof(sbyte) && - typeof(T) != typeof(short) && typeof(T) != typeof(ushort) && - typeof(T) != typeof(int) && typeof(T) != typeof(uint) && - typeof(T) != typeof(long) && typeof(T) != typeof(ulong) && - typeof(T) != typeof(nint) && typeof(T) != typeof(nuint) && - typeof(T) != typeof(float) && typeof(T) != typeof(double)) - { - throw new ArgumentException("Counter does not support the specified type", nameof(countTable)); - } - - if (countTable.TryAllocate(out int index)) - { - counter = new Counter(countTable, index); - - return true; - } - - counter = null; - - return false; - } } } diff --git a/ARMeilleure/Common/EntryTable.cs b/ARMeilleure/Common/EntryTable.cs index b8981496b..bb171dc67 100644 --- a/ARMeilleure/Common/EntryTable.cs +++ b/ARMeilleure/Common/EntryTable.cs @@ -1,43 +1,63 @@ using System; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.InteropServices; namespace ARMeilleure.Common { /// - /// Represents a fixed size table of the type , whose entries will remain at the same + /// Represents an expandable table of the type , whose entries will remain at the same /// address through out the table's lifetime. /// /// Type of the entry in the table - class EntryTable where TEntry : unmanaged + class EntryTable : IDisposable where TEntry : unmanaged { + private bool _disposed; private int _freeHint; - private readonly TEntry[] _table; + private readonly int _pageCapacity; // Number of entries per page. + private readonly int _pageLogCapacity; + private readonly Dictionary _pages; private readonly BitMap _allocated; /// - /// Initializes a new instance of the class with the specified capacity. + /// Initializes a new instance of the class with the desired page size. /// - /// Capacity of the table - /// is less than 0 - public EntryTable(int capacity) + /// Desired page size + /// is less than 0 + /// 's size is zero + /// + /// The actual page size may be smaller or larger depending on the size of . + /// + public unsafe EntryTable(int pageSize = 4096) { - if (capacity < 0) + if (pageSize < 0) { - throw new ArgumentOutOfRangeException(nameof(capacity)); + throw new ArgumentOutOfRangeException(nameof(pageSize), "Page size cannot be negative."); + } + + if (sizeof(TEntry) == 0) + { + throw new ArgumentException("Size of TEntry cannot be zero."); } - _freeHint = 0; _allocated = new BitMap(); - _table = GC.AllocateArray(capacity, pinned: true); + _pages = new Dictionary(); + _pageLogCapacity = BitOperations.Log2((uint)(pageSize / sizeof(TEntry))); + _pageCapacity = 1 << _pageLogCapacity; } /// - /// Tries to allocate an entry in the . Returns if - /// success; otherwise returns . + /// Allocates an entry in the . /// - /// Index of entry allocated in the table - /// if success; otherwise - public bool TryAllocate(out int index) + /// Index of entry allocated in the table + /// instance was disposed + public int Allocate() { + if (_disposed) + { + throw new ObjectDisposedException(null); + } + lock (_allocated) { if (_allocated.IsSet(_freeHint)) @@ -45,32 +65,37 @@ namespace ARMeilleure.Common _freeHint = _allocated.FindFirstUnset(); } - if (_freeHint >= 0 && _freeHint < _table.Length) - { - index = _freeHint++; + int index = _freeHint++; + var page = GetPage(index); - _allocated.Set(index); + _allocated.Set(index); - GetValue(index) = default; + GetValue(page, index) = default; - return true; - } + return index; } - - index = 0; - - return false; } /// /// Frees the entry at the specified . /// /// Index of entry to free + /// instance was disposed public void Free(int index) { + if (_disposed) + { + throw new ObjectDisposedException(null); + } + lock (_allocated) { - _allocated.Clear(index); + if (_allocated.IsSet(index)) + { + _allocated.Clear(index); + + _freeHint = index; + } } } @@ -78,25 +103,95 @@ namespace ARMeilleure.Common /// Gets a reference to the entry at the specified allocated . /// /// Index of the entry - /// Reference to the entry at the specified index + /// Reference to the entry at the specified + /// instance was disposed /// Entry at is not allocated - /// is outside of the table public ref TEntry GetValue(int index) { - if (index < 0 || index >= _table.Length) + if (_disposed) { - throw new ArgumentOutOfRangeException(nameof(index)); + throw new ObjectDisposedException(null); } + Span page; + lock (_allocated) { if (!_allocated.IsSet(index)) { throw new ArgumentException("Entry at the specified index was not allocated", nameof(index)); } + + page = GetPage(index); } - return ref _table[index]; + return ref GetValue(page, index); + } + + /// + /// Gets a reference to the entry at using the specified from the specified + /// . + /// + /// Page to use + /// Index to use + /// Reference to the entry + private ref TEntry GetValue(Span page, int index) + { + return ref page[index & (_pageCapacity - 1)]; + } + + /// + /// Gets the page for the specified . + /// + /// Index to use + /// Page for the specified + private unsafe Span GetPage(int index) + { + var pageIndex = (int)((uint)(index & ~(_pageCapacity - 1)) >> _pageLogCapacity); + + if (!_pages.TryGetValue(pageIndex, out IntPtr page)) + { + page = Marshal.AllocHGlobal(sizeof(TEntry) * _pageCapacity); + + _pages.Add(pageIndex, page); + } + + return new Span((void*)page, _pageCapacity); + } + + /// + /// Releases all resources used by the instance. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases all unmanaged and optionally managed resources used by the + /// instance. + /// + /// to dispose managed resources also; otherwise just unmanaged resouces + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + foreach (var page in _pages.Values) + { + Marshal.FreeHGlobal(page); + } + + _disposed = true; + } + } + + /// + /// Frees resources used by the instance. + /// + ~EntryTable() + { + Dispose(false); } } } diff --git a/ARMeilleure/Translation/PTC/Ptc.cs b/ARMeilleure/Translation/PTC/Ptc.cs index 25d9dc583..55a0f4d06 100644 --- a/ARMeilleure/Translation/PTC/Ptc.cs +++ b/ARMeilleure/Translation/PTC/Ptc.cs @@ -579,12 +579,7 @@ namespace ARMeilleure.Translation.PTC { RelocEntry[] relocEntries = GetRelocEntries(relocsReader, infoEntry.RelocEntriesCount); - if (!PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter)) - { - SkipUnwindInfo(unwindInfosReader); - - continue; - } + PatchCode(code, relocEntries, memory.PageTablePointer, jumpTable, countTable, out callCounter); } UnwindInfo unwindInfo = ReadUnwindInfo(unwindInfosReader); @@ -683,7 +678,7 @@ namespace ARMeilleure.Translation.PTC return relocEntries; } - private static bool PatchCode( + private static void PatchCode( Span code, RelocEntry[] relocEntries, IntPtr pageTablePointer, @@ -711,13 +706,9 @@ namespace ARMeilleure.Translation.PTC } else if (relocEntry.Index == CountTableIndex) { - // If we could not allocate an entry on the count table we dip. - if (!Counter.TryCreate(countTable, out Counter counter)) - { - return false; - } + callCounter = new Counter(countTable); - unsafe { imm = (ulong)Unsafe.AsPointer(ref counter.Value); } + unsafe { imm = (ulong)Unsafe.AsPointer(ref callCounter.Value); } } else if (Delegates.TryGetDelegateFuncPtrByIndex(relocEntry.Index, out IntPtr funcPtr)) { @@ -730,8 +721,6 @@ namespace ARMeilleure.Translation.PTC BinaryPrimitives.WriteUInt64LittleEndian(code.Slice(relocEntry.Position, 8), imm); } - - return true; } private static UnwindInfo ReadUnwindInfo(BinaryReader unwindInfosReader) diff --git a/ARMeilleure/Translation/Translator.cs b/ARMeilleure/Translation/Translator.cs index a8635c545..c5ccb89df 100644 --- a/ARMeilleure/Translation/Translator.cs +++ b/ARMeilleure/Translation/Translator.cs @@ -57,7 +57,7 @@ namespace ARMeilleure.Translation _backgroundTranslatorEvent = new AutoResetEvent(false); _backgroundTranslatorLock = new ReaderWriterLock(); - CountTable = new EntryTable(CountTableCapacity); + CountTable = new EntryTable(); JitCache.Initialize(allocator); @@ -174,6 +174,8 @@ namespace ARMeilleure.Translation _jumpTable.Dispose(); _jumpTable = null; + CountTable.Dispose(); + GCSettings.LargeObjectHeapCompactionMode = GCLargeObjectHeapCompactionMode.CompactOnce; } } @@ -393,14 +395,11 @@ namespace ARMeilleure.Translation return context.GetControlFlowGraph(); } - internal static void EmitRejitCheck(ArmEmitterContext context, out Counter counter) + internal static Counter EmitRejitCheck(ArmEmitterContext context, out Counter counter) { const int MinsCallForRejit = 100; - if (!Counter.TryCreate(context.CountTable, out counter)) - { - return; - } + counter = new Counter(context.CountTable); Operand lblEnd = Label(); @@ -413,6 +412,8 @@ namespace ARMeilleure.Translation context.Call(typeof(NativeInterface).GetMethod(nameof(NativeInterface.EnqueueForRejit)), Const(context.EntryAddress)); context.MarkLabel(lblEnd); + + return counter; } internal static void EmitSynchronization(EmitterContext context)