mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-01-09 12:19:12 +00:00
RO optimization.
Allows the same dynamic module (NRO) to always be remapped to the same base address, so that the Translator can reuse the same dynamic functions in it, without having to retranslate them and thus without having to add them back into the Jit Cache.
This commit is contained in:
parent
fbcf802fbc
commit
733b6b340b
12 changed files with 155 additions and 56 deletions
|
@ -10,7 +10,7 @@ namespace ARMeilleure.Memory
|
|||
|
||||
MemoryManagerType Type { get; }
|
||||
|
||||
event Action<ulong, ulong> UnmapEvent;
|
||||
event Action<ulong, ulong, bool> UnmapEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from CPU mapped memory.
|
||||
|
|
|
@ -453,15 +453,23 @@ namespace ARMeilleure.Translation
|
|||
context.MarkLabel(lblExit);
|
||||
}
|
||||
|
||||
public void InvalidateJitCacheRegion(ulong address, ulong size)
|
||||
public void InvalidateJitCacheRegion(ulong address, ulong size, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
||||
ClearRejitQueue(allowRequeue: true);
|
||||
|
||||
ulong[] overlapAddresses = Array.Empty<ulong>();
|
||||
|
||||
int overlapsCount = Functions.GetOverlaps(address, size, ref overlapAddresses);
|
||||
|
||||
if (overlapsCount != 0)
|
||||
{
|
||||
// If rejit is running, stop it as it may be trying to rejit a function on the invalidated region.
|
||||
ClearRejitQueue(allowRequeue: true);
|
||||
}
|
||||
|
||||
if (clearRejitQueueOnly)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (int index = 0; index < overlapsCount; index++)
|
||||
{
|
||||
ulong overlapAddress = overlapAddresses[index];
|
||||
|
|
|
@ -15,9 +15,9 @@ namespace Ryujinx.Cpu.Jit
|
|||
memory.UnmapEvent += UnmapHandler;
|
||||
}
|
||||
|
||||
private void UnmapHandler(ulong address, ulong size)
|
||||
private void UnmapHandler(ulong address, ulong size, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
_translator.InvalidateJitCacheRegion(address, size);
|
||||
_translator.InvalidateJitCacheRegion(address, size, clearRejitQueueOnly);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace Ryujinx.Cpu.Jit
|
|||
|
||||
public MemoryTracking Tracking { get; }
|
||||
|
||||
public event Action<ulong, ulong> UnmapEvent;
|
||||
public event Action<ulong, ulong, bool> UnmapEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the memory manager.
|
||||
|
@ -95,7 +95,7 @@ namespace Ryujinx.Cpu.Jit
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Unmap(ulong va, ulong size)
|
||||
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
// If size is 0, there's nothing to unmap, just exit early.
|
||||
if (size == 0)
|
||||
|
@ -105,7 +105,7 @@ namespace Ryujinx.Cpu.Jit
|
|||
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
UnmapEvent?.Invoke(va, size);
|
||||
UnmapEvent?.Invoke(va, size, clearRejitQueueOnly);
|
||||
Tracking.Unmap(va, size);
|
||||
|
||||
ulong remainingSize = size;
|
||||
|
|
|
@ -56,7 +56,7 @@ namespace Ryujinx.Cpu.Jit
|
|||
|
||||
public MemoryTracking Tracking { get; }
|
||||
|
||||
public event Action<ulong, ulong> UnmapEvent;
|
||||
public event Action<ulong, ulong, bool> UnmapEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the host mapped memory manager.
|
||||
|
@ -158,11 +158,11 @@ namespace Ryujinx.Cpu.Jit
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Unmap(ulong va, ulong size)
|
||||
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
UnmapEvent?.Invoke(va, size);
|
||||
UnmapEvent?.Invoke(va, size, clearRejitQueueOnly);
|
||||
Tracking.Unmap(va, size);
|
||||
|
||||
RemoveMapping(va, size);
|
||||
|
|
|
@ -140,7 +140,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override KernelResult Unmap(ulong address, ulong pagesCount)
|
||||
protected override KernelResult Unmap(ulong address, ulong pagesCount, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
KPageList pagesToClose = new KPageList();
|
||||
|
||||
|
@ -155,7 +155,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
}
|
||||
}
|
||||
|
||||
_cpuMemory.Unmap(address, pagesCount * PageSize);
|
||||
_cpuMemory.Unmap(address, pagesCount * PageSize, clearRejitQueueOnly);
|
||||
|
||||
pagesToClose.DecrementPagesReferenceCount(Context.MemoryManager);
|
||||
|
||||
|
|
|
@ -610,7 +610,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
}
|
||||
}
|
||||
|
||||
public KernelResult UnmapProcessCodeMemory(ulong dst, ulong src, ulong size)
|
||||
public KernelResult UnmapProcessCodeMemory(ulong dst, ulong src, ulong size, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
lock (_blockManager)
|
||||
{
|
||||
|
@ -656,7 +656,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
{
|
||||
ulong pagesCount = size / PageSize;
|
||||
|
||||
KernelResult result = Unmap(dst, pagesCount);
|
||||
KernelResult result = Unmap(dst, pagesCount, clearRejitQueueOnly);
|
||||
|
||||
if (result != KernelResult.Success)
|
||||
{
|
||||
|
@ -2957,7 +2957,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Memory
|
|||
/// <param name="address">Virtual address of the region to unmap</param>
|
||||
/// <param name="pagesCount">Number of pages to unmap</param>
|
||||
/// <returns>Result of the unmapping operation</returns>
|
||||
protected abstract KernelResult Unmap(ulong address, ulong pagesCount);
|
||||
protected abstract KernelResult Unmap(ulong address, ulong pagesCount, bool clearRejitQueueOnly = false);
|
||||
|
||||
/// <summary>
|
||||
/// Changes the permissions of a given virtual memory region.
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
[Service("ro:1")] // 7.0.0+
|
||||
class IRoInterface : DisposableIpcService
|
||||
{
|
||||
private const bool EnableOpt = true;
|
||||
|
||||
private const int MaxNrr = 0x40;
|
||||
private const int MaxNro = 0x40;
|
||||
private const int MaxMapRetries = 0x200;
|
||||
|
@ -28,18 +30,21 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
|
||||
private List<NrrInfo> _nrrInfos;
|
||||
private List<NroInfo> _nroInfos;
|
||||
private List<NroInfo> _nroInfosUnloaded;
|
||||
|
||||
private KProcess _owner;
|
||||
private IVirtualMemoryManager _ownerMm;
|
||||
|
||||
private static Random _random = new Random();
|
||||
private Random _random;
|
||||
|
||||
public IRoInterface(ServiceCtx context)
|
||||
{
|
||||
_nrrInfos = new List<NrrInfo>(MaxNrr);
|
||||
_nroInfos = new List<NroInfo>(MaxNro);
|
||||
_owner = null;
|
||||
_ownerMm = null;
|
||||
_nroInfosUnloaded = new List<NroInfo>(MaxNro);
|
||||
_owner = null;
|
||||
_ownerMm = null;
|
||||
_random = new Random();
|
||||
}
|
||||
|
||||
private ResultCode ParseNrr(out NrrInfo nrrInfo, ServiceCtx context, ulong nrrAddress, ulong nrrSize)
|
||||
|
@ -205,17 +210,18 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
private ResultCode MapNro(KProcess process, NroInfo info, out ulong nroMappedAddress)
|
||||
private ResultCode MapNro(KProcess process, NroInfo info, MapType mapType, out ulong nroMappedAddress)
|
||||
{
|
||||
KPageTableBase memMgr = process.MemoryManager;
|
||||
|
||||
int retryCount = 0;
|
||||
int maxMapRetries = mapType != MapType.Within ? MaxMapRetries : 1;
|
||||
|
||||
nroMappedAddress = 0;
|
||||
|
||||
while (retryCount++ < MaxMapRetries)
|
||||
while (retryCount++ < maxMapRetries)
|
||||
{
|
||||
ResultCode result = MapCodeMemoryInProcess(process, info.NroAddress, info.NroSize, out nroMappedAddress);
|
||||
ResultCode result = MapCodeMemoryInProcess(process, info, mapType, out nroMappedAddress);
|
||||
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
|
@ -269,31 +275,73 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
return false;
|
||||
}
|
||||
|
||||
private ResultCode MapCodeMemoryInProcess(KProcess process, ulong baseAddress, ulong size, out ulong targetAddress)
|
||||
private static bool NotOverlapsWith(ulong newAddress, ulong newSize, ulong oldAddress, ulong oldSize)
|
||||
{
|
||||
return newAddress + newSize <= oldAddress || oldAddress + oldSize <= newAddress;
|
||||
}
|
||||
|
||||
private ResultCode MapCodeMemoryInProcess(KProcess process, NroInfo info, MapType mapType, out ulong targetAddress)
|
||||
{
|
||||
KPageTableBase memMgr = process.MemoryManager;
|
||||
|
||||
int retryCount = 0;
|
||||
int maxMapRetries = mapType != MapType.Within ? MaxMapRetries : 1;
|
||||
|
||||
targetAddress = 0;
|
||||
|
||||
int retryCount;
|
||||
|
||||
ulong addressSpacePageLimit = (memMgr.GetAddrSpaceSize() - size) >> 12;
|
||||
|
||||
for (retryCount = 0; retryCount < MaxMapRetries; retryCount++)
|
||||
while (retryCount++ < maxMapRetries)
|
||||
{
|
||||
while (true)
|
||||
if (mapType == MapType.Default)
|
||||
{
|
||||
ulong randomOffset = (ulong)(uint)_random.Next(0, (int)addressSpacePageLimit) << 12;
|
||||
|
||||
targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset;
|
||||
|
||||
if (memMgr.InsideAddrSpace(targetAddress, size) && !memMgr.InsideHeapRegion(targetAddress, size) && !memMgr.InsideAliasRegion(targetAddress, size))
|
||||
while (true)
|
||||
{
|
||||
break;
|
||||
ulong addressSpacePageLimit = (memMgr.GetAddrSpaceSize() - info.NroSize) >> 12;
|
||||
ulong randomOffset = (ulong)(uint)_random.Next(0, (int)addressSpacePageLimit) << 12;
|
||||
|
||||
targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset;
|
||||
|
||||
if (memMgr.InsideAddrSpace(targetAddress, info.NroSize) &&
|
||||
!memMgr.InsideHeapRegion(targetAddress, info.NroSize) &&
|
||||
!memMgr.InsideAliasRegion(targetAddress, info.NroSize))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (mapType == MapType.Between)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
ulong addressSpacePageLimit = (memMgr.GetAddrSpaceSize() - info.NroSize) >> 12;
|
||||
ulong randomOffset = (ulong)(uint)_random.Next(0, (int)addressSpacePageLimit) << 12;
|
||||
|
||||
KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, baseAddress, size);
|
||||
targetAddress = memMgr.GetAddrSpaceBaseAddr() + randomOffset;
|
||||
|
||||
if (memMgr.InsideAddrSpace(targetAddress, info.NroSize) &&
|
||||
!memMgr.InsideHeapRegion(targetAddress, info.NroSize) &&
|
||||
!memMgr.InsideAliasRegion(targetAddress, info.NroSize))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ulong targetAddressTmp = targetAddress; // CS1628.
|
||||
if (_nroInfosUnloaded.TrueForAll((infoU) => NotOverlapsWith(targetAddressTmp, info.NroSize + info.BssSize, infoU.NroMappedAddress, infoU.NroSize + infoU.BssSize)))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else /* if (mapType == MapType.Within) */
|
||||
{
|
||||
NroInfo infoU = _nroInfosUnloaded.Find((infoU) => infoU.Hash.SequenceEqual(info.Hash));
|
||||
|
||||
targetAddress = infoU.NroMappedAddress;
|
||||
}
|
||||
|
||||
KernelResult result = memMgr.MapProcessCodeMemory(targetAddress, info.NroAddress, info.NroSize);
|
||||
|
||||
if (result == KernelResult.InvalidMemState)
|
||||
{
|
||||
|
@ -304,7 +352,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
return (ResultCode)result;
|
||||
}
|
||||
|
||||
if (!CanAddGuardRegionsInProcess(process, targetAddress, size))
|
||||
if (!CanAddGuardRegionsInProcess(process, targetAddress, info.NroSize))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
@ -312,12 +360,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
return ResultCode.Success;
|
||||
}
|
||||
|
||||
if (retryCount == MaxMapRetries)
|
||||
{
|
||||
return ResultCode.InsufficientAddressSpace;
|
||||
}
|
||||
|
||||
return ResultCode.Success;
|
||||
return ResultCode.InsufficientAddressSpace;
|
||||
}
|
||||
|
||||
private KernelResult SetNroMemoryPermissions(KProcess process, IExecutable relocatableObject, ulong baseAddress)
|
||||
|
@ -376,16 +419,20 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
{
|
||||
if (info.NroMappedAddress == nroMappedAddress)
|
||||
{
|
||||
if (EnableOpt)
|
||||
{
|
||||
_nroInfosUnloaded.Add(info);
|
||||
}
|
||||
_nroInfos.Remove(info);
|
||||
|
||||
return UnmapNroFromInfo(info);
|
||||
return UnmapNroFromInfo(info, clearRejitQueueOnly: EnableOpt);
|
||||
}
|
||||
}
|
||||
|
||||
return ResultCode.NotLoaded;
|
||||
}
|
||||
|
||||
private ResultCode UnmapNroFromInfo(NroInfo info)
|
||||
private ResultCode UnmapNroFromInfo(NroInfo info, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
ulong textSize = (ulong)info.Executable.Text.Length;
|
||||
ulong roSize = (ulong)info.Executable.Ro.Length;
|
||||
|
@ -399,7 +446,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
result = _owner.MemoryManager.UnmapProcessCodeMemory(
|
||||
info.NroMappedAddress + textSize + roSize + dataSize,
|
||||
info.Executable.BssAddress,
|
||||
bssSize);
|
||||
bssSize, clearRejitQueueOnly);
|
||||
}
|
||||
|
||||
if (result == KernelResult.Success)
|
||||
|
@ -407,14 +454,14 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
result = _owner.MemoryManager.UnmapProcessCodeMemory(
|
||||
info.NroMappedAddress + textSize + roSize,
|
||||
info.Executable.SourceAddress + textSize + roSize,
|
||||
dataSize);
|
||||
dataSize, clearRejitQueueOnly);
|
||||
|
||||
if (result == KernelResult.Success)
|
||||
{
|
||||
result = _owner.MemoryManager.UnmapProcessCodeMemory(
|
||||
info.NroMappedAddress,
|
||||
info.Executable.SourceAddress,
|
||||
textSize + roSize);
|
||||
textSize + roSize, clearRejitQueueOnly);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -431,6 +478,8 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
return ResultCode.InvalidProcess;
|
||||
}
|
||||
|
||||
private enum MapType { Default, Between, Within }
|
||||
|
||||
[CommandHipc(0)]
|
||||
// LoadNro(u64, u64, u64, u64, u64, pid) -> u64
|
||||
public ResultCode LoadNro(ServiceCtx context)
|
||||
|
@ -455,7 +504,48 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
result = MapNro(_owner, info, out nroMappedAddress);
|
||||
MapType mapType = MapType.Default;
|
||||
|
||||
if (_nroInfosUnloaded.Count != 0)
|
||||
{
|
||||
mapType = _nroInfosUnloaded.Exists((infoU) => infoU.Hash.SequenceEqual(info.Hash)) // One/zero match.
|
||||
? MapType.Within
|
||||
: MapType.Between;
|
||||
}
|
||||
|
||||
result = MapNro(_owner, info, mapType, out nroMappedAddress);
|
||||
|
||||
if (mapType == MapType.Between)
|
||||
{
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
_nroInfosUnloaded.Clear();
|
||||
|
||||
mapType = MapType.Default;
|
||||
|
||||
result = MapNro(_owner, info, mapType, out nroMappedAddress);
|
||||
}
|
||||
}
|
||||
else if (mapType == MapType.Within)
|
||||
{
|
||||
_nroInfosUnloaded.RemoveAll((infoU) => infoU.Hash.SequenceEqual(info.Hash)); // One match.
|
||||
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
mapType = MapType.Between;
|
||||
|
||||
result = MapNro(_owner, info, mapType, out nroMappedAddress);
|
||||
|
||||
if (result != ResultCode.Success)
|
||||
{
|
||||
_nroInfosUnloaded.Clear();
|
||||
|
||||
mapType = MapType.Default;
|
||||
|
||||
result = MapNro(_owner, info, mapType, out nroMappedAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == ResultCode.Success)
|
||||
{
|
||||
|
@ -598,6 +688,7 @@ namespace Ryujinx.HLE.HOS.Services.Ro
|
|||
}
|
||||
|
||||
_nroInfos.Clear();
|
||||
_nroInfosUnloaded.Clear();
|
||||
|
||||
if (_ownerMm is IRefCounted rc)
|
||||
{
|
||||
|
|
|
@ -65,7 +65,7 @@ namespace Ryujinx.Memory
|
|||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Unmap(ulong va, ulong size)
|
||||
public void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
AssertValidAddressAndSize(va, size);
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace Ryujinx.Memory
|
|||
/// </summary>
|
||||
/// <param name="va">Virtual address of the range to be unmapped</param>
|
||||
/// <param name="size">Size of the range to be unmapped</param>
|
||||
void Unmap(ulong va, ulong size);
|
||||
void Unmap(ulong va, ulong size, bool clearRejitQueueOnly = false);
|
||||
|
||||
/// <summary>
|
||||
/// Reads data from CPU mapped memory.
|
||||
|
|
|
@ -16,9 +16,9 @@ namespace Ryujinx.Tests.Cpu
|
|||
memory.UnmapEvent += UnmapHandler;
|
||||
}
|
||||
|
||||
private void UnmapHandler(ulong address, ulong size)
|
||||
private void UnmapHandler(ulong address, ulong size, bool clearRejitQueueOnly = false)
|
||||
{
|
||||
_translator.InvalidateJitCacheRegion(address, size);
|
||||
_translator.InvalidateJitCacheRegion(address, size, clearRejitQueueOnly);
|
||||
}
|
||||
|
||||
public static ExecutionContext CreateExecutionContext()
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Ryujinx.Tests.Memory
|
|||
public MemoryManagerType Type => MemoryManagerType.HostMappedUnsafe;
|
||||
|
||||
#pragma warning disable CS0067
|
||||
public event Action<ulong, ulong> UnmapEvent;
|
||||
public event Action<ulong, ulong, bool> UnmapEvent;
|
||||
#pragma warning restore CS0067
|
||||
|
||||
public ref T GetRef<T>(ulong va) where T : unmanaged
|
||||
|
|
Loading…
Reference in a new issue