summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2020-12-29 21:16:57 -0300
committerGravatar ReinUsesLisp2020-12-29 21:54:49 -0300
commitb3587102d160fb74a12935a79f06ee8a12712f12 (patch)
tree2afb7123bafef085cb97fc97f96be1aaf8c3a660 /src/common
parentMerge pull request #5248 from ReinUsesLisp/update-dynarmic (diff)
downloadyuzu-b3587102d160fb74a12935a79f06ee8a12712f12.tar.gz
yuzu-b3587102d160fb74a12935a79f06ee8a12712f12.tar.xz
yuzu-b3587102d160fb74a12935a79f06ee8a12712f12.zip
core/memory: Read and write page table atomically
Squash attributes into the pointer's integer, making them an uintptr_t pair containing 2 bits at the bottom and then the pointer. These bits are currently unused thanks to alignment requirements. Configure Dynarmic to mask out these bits on pointer reads. While we are at it, remove some unused attributes carried over from Citra. Read/Write and other hot functions use a two step unpacking process that is less readable to stop MSVC from emitting an extra AND instruction in the hot path: mov rdi,rcx shr rdx,0Ch mov r8,qword ptr [rax+8] mov rax,qword ptr [r8+rdx*8] mov rdx,rax -and al,3 and rdx,0FFFFFFFFFFFFFFFCh je Core::Memory::Memory::Impl::Read<unsigned char> mov rax,qword ptr [vaddr] movzx eax,byte ptr [rdx+rax]
Diffstat (limited to 'src/common')
-rw-r--r--src/common/page_table.cpp10
-rw-r--r--src/common/page_table.h68
-rw-r--r--src/common/virtual_buffer.h10
3 files changed, 65 insertions, 23 deletions
diff --git a/src/common/page_table.cpp b/src/common/page_table.cpp
index bccea0894..8fd8620fd 100644
--- a/src/common/page_table.cpp
+++ b/src/common/page_table.cpp
@@ -10,16 +10,10 @@ PageTable::PageTable() = default;
10 10
11PageTable::~PageTable() noexcept = default; 11PageTable::~PageTable() noexcept = default;
12 12
13void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, 13void PageTable::Resize(size_t address_space_width_in_bits, size_t page_size_in_bits) {
14 bool has_attribute) { 14 const size_t num_page_table_entries{1ULL << (address_space_width_in_bits - page_size_in_bits)};
15 const std::size_t num_page_table_entries{1ULL
16 << (address_space_width_in_bits - page_size_in_bits)};
17 pointers.resize(num_page_table_entries); 15 pointers.resize(num_page_table_entries);
18 backing_addr.resize(num_page_table_entries); 16 backing_addr.resize(num_page_table_entries);
19
20 if (has_attribute) {
21 attributes.resize(num_page_table_entries);
22 }
23} 17}
24 18
25} // namespace Common 19} // namespace Common
diff --git a/src/common/page_table.h b/src/common/page_table.h
index 9754fabf9..8d4ee9249 100644
--- a/src/common/page_table.h
+++ b/src/common/page_table.h
@@ -4,6 +4,7 @@
4 4
5#pragma once 5#pragma once
6 6
7#include <atomic>
7#include <tuple> 8#include <tuple>
8 9
9#include "common/common_types.h" 10#include "common/common_types.h"
@@ -20,10 +21,6 @@ enum class PageType : u8 {
20 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and 21 /// Page is mapped to regular memory, but also needs to check for rasterizer cache flushing and
21 /// invalidation 22 /// invalidation
22 RasterizerCachedMemory, 23 RasterizerCachedMemory,
23 /// Page is mapped to a I/O region. Writing and reading to this page is handled by functions.
24 Special,
25 /// Page is allocated for use.
26 Allocated,
27}; 24};
28 25
29struct SpecialRegion { 26struct SpecialRegion {
@@ -48,6 +45,59 @@ struct SpecialRegion {
48 * mimics the way a real CPU page table works. 45 * mimics the way a real CPU page table works.
49 */ 46 */
50struct PageTable { 47struct PageTable {
48 /// Number of bits reserved for attribute tagging.
49 /// This can be at most the guaranteed alignment of the pointers in the page table.
50 static constexpr int ATTRIBUTE_BITS = 2;
51
52 /**
53 * Pair of host pointer and page type attribute.
54 * This uses the lower bits of a given pointer to store the attribute tag.
55 * Writing and reading the pointer attribute pair is guaranteed to be atomic for the same method
56 * call. In other words, they are guaranteed to be synchronized at all times.
57 */
58 class PageInfo {
59 public:
60 /// Returns the page pointer
61 [[nodiscard]] u8* Pointer() const noexcept {
62 return ExtractPointer(raw.load(std::memory_order_relaxed));
63 }
64
65 /// Returns the page type attribute
66 [[nodiscard]] PageType Type() const noexcept {
67 return ExtractType(raw.load(std::memory_order_relaxed));
68 }
69
70 /// Returns the page pointer and attribute pair, extracted from the same atomic read
71 [[nodiscard]] std::pair<u8*, PageType> PointerType() const noexcept {
72 const uintptr_t non_atomic_raw = raw.load(std::memory_order_relaxed);
73 return {ExtractPointer(non_atomic_raw), ExtractType(non_atomic_raw)};
74 }
75
76 /// Returns the raw representation of the page information.
77 /// Use ExtractPointer and ExtractType to unpack the value.
78 [[nodiscard]] uintptr_t Raw() const noexcept {
79 return raw.load(std::memory_order_relaxed);
80 }
81
82 /// Write a page pointer and type pair atomically
83 void Store(u8* pointer, PageType type) noexcept {
84 raw.store(reinterpret_cast<uintptr_t>(pointer) | static_cast<uintptr_t>(type));
85 }
86
87 /// Unpack a pointer from a page info raw representation
88 [[nodiscard]] static u8* ExtractPointer(uintptr_t raw) noexcept {
89 return reinterpret_cast<u8*>(raw & (~uintptr_t{0} << ATTRIBUTE_BITS));
90 }
91
92 /// Unpack a page type from a page info raw representation
93 [[nodiscard]] static PageType ExtractType(uintptr_t raw) noexcept {
94 return static_cast<PageType>(raw & ((uintptr_t{1} << ATTRIBUTE_BITS) - 1));
95 }
96
97 private:
98 std::atomic<uintptr_t> raw;
99 };
100
51 PageTable(); 101 PageTable();
52 ~PageTable() noexcept; 102 ~PageTable() noexcept;
53 103
@@ -63,20 +113,16 @@ struct PageTable {
63 * 113 *
64 * @param address_space_width_in_bits The address size width in bits. 114 * @param address_space_width_in_bits The address size width in bits.
65 * @param page_size_in_bits The page size in bits. 115 * @param page_size_in_bits The page size in bits.
66 * @param has_attribute Whether or not this page has any backing attributes.
67 */ 116 */
68 void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits, 117 void Resize(size_t address_space_width_in_bits, size_t page_size_in_bits);
69 bool has_attribute);
70 118
71 /** 119 /**
72 * Vector of memory pointers backing each page. An entry can only be non-null if the 120 * Vector of memory pointers backing each page. An entry can only be non-null if the
73 * corresponding entry in the `attributes` vector is of type `Memory`. 121 * corresponding attribute element is of type `Memory`.
74 */ 122 */
75 VirtualBuffer<u8*> pointers; 123 VirtualBuffer<PageInfo> pointers;
76 124
77 VirtualBuffer<u64> backing_addr; 125 VirtualBuffer<u64> backing_addr;
78
79 VirtualBuffer<PageType> attributes;
80}; 126};
81 127
82} // namespace Common 128} // namespace Common
diff --git a/src/common/virtual_buffer.h b/src/common/virtual_buffer.h
index 91d430036..fb1a6f81f 100644
--- a/src/common/virtual_buffer.h
+++ b/src/common/virtual_buffer.h
@@ -15,10 +15,12 @@ void FreeMemoryPages(void* base, std::size_t size) noexcept;
15template <typename T> 15template <typename T>
16class VirtualBuffer final { 16class VirtualBuffer final {
17public: 17public:
18 static_assert( 18 // TODO: Uncomment this and change Common::PageTable::PageInfo to be trivially constructible
19 std::is_trivially_constructible_v<T>, 19 // using std::atomic_ref once libc++ has support for it
20 "T must be trivially constructible, as non-trivial constructors will not be executed " 20 // static_assert(
21 "with the current allocator"); 21 // std::is_trivially_constructible_v<T>,
22 // "T must be trivially constructible, as non-trivial constructors will not be executed "
23 // "with the current allocator");
22 24
23 constexpr VirtualBuffer() = default; 25 constexpr VirtualBuffer() = default;
24 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} { 26 explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {