mono-jit-v2 (#173)

second attempt at single layer address tables (first attempt https://git.ryujinx.app/projects/Ryubing/pulls/167).
should work with all games now.

Co-authored-by: LotP1 <68976644+LotP1@users.noreply.github.com>
Reviewed-on: https://git.ryujinx.app/projects/Ryubing/pulls/173
This commit is contained in:
LotP
2026-07-17 12:57:07 +00:00
co-authored by LotP1
parent deb5dca420
commit a82350bb77
23 changed files with 401 additions and 407 deletions
+8 -32
View File
@@ -20,45 +20,21 @@ namespace ARMeilleure.Common
new( 1, 6) new( 1, 6)
]; ];
private static readonly AddressTableLevel[] _levels64BitSparseTiny = private static readonly AddressTableLevel[] _levels64BitMono =
[ [
new( 11, 28), new( 2, 37)
new( 2, 9)
]; ];
private static readonly AddressTableLevel[] _levels32BitSparseTiny = private static readonly AddressTableLevel[] _levels32BitMono =
[ [
new( 10, 22), new( 1, 31)
new( 1, 9)
]; ];
private static readonly AddressTableLevel[] _levels64BitSparseGiant = public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool mono)
[
new( 38, 1),
new( 2, 36)
];
private static readonly AddressTableLevel[] _levels32BitSparseGiant =
[
new( 31, 1),
new( 1, 30)
];
//high power will run worse on DDR3 systems and some DDR4 systems due to the higher ram utilization
//low power will never run worse than non-sparse, but for most systems it won't be necessary
//high power is always used, but I've left low power in here for future reference
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse, bool lowPower = false)
{ {
if (sparse) if (mono)
{ {
if (lowPower) return for64Bits ? _levels64BitMono : _levels32BitMono;
{
return for64Bits ? _levels64BitSparseTiny : _levels32BitSparseTiny;
}
else
{
return for64Bits ? _levels64BitSparseGiant : _levels32BitSparseGiant;
}
} }
else else
{ {
@@ -0,0 +1,8 @@
namespace ARMeilleure.Common
{
public enum AddressTableType
{
Default,
Sparse
}
}
+9
View File
@@ -53,5 +53,14 @@ namespace ARMeilleure.Common
{ {
return (bits >> shift) | (bits << (size - shift)); return (bits >> shift) | (bits << (size - shift));
} }
public static T AlignUp<T>(T value, T size) where T : IBinaryInteger<T>
=> (value + (size - T.One)) & -size;
public static T AlignDown<T>(T value, T size) where T : IBinaryInteger<T>
=> value & -size;
public static T DivRoundUp<T>(T value, T dividend) where T : IBinaryInteger<T>
=> (value + (dividend - T.One)) / dividend;
} }
} }
+10 -4
View File
@@ -5,11 +5,10 @@ namespace ARMeilleure.Common
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
{ {
/// <summary> /// <summary>
/// True if the address table's bottom level is sparsely mapped. /// Gets the <see cref="AddressTableType"/> of the <see cref="IAddressTable{TEntry}"/> instance.
/// This also ensures the second bottom level is filled with a dummy page rather than 0.
/// </summary> /// </summary>
bool Sparse { get; } AddressTableType TableType { get; }
/// <summary> /// <summary>
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance. /// Gets the bits used by the <see cref="Levels"/> of the <see cref="IAddressTable{TEntry}"/> instance.
/// </summary> /// </summary>
@@ -31,6 +30,13 @@ namespace ARMeilleure.Common
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception> /// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
nint Base { get; } nint Base { get; }
/// <summary>
/// Signal that the given code range exists.
/// </summary>
/// <param name="address">Guest code range address</param>
/// <param name="size">Guest code range size</param>
void SignalCodeRange(ulong address, ulong size);
/// <summary> /// <summary>
/// Determines if the specified <paramref name="address"/> is in the range of the /// Determines if the specified <paramref name="address"/> is in the range of the
/// <see cref="IAddressTable{TEntry}"/>. /// <see cref="IAddressTable{TEntry}"/>.
@@ -5,7 +5,7 @@ using ARMeilleure.IntermediateRepresentation;
using ARMeilleure.State; using ARMeilleure.State;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using ARMeilleure.Translation.PTC; using ARMeilleure.Translation.PTC;
using System.Linq;
using static ARMeilleure.Instructions.InstEmitHelper; using static ARMeilleure.Instructions.InstEmitHelper;
using static ARMeilleure.IntermediateRepresentation.Operand.Factory; using static ARMeilleure.IntermediateRepresentation.Operand.Factory;
@@ -236,9 +236,9 @@ namespace ARMeilleure.Instructions
hostAddress = context.Load(OperandType.I64, hostAddressAddr); hostAddress = context.Load(OperandType.I64, hostAddressAddr);
} }
else if (table.Sparse) else if (table.TableType == AddressTableType.Sparse)
{ {
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels. // Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
// Deliberately attempts to avoid branches. // Deliberately attempts to avoid branches.
Operand tableBase = !context.HasPtc ? Operand tableBase = !context.HasPtc ?
@@ -247,24 +247,21 @@ namespace ARMeilleure.Instructions
hostAddress = tableBase; hostAddress = tableBase;
for (int i = 0; i < table.Levels.Length; i++) AddressTableLevel level = table.Levels.Last();
{ int clearBits = 64 - (level.Index + level.Length);
AddressTableLevel level = table.Levels[i];
int clearBits = 64 - (level.Index + level.Length);
Operand index = context.ShiftLeft( Operand index = context.ShiftLeft(
context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)), context.ShiftRightUI(context.ShiftLeft(guestAddress, Const(clearBits)), Const(clearBits + level.Index)),
Const(3) Const(3)
); );
hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index)); hostAddress = context.Load(OperandType.I64, context.Add(hostAddress, index));
}
} }
else else
{ {
hostAddress = !context.HasPtc ? hostAddress = !context.HasPtc ?
Const((long)context.Stubs.DispatchStub) : Const(context.Stubs.DispatchStub) :
Const((long)context.Stubs.DispatchStub, Ptc.DispatchStubSymbol); Const(context.Stubs.DispatchStub, Ptc.DispatchStubSymbol);
} }
if (isJump) if (isJump)
+1 -1
View File
@@ -34,7 +34,7 @@ namespace ARMeilleure.Translation.PTC
private const string OuterHeaderMagicString = "PTCohd\0\0"; private const string OuterHeaderMagicString = "PTCohd\0\0";
private const string InnerHeaderMagicString = "PTCihd\0\0"; private const string InnerHeaderMagicString = "PTCihd\0\0";
private const uint InternalVersion = 7019; //! To be incremented manually for each change to the ARMeilleure project. private const uint InternalVersion = 7020; //! To be incremented manually for each change to the ARMeilleure project.
private const string ActualDir = "0"; private const string ActualDir = "0";
private const string BackupDir = "1"; private const string BackupDir = "1";
+3 -1
View File
@@ -28,7 +28,7 @@ namespace ARMeilleure.Translation
private readonly Ptc _ptc; private readonly Ptc _ptc;
internal TranslatorCache<TranslatedFunction> Functions { get; } internal TranslatorCache<TranslatedFunction> Functions { get; }
internal IAddressTable<ulong> FunctionTable { get; } public IAddressTable<ulong> FunctionTable { get; }
internal EntryTable<uint> CountTable { get; } internal EntryTable<uint> CountTable { get; }
internal TranslatorStubs Stubs { get; } internal TranslatorStubs Stubs { get; }
internal TranslatorQueue Queue { get; } internal TranslatorQueue Queue { get; }
@@ -53,7 +53,9 @@ namespace ARMeilleure.Translation
CountTable = new EntryTable<uint>(); CountTable = new EntryTable<uint>();
Functions = new TranslatorCache<TranslatedFunction>(); Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = functionTable; FunctionTable = functionTable;
Stubs = new TranslatorStubs(JitCache, FunctionTable); Stubs = new TranslatorStubs(JitCache, FunctionTable);
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
+33 -294
View File
@@ -1,15 +1,9 @@
using ARMeilleure.Memory; using ARMeilleure.Common;
using Ryujinx.Common;
using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Threading;
using static Ryujinx.Cpu.MemoryEhMeilleure;
namespace ARMeilleure.Common namespace Ryujinx.Cpu
{ {
/// <summary> /// <summary>
/// Represents a table of guest address to a value. /// Represents a table of guest address to a value.
@@ -17,84 +11,12 @@ namespace ARMeilleure.Common
/// <typeparam name="TEntry">Type of the value</typeparam> /// <typeparam name="TEntry">Type of the value</typeparam>
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
{ {
/// <summary>
/// Represents a page of the address table.
/// </summary>
private readonly struct AddressTablePage
{
/// <summary>
/// True if the allocation belongs to a sparse block, false otherwise.
/// </summary>
public readonly bool IsSparse;
/// <summary>
/// Base address for the page.
/// </summary>
public readonly nint Address;
public AddressTablePage(bool isSparse, nint address)
{
IsSparse = isSparse;
Address = address;
}
}
/// <summary>
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
/// </summary>
private readonly struct TableSparseBlock : IDisposable
{
public readonly SparseMemoryBlock Block;
private readonly TrackingEventDelegate _trackingEvent;
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit)
{
SparseMemoryBlock block = new(size, pageInit, null);
_trackingEvent = (address, size, write) =>
{
ulong pointer = (ulong)block.Block.Pointer + address;
ensureMapped((nint)pointer);
return pointer;
};
bool added = NativeSignalHandler.AddTrackedRegion(
(nuint)block.Block.Pointer,
(nuint)(block.Block.Pointer + (nint)block.Block.Size),
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
if (!added)
{
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
}
Block = block;
}
public void Dispose()
{
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
Block.Dispose();
}
}
private bool _disposed; private bool _disposed;
private TEntry** _table; private TEntry** _table;
private readonly List<AddressTablePage> _pages; private readonly List<nint> _pages;
private TEntry _fill;
private readonly MemoryBlock _sparseFill; /// <inheritdoc/>
private readonly SparseMemoryBlock _fillBottomLevel; public AddressTableType TableType => AddressTableType.Default;
private readonly TEntry* _fillBottomLevelPtr;
private readonly List<TableSparseBlock> _sparseReserved;
private readonly ReaderWriterLockSlim _sparseLock;
private ulong _sparseBlockSize;
private ulong _sparseReservedOffset;
public bool Sparse { get; }
/// <inheritdoc/> /// <inheritdoc/>
public ulong Mask { get; } public ulong Mask { get; }
@@ -103,17 +25,7 @@ namespace ARMeilleure.Common
public AddressTableLevel[] Levels { get; } public AddressTableLevel[] Levels { get; }
/// <inheritdoc/> /// <inheritdoc/>
public TEntry Fill public TEntry Fill { get; set; }
{
get
{
return _fill;
}
set
{
UpdateFill(value);
}
}
/// <inheritdoc/> /// <inheritdoc/>
public nint Base public nint Base
@@ -131,17 +43,16 @@ namespace ARMeilleure.Common
/// <summary> /// <summary>
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of /// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
/// <see cref="Level"/>. /// <see cref="AddressTableLevel"/>.
/// </summary> /// </summary>
/// <param name="levels">Levels for the address table</param> /// <param name="levels">Levels for the address table</param>
/// <param name="sparse">True if the bottom page should be sparsely mapped</param>
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception> /// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception> /// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
public AddressTable(AddressTableLevel[] levels, bool sparse) public AddressTable(AddressTableLevel[] levels)
{ {
ArgumentNullException.ThrowIfNull(levels); ArgumentNullException.ThrowIfNull(levels);
_pages = new List<AddressTablePage>(capacity: 16); _pages = new List<nint>(capacity: 16);
Levels = levels; Levels = levels;
Mask = 0; Mask = 0;
@@ -150,25 +61,6 @@ namespace ARMeilleure.Common
{ {
Mask |= level.Mask; Mask |= level.Mask;
} }
Sparse = sparse;
if (sparse)
{
// If the address table is sparse, allocate a fill block
_sparseFill = new MemoryBlock(268435456ul, MemoryAllocationFlags.Mirrorable); //low Power TC uses size: 65536ul
ulong bottomLevelSize = (1ul << levels.Last().Length) * (ulong)sizeof(TEntry);
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
_sparseReserved = [];
_sparseLock = new ReaderWriterLockSlim();
_sparseBlockSize = bottomLevelSize;
}
} }
/// <summary> /// <summary>
@@ -176,29 +68,10 @@ namespace ARMeilleure.Common
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type. /// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
/// </summary> /// </summary>
/// <param name="for64Bits">True if the guest is A64, false otherwise</param> /// <param name="for64Bits">True if the guest is A64, false otherwise</param>
/// <param name="type">Memory manager type</param>
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns> /// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
public static AddressTable<TEntry> CreateForArm(bool for64Bits, MemoryManagerType type) public static AddressTable<TEntry> CreateForArm(bool for64Bits)
{ {
// Assume software memory means that we don't want to use any signal handlers. return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, false));
bool sparse = type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
return new AddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse);
}
/// <summary>
/// Update the fill value for the bottom level of the table.
/// </summary>
/// <param name="fillValue">New fill value</param>
private void UpdateFill(TEntry fillValue)
{
if (_sparseFill != null)
{
Span<byte> span = _sparseFill.GetSpan(0, (int)_sparseFill.Size);
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
}
_fill = fillValue;
} }
/// <summary> /// <summary>
@@ -206,17 +79,7 @@ namespace ARMeilleure.Common
/// </summary> /// </summary>
/// <param name="address"></param> /// <param name="address"></param>
/// <param name="size"></param> /// <param name="size"></param>
public void SignalCodeRange(ulong address, ulong size) public void SignalCodeRange(ulong address, ulong size) { }
{
AddressTableLevel bottom = Levels.Last();
ulong bottomLevelEntries = 1ul << bottom.Length;
ulong entryIndex = address >> bottom.Index;
ulong entries = size >> bottom.Index;
entries += entryIndex - BitUtils.AlignDown(entryIndex, bottomLevelEntries);
_sparseBlockSize = Math.Max(_sparseBlockSize, BitUtils.AlignUp(entries, bottomLevelEntries) * (ulong)sizeof(TEntry));
}
/// <inheritdoc/> /// <inheritdoc/>
public bool IsValid(ulong address) public bool IsValid(ulong address)
@@ -240,8 +103,6 @@ namespace ARMeilleure.Common
long index = Levels[^1].GetValue(address); long index = Levels[^1].GetValue(address);
EnsureMapped((nint)(page + index));
return ref page[index]; return ref page[index];
} }
} }
@@ -258,19 +119,19 @@ namespace ARMeilleure.Common
for (int i = 0; i < Levels.Length - 1; i++) for (int i = 0; i < Levels.Length - 1; i++)
{ {
ref AddressTableLevel level = ref Levels[i]; ref AddressTableLevel level = ref Levels[i];
ref TEntry* nextPage = ref page[level.GetValue(address)]; ref TEntry* nextPage = ref page![level.GetValue(address)];
if (nextPage == null || nextPage == _fillBottomLevelPtr) if (nextPage == null)
{ {
ref AddressTableLevel nextLevel = ref Levels[i + 1]; ref AddressTableLevel nextLevel = ref Levels[i + 1];
if (i == Levels.Length - 2) if (i == Levels.Length - 2)
{ {
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill, leaf: true); nextPage = (TEntry*)Allocate(1 << nextLevel.Length, Fill);
} }
else else
{ {
nextPage = (TEntry*)Allocate(1 << nextLevel.Length, GetFillValue(i), leaf: false); nextPage = (TEntry*)Allocate(1 << nextLevel.Length, nint.Zero);
} }
} }
@@ -280,57 +141,6 @@ namespace ARMeilleure.Common
return (TEntry*)page; return (TEntry*)page;
} }
/// <summary>
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
/// </summary>
/// <param name="ptr">Pointer to be mapped</param>
private void EnsureMapped(nint ptr)
{
if (Sparse)
{
// Check sparse allocations to see if the pointer is in any of them.
// Ensure the page is committed if there's a match.
_sparseLock.EnterReadLock();
try
{
foreach (TableSparseBlock reserved in _sparseReserved)
{
SparseMemoryBlock sparse = reserved.Block;
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size)
{
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
break;
}
}
}
finally
{
_sparseLock.ExitReadLock();
}
}
}
/// <summary>
/// Get the fill value for a non-leaf level of the table.
/// </summary>
/// <param name="level">Level to get the fill value for</param>
/// <returns>The fill value</returns>
private nint GetFillValue(int level)
{
if (_fillBottomLevel != null && level == Levels.Length - 2)
{
return (nint)_fillBottomLevelPtr;
}
else
{
return nint.Zero;
}
}
/// <summary> /// <summary>
/// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>. /// Lazily initialize and get the root page of the <see cref="AddressTable{TEntry}"/>.
/// </summary> /// </summary>
@@ -340,91 +150,35 @@ namespace ARMeilleure.Common
if (_table == null) if (_table == null)
{ {
if (Levels.Length == 1) if (Levels.Length == 1)
_table = (TEntry**)Allocate(1 << Levels[0].Length, Fill, leaf: true); _table = (TEntry**)Allocate(1 << Levels[0].Length, Fill);
else else
_table = (TEntry**)Allocate(1 << Levels[0].Length, GetFillValue(0), leaf: false); _table = (TEntry**)Allocate(1 << Levels[0].Length, nint.Zero);
} }
return _table; return _table;
} }
/// <summary>
/// Initialize a leaf page with the fill value.
/// </summary>
/// <param name="page">Page to initialize</param>
private void InitLeafPage(Span<byte> page)
{
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
}
/// <summary>
/// Reserve a new sparse block, and add it to the list.
/// </summary>
/// <returns>The new sparse block that was added</returns>
private TableSparseBlock ReserveNewSparseBlock()
{
TableSparseBlock block = new(_sparseBlockSize, EnsureMapped, InitLeafPage);
_sparseReserved.Add(block);
_sparseReservedOffset = 0;
return block;
}
/// <summary> /// <summary>
/// Allocates a block of memory of the specified type and length. /// Allocates a block of memory of the specified type and length.
/// </summary> /// </summary>
/// <typeparam name="T">Type of elements</typeparam> /// <typeparam name="T">Type of elements</typeparam>
/// <param name="length">Number of elements</param> /// <param name="length">Number of elements</param>
/// <param name="fill">Fill value</param> /// <param name="fill">Fill value</param>
/// <param name="leaf"><see langword="true"/> if leaf; otherwise <see langword="false"/></param>
/// <returns>Allocated block</returns> /// <returns>Allocated block</returns>
private nint Allocate<T>(int length, T fill, bool leaf) where T : unmanaged private nint Allocate<T>(int length, T fill) where T : unmanaged
{ {
int size = sizeof(T) * length; int size = sizeof(T) * length;
AddressTablePage page; nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
if (Sparse && leaf) Span<T> span = new((void*)address, length);
{ span.Fill(fill);
_sparseLock.EnterWriteLock();
SparseMemoryBlock block; _pages.Add(address);
if (_sparseReserved.Count == 0)
{
block = ReserveNewSparseBlock().Block;
}
else
{
block = _sparseReserved.Last().Block;
if (_sparseReservedOffset == block.Block.Size)
{
block = ReserveNewSparseBlock().Block;
}
}
page = new AddressTablePage(true, block.Block.Pointer + (nint)_sparseReservedOffset);
_sparseReservedOffset += (ulong)size;
_sparseLock.ExitWriteLock();
}
else
{
nint address = (nint)NativeAllocator.Instance.Allocate((uint)size);
page = new AddressTablePage(false, address);
Span<T> span = new((void*)page.Address, length);
span.Fill(fill);
}
_pages.Add(page);
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf); //TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
return page.Address; return address;
} }
/// <summary> /// <summary>
@@ -443,32 +197,17 @@ namespace ARMeilleure.Common
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param> /// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
protected virtual void Dispose(bool disposing) protected virtual void Dispose(bool disposing)
{ {
if (!_disposed) if (_disposed)
{ {
foreach (AddressTablePage page in _pages) return;
{
if (!page.IsSparse)
{
Marshal.FreeHGlobal(page.Address);
}
}
if (Sparse)
{
foreach (TableSparseBlock block in _sparseReserved)
{
block.Dispose();
}
_sparseReserved.Clear();
_fillBottomLevel.Dispose();
_sparseFill.Dispose();
_sparseLock.Dispose();
}
_disposed = true;
} }
foreach (nint page in _pages)
{
Marshal.FreeHGlobal(page);
}
_disposed = true;
} }
/// <summary> /// <summary>
+6 -4
View File
@@ -9,13 +9,15 @@ namespace Ryujinx.Cpu.Jit
{ {
private readonly ITickSource _tickSource; private readonly ITickSource _tickSource;
private readonly Translator _translator; private readonly Translator _translator;
private readonly AddressTable<ulong> _functionTable;
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
{ {
_tickSource = tickSource; _tickSource = tickSource;
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type);
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable); bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, functionTable);
if (memory.Type.IsHostMappedOrTracked) if (memory.Type.IsHostMappedOrTracked)
{ {
@@ -57,7 +59,7 @@ namespace Ryujinx.Cpu.Jit
/// <inheritdoc/> /// <inheritdoc/>
public void PrepareCodeRange(ulong address, ulong size) public void PrepareCodeRange(ulong address, ulong size)
{ {
_functionTable.SignalCodeRange(address, size); _translator.FunctionTable.SignalCodeRange(address, size);
_translator.PrepareCodeRange(address, size); _translator.PrepareCodeRange(address, size);
} }
@@ -13,7 +13,7 @@ namespace Ryujinx.Cpu.LightningJit
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
ExecutionMode executionMode, ExecutionMode executionMode,
Architecture targetArch) Architecture targetArch)
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
bool isThumb, bool isThumb,
Architecture targetArch) Architecture targetArch)
@@ -22,7 +22,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
public readonly RegisterAllocator RegisterAllocator; public readonly RegisterAllocator RegisterAllocator;
public readonly MemoryManagerType MemoryManagerType; public readonly MemoryManagerType MemoryManagerType;
public readonly TailMerger TailMerger; public readonly TailMerger TailMerger;
public readonly AddressTable<ulong> FuncTable; public readonly IAddressTable<ulong> FuncTable;
public readonly nint DispatchStubPointer; public readonly nint DispatchStubPointer;
private readonly RegisterSaveRestore _registerSaveRestore; private readonly RegisterSaveRestore _registerSaveRestore;
@@ -33,7 +33,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
RegisterAllocator registerAllocator, RegisterAllocator registerAllocator,
MemoryManagerType mmType, MemoryManagerType mmType,
TailMerger tailMerger, TailMerger tailMerger,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
RegisterSaveRestore registerSaveRestore, RegisterSaveRestore registerSaveRestore,
nint dispatchStubPointer, nint dispatchStubPointer,
nint pageTablePointer) nint pageTablePointer)
@@ -225,7 +225,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
} }
} }
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb) public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr, bool isThumb)
{ {
MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb); MultiBlock multiBlock = Decoder<InstEmit>.DecodeMulti(cpuPreset, memoryManager, address, isThumb);
@@ -3,6 +3,7 @@ using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@@ -132,7 +133,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
RegisterAllocator regAlloc, RegisterAllocator regAlloc,
TailMerger tailMerger, TailMerger tailMerger,
Action writeEpilogue, Action writeEpilogue,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint funcPtr, nint funcPtr,
int spillBaseOffset, int spillBaseOffset,
uint nextAddress, uint nextAddress,
@@ -143,7 +144,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
int tempGuestAddress = -1; int tempGuestAddress = -1;
bool inlineLookup = guestAddress.Kind != OperandKind.Constant && bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
funcTable is { Sparse: true }; funcTable.TableType == AddressTableType.Sparse;
if (guestAddress.Kind == OperandKind.Constant) if (guestAddress.Kind == OperandKind.Constant)
{ {
@@ -189,7 +190,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
} }
else if (inlineLookup) else if (inlineLookup)
{ {
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels. // Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress)); Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
@@ -203,18 +204,15 @@ namespace Ryujinx.Cpu.LightningJit.Arm32.Target.Arm64
// Index into the table. // Index into the table.
asm.Mov(rn, tableBase); asm.Mov(rn, tableBase);
for (int i = 0; i < funcTable.Levels.Length; i++) AddressTableLevel level = funcTable.Levels.Last();
{ asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
AddressTableLevel level = funcTable.Levels[i]; asm.Lsl(indexReg, indexReg, Const(3));
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
asm.Lsl(indexReg, indexReg, Const(3));
// Index into the page. // Index into the page.
asm.Add(rn, rn, indexReg); asm.Add(rn, rn, indexReg);
// Load the page address. // Load the page address.
asm.LdrRiUn(rn, rn, 0); asm.LdrRiUn(rn, rn, 0);
}
if (tempGuestAddress != -1) if (tempGuestAddress != -1)
{ {
@@ -12,7 +12,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64
CpuPreset cpuPreset, CpuPreset cpuPreset,
IMemoryManager memoryManager, IMemoryManager memoryManager,
ulong address, ulong address,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
Architecture targetArch) Architecture targetArch)
{ {
@@ -18,7 +18,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
public readonly CodeWriter Writer; public readonly CodeWriter Writer;
public readonly RegisterAllocator RegisterAllocator; public readonly RegisterAllocator RegisterAllocator;
public readonly TailMerger TailMerger; public readonly TailMerger TailMerger;
public readonly AddressTable<ulong> FuncTable; public readonly IAddressTable<ulong> FuncTable;
public readonly nint DispatchStubPointer; public readonly nint DispatchStubPointer;
private readonly MultiBlock _multiBlock; private readonly MultiBlock _multiBlock;
@@ -31,7 +31,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
TailMerger tailMerger, TailMerger tailMerger,
RegisterSaveRestore registerSaveRestore, RegisterSaveRestore registerSaveRestore,
MultiBlock multiBlock, MultiBlock multiBlock,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPointer, nint dispatchStubPointer,
nint pageTablePointer) nint pageTablePointer)
{ {
@@ -303,7 +303,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
} }
} }
public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, AddressTable<ulong> funcTable, nint dispatchStubPtr) public static CompiledFunction Compile(CpuPreset cpuPreset, IMemoryManager memoryManager, ulong address, IAddressTable<ulong> funcTable, nint dispatchStubPtr)
{ {
MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address); MultiBlock multiBlock = Decoder.DecodeMulti(cpuPreset, memoryManager, address);
@@ -3,6 +3,7 @@ using Ryujinx.Cpu.LightningJit.CodeGen;
using Ryujinx.Cpu.LightningJit.CodeGen.Arm64; using Ryujinx.Cpu.LightningJit.CodeGen.Arm64;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.Linq;
using System.Numerics; using System.Numerics;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
@@ -214,7 +215,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
RegisterAllocator regAlloc, RegisterAllocator regAlloc,
TailMerger tailMerger, TailMerger tailMerger,
Action writeEpilogue, Action writeEpilogue,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint dispatchStubPtr, nint dispatchStubPtr,
InstName name, InstName name,
ulong pc, ulong pc,
@@ -298,7 +299,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
RegisterAllocator regAlloc, RegisterAllocator regAlloc,
TailMerger tailMerger, TailMerger tailMerger,
Action writeEpilogue, Action writeEpilogue,
AddressTable<ulong> funcTable, IAddressTable<ulong> funcTable,
nint funcPtr, nint funcPtr,
int spillBaseOffset, int spillBaseOffset,
ulong pc, ulong pc,
@@ -309,7 +310,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
int tempGuestAddress = -1; int tempGuestAddress = -1;
bool inlineLookup = guestAddress.Kind != OperandKind.Constant && bool inlineLookup = guestAddress.Kind != OperandKind.Constant &&
funcTable is { Sparse: true }; funcTable.TableType == AddressTableType.Sparse;
if (guestAddress.Kind == OperandKind.Constant) if (guestAddress.Kind == OperandKind.Constant)
{ {
@@ -355,7 +356,7 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
} }
else if (inlineLookup) else if (inlineLookup)
{ {
// Inline table lookup. Only enabled when the sparse function table is enabled with 2 levels. // Inline table lookup. Only enabled when the sparse function table is enabled with 1 level.
Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress)); Operand indexReg = Register(NextFreeRegister(tempRegister + 1, tempGuestAddress));
@@ -369,18 +370,15 @@ namespace Ryujinx.Cpu.LightningJit.Arm64.Target.Arm64
// Index into the table. // Index into the table.
asm.Mov(rn, tableBase); asm.Mov(rn, tableBase);
for (int i = 0; i < funcTable.Levels.Length; i++) AddressTableLevel level = funcTable.Levels.Last();
{ asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
AddressTableLevel level = funcTable.Levels[i]; asm.Lsl(indexReg, indexReg, Const(3));
asm.Ubfx(indexReg, guestAddress, level.Index, level.Length);
asm.Lsl(indexReg, indexReg, Const(3));
// Index into the page. // Index into the page.
asm.Add(rn, rn, indexReg); asm.Add(rn, rn, indexReg);
// Load the page address. // Load the page address.
asm.LdrRiUn(rn, rn, 0); asm.LdrRiUn(rn, rn, 0);
}
if (tempGuestAddress != -1) if (tempGuestAddress != -1)
{ {
@@ -9,15 +9,15 @@ namespace Ryujinx.Cpu.LightningJit
{ {
private readonly ITickSource _tickSource; private readonly ITickSource _tickSource;
private readonly Translator _translator; private readonly Translator _translator;
private readonly AddressTable<ulong> _functionTable;
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
{ {
_tickSource = tickSource; _tickSource = tickSource;
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit, memory.Type); bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
_translator = new Translator(memory, _functionTable);
_translator = new Translator(memory, functionTable);
memory.UnmapEvent += UnmapHandler; memory.UnmapEvent += UnmapHandler;
} }
@@ -54,7 +54,7 @@ namespace Ryujinx.Cpu.LightningJit
/// <inheritdoc/> /// <inheritdoc/>
public void PrepareCodeRange(ulong address, ulong size) public void PrepareCodeRange(ulong address, ulong size)
{ {
_functionTable.SignalCodeRange(address, size); _translator.FunctionTable.SignalCodeRange(address, size);
} }
public void Dispose() public void Dispose()
+3 -2
View File
@@ -24,11 +24,11 @@ namespace Ryujinx.Cpu.LightningJit
private bool _disposed; private bool _disposed;
internal TranslatorCache<TranslatedFunction> Functions { get; } internal TranslatorCache<TranslatedFunction> Functions { get; }
internal AddressTable<ulong> FunctionTable { get; } internal IAddressTable<ulong> FunctionTable { get; }
internal TranslatorStubs Stubs { get; } internal TranslatorStubs Stubs { get; }
internal IMemoryManager Memory { get; } internal IMemoryManager Memory { get; }
public Translator(IMemoryManager memory, AddressTable<ulong> functionTable) public Translator(IMemoryManager memory, IAddressTable<ulong> functionTable)
{ {
Memory = memory; Memory = memory;
@@ -45,6 +45,7 @@ namespace Ryujinx.Cpu.LightningJit
Functions = new TranslatorCache<TranslatedFunction>(); Functions = new TranslatorCache<TranslatedFunction>();
FunctionTable = functionTable; FunctionTable = functionTable;
Stubs = new TranslatorStubs(_jitCache, FunctionTable, _noWxCache); Stubs = new TranslatorStubs(_jitCache, FunctionTable, _noWxCache);
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
+247
View File
@@ -0,0 +1,247 @@
using ARMeilleure.Common;
using Ryujinx.Common;
using Ryujinx.Cpu.Signal;
using Ryujinx.Memory;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using static Ryujinx.Cpu.MemoryEhMeilleure;
namespace Ryujinx.Cpu
{
/// <summary>
/// Represents a table of guest address to a value.
/// </summary>
/// <typeparam name="TEntry">Type of the value</typeparam>
public unsafe class SparseAddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
{
/// <summary>
/// A sparsely mapped block of memory with a signal handler to map pages as they're accessed.
/// </summary>
private readonly struct TableSparseBlock : IDisposable
{
public readonly SparseMemoryBlock Block;
private readonly TrackingEventDelegate _trackingEvent;
public TableSparseBlock(ulong size, Action<nint> ensureMapped, PageInitDelegate pageInit, MemoryBlock fill)
{
SparseMemoryBlock block = new(size, pageInit, fill);
_trackingEvent = (address, _, _) =>
{
ulong pointer = (ulong)block.Block.Pointer + address;
ensureMapped((nint)pointer);
return pointer;
};
bool added = NativeSignalHandler.AddTrackedRegion(
(nuint)block.Block.Pointer,
(nuint)(block.Block.Pointer + (nint)block.Block.Size),
Marshal.GetFunctionPointerForDelegate(_trackingEvent));
if (!added)
{
throw new InvalidOperationException("Number of allowed tracked regions exceeded.");
}
Block = block;
}
public void Dispose()
{
NativeSignalHandler.RemoveTrackedRegion((nuint)Block.Block.Pointer);
Block.Dispose();
}
}
private bool _disposed;
private TEntry* _table;
private TEntry _fill;
private readonly TableSparseBlock _block;
private readonly MemoryBlock _fillBlock;
/// <inheritdoc/>
public AddressTableType TableType => AddressTableType.Sparse;
/// <inheritdoc/>
public ulong Mask { get; }
/// <inheritdoc/>
public AddressTableLevel[] Levels { get; }
/// <inheritdoc/>
public TEntry Fill
{
get
{
return _fill;
}
set
{
UpdateFill(value);
}
}
/// <inheritdoc/>
public nint Base
{
get
{
ObjectDisposedException.ThrowIf(_disposed, this);
return (nint)_table;
}
}
/// <summary>
/// Constructs a new instance of the <see cref="AddressTable{TEntry}"/> class with the specified list of
/// <see cref="AddressTableLevel"/>.
/// </summary>
/// <param name="levels">Levels for the address table</param>
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
public SparseAddressTable(AddressTableLevel[] levels)
{
ArgumentNullException.ThrowIfNull(levels);
Levels = levels;
Mask = 0;
foreach (AddressTableLevel level in Levels)
{
Mask |= level.Mask;
}
_fillBlock = new MemoryBlock(MemoryBlock.GetPageSize() << 10, MemoryAllocationFlags.Mirrorable);
// We need to use the full size, as some games dynamically expand the code range (e.g. SSBU)
// Limiting the size to only the requested code range will cause crashes
// This should not be an issue tho, as the SparseBlock dynamically allocates memory as needed, and
// Falls back to the fill block in case guest code tries to call an unmapped function.
ulong bottomLevelSize = (1ul << Levels.Last().Length) * (ulong)sizeof(TEntry);
_block = new TableSparseBlock(bottomLevelSize, EnsureMapped, InitLeafPage, _fillBlock);
_table = (TEntry*)_block.Block.Block.Pointer;
}
/// <summary>
/// Create an <see cref="AddressTable{TEntry}"/> instance for an ARM function table.
/// Selects the best table structure for A32/A64, taking into account the selected memory manager type.
/// </summary>
/// <param name="for64Bits">True if the guest is A64, false otherwise</param>
/// <returns>An <see cref="AddressTable{TEntry}"/> for ARM function lookup</returns>
public static SparseAddressTable<TEntry> CreateForArm(bool for64Bits)
{
return new SparseAddressTable<TEntry>(AddressTablePresets.GetArmPreset(for64Bits, true));
}
/// <summary>
/// Update the fill value for the bottom level of the table.
/// </summary>
/// <param name="fillValue">New fill value</param>
private void UpdateFill(TEntry fillValue)
{
if (_fillBlock != null)
{
Span<byte> span = _fillBlock.GetSpan(0, (int)_fillBlock.Size);
MemoryMarshal.Cast<byte, TEntry>(span).Fill(fillValue);
}
_fill = fillValue;
}
/// <summary>
/// Signal that the given code range exists.
/// </summary>
/// <param name="address"></param>
/// <param name="size"></param>
public void SignalCodeRange(ulong address, ulong size)
{
}
/// <inheritdoc/>
public bool IsValid(ulong address)
{
return (address & ~Mask) == 0;
}
/// <inheritdoc/>
public ref TEntry GetValue(ulong address)
{
ObjectDisposedException.ThrowIf(_disposed, this);
if (!IsValid(address))
{
throw new ArgumentException($"Address 0x{address:X} is not mapped onto the table.", nameof(address));
}
long index = Levels.Last().GetValue(address);
EnsureMapped((nint)(_table + index));
return ref _table[index];
}
/// <summary>
/// Ensure the given pointer is mapped in any overlapping sparse reservations.
/// </summary>
/// <param name="ptr">Pointer to be mapped</param>
private void EnsureMapped(nint ptr)
{
SparseMemoryBlock sparse = _block.Block;
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (nint)sparse.Block.Size)
{
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
}
}
/// <summary>
/// Initialize a leaf page with the fill value.
/// </summary>
/// <param name="page">Page to initialize</param>
private void InitLeafPage(Span<byte> page)
{
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
}
/// <summary>
/// Releases all resources used by the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// Releases all unmanaged and optionally managed resources used by the <see cref="AddressTable{TEntry}"/>
/// instance.
/// </summary>
/// <param name="disposing"><see langword="true"/> to dispose managed resources also; otherwise just unmanaged resouces</param>
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
_block.Dispose();
_fillBlock.Dispose();
_disposed = true;
}
/// <summary>
/// Frees resources used by the <see cref="AddressTable{TEntry}"/> instance.
/// </summary>
~SparseAddressTable()
{
Dispose(false);
}
}
}
+17 -9
View File
@@ -21,6 +21,7 @@ namespace Ryujinx.Memory
private ulong _mappedBlockUsage; private ulong _mappedBlockUsage;
private readonly ulong[] _mappedPageBitmap; private readonly ulong[] _mappedPageBitmap;
private readonly MemoryBlock _fill;
public MemoryBlock Block => _reservedBlock; public MemoryBlock Block => _reservedBlock;
@@ -34,23 +35,24 @@ namespace Ryujinx.Memory
int pages = (int)BitUtils.DivRoundUp(size, _pageSize); int pages = (int)BitUtils.DivRoundUp(size, _pageSize);
int bitmapEntries = BitUtils.DivRoundUp(pages, 64); int bitmapEntries = BitUtils.DivRoundUp(pages, 64);
_mappedPageBitmap = new ulong[bitmapEntries]; _mappedPageBitmap = new ulong[bitmapEntries];
_fill = fill;
if (fill != null) if (_fill is not null)
{ {
if (_fill.Size % _pageSize !=0)
{
throw new ArgumentException("Fill memory block should be page sized.", nameof(_fill));
}
// Fill the block with mappings from the fill block. // Fill the block with mappings from the fill block.
if (fill.Size % _pageSize != 0) int repeats = (int)BitUtils.DivRoundUp(size, _fill.Size);
{
throw new ArgumentException("Fill memory block should be page aligned.", nameof(fill));
}
int repeats = (int)BitUtils.DivRoundUp(size, fill.Size);
ulong offset = 0; ulong offset = 0;
for (int i = 0; i < repeats; i++) for (int i = 0; i < repeats; i++)
{ {
_reservedBlock.MapView(fill, 0, offset, Math.Min(fill.Size, size - offset)); _reservedBlock.MapView(_fill, 0, offset, Math.Min(_fill.Size, size - offset));
offset += fill.Size; offset += _fill.Size;
} }
} }
@@ -75,6 +77,12 @@ namespace Ryujinx.Memory
} }
_pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize)); _pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize));
if (_fill is not null)
{
_reservedBlock.UnmapView(_fill, pageOffset, _pageSize);
}
_reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize); _reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize);
_mappedBlockUsage += _pageSize; _mappedBlockUsage += _pageSize;
+4 -1
View File
@@ -13,7 +13,10 @@ namespace Ryujinx.Tests.Cpu
public CpuContext(IMemoryManager memory, bool for64Bit) public CpuContext(IMemoryManager memory, bool for64Bit)
{ {
_translator = new Translator(new JitMemoryAllocator(), memory, AddressTable<ulong>.CreateForArm(for64Bit, memory.Type)); bool sparse = memory.Type is not MemoryManagerType.SoftwareMmu and not MemoryManagerType.SoftwarePageTable;
IAddressTable<ulong> functionTable = sparse ? SparseAddressTable<ulong>.CreateForArm(for64Bit) : AddressTable<ulong>.CreateForArm(for64Bit);
_translator = new Translator(new JitMemoryAllocator(), memory, functionTable);
memory.UnmapEvent += UnmapHandler; memory.UnmapEvent += UnmapHandler;
} }
+2 -2
View File
@@ -1,7 +1,7 @@
using ARMeilleure.Common; using ARMeilleure.Common;
using ARMeilleure.Memory;
using ARMeilleure.Translation; using ARMeilleure.Translation;
using NUnit.Framework; using NUnit.Framework;
using Ryujinx.Cpu;
using Ryujinx.Cpu.Jit; using Ryujinx.Cpu.Jit;
using Ryujinx.Tests.Memory; using Ryujinx.Tests.Memory;
using System; using System;
@@ -20,7 +20,7 @@ namespace Ryujinx.Tests.Cpu
_translator ??= new Translator( _translator ??= new Translator(
new JitMemoryAllocator(), new JitMemoryAllocator(),
new MockMemoryManager(), new MockMemoryManager(),
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable)); AddressTable<ulong>.CreateForArm(true));
} }
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
+1 -1
View File
@@ -60,7 +60,7 @@ namespace Ryujinx.Tests.Memory
_translator ??= new Translator( _translator ??= new Translator(
new JitMemoryAllocator(), new JitMemoryAllocator(),
new MockMemoryManager(), new MockMemoryManager(),
AddressTable<ulong>.CreateForArm(true, MemoryManagerType.SoftwarePageTable)); AddressTable<ulong>.CreateForArm(true));
NativeSignalHandler.InitializeSignalHandler(); NativeSignalHandler.InitializeSignalHandler();
} }