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
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
unsafe abstract class Allocator : IDisposable
|
public unsafe abstract class Allocator : IDisposable
|
||||||
{
|
{
|
||||||
public T* Allocate<T>(ulong count = 1) where T : unmanaged
|
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
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
unsafe sealed class NativeAllocator : Allocator
|
public unsafe sealed class NativeAllocator : Allocator
|
||||||
{
|
{
|
||||||
public static NativeAllocator Instance { get; } = new();
|
public static NativeAllocator Instance { get; } = new();
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace ARMeilleure.Signal
|
||||||
{
|
{
|
||||||
public static class NativeSignalHandlerGenerator
|
public static class NativeSignalHandlerGenerator
|
||||||
{
|
{
|
||||||
public const int MaxTrackedRanges = 8;
|
public const int MaxTrackedRanges = 16;
|
||||||
|
|
||||||
private const int StructAddressOffset = 0;
|
private const int StructAddressOffset = 0;
|
||||||
private const int StructWriteOffset = 4;
|
private const int StructWriteOffset = 4;
|
||||||
|
|
|
@ -46,7 +46,7 @@ namespace ARMeilleure.Translation
|
||||||
public IMemoryManager Memory { get; }
|
public IMemoryManager Memory { get; }
|
||||||
|
|
||||||
public EntryTable<uint> CountTable { get; }
|
public EntryTable<uint> CountTable { get; }
|
||||||
public AddressTable<ulong> FunctionTable { get; }
|
public IAddressTable<ulong> FunctionTable { get; }
|
||||||
public TranslatorStubs Stubs { get; }
|
public TranslatorStubs Stubs { get; }
|
||||||
|
|
||||||
public ulong EntryAddress { get; }
|
public ulong EntryAddress { get; }
|
||||||
|
@ -62,7 +62,7 @@ namespace ARMeilleure.Translation
|
||||||
public ArmEmitterContext(
|
public ArmEmitterContext(
|
||||||
IMemoryManager memory,
|
IMemoryManager memory,
|
||||||
EntryTable<uint> countTable,
|
EntryTable<uint> countTable,
|
||||||
AddressTable<ulong> funcTable,
|
IAddressTable<ulong> funcTable,
|
||||||
TranslatorStubs stubs,
|
TranslatorStubs stubs,
|
||||||
ulong entryAddress,
|
ulong entryAddress,
|
||||||
bool highCq,
|
bool highCq,
|
||||||
|
|
|
@ -22,47 +22,13 @@ namespace ARMeilleure.Translation
|
||||||
{
|
{
|
||||||
public class Translator
|
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 IJitMemoryAllocator _allocator;
|
||||||
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
private readonly ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
|
|
||||||
private readonly Ptc _ptc;
|
private readonly Ptc _ptc;
|
||||||
|
|
||||||
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
internal TranslatorCache<TranslatedFunction> Functions { get; }
|
||||||
internal AddressTable<ulong> FunctionTable { get; }
|
internal 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; }
|
||||||
|
@ -71,7 +37,7 @@ namespace ARMeilleure.Translation
|
||||||
private Thread[] _backgroundTranslationThreads;
|
private Thread[] _backgroundTranslationThreads;
|
||||||
private volatile int _threadCount;
|
private volatile int _threadCount;
|
||||||
|
|
||||||
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, bool for64Bits)
|
public Translator(IJitMemoryAllocator allocator, IMemoryManager memory, IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
_allocator = allocator;
|
_allocator = allocator;
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
|
@ -84,22 +50,9 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
JitCache.Initialize(allocator);
|
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>();
|
CountTable = new EntryTable<uint>();
|
||||||
Functions = new TranslatorCache<TranslatedFunction>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
FunctionTable = new AddressTable<ulong>(levels, useSparseTable);
|
FunctionTable = functionTable;
|
||||||
Stubs = new TranslatorStubs(FunctionTable);
|
Stubs = new TranslatorStubs(FunctionTable);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace ARMeilleure.Translation
|
||||||
|
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
|
||||||
private readonly AddressTable<ulong> _functionTable;
|
private readonly IAddressTable<ulong> _functionTable;
|
||||||
private readonly Lazy<IntPtr> _dispatchStub;
|
private readonly Lazy<IntPtr> _dispatchStub;
|
||||||
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
private readonly Lazy<DispatcherFunction> _dispatchLoop;
|
||||||
private readonly Lazy<WrapperFunction> _contextWrapper;
|
private readonly Lazy<WrapperFunction> _contextWrapper;
|
||||||
|
@ -86,7 +86,7 @@ namespace ARMeilleure.Translation
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="functionTable">Function table used to store pointers to the functions that the guest code will call</param>
|
/// <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>
|
/// <exception cref="ArgumentNullException"><paramref name="translator"/> is null</exception>
|
||||||
public TranslatorStubs(AddressTable<ulong> functionTable)
|
public TranslatorStubs(IAddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(functionTable);
|
ArgumentNullException.ThrowIfNull(functionTable);
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
using ARMeilleure.Diagnostics;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.Cpu.Signal;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
using static Ryujinx.Cpu.MemoryEhMeilleure;
|
||||||
|
|
||||||
namespace ARMeilleure.Common
|
namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
|
@ -12,7 +14,7 @@ namespace ARMeilleure.Common
|
||||||
/// Represents a table of guest address to a value.
|
/// Represents a table of guest address to a value.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <typeparam name="TEntry">Type of the value</typeparam>
|
/// <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>
|
/// <summary>
|
||||||
/// If true, the sparse 2-level table should be used to improve performance.
|
/// If true, the sparse 2-level table should be used to improve performance.
|
||||||
|
@ -20,48 +22,6 @@ namespace ARMeilleure.Common
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool UseSparseTable => true;
|
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
|
private readonly struct AddressTablePage
|
||||||
{
|
{
|
||||||
public readonly bool IsSparse;
|
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 bool _disposed;
|
||||||
private TEntry** _table;
|
private TEntry** _table;
|
||||||
private readonly List<AddressTablePage> _pages;
|
private readonly List<AddressTablePage> _pages;
|
||||||
|
@ -84,24 +88,19 @@ namespace ARMeilleure.Common
|
||||||
private readonly SparseMemoryBlock _fillBottomLevel;
|
private readonly SparseMemoryBlock _fillBottomLevel;
|
||||||
private readonly TEntry* _fillBottomLevelPtr;
|
private readonly TEntry* _fillBottomLevelPtr;
|
||||||
|
|
||||||
private readonly List<SparseMemoryBlock> _sparseReserved;
|
private readonly List<TableSparseBlock> _sparseReserved;
|
||||||
private readonly ulong _sparseBlockSize;
|
private readonly ulong _sparseBlockSize;
|
||||||
private readonly ReaderWriterLockSlim _sparseLock;
|
private readonly ReaderWriterLockSlim _sparseLock;
|
||||||
|
|
||||||
private ulong _sparseReservedOffset;
|
private ulong _sparseReservedOffset;
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets the bits used by the <see cref="Levels"/> of the <see cref="AddressTable{TEntry}"/> instance.
|
|
||||||
/// </summary>
|
|
||||||
public ulong Mask { get; }
|
public ulong Mask { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets the <see cref="Level"/>s used by the <see cref="AddressTable{TEntry}"/> instance.
|
public AddressTableLevel[] Levels { get; }
|
||||||
/// </summary>
|
|
||||||
public Level[] Levels { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets or sets the default fill value of newly created leaf pages.
|
|
||||||
/// </summary>
|
|
||||||
public TEntry Fill
|
public TEntry Fill
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -114,10 +113,7 @@ namespace ARMeilleure.Common
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// Gets the base address of the <see cref="EntryTable{TEntry}"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ObjectDisposedException"><see cref="EntryTable{TEntry}"/> instance was disposed</exception>
|
|
||||||
public IntPtr Base
|
public IntPtr Base
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
|
@ -139,7 +135,7 @@ namespace ARMeilleure.Common
|
||||||
/// <param name="sparse">True if the bottom page should be sparsely mapped</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(Level[] levels, bool sparse)
|
public AddressTable(AddressTableLevel[] levels, bool sparse)
|
||||||
{
|
{
|
||||||
ArgumentNullException.ThrowIfNull(levels);
|
ArgumentNullException.ThrowIfNull(levels);
|
||||||
|
|
||||||
|
@ -171,13 +167,30 @@ namespace ARMeilleure.Common
|
||||||
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
|
_fillBottomLevel = new SparseMemoryBlock(bottomLevelSize, null, _sparseFill);
|
||||||
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
|
_fillBottomLevelPtr = (TEntry*)_fillBottomLevel.Block.Pointer;
|
||||||
|
|
||||||
_sparseReserved = new List<SparseMemoryBlock>();
|
_sparseReserved = new List<TableSparseBlock>();
|
||||||
_sparseLock = new ReaderWriterLockSlim();
|
_sparseLock = new ReaderWriterLockSlim();
|
||||||
|
|
||||||
_sparseBlockSize = bottomLevelSize << 3;
|
_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)
|
private void UpdateFill(TEntry fillValue)
|
||||||
{
|
{
|
||||||
if (_sparseFill != null)
|
if (_sparseFill != null)
|
||||||
|
@ -189,24 +202,13 @@ namespace ARMeilleure.Common
|
||||||
_fill = fillValue;
|
_fill = fillValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// 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>
|
|
||||||
public bool IsValid(ulong address)
|
public bool IsValid(ulong address)
|
||||||
{
|
{
|
||||||
return (address & ~Mask) == 0;
|
return (address & ~Mask) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <inheritdoc/>
|
||||||
/// 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>
|
|
||||||
public ref TEntry GetValue(ulong address)
|
public ref TEntry GetValue(ulong address)
|
||||||
{
|
{
|
||||||
ObjectDisposedException.ThrowIf(_disposed, this);
|
ObjectDisposedException.ThrowIf(_disposed, this);
|
||||||
|
@ -239,12 +241,12 @@ namespace ARMeilleure.Common
|
||||||
|
|
||||||
for (int i = 0; i < Levels.Length - 1; i++)
|
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)];
|
ref TEntry* nextPage = ref page[level.GetValue(address)];
|
||||||
|
|
||||||
if (nextPage == null || nextPage == _fillBottomLevelPtr)
|
if (nextPage == null || nextPage == _fillBottomLevelPtr)
|
||||||
{
|
{
|
||||||
ref Level nextLevel = ref Levels[i + 1];
|
ref AddressTableLevel nextLevel = ref Levels[i + 1];
|
||||||
|
|
||||||
if (i == Levels.Length - 2)
|
if (i == Levels.Length - 2)
|
||||||
{
|
{
|
||||||
|
@ -273,8 +275,10 @@ namespace ARMeilleure.Common
|
||||||
|
|
||||||
try
|
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)
|
if (ptr >= sparse.Block.Pointer && ptr < sparse.Block.Pointer + (IntPtr)sparse.Block.Size)
|
||||||
{
|
{
|
||||||
sparse.EnsureMapped((ulong)(ptr - sparse.Block.Pointer));
|
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)
|
private IntPtr GetFillValue(int level)
|
||||||
{
|
{
|
||||||
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
if (_fillBottomLevel != null && level == Levels.Length - 2)
|
||||||
|
@ -316,9 +325,28 @@ namespace ARMeilleure.Common
|
||||||
return _table;
|
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)
|
private void InitLeafPage(Span<byte> page)
|
||||||
{
|
{
|
||||||
MemoryMarshal.Cast<byte, TEntry>(page).Fill(_fill);
|
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>
|
/// <summary>
|
||||||
|
@ -333,6 +361,8 @@ namespace ARMeilleure.Common
|
||||||
{
|
{
|
||||||
var size = sizeof(T) * length;
|
var size = sizeof(T) * length;
|
||||||
|
|
||||||
|
reservedSize += size;
|
||||||
|
|
||||||
AddressTablePage page;
|
AddressTablePage page;
|
||||||
|
|
||||||
if (_sparse && leaf)
|
if (_sparse && leaf)
|
||||||
|
@ -341,12 +371,10 @@ namespace ARMeilleure.Common
|
||||||
|
|
||||||
if (_sparseReserved.Count == 0 || _sparseReservedOffset == _sparseBlockSize)
|
if (_sparseReserved.Count == 0 || _sparseReservedOffset == _sparseBlockSize)
|
||||||
{
|
{
|
||||||
_sparseReserved.Add(new SparseMemoryBlock(_sparseBlockSize, InitLeafPage, _sparseFill));
|
ReserveNewSparseBlock();
|
||||||
|
|
||||||
_sparseReservedOffset = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SparseMemoryBlock block = _sparseReserved.Last();
|
SparseMemoryBlock block = _sparseReserved.Last().Block;
|
||||||
|
|
||||||
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
page = new AddressTablePage(true, block.Block.Pointer + (IntPtr)_sparseReservedOffset);
|
||||||
|
|
||||||
|
@ -365,7 +393,7 @@ namespace ARMeilleure.Common
|
||||||
|
|
||||||
_pages.Add(page);
|
_pages.Add(page);
|
||||||
|
|
||||||
TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
//TranslatorEventSource.Log.AddressTableAllocated(size, leaf);
|
||||||
|
|
||||||
return page.Address;
|
return page.Address;
|
||||||
}
|
}
|
||||||
|
@ -398,11 +426,13 @@ namespace ARMeilleure.Common
|
||||||
|
|
||||||
if (_sparse)
|
if (_sparse)
|
||||||
{
|
{
|
||||||
foreach (SparseMemoryBlock block in _sparseReserved)
|
foreach (TableSparseBlock block in _sparseReserved)
|
||||||
{
|
{
|
||||||
block.Dispose();
|
block.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_sparseReserved.Clear();
|
||||||
|
|
||||||
_fillBottomLevel.Dispose();
|
_fillBottomLevel.Dispose();
|
||||||
_sparseFill.Dispose();
|
_sparseFill.Dispose();
|
||||||
_sparseLock.Dispose();
|
_sparseLock.Dispose();
|
|
@ -1,3 +1,4 @@
|
||||||
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using ARMeilleure.Translation;
|
using ARMeilleure.Translation;
|
||||||
using Ryujinx.Cpu.Signal;
|
using Ryujinx.Cpu.Signal;
|
||||||
|
@ -9,11 +10,14 @@ 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;
|
||||||
_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())
|
if (memory.Type.IsHostMappedOrTracked())
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
using ARMeilleure.Common;
|
||||||
using ARMeilleure.Memory;
|
using ARMeilleure.Memory;
|
||||||
using Ryujinx.Cpu.Jit;
|
using Ryujinx.Cpu.Jit;
|
||||||
using Ryujinx.Cpu.LightningJit.State;
|
using Ryujinx.Cpu.LightningJit.State;
|
||||||
|
@ -8,11 +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;
|
||||||
_translator = new Translator(memory, for64Bit);
|
_functionTable = AddressTable<ulong>.CreateForArm(for64Bit);
|
||||||
|
|
||||||
|
_translator = new Translator(memory, _functionTable);
|
||||||
|
|
||||||
memory.UnmapEvent += UnmapHandler;
|
memory.UnmapEvent += UnmapHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,39 +19,6 @@ namespace Ryujinx.Cpu.LightningJit
|
||||||
// Should be enabled on platforms that enforce W^X.
|
// Should be enabled on platforms that enforce W^X.
|
||||||
private static bool IsNoWxPlatform => false;
|
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 ConcurrentQueue<KeyValuePair<ulong, TranslatedFunction>> _oldFuncs;
|
||||||
private readonly NoWxCache _noWxCache;
|
private readonly NoWxCache _noWxCache;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
@ -61,7 +28,7 @@ namespace Ryujinx.Cpu.LightningJit
|
||||||
internal TranslatorStubs Stubs { get; }
|
internal TranslatorStubs Stubs { get; }
|
||||||
internal IMemoryManager Memory { get; }
|
internal IMemoryManager Memory { get; }
|
||||||
|
|
||||||
public Translator(IMemoryManager memory, bool for64Bits)
|
public Translator(IMemoryManager memory, AddressTable<ulong> functionTable)
|
||||||
{
|
{
|
||||||
Memory = memory;
|
Memory = memory;
|
||||||
|
|
||||||
|
@ -76,21 +43,8 @@ namespace Ryujinx.Cpu.LightningJit
|
||||||
JitCache.Initialize(new JitMemoryAllocator(forJit: true));
|
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>();
|
Functions = new TranslatorCache<TranslatedFunction>();
|
||||||
FunctionTable = new AddressTable<ulong>(levels, useSparseTable);
|
FunctionTable = functionTable;
|
||||||
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
Stubs = new TranslatorStubs(FunctionTable, _noWxCache);
|
||||||
|
|
||||||
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub;
|
||||||
|
|
|
@ -2,6 +2,7 @@ using Ryujinx.Common;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Ryujinx.Memory
|
namespace Ryujinx.Memory
|
||||||
{
|
{
|
||||||
|
@ -66,15 +67,15 @@ namespace Ryujinx.Memory
|
||||||
{
|
{
|
||||||
// Need to map some more memory.
|
// Need to map some more memory.
|
||||||
|
|
||||||
block = new MemoryBlock(MapGranularity, MemoryAllocationFlags.Mirrorable | MemoryAllocationFlags.NoMap);
|
block = new MemoryBlock(MapGranularity, MemoryAllocationFlags.Mirrorable);
|
||||||
|
|
||||||
_mappedBlocks.Add(block);
|
_mappedBlocks.Add(block);
|
||||||
|
|
||||||
_mappedBlockUsage = 0;
|
_mappedBlockUsage = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_pageInit(block.GetSpan(_mappedBlockUsage, (int)_pageSize));
|
||||||
_reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize);
|
_reservedBlock.MapView(block, _mappedBlockUsage, pageOffset, _pageSize);
|
||||||
_pageInit(_reservedBlock.GetSpan(pageOffset, (int)_pageSize));
|
|
||||||
|
|
||||||
_mappedBlockUsage += _pageSize;
|
_mappedBlockUsage += _pageSize;
|
||||||
}
|
}
|
||||||
|
@ -87,7 +88,7 @@ namespace Ryujinx.Memory
|
||||||
ref ulong entry = ref _mappedPageBitmap[bitmapIndex];
|
ref ulong entry = ref _mappedPageBitmap[bitmapIndex];
|
||||||
ulong bit = 1UL << (pageIndex & 63);
|
ulong bit = 1UL << (pageIndex & 63);
|
||||||
|
|
||||||
if ((entry & bit) == 0)
|
if ((Volatile.Read(ref entry) & bit) == 0)
|
||||||
{
|
{
|
||||||
// Not mapped.
|
// Not mapped.
|
||||||
|
|
||||||
|
@ -95,11 +96,15 @@ namespace Ryujinx.Memory
|
||||||
{
|
{
|
||||||
// Check the bit while locked to make sure that this only happens once.
|
// 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));
|
MapPage(offset & ~(_pageSize - 1));
|
||||||
|
|
||||||
entry |= bit;
|
lockedEntry |= bit;
|
||||||
|
|
||||||
|
Interlocked.Exchange(ref entry, lockedEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue