From 5c67efd291ef474bc62bbd35d168f81b4d923a44 Mon Sep 17 00:00:00 2001 From: VocalFan <45863583+FluffyOMC@users.noreply.github.com> Date: Mon, 3 Feb 2025 03:55:38 -0500 Subject: [PATCH] MacOS gets it too :3 --- .../LightningJit/Cache/JitCache.cs | 94 ++++++++++++------- 1 file changed, 61 insertions(+), 33 deletions(-) diff --git a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs index 5849401ab..f64de9ba1 100644 --- a/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs +++ b/src/Ryujinx.Cpu/LightningJit/Cache/JitCache.cs @@ -1,4 +1,6 @@ using ARMeilleure.Memory; +using Humanizer; +using Ryujinx.Common.Logging; using Ryujinx.Memory; using System; using System.Collections.Generic; @@ -15,9 +17,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache private static readonly int _pageMask = _pageSize - 1; private const int CodeAlignment = 4; // Bytes. - private const int CacheSize = 2047 * 1024 * 1024; + private const int CacheSize = 128 * 1024 * 1024; - private static ReservedRegion _jitRegion; private static JitCacheInvalidation _jitCacheInvalidator; private static CacheMemoryAllocator _cacheAllocator; @@ -26,6 +27,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache private static readonly Lock _lock = new(); private static bool _initialized; + private static readonly List _jitRegions = new(); + private static int _activeRegionIndex = 0; [SupportedOSPlatform("windows")] [LibraryImport("kernel32.dll", SetLastError = true)] @@ -45,7 +48,9 @@ namespace Ryujinx.Cpu.LightningJit.Cache return; } - _jitRegion = new ReservedRegion(allocator, CacheSize); + var firstRegion = new ReservedRegion(allocator, CacheSize); + _jitRegions.Add(firstRegion); + _activeRegionIndex = 0; if (!OperatingSystem.IsWindows() && !OperatingSystem.IsMacOS()) { @@ -65,8 +70,8 @@ namespace Ryujinx.Cpu.LightningJit.Cache Debug.Assert(_initialized); int funcOffset = Allocate(code.Length); - - nint funcPtr = _jitRegion.Pointer + funcOffset; + ReservedRegion targetRegion = _jitRegions[_activeRegionIndex]; + nint funcPtr = targetRegion.Pointer + funcOffset; if (OperatingSystem.IsMacOS() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) { @@ -80,18 +85,11 @@ namespace Ryujinx.Cpu.LightningJit.Cache } else { - ReprotectAsWritable(funcOffset, code.Length); - code.CopyTo(new Span((void*)funcPtr, code.Length)); - ReprotectAsExecutable(funcOffset, code.Length); + ReprotectAsWritable(targetRegion, funcOffset, code.Length); + Marshal.Copy(code.ToArray(), 0, funcPtr, code.Length); + ReprotectAsExecutable(targetRegion, funcOffset, code.Length); - if (OperatingSystem.IsWindows() && RuntimeInformation.ProcessArchitecture == Architecture.Arm64) - { - FlushInstructionCache(Process.GetCurrentProcess().Handle, funcPtr, (nuint)code.Length); - } - else - { - _jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length); - } + _jitCacheInvalidator?.Invalidate(funcPtr, (ulong)code.Length); } Add(funcOffset, code.Length); @@ -106,50 +104,80 @@ namespace Ryujinx.Cpu.LightningJit.Cache { Debug.Assert(_initialized); - int funcOffset = (int)(pointer.ToInt64() - _jitRegion.Pointer.ToInt64()); - - if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset) + foreach (var region in _jitRegions) { - _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); - _cacheEntries.RemoveAt(entryIndex); + if (pointer.ToInt64() < region.Pointer.ToInt64() || + pointer.ToInt64() >= (region.Pointer + CacheSize).ToInt64()) + { + continue; + } + + int funcOffset = (int)(pointer.ToInt64() - region.Pointer.ToInt64()); + + if (TryFind(funcOffset, out CacheEntry entry, out int entryIndex) && entry.Offset == funcOffset) + { + _cacheAllocator.Free(funcOffset, AlignCodeSize(entry.Size)); + _cacheEntries.RemoveAt(entryIndex); + } + + return; } } } - private static void ReprotectAsWritable(int offset, int size) + private static void ReprotectAsWritable(ReservedRegion region, int offset, int size) { int endOffs = offset + size; - int regionStart = offset & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask; - _jitRegion.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); + region.Block.MapAsRwx((ulong)regionStart, (ulong)(regionEnd - regionStart)); } - private static void ReprotectAsExecutable(int offset, int size) + private static void ReprotectAsExecutable(ReservedRegion region, int offset, int size) { int endOffs = offset + size; - int regionStart = offset & ~_pageMask; int regionEnd = (endOffs + _pageMask) & ~_pageMask; - _jitRegion.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); + region.Block.MapAsRx((ulong)regionStart, (ulong)(regionEnd - regionStart)); } private static int Allocate(int codeSize) { codeSize = AlignCodeSize(codeSize); - int allocOffset = _cacheAllocator.Allocate(codeSize); - - if (allocOffset < 0) + for (int i = _activeRegionIndex; i < _jitRegions.Count; i++) { - throw new OutOfMemoryException("JIT Cache exhausted."); + int allocOffset = _cacheAllocator.Allocate(codeSize); + + if (allocOffset >= 0) + { + _jitRegions[i].ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + _activeRegionIndex = i; + return allocOffset; + } } - _jitRegion.ExpandIfNeeded((ulong)allocOffset + (ulong)codeSize); + int exhaustedRegion = _activeRegionIndex; + var newRegion = new ReservedRegion(_jitRegions[0].Allocator, CacheSize); + _jitRegions.Add(newRegion); + _activeRegionIndex = _jitRegions.Count - 1; + + int newRegionNumber = _activeRegionIndex; - return allocOffset; + Logger.Warning?.Print(LogClass.Cpu, $"JIT Cache Region {exhaustedRegion} exhausted, creating new Cache Region {newRegionNumber} ({((newRegionNumber + 1) * CacheSize).Bytes()} Total Allocation)."); + + _cacheAllocator = new CacheMemoryAllocator(CacheSize); + + int allocOffsetNew = _cacheAllocator.Allocate(codeSize); + if (allocOffsetNew < 0) + { + throw new OutOfMemoryException("Failed to allocate in new Cache Region!"); + } + + newRegion.ExpandIfNeeded((ulong)allocOffsetNew + (ulong)codeSize); + return allocOffsetNew; } private static int AlignCodeSize(int codeSize)