mirror of
https://github.com/Ryujinx/Ryujinx.git
synced 2024-11-17 19:16:39 +00:00
Implement sparse table
This commit is contained in:
parent
a3199f0b54
commit
74fe391150
14 changed files with 292 additions and 195 deletions
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
44
src/ARMeilleure/Common/AddressTableLevel.cs
Normal file
|
@ -0,0 +1,44 @@
|
|||
namespace ARMeilleure.Common
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a level in an <see cref="IAddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
public readonly struct AddressTableLevel
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the index of the <see cref="Level"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Index { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the <see cref="AddressTableLevel"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mask which masks the bits used by the <see cref="AddressTableLevel"/>.
|
||||
/// </summary>
|
||||
public ulong Mask => ((1ul << Length) - 1) << Index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AddressTableLevel"/> structure with the specified
|
||||
/// <paramref name="index"/> and <paramref name="length"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the <see cref="AddressTableLevel"/></param>
|
||||
/// <param name="length">Length of the <see cref="AddressTableLevel"/></param>
|
||||
public AddressTableLevel(int index, int length)
|
||||
{
|
||||
(Index, Length) = (index, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Value of the <see cref="AddressTableLevel"/> from the specified guest <paramref name="address"/></returns>
|
||||
public int GetValue(ulong address)
|
||||
{
|
||||
return (int)((address & Mask) >> Index);
|
||||
}
|
||||
}
|
||||
}
|
51
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
51
src/ARMeilleure/Common/AddressTablePresets.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
namespace ARMeilleure.Common
|
||||
{
|
||||
public static class AddressTablePresets
|
||||
{
|
||||
private static readonly AddressTableLevel[] _levels64Bit =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 2, 5),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels32Bit =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 1, 6),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels64BitSparse =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new(23, 16),
|
||||
new( 2, 21),
|
||||
};
|
||||
|
||||
private static readonly AddressTableLevel[] _levels32BitSparse =
|
||||
new AddressTableLevel[]
|
||||
{
|
||||
new(22, 10),
|
||||
new( 1, 21),
|
||||
};
|
||||
|
||||
public static AddressTableLevel[] GetArmPreset(bool for64Bits, bool sparse)
|
||||
{
|
||||
if (sparse)
|
||||
{
|
||||
return for64Bits ? _levels64BitSparse : _levels32BitSparse;
|
||||
}
|
||||
else
|
||||
{
|
||||
return for64Bits ? _levels64Bit : _levels32Bit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ using System;
|
|||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
unsafe abstract class Allocator : IDisposable
|
||||
public unsafe abstract class Allocator : IDisposable
|
||||
{
|
||||
public T* Allocate<T>(ulong count = 1) where T : unmanaged
|
||||
{
|
||||
|
|
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
51
src/ARMeilleure/Common/IAddressTable.cs
Normal file
|
@ -0,0 +1,51 @@
|
|||
using System;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
public interface IAddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// If true, the sparse 2-level table should be used to improve performance.
|
||||
/// If false, the platform doesn't properly support it, or will be negatively impacted.
|
||||
/// </summary>
|
||||
static bool UseSparseTable { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
ulong Mask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
AddressTableLevel[] Levels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default fill value of newly created leaf pages.
|
||||
/// </summary>
|
||||
TEntry Fill { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
IntPtr Base { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||
/// <see cref="AddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
||||
bool IsValid(ulong address);
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||
ref TEntry GetValue(ulong address);
|
||||
}
|
||||
}
|
|
@ -3,7 +3,7 @@ using System.Runtime.InteropServices;
|
|||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
unsafe sealed class NativeAllocator : Allocator
|
||||
public unsafe sealed class NativeAllocator : Allocator
|
||||
{
|
||||
public static NativeAllocator Instance { get; } = new();
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
|||
{
|
||||
public static class NativeSignalHandlerGenerator
|
||||
{
|
||||
public const int MaxTrackedRanges = 8;
|
||||
public const int MaxTrackedRanges = 16;
|
||||
|
||||
private const int StructAddressOffset = 0;
|
||||
private const int StructWriteOffset = 4;
|
||||
|
|
|
@ -46,7 +46,7 @@ namespace ARMeilleure.Translation
|
|||
public IMemoryManager Memory { get; }
|
||||
|
||||
public EntryTable<uint> CountTable { get; }
|
||||
public AddressTable<ulong> FunctionTable { get; }
|
||||
public IAddressTable<ulong> FunctionTable { get; }
|
||||
public TranslatorStubs Stubs { get; }
|
||||
|
||||
public ulong EntryAddress { get; }
|
||||
|
@ -62,7 +62,7 @@ namespace ARMeilleure.Translation
|
|||
public ArmEmitterContext(
|
||||
IMemoryManager memory,
|
||||
EntryTable<uint> countTable,
|
||||
AddressTable<ulong> funcTable,
|
||||
IAddressTable<ulong> funcTable,
|
||||
TranslatorStubs stubs,
|
||||
ulong entryAddress,
|
||||
bool highCq,
|
||||
|
|
|
@ -22,47 +22,13 @@ namespace ARMeilleure.Translation
|
|||
{
|
||||
public class Translator
|
||||
{
|
||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 2, 5),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels32Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 1, 6),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels64BitSparse =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(23, 16),
|
||||
new( 2, 21),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels32BitSparse =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(22, 10),
|
||||
new( 1, 21),
|
||||
};
|
||||
|
||||
private readonly IJitMemoryAllocator _allocator;
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
|
||||
private readonly Ptc _ptc;
|
||||
|
||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||
internal AddressTable<ulong> FunctionTable { get; }
|
||||
internal IAddressTable<ulong> FunctionTable { get; }
|
||||
internal EntryTable<uint> CountTable { get; }
|
||||
internal TranslatorStubs Stubs { get; }
|
||||
internal TranslatorQueue Queue { get; }
|
||||
|
@ -71,7 +37,7 @@ namespace ARMeilleure.Translation
|
|||
private Thread[] _backgroundTranslationThreads;
|
||||
private volatile int _threadCount;
|
||||
|
||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, IAddressTable<ulong> functionTable)
|
||||
{
|
||||
_allocator = allocator;
|
||||
Memory = memory;
|
||||
|
@ -84,22 +50,9 @@ namespace ARMeilleure.Translation
|
|||
|
||||
JitCache.Initialize(allocator);
|
||||
|
||||
AddressTable<ulong>.Level[] levels;
|
||||
|
||||
bool useSparseTable = AddressTable<ulong>.UseSparseTable;
|
||||
|
||||
if (useSparseTable)
|
||||
{
|
||||
levels = for64Bits ? _levels64BitSparse : _levels32BitSparse;
|
||||
}
|
||||
else
|
||||
{
|
||||
levels = for64Bits ? _levels64Bit : _levels32Bit;
|
||||
}
|
||||
|
||||
CountTable = new EntryTable<uint>();
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(levels, useSparseTable);
|
||||
FunctionTable = functionTable;
|
||||
Stubs = new TranslatorStubs(FunctionTable);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace ARMeilleure.Translation
|
|||
|
||||
private bool _disposed;
|
||||
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
private readonly IAddressTable<ulong> _functionTable;
|
||||
private readonly Lazy<IntPtr> _dispatchStub;
|
||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||
|
@ -86,7 +86,7 @@ namespace ARMeilleure.Translation
|
|||
/// </summary>
|
||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||
public TranslatorStubs(AddressTable<ulong> functionTable)
|
||||
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(functionTable);
|
||||
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
using ARMeilleure.Diagnostics;
|
||||
using Ryujinx.Common.Logging;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
using Ryujinx.Memory;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
using static Ryujinx.Cpu.MemoryEhMeilleure;
|
||||
|
||||
namespace ARMeilleure.Common
|
||||
{
|
||||
|
@ -12,7 +14,7 @@ namespace ARMeilleure.Common
|
|||
/// Represents a table of guest address to a value.
|
||||
/// </summary>
|
||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
||||
public unsafe class AddressTable<TEntry> : IDisposable where TEntry : unmanaged
|
||||
public unsafe class AddressTable<TEntry> : IAddressTable<TEntry> where TEntry : unmanaged
|
||||
{
|
||||
/// <summary>
|
||||
/// If true, the sparse 2-level table should be used to improve performance.
|
||||
|
@ -20,48 +22,6 @@ namespace ARMeilleure.Common
|
|||
/// </summary>
|
||||
public static bool UseSparseTable => true;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a level in an <see cref="AddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
public readonly struct Level
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the index of the <see cref="Level"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Index { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the length of the <see cref="Level"/> in the guest address.
|
||||
/// </summary>
|
||||
public int Length { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the mask which masks the bits used by the <see cref="Level"/>.
|
||||
/// </summary>
|
||||
public ulong Mask => ((1ul << Length) - 1) << Index;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Level"/> structure with the specified
|
||||
/// <paramref name="index"/> and <paramref name="length"/>.
|
||||
/// </summary>
|
||||
/// <param name="index">Index of the <see cref="Level"/></param>
|
||||
/// <param name="length">Length of the <see cref="Level"/></param>
|
||||
public Level(int index, int length)
|
||||
{
|
||||
(Index, Length) = (index, length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of the <see cref="Level"/> from the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Value of the <see cref="Level"/> from the specified guest <paramref name="address"/></returns>
|
||||
public int GetValue(ulong address)
|
||||
{
|
||||
return (int)((address & Mask) >> Index);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly struct AddressTablePage
|
||||
{
|
||||
public readonly bool IsSparse;
|
||||
|
@ -74,6 +34,50 @@ namespace ARMeilleure.Common
|
|||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
public readonly TrackingEventDelegate TrackingEvent;
|
||||
|
||||
public TableSparseBlock(ulong size, Action<IntPtr> ensureMapped, PageInitDelegate pageInit)
|
||||
{
|
||||
var block = new SparseMemoryBlock(size, pageInit, null);
|
||||
|
||||
TrackingEvent = (ulong address, ulong size, bool write) =>
|
||||
{
|
||||
Logger.Error?.PrintMsg(LogClass.Cpu, $"Triggered from exception");
|
||||
|
||||
ulong pointer = (ulong)block.Block.Pointer + address;
|
||||
|
||||
ensureMapped((IntPtr)pointer);
|
||||
|
||||
return pointer;
|
||||
};
|
||||
|
||||
bool added = NativeSignalHandler.AddTrackedRegion(
|
||||
(nuint)block.Block.Pointer,
|
||||
(nuint)(block.Block.Pointer + (IntPtr)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 readonly List<AddressTablePage> _pages;
|
||||
|
@ -84,24 +88,19 @@ namespace ARMeilleure.Common
|
|||
private readonly SparseMemoryBlock _fillBottomLevel;
|
||||
private readonly TEntry* _fillBottomLevelPtr;
|
||||
|
||||
private readonly List<SparseMemoryBlock> _sparseReserved;
|
||||
private readonly List<TableSparseBlock> _sparseReserved;
|
||||
private readonly ulong _sparseBlockSize;
|
||||
private readonly ReaderWriterLockSlim _sparseLock;
|
||||
|
||||
private ulong _sparseReservedOffset;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public ulong Mask { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
|
||||
/// </summary>
|
||||
public Level[] Levels { get; }
|
||||
/// <inheritdoc/>
|
||||
public AddressTableLevel[] Levels { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the default fill value of newly created leaf pages.
|
||||
/// </summary>
|
||||
/// <inheritdoc/>
|
||||
public TEntry Fill
|
||||
{
|
||||
get
|
||||
|
@ -114,10 +113,7 @@ namespace ARMeilleure.Common
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
/// <inheritdoc/>
|
||||
public IntPtr Base
|
||||
{
|
||||
get
|
||||
|
@ -139,7 +135,7 @@ namespace ARMeilleure.Common
|
|||
/// <param name="sparse">True if the bottom page should be sparsely mapped</param>
|
||||
/// <exception cref="ArgumentNullException"><paramref name="levels"/> is null</exception>
|
||||
/// <exception cref="ArgumentException">Length of <paramref name="levels"/> is less than 2</exception>
|
||||
public AddressTable(Level[] levels, bool sparse)
|
||||
public AddressTable(AddressTableLevel[] levels, bool sparse)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(levels);
|
||||
|
||||
|
@ -171,13 +167,30 @@ namespace ARMeilleure.Common
|
|||
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
|
||||
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
|
||||
|
||||
_sparseReserved = new List<SparseMemoryBlock>();
|
||||
_sparseReserved = new List<TableSparseBlock>();
|
||||
_sparseLock = new ReaderWriterLockSlim();
|
||||
|
||||
_sparseBlockSize = bottomLevelSize << 3;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an <see cref="AddressTable{TEntry}"/> instance for an ARM function table.
|
||||
/// Selects the best table structure for A32/A64, taking into account whether sparse mapping is supported.
|
||||
/// </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 AddressTable<TEntry> CreateForArm(bool for64Bits)
|
||||
{
|
||||
bool sparse = UseSparseTable;
|
||||
|
||||
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)
|
||||
|
@ -189,24 +202,13 @@ namespace ARMeilleure.Common
|
|||
_fill = fillValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the specified <paramref name="address"/> is in the range of the
|
||||
/// <see cref="AddressTable{TEntry}"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns><see langword="true"/> if is valid; otherwise <see langword="false"/></returns>
|
||||
/// <inheritdoc/>
|
||||
public bool IsValid(ulong address)
|
||||
{
|
||||
return (address & ~Mask) == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a reference to the value at the specified guest <paramref name="address"/>.
|
||||
/// </summary>
|
||||
/// <param name="address">Guest address</param>
|
||||
/// <returns>Reference to the value at the specified guest <paramref name="address"/></returns>
|
||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
||||
/// <exception cref="ArgumentException"><paramref name="address"/> is not mapped</exception>
|
||||
/// <inheritdoc/>
|
||||
public ref TEntry GetValue(ulong address)
|
||||
{
|
||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||
|
@ -239,12 +241,12 @@ namespace ARMeilleure.Common
|
|||
|
||||
for (int i = 0; i < Levels.Length - 1; i++)
|
||||
{
|
||||
ref Level level = ref Levels[i];
|
||||
ref AddressTableLevel level = ref Levels[i];
|
||||
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
||||
|
||||
if (nextPage == null || nextPage == _fillBottomLevelPtr)
|
||||
{
|
||||
ref Level nextLevel = ref Levels[i + 1];
|
||||
ref AddressTableLevel nextLevel = ref Levels[i + 1];
|
||||
|
||||
if (i == Levels.Length - 2)
|
||||
{
|
||||
|
@ -273,8 +275,10 @@ namespace ARMeilleure.Common
|
|||
|
||||
try
|
||||
{
|
||||
foreach (SparseMemoryBlock sparse in _sparseReserved)
|
||||
foreach (TableSparseBlock reserved in _sparseReserved)
|
||||
{
|
||||
SparseMemoryBlock sparse = reserved.Block;
|
||||
|
||||
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
|
||||
{
|
||||
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
||||
|
@ -290,6 +294,11 @@ namespace ARMeilleure.Common
|
|||
}
|
||||
}
|
||||
|
||||
/// <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 IntPtr GetFillValue(int level)
|
||||
{
|
||||
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||
|
@ -316,9 +325,28 @@ namespace ARMeilleure.Common
|
|||
return _table;
|
||||
}
|
||||
|
||||
private int initedSize = 0;
|
||||
private int reservedSize = 0;
|
||||
|
||||
/// <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);
|
||||
|
||||
initedSize += page.Length;
|
||||
|
||||
Ryujinx.Common.Logging.Logger.Info?.PrintMsg(LogClass.Cpu, $"Using memory {initedSize}/{reservedSize} bytes");
|
||||
}
|
||||
|
||||
private void ReserveNewSparseBlock()
|
||||
{
|
||||
var block = new TableSparseBlock(_sparseBlockSize, EnsureMapped, InitLeafPage);
|
||||
|
||||
_sparseReserved.Add(block);
|
||||
_sparseReservedOffset = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -333,6 +361,8 @@ namespace ARMeilleure.Common
|
|||
{
|
||||
var size = sizeof(T) * length;
|
||||
|
||||
reservedSize += size;
|
||||
|
||||
AddressTablePage page;
|
||||
|
||||
if (_sparse && leaf)
|
||||
|
@ -341,12 +371,10 @@ namespace ARMeilleure.Common
|
|||
|
||||
if (_sparseReserved.Count == 0 || _sparseReservedOffset == _sparseBlockSize)
|
||||
{
|
||||
_sparseReserved.Add(new SparseMemoryBlock(_sparseBlockSize, InitLeafPage, _sparseFill));
|
||||
|
||||
_sparseReservedOffset = 0;
|
||||
ReserveNewSparseBlock();
|
||||
}
|
||||
|
||||
SparseMemoryBlock block = _sparseReserved.Last();
|
||||
SparseMemoryBlock block = _sparseReserved.Last().Block;
|
||||
|
||||
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
||||
|
||||
|
@ -365,7 +393,7 @@ namespace ARMeilleure.Common
|
|||
|
||||
_pages.Add(page);
|
||||
|
||||
TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||
|
||||
return page.Address;
|
||||
}
|
||||
|
@ -398,11 +426,13 @@ namespace ARMeilleure.Common
|
|||
|
||||
if (_sparse)
|
||||
{
|
||||
foreach (SparseMemoryBlock block in _sparseReserved)
|
||||
foreach (TableSparseBlock block in _sparseReserved)
|
||||
{
|
||||
block.Dispose();
|
||||
}
|
||||
|
||||
_sparseReserved.Clear();
|
||||
|
||||
_fillBottomLevel.Dispose();
|
||||
_sparseFill.Dispose();
|
||||
_sparseLock.Dispose();
|
|
@ -1,3 +1,4 @@
|
|||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using ARMeilleure.Translation;
|
||||
using Ryujinx.Cpu.Signal;
|
||||
|
@ -9,11 +10,14 @@ namespace Ryujinx.Cpu.Jit
|
|||
{
|
||||
private readonly ITickSource _tickSource;
|
||||
private readonly Translator _translator;
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
|
||||
public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, for64Bit);
|
||||
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit);
|
||||
|
||||
_translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable);
|
||||
|
||||
if (memory.Type.IsHostMappedOrTracked())
|
||||
{
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
using ARMeilleure.Common;
|
||||
using ARMeilleure.Memory;
|
||||
using Ryujinx.Cpu.Jit;
|
||||
using Ryujinx.Cpu.LightningJit.State;
|
||||
|
@ -8,11 +9,15 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
{
|
||||
private readonly ITickSource _tickSource;
|
||||
private readonly Translator _translator;
|
||||
private readonly AddressTable<ulong> _functionTable;
|
||||
|
||||
public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit)
|
||||
{
|
||||
_tickSource = tickSource;
|
||||
_translator = new Translator(memory, for64Bit);
|
||||
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit);
|
||||
|
||||
_translator = new Translator(memory, _functionTable);
|
||||
|
||||
memory.UnmapEvent += UnmapHandler;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,39 +19,6 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
// Should be enabled on platforms that enforce W^X.
|
||||
private static bool IsNoWxPlatform => false;
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels64Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(31, 17),
|
||||
new(23, 8),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 2, 5),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels32Bit =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(23, 9),
|
||||
new(15, 8),
|
||||
new( 7, 8),
|
||||
new( 1, 6),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels64BitSparse =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(23, 16),
|
||||
new( 2, 21),
|
||||
};
|
||||
|
||||
private static readonly AddressTable<ulong>.Level[] _levels32BitSparse =
|
||||
new AddressTable<ulong>.Level[]
|
||||
{
|
||||
new(22, 10),
|
||||
new( 1, 21),
|
||||
};
|
||||
|
||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||
private readonly NoWxCache _noWxCache;
|
||||
private bool _disposed;
|
||||
|
@ -61,7 +28,7 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
internal TranslatorStubs Stubs { get; }
|
||||
internal IMemoryManager Memory { get; }
|
||||
|
||||
public Translator(IMemoryManager memory, bool for64Bits)
|
||||
public Translator(IMemoryManager memory, AddressTable<ulong> functionTable)
|
||||
{
|
||||
Memory = memory;
|
||||
|
||||
|
@ -76,21 +43,8 @@ namespace Ryujinx.Cpu.LightningJit
|
|||
JitCache.Initialize(new JitMemoryAllocator(forJit: true));
|
||||
}
|
||||
|
||||
bool useSparseTable = AddressTable<ulong>.UseSparseTable;
|
||||
|
||||
AddressTable<ulong>.Level[] levels;
|
||||
|
||||
if (useSparseTable)
|
||||
{
|
||||
levels = for64Bits ? _levels64BitSparse : _levels32BitSparse;
|
||||
}
|
||||
else
|
||||
{
|
||||
levels = for64Bits ? _levels64Bit : _levels32Bit;
|
||||
}
|
||||
|
||||
Functions = new TranslatorCache<TranslatedFunction>();
|
||||
FunctionTable = new AddressTable<ulong>(levels, useSparseTable);
|
||||
FunctionTable = functionTable;
|
||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
||||
|
||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Common;
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
|
||||
namespace Ryujinx.Memory
|
||||
{
|
||||
|
@ -66,15 +67,15 @@ namespace Ryujinx.Memory
|
|||
{
|
||||
// Need to map some more memory.
|
||||
|
||||
block = new MemoryBlock(MapGranularity, MemoryAllocationFlags.Mirrorable | MemoryAllocationFlags.NoMap);
|
||||
block = new MemoryBlock(MapGranularity, MemoryAllocationFlags.Mirrorable);
|
||||
|
||||
_mappedBlocks.Add(block);
|
||||
|
||||
_mappedBlockUsage = 0;
|
||||
}
|
||||
|
||||
_pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize));
|
||||
_reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize);
|
||||
_pageInit(_reservedBlock.GetSpan(pageOffset, (int)_pageSize));
|
||||
|
||||
_mappedBlockUsage += _pageSize;
|
||||
}
|
||||
|
@ -87,7 +88,7 @@ namespace Ryujinx.Memory
|
|||
ref ulong entry = ref _mappedPageBitmap[bitmapIndex];
|
||||
ulong bit = 1UL << (pageIndex & 63);
|
||||
|
||||
if ((entry & bit) == 0)
|
||||
if ((Volatile.Read(ref entry) & bit) == 0)
|
||||
{
|
||||
// Not mapped.
|
||||
|
||||
|
@ -95,11 +96,15 @@ namespace Ryujinx.Memory
|
|||
{
|
||||
// Check the bit while locked to make sure that this only happens once.
|
||||
|
||||
if ((entry & bit) == 0)
|
||||
ulong lockedEntry = Volatile.Read(ref entry);
|
||||
|
||||
if ((lockedEntry & bit) == 0)
|
||||
{
|
||||
MapPage(offset & ~(_pageSize - 1));
|
||||
|
||||
entry |= bit;
|
||||
lockedEntry |= bit;
|
||||
|
||||
Interlocked.Exchange(ref entry, lockedEntry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue