diff --git a/src/ARMeilleure/Common/AddressTableLevel.cs b/src/ARMeilleure/Common/AddressTableLevel.cs new file mode 100644 index 000000000..6107726ee --- /dev/null +++ b/src/ARMeilleure/Common/AddressTableLevel.cs @@ -0,0 +1,44 @@ +namespace ARMeilleure.Common +{ + /// + /// Represents a level in an . + /// + public readonly struct AddressTableLevel + { + /// + /// Gets the index of the in the guest address. + /// + public int Index { get; } + + /// + /// Gets the length of the in the guest address. + /// + public int Length { get; } + + /// + /// Gets the mask which masks the bits used by the . + /// + public ulong Mask => ((1ul << Length) - 1) << Index; + + /// + /// Initializes a new instance of the structure with the specified + /// and . + /// + /// Index of the + /// Length of the + public AddressTableLevel(int index, int length) + { + (Index, Length) = (index, length); + } + + /// + /// Gets the value of the from the specified guest . + /// + /// Guest address + /// Value of the from the specified guest + public int GetValue(ulong address) + { + return (int)((address & Mask) >> Index); + } + } +} diff --git a/src/ARMeilleure/Common/AddressTablePresets.cs b/src/ARMeilleure/Common/AddressTablePresets.cs new file mode 100644 index 000000000..e7eaf62cd --- /dev/null +++ b/src/ARMeilleure/Common/AddressTablePresets.cs @@ -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; + } + } + } +} diff --git a/src/ARMeilleure/Common/Allocator.cs b/src/ARMeilleure/Common/Allocator.cs index 6905a614f..de6a77ebe 100644 --- a/src/ARMeilleure/Common/Allocator.cs +++ b/src/ARMeilleure/Common/Allocator.cs @@ -2,7 +2,7 @@ using System; namespace ARMeilleure.Common { - unsafe abstract class Allocator : IDisposable + public unsafe abstract class Allocator : IDisposable { public T* Allocate(ulong count = 1) where T : unmanaged { diff --git a/src/ARMeilleure/Common/IAddressTable.cs b/src/ARMeilleure/Common/IAddressTable.cs new file mode 100644 index 000000000..4924b448b --- /dev/null +++ b/src/ARMeilleure/Common/IAddressTable.cs @@ -0,0 +1,51 @@ +using System; + +namespace ARMeilleure.Common +{ + public interface IAddressTable : IDisposable where TEntry : unmanaged + { + /// + /// 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. + /// + static bool UseSparseTable { get; } + + /// + /// Gets the bits used by the of the instance. + /// + ulong Mask { get; } + + /// + /// Gets the s used by the instance. + /// + AddressTableLevel[] Levels { get; } + + /// + /// Gets or sets the default fill value of newly created leaf pages. + /// + TEntry Fill { get; set; } + + /// + /// Gets the base address of the . + /// + /// instance was disposed + IntPtr Base { get; } + + /// + /// Determines if the specified is in the range of the + /// . + /// + /// Guest address + /// if is valid; otherwise + bool IsValid(ulong address); + + /// + /// Gets a reference to the value at the specified guest . + /// + /// Guest address + /// Reference to the value at the specified guest + /// instance was disposed + /// is not mapped + ref TEntry GetValue(ulong address); + } +} diff --git a/src/ARMeilleure/Common/NativeAllocator.cs b/src/ARMeilleure/Common/NativeAllocator.cs index 93c48adda..102e33ebe 100644 --- a/src/ARMeilleure/Common/NativeAllocator.cs +++ b/src/ARMeilleure/Common/NativeAllocator.cs @@ -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(); diff --git a/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs index 2ec5bc1b3..896d372d1 100644 --- a/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs +++ b/src/ARMeilleure/Signal/NativeSignalHandlerGenerator.cs @@ -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; diff --git a/src/ARMeilleure/Translation/ArmEmitterContext.cs b/src/ARMeilleure/Translation/ArmEmitterContext.cs index e24074739..54cd97d53 100644 --- a/src/ARMeilleure/Translation/ArmEmitterContext.cs +++ b/src/ARMeilleure/Translation/ArmEmitterContext.cs @@ -46,7 +46,7 @@ namespace ARMeilleure.Translation public IMemoryManager Memory { get; } public EntryTable CountTable { get; } - public AddressTable FunctionTable { get; } + public IAddressTable FunctionTable { get; } public TranslatorStubs Stubs { get; } public ulong EntryAddress { get; } @@ -62,7 +62,7 @@ namespace ARMeilleure.Translation public ArmEmitterContext( IMemoryManager memory, EntryTable countTable, - AddressTable funcTable, + IAddressTable funcTable, TranslatorStubs stubs, ulong entryAddress, bool highCq, diff --git a/src/ARMeilleure/Translation/Translator.cs b/src/ARMeilleure/Translation/Translator.cs index 9a3d7cec5..45758059c 100644 --- a/src/ARMeilleure/Translation/Translator.cs +++ b/src/ARMeilleure/Translation/Translator.cs @@ -22,47 +22,13 @@ namespace ARMeilleure.Translation { public class Translator { - private static readonly AddressTable.Level[] _levels64Bit = - new AddressTable.Level[] - { - new(31, 17), - new(23, 8), - new(15, 8), - new( 7, 8), - new( 2, 5), - }; - - private static readonly AddressTable.Level[] _levels32Bit = - new AddressTable.Level[] - { - new(31, 17), - new(23, 8), - new(15, 8), - new( 7, 8), - new( 1, 6), - }; - - private static readonly AddressTable.Level[] _levels64BitSparse = - new AddressTable.Level[] - { - new(23, 16), - new( 2, 21), - }; - - private static readonly AddressTable.Level[] _levels32BitSparse = - new AddressTable.Level[] - { - new(22, 10), - new( 1, 21), - }; - private readonly IJitMemoryAllocator _allocator; private readonly ConcurrentQueue> _oldFuncs; private readonly Ptc _ptc; internal TranslatorCache Functions { get; } - internal AddressTable FunctionTable { get; } + internal IAddressTable FunctionTable { get; } internal EntryTable 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 functionTable) { _allocator = allocator; Memory = memory; @@ -84,22 +50,9 @@ namespace ARMeilleure.Translation JitCache.Initialize(allocator); - AddressTable.Level[] levels; - - bool useSparseTable = AddressTable.UseSparseTable; - - if (useSparseTable) - { - levels = for64Bits ? _levels64BitSparse : _levels32BitSparse; - } - else - { - levels = for64Bits ? _levels64Bit : _levels32Bit; - } - CountTable = new EntryTable(); Functions = new TranslatorCache(); - FunctionTable = new AddressTable(levels, useSparseTable); + FunctionTable = functionTable; Stubs = new TranslatorStubs(FunctionTable); FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; diff --git a/src/ARMeilleure/Translation/TranslatorStubs.cs b/src/ARMeilleure/Translation/TranslatorStubs.cs index d80823a8b..379caa283 100644 --- a/src/ARMeilleure/Translation/TranslatorStubs.cs +++ b/src/ARMeilleure/Translation/TranslatorStubs.cs @@ -19,7 +19,7 @@ namespace ARMeilleure.Translation private bool _disposed; - private readonly AddressTable _functionTable; + private readonly IAddressTable _functionTable; private readonly Lazy _dispatchStub; private readonly Lazy _dispatchLoop; private readonly Lazy _contextWrapper; @@ -86,7 +86,7 @@ namespace ARMeilleure.Translation /// /// Function table used to store pointers to the functions that the guest code will call /// is null - public TranslatorStubs(AddressTable functionTable) + public TranslatorStubs(IAddressTable functionTable) { ArgumentNullException.ThrowIfNull(functionTable); diff --git a/src/ARMeilleure/Common/AddressTable.cs b/src/Ryujinx.Cpu/AddressTable.cs similarity index 72% rename from src/ARMeilleure/Common/AddressTable.cs rename to src/Ryujinx.Cpu/AddressTable.cs index ebe5dfb01..e8a0c423b 100644 --- a/src/ARMeilleure/Common/AddressTable.cs +++ b/src/Ryujinx.Cpu/AddressTable.cs @@ -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. /// /// Type of the value - public unsafe class AddressTable : IDisposable where TEntry : unmanaged + public unsafe class AddressTable : IAddressTable where TEntry : unmanaged { /// /// If true, the sparse 2-level table should be used to improve performance. @@ -20,48 +22,6 @@ namespace ARMeilleure.Common /// public static bool UseSparseTable => true; - /// - /// Represents a level in an . - /// - public readonly struct Level - { - /// - /// Gets the index of the in the guest address. - /// - public int Index { get; } - - /// - /// Gets the length of the in the guest address. - /// - public int Length { get; } - - /// - /// Gets the mask which masks the bits used by the . - /// - public ulong Mask => ((1ul << Length) - 1) << Index; - - /// - /// Initializes a new instance of the structure with the specified - /// and . - /// - /// Index of the - /// Length of the - public Level(int index, int length) - { - (Index, Length) = (index, length); - } - - /// - /// Gets the value of the from the specified guest . - /// - /// Guest address - /// Value of the from the specified guest - 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 } } + /// + /// A sparsely mapped block of memory with a signal handler to map pages as they're accessed. + /// + private readonly struct TableSparseBlock : IDisposable + { + public readonly SparseMemoryBlock Block; + public readonly TrackingEventDelegate TrackingEvent; + + public TableSparseBlock(ulong size, Action 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 _pages; @@ -84,24 +88,19 @@ namespace ARMeilleure.Common private readonly SparseMemoryBlock _fillBottomLevel; private readonly TEntry* _fillBottomLevelPtr; - private readonly List _sparseReserved; + private readonly List _sparseReserved; private readonly ulong _sparseBlockSize; private readonly ReaderWriterLockSlim _sparseLock; + private ulong _sparseReservedOffset; - /// - /// Gets the bits used by the of the instance. - /// + /// public ulong Mask { get; } - /// - /// Gets the s used by the instance. - /// - public Level[] Levels { get; } + /// + public AddressTableLevel[] Levels { get; } - /// - /// Gets or sets the default fill value of newly created leaf pages. - /// + /// public TEntry Fill { get @@ -114,10 +113,7 @@ namespace ARMeilleure.Common } } - /// - /// Gets the base address of the . - /// - /// instance was disposed + /// public IntPtr Base { get @@ -139,7 +135,7 @@ namespace ARMeilleure.Common /// True if the bottom page should be sparsely mapped /// is null /// Length of is less than 2 - 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(); + _sparseReserved = new List(); _sparseLock = new ReaderWriterLockSlim(); _sparseBlockSize = bottomLevelSize << 3; } } + /// + /// Create an instance for an ARM function table. + /// Selects the best table structure for A32/A64, taking into account whether sparse mapping is supported. + /// + /// True if the guest is A64, false otherwise + /// An for ARM function lookup + public static AddressTable CreateForArm(bool for64Bits) + { + bool sparse = UseSparseTable; + + return new AddressTable(AddressTablePresets.GetArmPreset(for64Bits, sparse), sparse); + } + + /// + /// Update the fill value for the bottom level of the table. + /// + /// New fill value private void UpdateFill(TEntry fillValue) { if (_sparseFill != null) @@ -189,24 +202,13 @@ namespace ARMeilleure.Common _fill = fillValue; } - /// - /// Determines if the specified is in the range of the - /// . - /// - /// Guest address - /// if is valid; otherwise + /// public bool IsValid(ulong address) { return (address & ~Mask) == 0; } - /// - /// Gets a reference to the value at the specified guest . - /// - /// Guest address - /// Reference to the value at the specified guest - /// instance was disposed - /// is not mapped + /// 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 } } + /// + /// Get the fill value for a non-leaf level of the table. + /// + /// Level to get the fill value for + /// The fill value 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; + + /// + /// Initialize a leaf page with the fill value. + /// + /// Page to initialize private void InitLeafPage(Span page) { MemoryMarshal.Cast(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; } /// @@ -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(); diff --git a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs index 9893c59b2..2b70b0239 100644 --- a/src/Ryujinx.Cpu/Jit/JitCpuContext.cs +++ b/src/Ryujinx.Cpu/Jit/JitCpuContext.cs @@ -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 _functionTable; public JitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) { _tickSource = tickSource; - _translator = new Translator(new JitMemoryAllocator(forJit: true), memory, for64Bit); + _functionTable = AddressTable.CreateForArm(for64Bit); + + _translator = new Translator(new JitMemoryAllocator(forJit: true), memory, _functionTable); if (memory.Type.IsHostMappedOrTracked()) { diff --git a/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs b/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs index b63636e39..2d4ca0fca 100644 --- a/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs +++ b/src/Ryujinx.Cpu/LightningJit/LightningJitCpuContext.cs @@ -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 _functionTable; public LightningJitCpuContext(ITickSource tickSource, IMemoryManager memory, bool for64Bit) { _tickSource = tickSource; - _translator = new Translator(memory, for64Bit); + _functionTable = AddressTable.CreateForArm(for64Bit); + + _translator = new Translator(memory, _functionTable); + memory.UnmapEvent += UnmapHandler; } diff --git a/src/Ryujinx.Cpu/LightningJit/Translator.cs b/src/Ryujinx.Cpu/LightningJit/Translator.cs index 19f883efa..bb12ac5fd 100644 --- a/src/Ryujinx.Cpu/LightningJit/Translator.cs +++ b/src/Ryujinx.Cpu/LightningJit/Translator.cs @@ -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.Level[] _levels64Bit = - new AddressTable.Level[] - { - new(31, 17), - new(23, 8), - new(15, 8), - new( 7, 8), - new( 2, 5), - }; - - private static readonly AddressTable.Level[] _levels32Bit = - new AddressTable.Level[] - { - new(23, 9), - new(15, 8), - new( 7, 8), - new( 1, 6), - }; - - private static readonly AddressTable.Level[] _levels64BitSparse = - new AddressTable.Level[] - { - new(23, 16), - new( 2, 21), - }; - - private static readonly AddressTable.Level[] _levels32BitSparse = - new AddressTable.Level[] - { - new(22, 10), - new( 1, 21), - }; - private readonly ConcurrentQueue> _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 functionTable) { Memory = memory; @@ -76,21 +43,8 @@ namespace Ryujinx.Cpu.LightningJit JitCache.Initialize(new JitMemoryAllocator(forJit: true)); } - bool useSparseTable = AddressTable.UseSparseTable; - - AddressTable.Level[] levels; - - if (useSparseTable) - { - levels = for64Bits ? _levels64BitSparse : _levels32BitSparse; - } - else - { - levels = for64Bits ? _levels64Bit : _levels32Bit; - } - Functions = new TranslatorCache(); - FunctionTable = new AddressTable(levels, useSparseTable); + FunctionTable = functionTable; Stubs = new TranslatorStubs(FunctionTable, _noWxCache); FunctionTable.Fill = (ulong)Stubs.SlowDispatchStub; diff --git a/src/Ryujinx.Memory/SparseMemoryBlock.cs b/src/Ryujinx.Memory/SparseMemoryBlock.cs index 8c6dbea86..523685de1 100644 --- a/src/Ryujinx.Memory/SparseMemoryBlock.cs +++ b/src/Ryujinx.Memory/SparseMemoryBlock.cs @@ -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); } } }