Преглед изворни кода

Merge pull request #5249 from ReinUsesLisp/lock-free-pages

core/memory: Read and write page table atomically
bunnei пре 5 година
родитељ
комит
eb318ffffc

+ 2 - 8
src/common/page_table.cpp

@@ -10,16 +10,10 @@ PageTable::PageTable() = default;
 
 
 PageTable::~PageTable() noexcept = default;
 PageTable::~PageTable() noexcept = default;
 
 
-void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
-                       bool has_attribute) {
-    const std::size_t num_page_table_entries{1ULL
-                                             << (address_space_width_in_bits - page_size_in_bits)};
+void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
+    const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
     pointers.resize(num_page_table_entries);
     pointers.resize(num_page_table_entries);
     backing_addr.resize(num_page_table_entries);
     backing_addr.resize(num_page_table_entries);
-
-    if (has_attribute) {
-        attributes.resize(num_page_table_entries);
-    }
 }
 }
 
 
 } // namespace Common
 } // namespace Common

+ 57 - 11
src/common/page_table.h

@@ -4,6 +4,7 @@
 
 
 #pragma once
 #pragma once
 
 
+#include <atomic>
 #include <tuple>
 #include <tuple>
 
 
 #include "common/common_types.h"
 #include "common/common_types.h"
@@ -20,10 +21,6 @@ enum class PageType : u8 {
     /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
     /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
     /// invalidation
     /// invalidation
     RasterizerCachedMemory,
     RasterizerCachedMemory,
-    /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
-    Special,
-    /// Page is allocated for use.
-    Allocated,
 };
 };
 
 
 struct SpecialRegion {
 struct SpecialRegion {
@@ -48,6 +45,59 @@ struct SpecialRegion {
  * mimics the way a real CPU page table works.
  * mimics the way a real CPU page table works.
  */
  */
 struct PageTable {
 struct PageTable {
+    /// Number of bits reserved for attribute tagging.
+    /// This can be at most the guaranteed alignment of the pointers in the page table.
+    static constexpr int ATTRIBUTE_BITS = 2;
+
+    /**
+     * Pair of host pointer and page type attribute.
+     * This uses the lower bits of a given pointer to store the attribute tag.
+     * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
+     * call. In other words, they are guaranteed to be synchronized at all times.
+     */
+    class PageInfo {
+    public:
+        /// Returns the page pointer
+        [[nodiscard]] u8* Pointer() const noexcept {
+            return ExtractPointer(raw.load(std::memory_order_relaxed));
+        }
+
+        /// Returns the page type attribute
+        [[nodiscard]] PageType Type() const noexcept {
+            return ExtractType(raw.load(std::memory_order_relaxed));
+        }
+
+        /// Returns the page pointer and attribute pair, extracted from the same atomic read
+        [[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept {
+            const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
+            return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
+        }
+
+        /// Returns the raw representation of the page information.
+        /// Use ExtractPointer and ExtractType to unpack the value.
+        [[nodiscard]] uintptr_t Raw() const noexcept {
+            return raw.load(std::memory_order_relaxed);
+        }
+
+        /// Write a page pointer and type pair atomically
+        void Store(u8* pointer, PageType type) noexcept {
+            raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type));
+        }
+
+        /// Unpack a pointer from a page info raw representation
+        [[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept {
+            return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS));
+        }
+
+        /// Unpack a page type from a page info raw representation
+        [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
+            return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
+        }
+
+    private:
+        std::atomic<uintptr_t> raw;
+    };
+
     PageTable();
     PageTable();
     ~PageTable() noexcept;
     ~PageTable() noexcept;
 
 
@@ -63,20 +113,16 @@ struct PageTable {
      *
      *
      * @param address_space_width_in_bits The address size width in bits.
      * @param address_space_width_in_bits The address size width in bits.
      * @param page_size_in_bits           The page size in bits.
      * @param page_size_in_bits           The page size in bits.
-     * @param has_attribute               Whether or not this page has any backing attributes.
      */
      */
-    void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
-                bool has_attribute);
+    void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
 
 
     /**
     /**
      * Vector of memory pointers backing each page. An entry can only be non-null if the
      * Vector of memory pointers backing each page. An entry can only be non-null if the
-     * corresponding entry in the `attributes` vector is of type `Memory`.
+     * corresponding attribute element is of type `Memory`.
      */
      */
-    VirtualBuffer<u8*> pointers;
+    VirtualBuffer<PageInfo> pointers;
 
 
     VirtualBuffer<u64> backing_addr;
     VirtualBuffer<u64> backing_addr;
-
-    VirtualBuffer<PageType> attributes;
 };
 };
 
 
 } // namespace Common
 } // namespace Common

+ 6 - 4
src/common/virtual_buffer.h

@@ -15,10 +15,12 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
 template <typename T>
 template <typename T>
 class VirtualBuffer final {
 class VirtualBuffer final {
 public:
 public:
-    static_assert(
-        std::is_trivially_constructible_v<T>,
-        "T must be trivially constructible, as non-trivial constructors will not be executed "
-        "with the current allocator");
+    // TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
+    // using std::atomic_ref once libc++ has support for it
+    // static_assert(
+    //     std::is_trivially_constructible_v<T>,
+    //     "T must be trivially constructible, as non-trivial constructors will not be executed "
+    //     "with the current allocator");
 
 
     constexpr VirtualBuffer() = default;
     constexpr VirtualBuffer() = default;
     explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
     explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {

+ 1 - 0
src/core/arm/dynarmic/arm_dynarmic_32.cpp

@@ -133,6 +133,7 @@ std::shared_ptr<Dynarmic::A32::Jit> ARM_Dynarmic_32::MakeJit(Common::PageTable&
     config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
     config.page_table = reinterpret_cast<std::array<std::uint8_t*, NUM_PAGE_TABLE_ENTRIES>*>(
         page_table.pointers.data());
         page_table.pointers.data());
     config.absolute_offset_page_table = true;
     config.absolute_offset_page_table = true;
+    config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
     config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
     config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
     config.only_detect_misalignment_via_page_table_on_page_boundary = true;
     config.only_detect_misalignment_via_page_table_on_page_boundary = true;
 
 

+ 1 - 0
src/core/arm/dynarmic/arm_dynarmic_64.cpp

@@ -152,6 +152,7 @@ std::shared_ptr<Dynarmic::A64::Jit> ARM_Dynarmic_64::MakeJit(Common::PageTable&
     // Memory
     // Memory
     config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
     config.page_table = reinterpret_cast<void**>(page_table.pointers.data());
     config.page_table_address_space_bits = address_space_bits;
     config.page_table_address_space_bits = address_space_bits;
+    config.page_table_pointer_mask_bits = Common::PageTable::ATTRIBUTE_BITS;
     config.silently_mirror_page_table = false;
     config.silently_mirror_page_table = false;
     config.absolute_offset_page_table = true;
     config.absolute_offset_page_table = true;
     config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;
     config.detect_misaligned_access_via_page_table = 16 | 32 | 64 | 128;

+ 1 - 1
src/core/hle/kernel/memory/page_table.cpp

@@ -265,7 +265,7 @@ ResultCode PageTable::InitializeForProcess(FileSys::ProgramAddressSpaceType as_t
     physical_memory_usage = 0;
     physical_memory_usage = 0;
     memory_pool = pool;
     memory_pool = pool;
 
 
-    page_table_impl.Resize(address_space_width, PageBits, true);
+    page_table_impl.Resize(address_space_width, PageBits);
 
 
     return InitializeMemoryLayout(start, end);
     return InitializeMemoryLayout(start, end);
 }
 }

+ 64 - 123
src/core/memory.cpp

@@ -4,7 +4,6 @@
 
 
 #include <algorithm>
 #include <algorithm>
 #include <cstring>
 #include <cstring>
-#include <mutex>
 #include <optional>
 #include <optional>
 #include <utility>
 #include <utility>
 
 
@@ -68,21 +67,8 @@ struct Memory::Impl {
 
 
     bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
     bool IsValidVirtualAddress(const Kernel::Process& process, const VAddr vaddr) const {
         const auto& page_table = process.PageTable().PageTableImpl();
         const auto& page_table = process.PageTable().PageTableImpl();
-
-        const u8* const page_pointer = page_table.pointers[vaddr >> PAGE_BITS];
-        if (page_pointer != nullptr) {
-            return true;
-        }
-
-        if (page_table.attributes[vaddr >> PAGE_BITS] == Common::PageType::RasterizerCachedMemory) {
-            return true;
-        }
-
-        if (page_table.attributes[vaddr >> PAGE_BITS] != Common::PageType::Special) {
-            return false;
-        }
-
-        return false;
+        const auto [pointer, type] = page_table.pointers[vaddr >> PAGE_BITS].PointerType();
+        return pointer != nullptr || type == Common::PageType::RasterizerCachedMemory;
     }
     }
 
 
     bool IsValidVirtualAddress(VAddr vaddr) const {
     bool IsValidVirtualAddress(VAddr vaddr) const {
@@ -100,17 +86,15 @@ struct Memory::Impl {
     }
     }
 
 
     u8* GetPointer(const VAddr vaddr) const {
     u8* GetPointer(const VAddr vaddr) const {
-        u8* const page_pointer{current_page_table->pointers[vaddr >> PAGE_BITS]};
-        if (page_pointer) {
-            return page_pointer + vaddr;
+        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
+            return pointer + vaddr;
         }
         }
-
-        if (current_page_table->attributes[vaddr >> PAGE_BITS] ==
-            Common::PageType::RasterizerCachedMemory) {
+        const auto type = Common::PageTable::PageInfo::ExtractType(raw_pointer);
+        if (type == Common::PageType::RasterizerCachedMemory) {
             return GetPointerFromRasterizerCachedMemory(vaddr);
             return GetPointerFromRasterizerCachedMemory(vaddr);
         }
         }
-
-        return {};
+        return nullptr;
     }
     }
 
 
     u8 Read8(const VAddr addr) {
     u8 Read8(const VAddr addr) {
@@ -222,7 +206,8 @@ struct Memory::Impl {
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
 
 
-            switch (page_table.attributes[page_index]) {
+            const auto [pointer, type] = page_table.pointers[page_index].PointerType();
+            switch (type) {
             case Common::PageType::Unmapped: {
             case Common::PageType::Unmapped: {
                 LOG_ERROR(HW_Memory,
                 LOG_ERROR(HW_Memory,
                           "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
                           "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -231,10 +216,8 @@ struct Memory::Impl {
                 break;
                 break;
             }
             }
             case Common::PageType::Memory: {
             case Common::PageType::Memory: {
-                DEBUG_ASSERT(page_table.pointers[page_index]);
-
-                const u8* const src_ptr =
-                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
+                DEBUG_ASSERT(pointer);
+                const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
                 std::memcpy(dest_buffer, src_ptr, copy_amount);
                 std::memcpy(dest_buffer, src_ptr, copy_amount);
                 break;
                 break;
             }
             }
@@ -268,7 +251,8 @@ struct Memory::Impl {
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
 
 
-            switch (page_table.attributes[page_index]) {
+            const auto [pointer, type] = page_table.pointers[page_index].PointerType();
+            switch (type) {
             case Common::PageType::Unmapped: {
             case Common::PageType::Unmapped: {
                 LOG_ERROR(HW_Memory,
                 LOG_ERROR(HW_Memory,
                           "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
                           "Unmapped ReadBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -277,10 +261,8 @@ struct Memory::Impl {
                 break;
                 break;
             }
             }
             case Common::PageType::Memory: {
             case Common::PageType::Memory: {
-                DEBUG_ASSERT(page_table.pointers[page_index]);
-
-                const u8* const src_ptr =
-                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
+                DEBUG_ASSERT(pointer);
+                const u8* const src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
                 std::memcpy(dest_buffer, src_ptr, copy_amount);
                 std::memcpy(dest_buffer, src_ptr, copy_amount);
                 break;
                 break;
             }
             }
@@ -320,7 +302,8 @@ struct Memory::Impl {
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
 
 
-            switch (page_table.attributes[page_index]) {
+            const auto [pointer, type] = page_table.pointers[page_index].PointerType();
+            switch (type) {
             case Common::PageType::Unmapped: {
             case Common::PageType::Unmapped: {
                 LOG_ERROR(HW_Memory,
                 LOG_ERROR(HW_Memory,
                           "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
                           "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -328,10 +311,8 @@ struct Memory::Impl {
                 break;
                 break;
             }
             }
             case Common::PageType::Memory: {
             case Common::PageType::Memory: {
-                DEBUG_ASSERT(page_table.pointers[page_index]);
-
-                u8* const dest_ptr =
-                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
+                DEBUG_ASSERT(pointer);
+                u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
                 std::memcpy(dest_ptr, src_buffer, copy_amount);
                 std::memcpy(dest_ptr, src_buffer, copy_amount);
                 break;
                 break;
             }
             }
@@ -364,7 +345,8 @@ struct Memory::Impl {
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
 
 
-            switch (page_table.attributes[page_index]) {
+            const auto [pointer, type] = page_table.pointers[page_index].PointerType();
+            switch (type) {
             case Common::PageType::Unmapped: {
             case Common::PageType::Unmapped: {
                 LOG_ERROR(HW_Memory,
                 LOG_ERROR(HW_Memory,
                           "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
                           "Unmapped WriteBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -372,10 +354,8 @@ struct Memory::Impl {
                 break;
                 break;
             }
             }
             case Common::PageType::Memory: {
             case Common::PageType::Memory: {
-                DEBUG_ASSERT(page_table.pointers[page_index]);
-
-                u8* const dest_ptr =
-                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
+                DEBUG_ASSERT(pointer);
+                u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
                 std::memcpy(dest_ptr, src_buffer, copy_amount);
                 std::memcpy(dest_ptr, src_buffer, copy_amount);
                 break;
                 break;
             }
             }
@@ -414,7 +394,8 @@ struct Memory::Impl {
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
 
 
-            switch (page_table.attributes[page_index]) {
+            const auto [pointer, type] = page_table.pointers[page_index].PointerType();
+            switch (type) {
             case Common::PageType::Unmapped: {
             case Common::PageType::Unmapped: {
                 LOG_ERROR(HW_Memory,
                 LOG_ERROR(HW_Memory,
                           "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
                           "Unmapped ZeroBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -422,10 +403,8 @@ struct Memory::Impl {
                 break;
                 break;
             }
             }
             case Common::PageType::Memory: {
             case Common::PageType::Memory: {
-                DEBUG_ASSERT(page_table.pointers[page_index]);
-
-                u8* dest_ptr =
-                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
+                DEBUG_ASSERT(pointer);
+                u8* const dest_ptr = pointer + page_offset + (page_index << PAGE_BITS);
                 std::memset(dest_ptr, 0, copy_amount);
                 std::memset(dest_ptr, 0, copy_amount);
                 break;
                 break;
             }
             }
@@ -461,7 +440,8 @@ struct Memory::Impl {
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
                 std::min(static_cast<std::size_t>(PAGE_SIZE) - page_offset, remaining_size);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
             const auto current_vaddr = static_cast<VAddr>((page_index << PAGE_BITS) + page_offset);
 
 
-            switch (page_table.attributes[page_index]) {
+            const auto [pointer, type] = page_table.pointers[page_index].PointerType();
+            switch (type) {
             case Common::PageType::Unmapped: {
             case Common::PageType::Unmapped: {
                 LOG_ERROR(HW_Memory,
                 LOG_ERROR(HW_Memory,
                           "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
                           "Unmapped CopyBlock @ 0x{:016X} (start address = 0x{:016X}, size = {})",
@@ -470,9 +450,8 @@ struct Memory::Impl {
                 break;
                 break;
             }
             }
             case Common::PageType::Memory: {
             case Common::PageType::Memory: {
-                DEBUG_ASSERT(page_table.pointers[page_index]);
-                const u8* src_ptr =
-                    page_table.pointers[page_index] + page_offset + (page_index << PAGE_BITS);
+                DEBUG_ASSERT(pointer);
+                const u8* src_ptr = pointer + page_offset + (page_index << PAGE_BITS);
                 WriteBlock(process, dest_addr, src_ptr, copy_amount);
                 WriteBlock(process, dest_addr, src_ptr, copy_amount);
                 break;
                 break;
             }
             }
@@ -498,34 +477,19 @@ struct Memory::Impl {
         return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
         return CopyBlock(*system.CurrentProcess(), dest_addr, src_addr, size);
     }
     }
 
 
-    struct PageEntry {
-        u8* const pointer;
-        const Common::PageType attribute;
-    };
-
-    PageEntry SafePageEntry(std::size_t base) const {
-        std::lock_guard lock{rasterizer_cache_guard};
-        return {
-            .pointer = current_page_table->pointers[base],
-            .attribute = current_page_table->attributes[base],
-        };
-    }
-
     void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
     void RasterizerMarkRegionCached(VAddr vaddr, u64 size, bool cached) {
-        std::lock_guard lock{rasterizer_cache_guard};
         if (vaddr == 0) {
         if (vaddr == 0) {
             return;
             return;
         }
         }
-
         // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
         // Iterate over a contiguous CPU address space, which corresponds to the specified GPU
         // address space, marking the region as un/cached. The region is marked un/cached at a
         // address space, marking the region as un/cached. The region is marked un/cached at a
         // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
         // granularity of CPU pages, hence why we iterate on a CPU page basis (note: GPU page size
         // is different). This assumes the specified GPU address region is contiguous as well.
         // is different). This assumes the specified GPU address region is contiguous as well.
 
 
-        u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
-        for (unsigned i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
-            Common::PageType& page_type{current_page_table->attributes[vaddr >> PAGE_BITS]};
-
+        const u64 num_pages = ((vaddr + size - 1) >> PAGE_BITS) - (vaddr >> PAGE_BITS) + 1;
+        for (u64 i = 0; i < num_pages; ++i, vaddr += PAGE_SIZE) {
+            const Common::PageType page_type{
+                current_page_table->pointers[vaddr >> PAGE_BITS].Type()};
             if (cached) {
             if (cached) {
                 // Switch page type to cached if now cached
                 // Switch page type to cached if now cached
                 switch (page_type) {
                 switch (page_type) {
@@ -534,8 +498,8 @@ struct Memory::Impl {
                     // space, for example, a system module need not have a VRAM mapping.
                     // space, for example, a system module need not have a VRAM mapping.
                     break;
                     break;
                 case Common::PageType::Memory:
                 case Common::PageType::Memory:
-                    page_type = Common::PageType::RasterizerCachedMemory;
-                    current_page_table->pointers[vaddr >> PAGE_BITS] = nullptr;
+                    current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+                        nullptr, Common::PageType::RasterizerCachedMemory);
                     break;
                     break;
                 case Common::PageType::RasterizerCachedMemory:
                 case Common::PageType::RasterizerCachedMemory:
                     // There can be more than one GPU region mapped per CPU region, so it's common
                     // There can be more than one GPU region mapped per CPU region, so it's common
@@ -556,16 +520,16 @@ struct Memory::Impl {
                     // that this area is already unmarked as cached.
                     // that this area is already unmarked as cached.
                     break;
                     break;
                 case Common::PageType::RasterizerCachedMemory: {
                 case Common::PageType::RasterizerCachedMemory: {
-                    u8* pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
+                    u8* const pointer{GetPointerFromRasterizerCachedMemory(vaddr & ~PAGE_MASK)};
                     if (pointer == nullptr) {
                     if (pointer == nullptr) {
                         // It's possible that this function has been called while updating the
                         // It's possible that this function has been called while updating the
                         // pagetable after unmapping a VMA. In that case the underlying VMA will no
                         // pagetable after unmapping a VMA. In that case the underlying VMA will no
                         // longer exist, and we should just leave the pagetable entry blank.
                         // longer exist, and we should just leave the pagetable entry blank.
-                        page_type = Common::PageType::Unmapped;
+                        current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+                            nullptr, Common::PageType::Unmapped);
                     } else {
                     } else {
-                        current_page_table->pointers[vaddr >> PAGE_BITS] =
-                            pointer - (vaddr & ~PAGE_MASK);
-                        page_type = Common::PageType::Memory;
+                        current_page_table->pointers[vaddr >> PAGE_BITS].Store(
+                            pointer - (vaddr & ~PAGE_MASK), Common::PageType::Memory);
                     }
                     }
                     break;
                     break;
                 }
                 }
@@ -595,7 +559,7 @@ struct Memory::Impl {
             auto& gpu = system.GPU();
             auto& gpu = system.GPU();
             for (u64 i = 0; i < size; i++) {
             for (u64 i = 0; i < size; i++) {
                 const auto page = base + i;
                 const auto page = base + i;
-                if (page_table.attributes[page] == Common::PageType::RasterizerCachedMemory) {
+                if (page_table.pointers[page].Type() == Common::PageType::RasterizerCachedMemory) {
                     gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
                     gpu.FlushAndInvalidateRegion(page << PAGE_BITS, PAGE_SIZE);
                 }
                 }
             }
             }
@@ -610,20 +574,18 @@ struct Memory::Impl {
                        "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
                        "Mapping memory page without a pointer @ {:016x}", base * PAGE_SIZE);
 
 
             while (base != end) {
             while (base != end) {
-                page_table.attributes[base] = type;
-                page_table.pointers[base] = nullptr;
+                page_table.pointers[base].Store(nullptr, type);
                 page_table.backing_addr[base] = 0;
                 page_table.backing_addr[base] = 0;
 
 
                 base += 1;
                 base += 1;
             }
             }
         } else {
         } else {
             while (base != end) {
             while (base != end) {
-                page_table.pointers[base] =
-                    system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS);
-                page_table.attributes[base] = type;
+                page_table.pointers[base].Store(
+                    system.DeviceMemory().GetPointer(target) - (base << PAGE_BITS), type);
                 page_table.backing_addr[base] = target - (base << PAGE_BITS);
                 page_table.backing_addr[base] = target - (base << PAGE_BITS);
 
 
-                ASSERT_MSG(page_table.pointers[base],
+                ASSERT_MSG(page_table.pointers[base].Pointer(),
                            "memory mapping base yield a nullptr within the table");
                            "memory mapping base yield a nullptr within the table");
 
 
                 base += 1;
                 base += 1;
@@ -646,21 +608,13 @@ struct Memory::Impl {
     template <typename T>
     template <typename T>
     T Read(const VAddr vaddr) {
     T Read(const VAddr vaddr) {
         // Avoid adding any extra logic to this fast-path block
         // Avoid adding any extra logic to this fast-path block
-        if (const u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) {
+        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+        if (const u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
             T value;
             T value;
             std::memcpy(&value, &pointer[vaddr], sizeof(T));
             std::memcpy(&value, &pointer[vaddr], sizeof(T));
             return value;
             return value;
         }
         }
-
-        // Otherwise, we need to grab the page with a lock, in case it is currently being modified
-        const auto entry = SafePageEntry(vaddr >> PAGE_BITS);
-        if (entry.pointer) {
-            T value;
-            std::memcpy(&value, &entry.pointer[vaddr], sizeof(T));
-            return value;
-        }
-
-        switch (entry.attribute) {
+        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
         case Common::PageType::Unmapped:
         case Common::PageType::Unmapped:
             LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
             LOG_ERROR(HW_Memory, "Unmapped Read{} @ 0x{:08X}", sizeof(T) * 8, vaddr);
             return 0;
             return 0;
@@ -692,20 +646,12 @@ struct Memory::Impl {
     template <typename T>
     template <typename T>
     void Write(const VAddr vaddr, const T data) {
     void Write(const VAddr vaddr, const T data) {
         // Avoid adding any extra logic to this fast-path block
         // Avoid adding any extra logic to this fast-path block
-        if (u8* const pointer = current_page_table->pointers[vaddr >> PAGE_BITS]) {
+        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
             std::memcpy(&pointer[vaddr], &data, sizeof(T));
             std::memcpy(&pointer[vaddr], &data, sizeof(T));
             return;
             return;
         }
         }
-
-        // Otherwise, we need to grab the page with a lock, in case it is currently being modified
-        const auto entry = SafePageEntry(vaddr >> PAGE_BITS);
-        if (entry.pointer) {
-            // Memory was mapped, we are done
-            std::memcpy(&entry.pointer[vaddr], &data, sizeof(T));
-            return;
-        }
-
-        switch (entry.attribute) {
+        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
         case Common::PageType::Unmapped:
         case Common::PageType::Unmapped:
             LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
             LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
                       static_cast<u32>(data), vaddr);
                       static_cast<u32>(data), vaddr);
@@ -726,15 +672,13 @@ struct Memory::Impl {
 
 
     template <typename T>
     template <typename T>
     bool WriteExclusive(const VAddr vaddr, const T data, const T expected) {
     bool WriteExclusive(const VAddr vaddr, const T data, const T expected) {
-        u8* page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
-        if (page_pointer != nullptr) {
+        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
             // NOTE: Avoid adding any extra logic to this fast-path block
             // NOTE: Avoid adding any extra logic to this fast-path block
-            auto* pointer = reinterpret_cast<volatile T*>(&page_pointer[vaddr]);
-            return Common::AtomicCompareAndSwap(pointer, data, expected);
+            const auto volatile_pointer = reinterpret_cast<volatile T*>(&pointer[vaddr]);
+            return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
         }
         }
-
-        const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
-        switch (type) {
+        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
         case Common::PageType::Unmapped:
         case Common::PageType::Unmapped:
             LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
             LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}", sizeof(data) * 8,
                       static_cast<u32>(data), vaddr);
                       static_cast<u32>(data), vaddr);
@@ -755,15 +699,13 @@ struct Memory::Impl {
     }
     }
 
 
     bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) {
     bool WriteExclusive128(const VAddr vaddr, const u128 data, const u128 expected) {
-        u8* const page_pointer = current_page_table->pointers[vaddr >> PAGE_BITS];
-        if (page_pointer != nullptr) {
+        const uintptr_t raw_pointer = current_page_table->pointers[vaddr >> PAGE_BITS].Raw();
+        if (u8* const pointer = Common::PageTable::PageInfo::ExtractPointer(raw_pointer)) {
             // NOTE: Avoid adding any extra logic to this fast-path block
             // NOTE: Avoid adding any extra logic to this fast-path block
-            auto* pointer = reinterpret_cast<volatile u64*>(&page_pointer[vaddr]);
-            return Common::AtomicCompareAndSwap(pointer, data, expected);
+            const auto volatile_pointer = reinterpret_cast<volatile u64*>(&pointer[vaddr]);
+            return Common::AtomicCompareAndSwap(volatile_pointer, data, expected);
         }
         }
-
-        const Common::PageType type = current_page_table->attributes[vaddr >> PAGE_BITS];
-        switch (type) {
+        switch (Common::PageTable::PageInfo::ExtractType(raw_pointer)) {
         case Common::PageType::Unmapped:
         case Common::PageType::Unmapped:
             LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
             LOG_ERROR(HW_Memory, "Unmapped Write{} 0x{:08X} @ 0x{:016X}{:016X}", sizeof(data) * 8,
                       static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
                       static_cast<u64>(data[1]), static_cast<u64>(data[0]), vaddr);
@@ -783,7 +725,6 @@ struct Memory::Impl {
         return true;
         return true;
     }
     }
 
 
-    mutable std::mutex rasterizer_cache_guard;
     Common::PageTable* current_page_table = nullptr;
     Common::PageTable* current_page_table = nullptr;
     Core::System& system;
     Core::System& system;
 };
 };