Merge branch 'master' into vulkan
This commit is contained in:
@@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include "xenia/base/assert.h"
|
#include "xenia/base/assert.h"
|
||||||
#include "xenia/base/byte_order.h"
|
#include "xenia/base/byte_order.h"
|
||||||
|
#include "xenia/base/platform.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace memory {
|
namespace memory {
|
||||||
@@ -96,12 +97,21 @@ void AlignedFree(T* ptr) {
|
|||||||
#endif // XE_COMPILER_MSVC
|
#endif // XE_COMPILER_MSVC
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if XE_PLATFORM_WIN32
|
||||||
|
// HANDLE.
|
||||||
typedef void* FileMappingHandle;
|
typedef void* FileMappingHandle;
|
||||||
|
constexpr FileMappingHandle kFileMappingHandleInvalid = nullptr;
|
||||||
|
#else
|
||||||
|
// File descriptor.
|
||||||
|
typedef int FileMappingHandle;
|
||||||
|
constexpr FileMappingHandle kFileMappingHandleInvalid = -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
||||||
size_t length, PageAccess access,
|
size_t length, PageAccess access,
|
||||||
bool commit);
|
bool commit);
|
||||||
void CloseFileMappingHandle(FileMappingHandle handle);
|
void CloseFileMappingHandle(FileMappingHandle handle,
|
||||||
|
const std::filesystem::path& path);
|
||||||
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||||
PageAccess access, size_t file_offset);
|
PageAccess access, size_t file_offset);
|
||||||
bool UnmapFileView(FileMappingHandle handle, void* base_address, size_t length);
|
bool UnmapFileView(FileMappingHandle handle, void* base_address, size_t length);
|
||||||
|
|||||||
@@ -40,7 +40,13 @@ void* AllocFixed(void* base_address, size_t length,
|
|||||||
AllocationType allocation_type, PageAccess access) {
|
AllocationType allocation_type, PageAccess access) {
|
||||||
// mmap does not support reserve / commit, so ignore allocation_type.
|
// mmap does not support reserve / commit, so ignore allocation_type.
|
||||||
uint32_t prot = ToPosixProtectFlags(access);
|
uint32_t prot = ToPosixProtectFlags(access);
|
||||||
return mmap(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
void* result = mmap(base_address, length, prot,
|
||||||
|
MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
|
||||||
|
if (result == MAP_FAILED) {
|
||||||
|
return nullptr;
|
||||||
|
} else {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeallocFixed(void* base_address, size_t length,
|
bool DeallocFixed(void* base_address, size_t length,
|
||||||
@@ -80,25 +86,28 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
|||||||
assert_always();
|
assert_always();
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
oflag |= O_CREAT;
|
oflag |= O_CREAT;
|
||||||
int ret = shm_open(path.c_str(), oflag, 0777);
|
auto full_path = "/" / path;
|
||||||
if (ret > 0) {
|
int ret = shm_open(full_path.c_str(), oflag, 0777);
|
||||||
ftruncate64(ret, length);
|
if (ret < 0) {
|
||||||
|
return kFileMappingHandleInvalid;
|
||||||
}
|
}
|
||||||
|
ftruncate64(ret, length);
|
||||||
return ret <= 0 ? nullptr : reinterpret_cast<FileMappingHandle>(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloseFileMappingHandle(FileMappingHandle handle) {
|
void CloseFileMappingHandle(FileMappingHandle handle,
|
||||||
close((intptr_t)handle);
|
const std::filesystem::path& path) {
|
||||||
|
close(handle);
|
||||||
|
auto full_path = "/" / path;
|
||||||
|
shm_unlink(full_path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||||
PageAccess access, size_t file_offset) {
|
PageAccess access, size_t file_offset) {
|
||||||
uint32_t prot = ToPosixProtectFlags(access);
|
uint32_t prot = ToPosixProtectFlags(access);
|
||||||
return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS,
|
return mmap64(base_address, length, prot, MAP_PRIVATE | MAP_ANONYMOUS, handle,
|
||||||
reinterpret_cast<intptr_t>(handle), file_offset);
|
file_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool UnmapFileView(FileMappingHandle handle, void* base_address,
|
bool UnmapFileView(FileMappingHandle handle, void* base_address,
|
||||||
|
|||||||
@@ -147,12 +147,16 @@ FileMappingHandle CreateFileMappingHandle(const std::filesystem::path& path,
|
|||||||
bool commit) {
|
bool commit) {
|
||||||
DWORD protect =
|
DWORD protect =
|
||||||
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
|
ToWin32ProtectFlags(access) | (commit ? SEC_COMMIT : SEC_RESERVE);
|
||||||
|
auto full_path = "Local" / path;
|
||||||
return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect,
|
return CreateFileMappingW(INVALID_HANDLE_VALUE, NULL, protect,
|
||||||
static_cast<DWORD>(length >> 32),
|
static_cast<DWORD>(length >> 32),
|
||||||
static_cast<DWORD>(length), path.c_str());
|
static_cast<DWORD>(length), full_path.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CloseFileMappingHandle(FileMappingHandle handle) { CloseHandle(handle); }
|
void CloseFileMappingHandle(FileMappingHandle handle,
|
||||||
|
const std::filesystem::path& path) {
|
||||||
|
CloseHandle(handle);
|
||||||
|
}
|
||||||
|
|
||||||
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
void* MapFileView(FileMappingHandle handle, void* base_address, size_t length,
|
||||||
PageAccess access, size_t file_offset) {
|
PageAccess access, size_t file_offset) {
|
||||||
|
|||||||
@@ -10,6 +10,9 @@
|
|||||||
#include "xenia/base/memory.h"
|
#include "xenia/base/memory.h"
|
||||||
|
|
||||||
#include "third_party/catch/include/catch.hpp"
|
#include "third_party/catch/include/catch.hpp"
|
||||||
|
#include "third_party/fmt/include/fmt/format.h"
|
||||||
|
|
||||||
|
#include "xenia/base/clock.h"
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace base {
|
namespace base {
|
||||||
@@ -414,6 +417,58 @@ TEST_CASE("copy_and_swap_16_in_32_unaligned", "Copy and Swap") {
|
|||||||
REQUIRE(true == true);
|
REQUIRE(true == true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("create_and_close_file_mapping", "Virtual Memory Mapping") {
|
||||||
|
auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount());
|
||||||
|
auto memory = xe::memory::CreateFileMappingHandle(
|
||||||
|
path, 0x100, xe::memory::PageAccess::kReadWrite, true);
|
||||||
|
REQUIRE(memory != xe::memory::FileMappingHandleInvalid);
|
||||||
|
xe::memory::CloseFileMappingHandle(memory, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("map_view", "Virtual Memory Mapping") {
|
||||||
|
auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount());
|
||||||
|
const size_t length = 0x100;
|
||||||
|
auto memory = xe::memory::CreateFileMappingHandle(
|
||||||
|
path, length, xe::memory::PageAccess::kReadWrite, true);
|
||||||
|
REQUIRE(memory != xe::memory::FileMappingHandleInvalid);
|
||||||
|
|
||||||
|
uintptr_t address = 0x100000000;
|
||||||
|
auto view =
|
||||||
|
xe::memory::MapFileView(memory, reinterpret_cast<void*>(address), length,
|
||||||
|
xe::memory::PageAccess::kReadWrite, 0);
|
||||||
|
REQUIRE(reinterpret_cast<uintptr_t>(view) == address);
|
||||||
|
|
||||||
|
xe::memory::UnmapFileView(memory, reinterpret_cast<void*>(address), length);
|
||||||
|
xe::memory::CloseFileMappingHandle(memory, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("read_write_view", "Virtual Memory Mapping") {
|
||||||
|
const size_t length = 0x100;
|
||||||
|
auto path = fmt::format("xenia_test_{}", Clock::QueryHostTickCount());
|
||||||
|
auto memory = xe::memory::CreateFileMappingHandle(
|
||||||
|
path, length, xe::memory::PageAccess::kReadWrite, true);
|
||||||
|
REQUIRE(memory != xe::memory::FileMappingHandleInvalid);
|
||||||
|
|
||||||
|
uintptr_t address = 0x100000000;
|
||||||
|
auto view =
|
||||||
|
xe::memory::MapFileView(memory, reinterpret_cast<void*>(address), length,
|
||||||
|
xe::memory::PageAccess::kReadWrite, 0);
|
||||||
|
REQUIRE(reinterpret_cast<uintptr_t>(view) == address);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < length; i += sizeof(uint8_t)) {
|
||||||
|
auto p_value = reinterpret_cast<uint8_t*>(address + i);
|
||||||
|
*p_value = i;
|
||||||
|
}
|
||||||
|
for (uint32_t i = 0; i < length; i += sizeof(uint8_t)) {
|
||||||
|
auto p_value = reinterpret_cast<uint8_t*>(address + i);
|
||||||
|
uint8_t value = *p_value;
|
||||||
|
REQUIRE(value == i);
|
||||||
|
}
|
||||||
|
|
||||||
|
xe::memory::UnmapFileView(memory, reinterpret_cast<void*>(address), length);
|
||||||
|
xe::memory::CloseFileMappingHandle(memory, path);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace base
|
} // namespace base
|
||||||
} // namespace xe
|
} // namespace xe
|
||||||
|
|||||||
@@ -813,7 +813,7 @@ TEST_CASE("Create and Run Thread", "Thread") {
|
|||||||
result = Wait(Thread::GetCurrentThread(), false, 50ms);
|
result = Wait(Thread::GetCurrentThread(), false, 50ms);
|
||||||
REQUIRE(result == WaitResult::kTimeout);
|
REQUIRE(result == WaitResult::kTimeout);
|
||||||
|
|
||||||
params.stack_size = 16 * 1024;
|
params.stack_size = 16 * 1024 * 1024;
|
||||||
thread = Thread::Create(params, [] {
|
thread = Thread::Create(params, [] {
|
||||||
while (true) {
|
while (true) {
|
||||||
Thread::Exit(-1);
|
Thread::Exit(-1);
|
||||||
|
|||||||
@@ -505,6 +505,7 @@ class PosixCondition<Thread> : public PosixConditionBase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (pthread_create(&thread_, &attr, ThreadStartRoutine, start_data) != 0) {
|
if (pthread_create(&thread_, &attr, ThreadStartRoutine, start_data) != 0) {
|
||||||
|
pthread_attr_destroy(&attr);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
pthread_attr_destroy(&attr);
|
pthread_attr_destroy(&attr);
|
||||||
@@ -1006,7 +1007,10 @@ Thread* Thread::GetCurrentThread() {
|
|||||||
pthread_t handle = pthread_self();
|
pthread_t handle = pthread_self();
|
||||||
|
|
||||||
current_thread_ = new PosixThread(handle);
|
current_thread_ = new PosixThread(handle);
|
||||||
atexit([] { delete current_thread_; });
|
// TODO(bwrsandman): Disabling deleting thread_local current thread to prevent
|
||||||
|
// assert in destructor. Since this is thread local, the
|
||||||
|
// "memory leaking" is controlled.
|
||||||
|
// atexit([] { delete current_thread_; });
|
||||||
|
|
||||||
return current_thread_;
|
return current_thread_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -40,11 +40,11 @@ X64CodeCache::~X64CodeCache() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unmap all views and close mapping.
|
// Unmap all views and close mapping.
|
||||||
if (mapping_) {
|
if (mapping_ != xe::memory::kFileMappingHandleInvalid) {
|
||||||
xe::memory::UnmapFileView(mapping_, generated_code_base_,
|
xe::memory::UnmapFileView(mapping_, generated_code_base_,
|
||||||
kGeneratedCodeSize);
|
kGeneratedCodeSize);
|
||||||
xe::memory::CloseFileMappingHandle(mapping_);
|
xe::memory::CloseFileMappingHandle(mapping_, file_name_);
|
||||||
mapping_ = nullptr;
|
mapping_ = xe::memory::kFileMappingHandleInvalid;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,12 +63,11 @@ bool X64CodeCache::Initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create mmap file. This allows us to share the code cache with the debugger.
|
// Create mmap file. This allows us to share the code cache with the debugger.
|
||||||
file_name_ =
|
file_name_ = fmt::format("xenia_code_cache_{}", Clock::QueryHostTickCount());
|
||||||
fmt::format("Local\\xenia_code_cache_{}", Clock::QueryHostTickCount());
|
|
||||||
mapping_ = xe::memory::CreateFileMappingHandle(
|
mapping_ = xe::memory::CreateFileMappingHandle(
|
||||||
file_name_, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite,
|
file_name_, kGeneratedCodeSize, xe::memory::PageAccess::kExecuteReadWrite,
|
||||||
false);
|
false);
|
||||||
if (!mapping_) {
|
if (mapping_ == xe::memory::kFileMappingHandleInvalid) {
|
||||||
XELOGE("Unable to create code cache mmap");
|
XELOGE("Unable to create code cache mmap");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,7 +100,8 @@ class X64CodeCache : public CodeCache {
|
|||||||
UnwindReservation unwind_reservation) {}
|
UnwindReservation unwind_reservation) {}
|
||||||
|
|
||||||
std::filesystem::path file_name_;
|
std::filesystem::path file_name_;
|
||||||
xe::memory::FileMappingHandle mapping_ = nullptr;
|
xe::memory::FileMappingHandle mapping_ =
|
||||||
|
xe::memory::kFileMappingHandleInvalid;
|
||||||
|
|
||||||
// NOTE: the global critical region must be held when manipulating the offsets
|
// NOTE: the global critical region must be held when manipulating the offsets
|
||||||
// or counts of anything, to keep the tables consistent and ordered.
|
// or counts of anything, to keep the tables consistent and ordered.
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ class Param {
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
Param() : ordinal_(-1) {}
|
Param() : ordinal_(-1) {}
|
||||||
explicit Param(Init& init) : ordinal_(--init.ordinal) {}
|
explicit Param(Init& init) : ordinal_(init.ordinal++) {}
|
||||||
|
|
||||||
template <typename V>
|
template <typename V>
|
||||||
void LoadValue(Init& init, V* out_value) {
|
void LoadValue(Init& init, V* out_value) {
|
||||||
@@ -519,10 +519,13 @@ xe::cpu::Export* RegisterExport(R (*fn)(Ps&...), const char* name,
|
|||||||
++export_entry->function_data.call_count;
|
++export_entry->function_data.call_count;
|
||||||
Param::Init init = {
|
Param::Init init = {
|
||||||
ppc_context,
|
ppc_context,
|
||||||
sizeof...(Ps),
|
|
||||||
0,
|
0,
|
||||||
};
|
};
|
||||||
auto params = std::make_tuple<Ps...>(Ps(init)...);
|
// Using braces initializer instead of make_tuple because braces
|
||||||
|
// enforce execution order across compilers.
|
||||||
|
// The make_tuple order is undefined per the C++ standard and
|
||||||
|
// cause inconsitencies between msvc and clang.
|
||||||
|
std::tuple<Ps...> params = {Ps(init)...};
|
||||||
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
||||||
(!(export_entry->tags & xe::cpu::ExportTag::kHighFrequency) ||
|
(!(export_entry->tags & xe::cpu::ExportTag::kHighFrequency) ||
|
||||||
cvars::log_high_frequency_kernel_calls)) {
|
cvars::log_high_frequency_kernel_calls)) {
|
||||||
@@ -554,9 +557,13 @@ xe::cpu::Export* RegisterExport(void (*fn)(Ps&...), const char* name,
|
|||||||
++export_entry->function_data.call_count;
|
++export_entry->function_data.call_count;
|
||||||
Param::Init init = {
|
Param::Init init = {
|
||||||
ppc_context,
|
ppc_context,
|
||||||
sizeof...(Ps),
|
0,
|
||||||
};
|
};
|
||||||
auto params = std::make_tuple<Ps...>(Ps(init)...);
|
// Using braces initializer instead of make_tuple because braces
|
||||||
|
// enforce execution order across compilers.
|
||||||
|
// The make_tuple order is undefined per the C++ standard and
|
||||||
|
// cause inconsitencies between msvc and clang.
|
||||||
|
std::tuple<Ps...> params = {Ps(init)...};
|
||||||
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
if (export_entry->tags & xe::cpu::ExportTag::kLog &&
|
||||||
(!(export_entry->tags & xe::cpu::ExportTag::kHighFrequency) ||
|
(!(export_entry->tags & xe::cpu::ExportTag::kHighFrequency) ||
|
||||||
cvars::log_high_frequency_kernel_calls)) {
|
cvars::log_high_frequency_kernel_calls)) {
|
||||||
|
|||||||
@@ -113,11 +113,11 @@ Memory::~Memory() {
|
|||||||
heaps_.physical.Dispose();
|
heaps_.physical.Dispose();
|
||||||
|
|
||||||
// Unmap all views and close mapping.
|
// Unmap all views and close mapping.
|
||||||
if (mapping_) {
|
if (mapping_ != xe::memory::kFileMappingHandleInvalid) {
|
||||||
UnmapViews();
|
UnmapViews();
|
||||||
xe::memory::CloseFileMappingHandle(mapping_);
|
xe::memory::CloseFileMappingHandle(mapping_, file_name_);
|
||||||
mapping_base_ = nullptr;
|
mapping_base_ = nullptr;
|
||||||
mapping_ = nullptr;
|
mapping_ = xe::memory::kFileMappingHandleInvalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual_membase_ = nullptr;
|
virtual_membase_ = nullptr;
|
||||||
@@ -125,8 +125,7 @@ Memory::~Memory() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool Memory::Initialize() {
|
bool Memory::Initialize() {
|
||||||
file_name_ =
|
file_name_ = fmt::format("xenia_memory_{}", Clock::QueryHostTickCount());
|
||||||
fmt::format("Local\\xenia_memory_{}", Clock::QueryHostTickCount());
|
|
||||||
|
|
||||||
// Create main page file-backed mapping. This is all reserved but
|
// Create main page file-backed mapping. This is all reserved but
|
||||||
// uncommitted (so it shouldn't expand page file).
|
// uncommitted (so it shouldn't expand page file).
|
||||||
@@ -134,9 +133,9 @@ bool Memory::Initialize() {
|
|||||||
file_name_,
|
file_name_,
|
||||||
// entire 4gb space + 512mb physical:
|
// entire 4gb space + 512mb physical:
|
||||||
0x11FFFFFFF, xe::memory::PageAccess::kReadWrite, false);
|
0x11FFFFFFF, xe::memory::PageAccess::kReadWrite, false);
|
||||||
if (!mapping_) {
|
if (mapping_ == xe::memory::kFileMappingHandleInvalid) {
|
||||||
XELOGE("Unable to reserve the 4gb guest address space.");
|
XELOGE("Unable to reserve the 4gb guest address space.");
|
||||||
assert_not_null(mapping_);
|
assert_always();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -491,7 +491,8 @@ class Memory {
|
|||||||
uint8_t* virtual_membase_ = nullptr;
|
uint8_t* virtual_membase_ = nullptr;
|
||||||
uint8_t* physical_membase_ = nullptr;
|
uint8_t* physical_membase_ = nullptr;
|
||||||
|
|
||||||
xe::memory::FileMappingHandle mapping_ = nullptr;
|
xe::memory::FileMappingHandle mapping_ =
|
||||||
|
xe::memory::kFileMappingHandleInvalid;
|
||||||
uint8_t* mapping_base_ = nullptr;
|
uint8_t* mapping_base_ = nullptr;
|
||||||
union {
|
union {
|
||||||
struct {
|
struct {
|
||||||
|
|||||||
Reference in New Issue
Block a user