Compare commits

..

38 Commits

Author SHA1 Message Date
lizzie a19d270b3f fix emul atomic 2026-05-14 01:53:53 +02:00
lizzie 9723e348d4 [hid_core] remove contentious mutex from EmulatedController and just rely on atomic semantics for fields
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-14 01:53:53 +02:00
lizzie db7ed8f95a license 2026-05-14 01:53:53 +02:00
lizzie 5a7f17d118 fx 2026-05-14 01:53:53 +02:00
lizzie 50d6f30d60 [dynarmic, macroHLE] Use faster ankerl for xbyak maps
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-14 01:53:53 +02:00
lizzie bfcbf27794 fix windows shit 2026-05-14 01:53:53 +02:00
lizzie 6c0c1535d5 fuck android 2026-05-14 01:53:53 +02:00
lizzie 66b8d0b5c1 fixup nce 2026-05-14 01:53:53 +02:00
lizzie 8bc6868fcf rem indir func 2026-05-14 01:53:53 +02:00
lizzie 6d183b8e57 fix cmake 2026-05-14 01:53:53 +02:00
lizzie 7bac557025 [common] remove ptr indirection on WallClock
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-14 01:53:53 +02:00
lizzie e615845774 e 2026-05-14 01:53:53 +02:00
lizzie 6154679802 fuck fix 2026-05-14 01:53:53 +02:00
lizzie 0677e26387 f 2026-05-14 01:53:53 +02:00
lizzie b51165beae suboptimal cgen for windows 2026-05-14 01:53:53 +02:00
lizzie 3ff2d39faf oops 2026-05-14 01:53:53 +02:00
lizzie 6f974dca83 fix license 2026-05-14 01:53:53 +02:00
lizzie c9daf37714 [dynarmic] use constant resolution instead of clobbering a register when doing spinlocks
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-14 01:53:53 +02:00
lizzie 6db95b529f use 2mb pages 2026-05-14 01:53:53 +02:00
lizzie 8581d95b82 oopsie 2026-05-14 01:53:53 +02:00
lizzie 170669fbd2 fix linux readahead 2026-05-14 01:53:53 +02:00
lizzie 4e09c6a23f alignment with log2 2026-05-14 01:53:53 +02:00
lizzie 1a74f10c9d license 2026-05-14 01:53:53 +02:00
Caio Oliveira a47b1459ab f you msvc 2026-05-14 01:53:53 +02:00
Caio Oliveira 74772d651c [chore] It can possible kill someone
Signed-off-by: Caio Oliveira <caiooliveirafarias0@gmail.com>
2026-05-14 01:53:53 +02:00
lizzie 8a8e75a99a [chore] Fix windows building 2026-05-14 01:53:53 +02:00
lizzie f4d54772f1 Add for windows and Android 2026-05-14 01:53:53 +02:00
lizzie 32fa2475d3 fuck macos 2026-05-14 01:53:53 +02:00
lizzie 29ca9aded2 super align + nosync opts 2026-05-14 01:53:53 +02:00
lizzie 66c0a295da add cstring 4 std::memcpy 2026-05-14 01:53:53 +02:00
lizzie 720e6fedb5 fucking license 2026-05-14 01:53:53 +02:00
lizzie 8fff64adc3 [fs] use mmap() to read files off the mmap system for higher throughput
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-14 01:53:53 +02:00
lizzie 57ddcf73b1 fx 2026-05-14 01:53:53 +02:00
lizzie 9d1bb08333 fx 2026-05-14 01:53:53 +02:00
lizzie eb1c28d494 ershuf 2026-05-14 01:53:53 +02:00
lizzie 5da1621921 better generation of time system archives 2026-05-14 01:53:53 +02:00
lizzie f15846eb40 Revert "[externals] nuke nx_tzdb"
This reverts commit a0b68e383df1d39022479f11b1864484ce354db9.
2026-05-14 01:53:53 +02:00
lizzie 6c243241e4 [externals] nuke nx_tzdb
Signed-off-by: lizzie <lizzie@eden-emu.dev>
2026-05-14 01:53:53 +02:00
41 changed files with 1299 additions and 1152 deletions
+281 -159
View File
@@ -7,18 +7,25 @@
#include <vector>
#include "common/assert.h"
#include "common/bit_util.h"
#include "common/fs/file.h"
#include "common/fs/fs.h"
#include "common/fs/fs_types.h"
#ifdef ANDROID
#include "common/fs/fs_android.h"
#endif
#include "common/logging.h"
#include "common/literals.h"
#ifdef _WIN32
#include <io.h>
#include <share.h>
#include <windows.h>
#else
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#endif
#ifdef _MSC_VER
@@ -36,13 +43,13 @@ namespace {
#ifdef _WIN32
/**
* Converts the file access mode and file type enums to a file access mode wide string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a wide string representing the file access mode.
*/
* Converts the file access mode and file type enums to a file access mode wide string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a wide string representing the file access mode.
*/
[[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) {
switch (type) {
case FileType::BinaryFile:
@@ -79,12 +86,12 @@ namespace {
}
/**
* Converts the file-share access flag enum to a Windows defined file-share access flag.
*
* @param flag File-share access flag
*
* @returns Windows defined file-share access flag.
*/
* Converts the file-share access flag enum to a Windows defined file-share access flag.
*
* @param flag File-share access flag
*
* @returns Windows defined file-share access flag.
*/
[[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) {
switch (flag) {
case FileShareFlag::ShareNone:
@@ -102,13 +109,13 @@ namespace {
#else
/**
* Converts the file access mode and file type enums to a file access mode string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a string representing the file access mode.
*/
* Converts the file access mode and file type enums to a file access mode string.
*
* @param mode File access mode
* @param type File type
*
* @returns A pointer to a string representing the file access mode.
*/
[[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) {
switch (type) {
case FileType::BinaryFile:
@@ -147,12 +154,12 @@ namespace {
#endif
/**
* Converts the seek origin enum to a seek origin integer.
*
* @param origin Seek origin
*
* @returns Seek origin integer.
*/
* Converts the seek origin enum to a seek origin integer.
*
* @param origin Seek origin
*
* @returns Seek origin integer.
*/
[[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) {
switch (origin) {
case SeekOrigin::SetOrigin:
@@ -178,7 +185,7 @@ std::string ReadStringFromFile(const std::filesystem::path& path, FileType type)
}
size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
std::string_view string) {
if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -189,7 +196,7 @@ size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
}
size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string) {
std::string_view string) {
if (Exists(path) && !IsFile(path)) {
return 0;
}
@@ -244,69 +251,189 @@ FileType IOFile::GetType() const {
return file_type;
}
#if defined(__unix__)
static int PlatformMapReadOnly(IOFile& io, const char* path) {
io.mmap_fd = open(path, O_RDONLY);
if (io.mmap_fd > 0) {
struct stat st;
fstat(io.mmap_fd, &st);
io.mmap_size = st.st_size;
int map_flags = MAP_PRIVATE;
#ifdef MAP_PREFAULT_READ
// Prefaults reads so the final resulting pagetable from this big stupid mmap()
// isn't comically lazily loaded, we just coalesce everything in-place for our
// lovely mmap flags; if we didn't prefault the reads the page table will be
// constructed in-place (i.e on a read-by-read basis) causing lovely soft-faults
// which would nuke any performance gains.
//
// This of course incurs a cost in the initial mmap(2) call, but that is fine.
map_flags |= MAP_PREFAULT_READ;
#endif
#ifdef MAP_NOSYNC
// This causes physical media to not be synched to our file/memory
// This means that if the read-only file is written to, we won't see changes
// or we may see changes which are just funnily scattered, in any case
// this presumes the files won't be changed during execution
//
// Do not ever use this on write files (if we ever support that); this will create
// a fun amount of fragmentation on the disk.
map_flags |= MAP_NOSYNC;
#endif
#if defined(NDEBUG) && defined(MAP_NOCORE)
map_flags |= MAP_NOCORE;
#endif
#ifdef MAP_HUGE_2MB
map_flags |= MAP_HUGE_2MB; //2mb pages
#elif defined(MAP_ALIGNED)
// File must be big enough that it's worth to super align. We can't just super-align every
// file otherwise we will run out of alignments for actually important files :)
// System doesn't guarantee a super alignment, but if it's available it will delete
// about 3 layers(?) of the TLB tree for each read/write.
// Again the cost of faults may make this negligible gains, but hey, we gotta work
// what we gotta work with.
map_flags |= MAP_ALIGNED(21); //2^21 = 2mb use 2MB pages
#endif
io.mmap_base = (u8*)mmap(nullptr, io.mmap_size, PROT_READ, map_flags, io.mmap_fd, 0);
if (io.mmap_base == MAP_FAILED) {
close(io.mmap_fd);
io.mmap_fd = -1;
} else {
using namespace Common::Literals;
// For small files it is acceptable to use a full readahead
// See https://github.com/torvalds/linux/blob/e80d033851b3bc94c3d254ac66660ddd0a49d72c/include/linux/pagemap.h#L1392
if (u64(st.st_size) >= 256_MiB) {
posix_madvise(io.mmap_base, io.mmap_size, POSIX_MADV_RANDOM);
} else {
posix_madvise(io.mmap_base, io.mmap_size, POSIX_MADV_SEQUENTIAL);
}
}
}
return io.mmap_fd;
}
static void PlatformUnmap(IOFile& io) {
if (io.mmap_fd != -1) {
munmap(io.mmap_base, io.mmap_size);
close(io.mmap_fd);
io.mmap_fd = -1;
}
}
#elif defined(__APPLE__)
// NO IMPLEMENTATION YET
#else
static int PlatformMapReadOnly(IOFile& io, const wchar_t* path) {
io.file_handle = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, nullptr);
if (HANDLE(io.file_handle) != INVALID_HANDLE_VALUE) {
io.mapping_handle = CreateFileMappingW(HANDLE(io.file_handle), nullptr, PAGE_READONLY, 0, 0, nullptr);
if (io.mapping_handle) {
io.mmap_base = (u8*)MapViewOfFile(HANDLE(io.mapping_handle), FILE_MAP_READ, 0, 0, 0);
if (io.mmap_base) {
_LARGE_INTEGER pvalue;
GetFileSizeEx(io.file_handle, &pvalue);
io.mmap_size = uint32_t(pvalue.QuadPart);
} else {
CloseHandle(io.mapping_handle);
CloseHandle(io.file_handle);
return -1;
}
} else {
CloseHandle(io.file_handle);
return -1;
}
}
return 0;
}
static void PlatformUnmap(IOFile& io) {
if(io.mapping_handle) {
if(io.mmap_base)
UnmapViewOfFile(HANDLE(io.mmap_base));
CloseHandle(HANDLE(io.mapping_handle));
}
if(io.file_handle != INVALID_HANDLE_VALUE)
CloseHandle(HANDLE(io.file_handle));
}
#endif
void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) {
Close();
file_path = path;
file_access_mode = mode;
file_type = type;
errno = 0;
#ifdef _WIN32
if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
// TODO: this probably can use better logic but oh well I'm not a windowser
file_handle = nullptr;
if (type == FileType::BinaryFile && mode == FileAccessMode::Read) {
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file"); //: {}", path.c_str());
}
}
if (file_handle == nullptr) {
if (flag != FileShareFlag::ShareNone) {
file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag));
} else {
_wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type));
}
}
#elif ANDROID
if (Android::IsContentUri(path)) {
ASSERT_MSG(mode == FileAccessMode::Read, "Content URI file access is for read-only!");
const auto fd = Android::OpenContentUri(path, Android::OpenMode::Read);
if (fd != -1) {
file = fdopen(fd, "r");
const auto error_num = errno;
if (error_num != 0 && file == nullptr) {
LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(),
strerror(error_num));
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str());
int const fd = Android::OpenContentUri(path, Android::OpenMode::Read);
if (fd != -1) {
file = fdopen(fd, "r");
if (errno != 0 && file == nullptr)
LOG_ERROR(Common_Filesystem, "Error opening file: {}, error: {}", path.c_str(), strerror(errno));
} else {
LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
}
} else {
LOG_ERROR(Common_Filesystem, "Error opening file: {}", path.c_str());
}
} else {
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
}
#elif defined(__HAIKU__) || defined(__managarm__) || defined(__OPENORBIS__) || defined(__APPLE__)
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#elif defined(__unix__)
if (type == FileType::BinaryFile && mode == FileAccessMode::Read) {
if (PlatformMapReadOnly(*this, path.c_str()) == -1) {
LOG_ERROR(Common_Filesystem, "Error mmap'ing file: {}", path.c_str());
}
}
if (mmap_fd == -1) {
file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); // mmap(2) failed or simply we can't use it
}
#else
// Some other fancy OS (ahem fucking Darwin/Mac OSX)
file = std::fopen(path.c_str(), AccessModeToStr(mode, type));
#endif
if (!IsOpen()) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
PathToUTF8String(file_path), ec.message());
}
}
void IOFile::Close() {
if (!IsOpen()) {
return;
#if defined(__APPLE__)
// NO IMPLEMENTATION YET
#else
PlatformUnmap(*this);
#endif
if (file) {
errno = 0;
const auto close_result = std::fclose(file) == 0;
if (!close_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
file = nullptr;
}
errno = 0;
const auto close_result = std::fclose(file) == 0;
if (!close_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
file = nullptr;
}
bool IOFile::IsOpen() const {
return file != nullptr;
return file != nullptr || IsMappedFile();
}
std::string IOFile::ReadString(size_t length) const {
@@ -323,137 +450,132 @@ size_t IOFile::WriteString(std::span<const char> string) const {
}
bool IOFile::Flush() const {
if (!IsOpen()) {
return false;
ASSERT(!IsMappedFile());
if (file) {
errno = 0;
auto const flush_result = std::fflush(file) == 0;
if (!flush_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return flush_result;
}
errno = 0;
#ifdef _WIN32
const auto flush_result = std::fflush(file) == 0;
#else
const auto flush_result = std::fflush(file) == 0;
#endif
if (!flush_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return flush_result;
return false;
}
bool IOFile::Commit() const {
if (!IsOpen()) {
return false;
}
errno = 0;
ASSERT(!IsMappedFile());
if (file) {
errno = 0;
#ifdef _WIN32
const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
const auto commit_result = std::fflush(file) == 0 && _commit(fileno(file)) == 0;
#else
const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
const auto commit_result = std::fflush(file) == 0 && fsync(fileno(file)) == 0;
#endif
if (!commit_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
if (!commit_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to commit the file at path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
}
return commit_result;
}
return commit_result;
return false;
}
bool IOFile::SetSize(u64 size) const {
if (!IsOpen()) {
return false;
}
errno = 0;
ASSERT(!IsMappedFile());
if (file) {
errno = 0;
#ifdef _WIN32
const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0;
const auto set_size_result = _chsize_s(fileno(file), s64(size)) == 0;
#else
const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0;
const auto set_size_result = ftruncate(fileno(file), s64(size)) == 0;
#endif
if (!set_size_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
PathToUTF8String(file_path), size, ec.message());
if (!set_size_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}",
PathToUTF8String(file_path), size, ec.message());
}
return set_size_result;
}
return set_size_result;
return false;
}
u64 IOFile::GetSize() const {
if (!IsOpen()) {
return 0;
}
// Flush any unwritten buffered data into the file prior to retrieving the file size.
std::fflush(file);
if (IsMappedFile())
return mmap_size;
if (file) {
// Flush any unwritten buffered data into the file prior to retrieving the file mmap_size.
std::fflush(file);
#if ANDROID
u64 file_size = 0;
if (Android::IsContentUri(file_path)) {
file_size = Android::GetSize(file_path);
} else {
u64 file_size = 0;
if (Android::IsContentUri(file_path)) {
file_size = Android::GetSize(file_path);
} else {
std::error_code ec;
file_size = fs::file_size(file_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file mmap_size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
}
#else
std::error_code ec;
file_size = fs::file_size(file_path, ec);
auto const file_size = fs::file_size(file_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem,
"Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file mmap_size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
}
#else
std::error_code ec;
const auto file_size = fs::file_size(file_path, ec);
if (ec) {
LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}",
PathToUTF8String(file_path), ec.message());
return 0;
}
#endif
return file_size;
return file_size;
}
return 0;
}
bool IOFile::Seek(s64 offset, SeekOrigin origin) const {
if (!IsOpen()) {
return false;
if (IsMappedFile()) {
// fuck you to whoever made this method const
switch (origin) {
case SeekOrigin::SetOrigin:
mmap_offset = s64(offset);
break;
case SeekOrigin::CurrentPosition:
mmap_offset += s64(offset);
break;
case SeekOrigin::End:
mmap_offset = s64(mmap_size) + s64(offset);
break;
}
return true;
}
errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
if (!seek_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem,
"Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
PathToUTF8String(file_path), offset, origin, ec.message());
if (file) {
errno = 0;
const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0;
if (!seek_result) {
const auto ec = std::error_code{errno, std::generic_category()};
LOG_ERROR(Common_Filesystem, "Failed to seek the file at path={}, offset={}, origin={}, ec_message={}",
PathToUTF8String(file_path), offset, origin, ec.message());
}
return seek_result;
}
return seek_result;
return false;
}
s64 IOFile::Tell() const {
if (!IsOpen()) {
return 0;
if (IsMappedFile()) {
errno = 0;
return s64(mmap_offset);
}
errno = 0;
return ftello(file);
if (file) {
errno = 0;
return ftello(file);
}
return 0;
}
} // namespace Common::FS
+258 -249
View File
@@ -1,9 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <cstdio>
#include <cstring>
#include <filesystem>
#include <span>
#include <type_traits>
@@ -21,12 +25,12 @@ enum class SeekOrigin {
};
/**
* Opens a file stream at path with the specified open mode.
*
* @param file_stream Reference to file stream
* @param path Filesystem path
* @param open_mode File stream open mode
*/
* Opens a file stream at path with the specified open mode.
*
* @param file_stream Reference to file stream
* @param path Filesystem path
* @param open_mode File stream open mode
*/
template <typename FileStream>
void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path,
std::ios_base::openmode open_mode) {
@@ -45,14 +49,14 @@ void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::op
#endif
/**
* Reads an entire file at path and returns a string of the contents read from the file.
* If the filesystem object at path is not a regular file, this function returns an empty string.
*
* @param path Filesystem path
* @param type File type
*
* @returns A string of the contents read from the file.
*/
* Reads an entire file at path and returns a string of the contents read from the file.
* If the filesystem object at path is not a regular file, this function returns an empty string.
*
* @param path Filesystem path
* @param type File type
*
* @returns A string of the contents read from the file.
*/
[[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type);
#ifdef _WIN32
@@ -67,18 +71,18 @@ template <typename Path>
#endif
/**
* Writes a string to a file at path and returns the number of characters successfully written.
* If a file already exists at path, its contents will be erased.
* If a file does not exist at path, it creates and opens a new empty file for writing.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
* Writes a string to a file at path and returns the number of characters successfully written.
* If a file already exists at path, its contents will be erased.
* If a file does not exist at path, it creates and opens a new empty file for writing.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string);
std::string_view string);
#ifdef _WIN32
template <typename Path>
@@ -92,15 +96,15 @@ template <typename Path>
#endif
/**
* Appends a string to a file at path and returns the number of characters successfully written.
* If a file does not exist at path, it creates and opens a new empty file for appending.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
* Appends a string to a file at path and returns the number of characters successfully written.
* If a file does not exist at path, it creates and opens a new empty file for appending.
* If the filesystem object at path exists and is not a regular file, this function returns 0.
*
* @param path Filesystem path
* @param type File type
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type,
std::string_view string);
@@ -128,14 +132,14 @@ public:
FileShareFlag flag = FileShareFlag::ShareReadOnly);
/**
* An IOFile is a lightweight wrapper on C Library file operations.
* Automatically closes an open file on the destruction of an IOFile object.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
* An IOFile is a lightweight wrapper on C Library file operations.
* Automatically closes an open file on the destruction of an IOFile object.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
@@ -149,84 +153,70 @@ public:
IOFile& operator=(IOFile&& other) noexcept;
/**
* Gets the path of the file.
*
* @returns The path of the file.
*/
* Gets the path of the file.
*
* @returns The path of the file.
*/
[[nodiscard]] std::filesystem::path GetPath() const;
/**
* Gets the access mode of the file.
*
* @returns The access mode of the file.
*/
* Gets the access mode of the file.
*
* @returns The access mode of the file.
*/
[[nodiscard]] FileAccessMode GetAccessMode() const;
/**
* Gets the type of the file.
*
* @returns The type of the file.
*/
* Gets the type of the file.
*
* @returns The type of the file.
*/
[[nodiscard]] FileType GetType() const;
/**
* Opens a file at path with the specified file access mode.
* This function behaves differently depending on the FileAccessMode.
* These behaviors are documented in each enum value of FileAccessMode.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
* Opens a file at path with the specified file access mode.
* This function behaves differently depending on the FileAccessMode.
* These behaviors are documented in each enum value of FileAccessMode.
*
* @param path Filesystem path
* @param mode File access mode
* @param type File type, default is BinaryFile. Use TextFile to open the file as a text file
* @param flag (Windows only) File-share access flag, default is ShareReadOnly
*/
void Open(const std::filesystem::path& path, FileAccessMode mode,
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
// #ifdef _WIN32
// template <typename Path>
// void Open(const Path& path, FileAccessMode mode, FileType type = FileType::BinaryFile,
// FileShareFlag flag = FileShareFlag::ShareReadOnly) {
// using ValueType = typename Path::value_type;
// if constexpr (IsChar<ValueType>) {
// Open(ToU8String(path), mode, type, flag);
// } else {
// Open(std::filesystem::path{path}, mode, type, flag);
// }
// }
// #endif
FileType type = FileType::BinaryFile,
FileShareFlag flag = FileShareFlag::ShareReadOnly);
/// Closes the file if it is opened.
void Close();
/**
* Checks whether the file is open.
* Use this to check whether the calls to Open() or Close() succeeded.
*
* @returns True if the file is open, false otherwise.
*/
* Checks whether the file is open.
* Use this to check whether the calls to Open() or Close() succeeded.
*
* @returns True if the file is open, false otherwise.
*/
[[nodiscard]] bool IsOpen() const;
/**
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
* calls ReadObject and T must be a trivially copyable object.
*
* See ReadSpan for more details if T is a contiguous container.
* See ReadObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or reference to object
*
* @returns Count of T::value_type data or objects successfully read.
*/
* Helper function which deduces the value type of a contiguous STL container used in ReadSpan.
* If T is not a contiguous container as defined by the concept IsContiguousContainer, this
* calls ReadObject and T must be a trivially copyable object.
*
* See ReadSpan for more details if T is a contiguous container.
* See ReadObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or reference to object
*
* @returns Count of T::value_type data or objects successfully read.
*/
template <typename T>
[[nodiscard]] size_t Read(T& data) const {
if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<ContiguousType>, "Data type must be trivially copyable.");
return ReadSpan<ContiguousType>(data);
} else {
return ReadObject(data) ? 1 : 0;
@@ -234,25 +224,24 @@ public:
}
/**
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
* calls WriteObject and T must be a trivially copyable object.
*
* See WriteSpan for more details if T is a contiguous container.
* See WriteObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or const reference to object
*
* @returns Count of T::value_type data or objects successfully written.
*/
* Helper function which deduces the value type of a contiguous STL container used in WriteSpan.
* If T is not a contiguous STL container as defined by the concept IsContiguousContainer, this
* calls WriteObject and T must be a trivially copyable object.
*
* See WriteSpan for more details if T is a contiguous container.
* See WriteObject for more details if T is a trivially copyable object.
*
* @tparam T Contiguous container or trivially copyable object
*
* @param data Container of T::value_type data or const reference to object
*
* @returns Count of T::value_type data or objects successfully written.
*/
template <typename T>
[[nodiscard]] size_t Write(const T& data) const {
if constexpr (IsContiguousContainer<T>) {
using ContiguousType = typename T::value_type;
static_assert(std::is_trivially_copyable_v<ContiguousType>,
"Data type must be trivially copyable.");
static_assert(std::is_trivially_copyable_v<ContiguousType>, "Data type must be trivially copyable.");
return WriteSpan<ContiguousType>(data);
} else {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
@@ -261,199 +250,219 @@ public:
}
/**
* Reads a span of T data from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully read.
*/
* Reads a span of T data from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully read.
*/
template <typename T>
[[nodiscard]] size_t ReadSpan(std::span<T> data) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
if (!IsOpen()) {
return 0;
if (IsMappedFile()) {
std::memcpy(data.data(), mmap_base + mmap_offset, sizeof(T) * data.size());
return data.size();
}
return std::fread(data.data(), sizeof(T), data.size(), file);
return IsOpen() ? std::fread(data.data(), sizeof(T), data.size(), file) : 0;
}
/**
* Writes a span of T data to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully written.
*/
* Writes a span of T data to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the (count of T * sizeof(T)) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param data Span of T data
*
* @returns Count of T data successfully written.
*/
template <typename T>
[[nodiscard]] size_t WriteSpan(std::span<const T> data) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
if (!IsOpen()) {
return 0;
if (IsMappedFile()) {
std::memcpy(mmap_base + mmap_offset, data.data(), sizeof(T) * data.size());
return data.size();
}
return std::fwrite(data.data(), sizeof(T), data.size(), file);
return IsOpen() ? std::fwrite(data.data(), sizeof(T), data.size(), file) : 0;
}
/**
* Reads a T object from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param object Reference to object
*
* @returns True if the object is successfully read from the file, false otherwise.
*/
* Reads a T object from a file sequentially.
* This function reads from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully read.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks read permissions
* - Attempting to read beyond the end-of-file
*
* @tparam T Data type
*
* @param object Reference to object
*
* @returns True if the object is successfully read from the file, false otherwise.
*/
template <typename T>
[[nodiscard]] bool ReadObject(T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (!IsOpen()) {
return false;
if (IsMappedFile()) {
std::memcpy(&object, mmap_base + mmap_offset, sizeof(T));
#ifdef _WIN32
return bool(sizeof(T));
#else
return sizeof(T);
#endif
}
return std::fread(&object, sizeof(T), 1, file) == 1;
return IsOpen() ? std::fread(&object, sizeof(T), 1, file) == 1 : false;
}
/**
* Writes a T object to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param object Const reference to object
*
* @returns True if the object is successfully written to the file, false otherwise.
*/
* Writes a T object to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the sizeof(T) bytes successfully written.
*
* Failures occur when:
* - The file is not open
* - The opened file lacks write permissions
*
* @tparam T Data type
*
* @param object Const reference to object
*
* @returns True if the object is successfully written to the file, false otherwise.
*/
template <typename T>
[[nodiscard]] bool WriteObject(const T& object) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
if (!IsOpen()) {
return false;
if (IsMappedFile()) {
std::memcpy(mmap_base + mmap_offset, &object, sizeof(T));
#ifdef _WIN32
return sizeof(T) != 0;
#else
return sizeof(T);
#endif
}
return std::fwrite(&object, sizeof(T), 1, file) == 1;
return IsOpen() ? std::fwrite(&object, sizeof(T), 1, file) == 1 : false;
}
/**
* Specialized function to read a string of a given length from a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully read.
* The size of the returned string may not match length if not all bytes are successfully read.
*
* @param length Length of the string
*
* @returns A string read from the file.
*/
* Specialized function to read a string of a given length from a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully read.
* The size of the returned string may not match length if not all bytes are successfully read.
*
* @param length Length of the string
*
* @returns A string read from the file.
*/
[[nodiscard]] std::string ReadString(size_t length) const;
/**
* Specialized function to write a string to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully written.
*
* @param string Span of const char backed std::string or std::string_view
*
* @returns Number of characters successfully written.
*/
* Specialized function to write a string to a file sequentially.
* This function writes from the current position of the file pointer and
* advances it by the number of characters successfully written.
*
* @param string Span of const char backed std::string or std::string_view
*
* @returns Number of characters successfully written.
*/
[[nodiscard]] size_t WriteString(std::span<const char> string) const;
/**
* Attempts to flush any unwritten buffered data into the file.
*
* @returns True if the flush was successful, false otherwise.
*/
* Attempts to flush any unwritten buffered data into the file.
*
* @returns True if the flush was successful, false otherwise.
*/
bool Flush() const;
/**
* Attempts to commit the file into the disk.
* Note that this is an expensive operation as this forces the operating system to write
* the contents of the file associated with the file descriptor into the disk.
*
* @returns True if the commit was successful, false otherwise.
*/
* Attempts to commit the file into the disk.
* Note that this is an expensive operation as this forces the operating system to write
* the contents of the file associated with the file descriptor into the disk.
*
* @returns True if the commit was successful, false otherwise.
*/
bool Commit() const;
/**
* Resizes the file to a given size.
* If the file is resized to a smaller size, the remainder of the file is discarded.
* If the file is resized to a larger size, the new area appears as if zero-filled.
*
* Failures occur when:
* - The file is not open
*
* @param size File size in bytes
*
* @returns True if the file resize succeeded, false otherwise.
*/
* Resizes the file to a given size.
* If the file is resized to a smaller size, the remainder of the file is discarded.
* If the file is resized to a larger size, the new area appears as if zero-filled.
*
* Failures occur when:
* - The file is not open
*
* @param size File size in bytes
*
* @returns True if the file resize succeeded, false otherwise.
*/
[[nodiscard]] bool SetSize(u64 size) const;
/**
* Gets the size of the file.
*
* Failures occur when:
* - The file is not open
*
* @returns The file size in bytes of the file. Returns 0 on failure.
*/
* Gets the size of the file.
*
* Failures occur when:
* - The file is not open
*
* @returns The file size in bytes of the file. Returns 0 on failure.
*/
[[nodiscard]] u64 GetSize() const;
/**
* Moves the current position of the file pointer with the specified offset and seek origin.
*
* @param offset Offset from seek origin
* @param origin Seek origin
*
* @returns True if the file pointer has moved to the specified offset, false otherwise.
*/
* Moves the current position of the file pointer with the specified offset and seek origin.
*
* @param offset Offset from seek origin
* @param origin Seek origin
*
* @returns True if the file pointer has moved to the specified offset, false otherwise.
*/
[[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
/**
* Gets the current position of the file pointer.
*
* @returns The current position of the file pointer.
*/
* Gets the current position of the file pointer.
*
* @returns The current position of the file pointer.
*/
[[nodiscard]] s64 Tell() const;
private:
#ifdef _WIN32
inline bool IsMappedFile() const noexcept { return mapping_handle != nullptr; }
#else // POSIX
inline bool IsMappedFile() const noexcept { return mmap_fd != -1; }
#endif
std::filesystem::path file_path;
FileAccessMode file_access_mode{};
FileType file_type{};
std::FILE* file = nullptr;
// Any decent system should have mmap() for files
// Systems with artifical mmap() limitations should simply change the logic within file.cpp
// and reduce the threshold for which the mmap() is set to
#ifdef _WIN32
void *mapping_handle = nullptr;
void *file_handle = nullptr;
#else // POSIX
int mmap_fd = -1;
#endif
u8* mmap_base = nullptr;
size_t mmap_size = 0;
mutable s64 mmap_offset = 0; // fuck you
};
} // namespace Common::FS
+276 -1
View File
@@ -1,12 +1,287 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <type_traits>
#include <bitset>
#include <initializer_list>
#include "common/assert.h"
// xbyak hates human beings
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wshadow"
#endif
// You must ensure this matches with src/common/x64/xbyak.h on root dir
#include <ankerl/unordered_dense.h>
#include <boost/unordered_map.hpp>
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
#define XBYAK_STD_UNORDERED_MULTIMAP std::unordered_multimap
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
#include <xbyak/xbyak.h>
#include <xbyak/xbyak_util.h>
#include <xbyak/xbyak.h>
namespace Common::X64 {
constexpr size_t RegToIndex(const Xbyak::Reg& reg) {
using Kind = Xbyak::Reg::Kind;
ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
"RegSet only support GPRs and XMM registers.");
ASSERT_MSG(reg.getIdx() < 16, "RegSet only supports XXM0-15.");
return static_cast<size_t>(reg.getIdx()) + (reg.getKind() == Kind::REG ? 0 : 16);
}
constexpr Xbyak::Reg64 IndexToReg64(size_t reg_index) {
ASSERT(reg_index < 16);
return Xbyak::Reg64(static_cast<int>(reg_index));
}
constexpr Xbyak::Xmm IndexToXmm(size_t reg_index) {
ASSERT(reg_index >= 16 && reg_index < 32);
return Xbyak::Xmm(static_cast<int>(reg_index - 16));
}
constexpr Xbyak::Reg IndexToReg(size_t reg_index) {
if (reg_index < 16) {
return IndexToReg64(reg_index);
} else {
return IndexToXmm(reg_index);
}
}
constexpr std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
size_t bits = 0;
for (const Xbyak::Reg& reg : regs) {
bits |= size_t{1} << RegToIndex(reg);
}
return {bits};
}
constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
constexpr inline Xbyak::Reg ABI_JIT_REG = Xbyak::util::rbx;
#ifdef _WIN32
// Microsoft x64 ABI
constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
});
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rsi,
Xbyak::util::rdi,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
// XMMs
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
constexpr size_t ABI_SHADOW_SPACE = 0x20;
#else
// System V x86-64 ABI
constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::rdi,
Xbyak::util::rsi,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
});
constexpr size_t ABI_SHADOW_SPACE = 0;
#endif
struct ABIFrameInfo {
s32 subtraction;
s32 xmm_offset;
};
inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment,
size_t needed_frame_size) {
const auto count = (regs & ABI_ALL_GPRS).count();
rsp_alignment -= count * 8;
size_t subtraction = 0;
const auto xmm_count = (regs & ABI_ALL_XMMS).count();
if (xmm_count) {
// If we have any XMMs to save, we must align the stack here.
subtraction = rsp_alignment & 0xF;
}
subtraction += 0x10 * xmm_count;
size_t xmm_base_subtraction = subtraction;
subtraction += needed_frame_size;
subtraction += ABI_SHADOW_SPACE;
// Final alignment.
rsp_alignment -= subtraction;
subtraction += rsp_alignment & 0xF;
return ABIFrameInfo{
s32(subtraction),
s32(subtraction - xmm_base_subtraction)
};
}
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
for (size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_GPRS[i]) {
code.push(IndexToReg64(i));
}
}
if (frame_info.subtraction != 0) {
code.sub(code.rsp, frame_info.subtraction);
}
for (size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_XMMS[i]) {
code.movaps(code.xword[code.rsp + frame_info.xmm_offset], IndexToXmm(i));
frame_info.xmm_offset += 0x10;
}
}
return ABI_SHADOW_SPACE;
}
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
for (size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_XMMS[i]) {
code.movaps(IndexToXmm(i), code.xword[code.rsp + frame_info.xmm_offset]);
frame_info.xmm_offset += 0x10;
}
}
if (frame_info.subtraction != 0) {
code.add(code.rsp, frame_info.subtraction);
}
// GPRs need to be popped in reverse order
for (size_t j = 0; j < regs.size(); ++j) {
const size_t i = regs.size() - j - 1;
if (regs[i] && ABI_ALL_GPRS[i]) {
code.pop(IndexToReg64(i));
}
}
}
// Constants for use with cmpps/cmpss
enum {
CMP_EQ = 0,
CMP_LT = 1,
CMP_LE = 2,
CMP_UNORD = 3,
CMP_NEQ = 4,
CMP_NLT = 5,
CMP_NLE = 6,
CMP_ORD = 7,
};
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
const u64 distance = target - (ref + 5);
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
}
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
return IsWithin2G(reinterpret_cast<uintptr_t>(code.getCurr()), target);
}
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
size_t addr = reinterpret_cast<size_t>(f);
if (IsWithin2G(code, addr)) {
code.call(f);
} else {
// ABI_RETURN is a safe temp register to use before a call
code.mov(ABI_RETURN, addr);
code.call(ABI_RETURN);
}
}
} // namespace Common::X64
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -7,28 +10,30 @@
namespace Core {
DynarmicExclusiveMonitor::DynarmicExclusiveMonitor(Memory::Memory& memory_, std::size_t core_count_)
: monitor{core_count_}, memory{memory_} {}
: monitor{}
, memory{memory_}
{}
DynarmicExclusiveMonitor::~DynarmicExclusiveMonitor() = default;
u8 DynarmicExclusiveMonitor::ExclusiveRead8(std::size_t core_index, VAddr addr) {
return monitor.ReadAndMark<u8>(core_index, addr, [&]() -> u8 { return memory.Read8(addr); });
return monitor.ReadAndMark<u8>(core_index, addr, [=]() -> u8 { return memory.Read8(addr); });
}
u16 DynarmicExclusiveMonitor::ExclusiveRead16(std::size_t core_index, VAddr addr) {
return monitor.ReadAndMark<u16>(core_index, addr, [&]() -> u16 { return memory.Read16(addr); });
return monitor.ReadAndMark<u16>(core_index, addr, [=]() -> u16 { return memory.Read16(addr); });
}
u32 DynarmicExclusiveMonitor::ExclusiveRead32(std::size_t core_index, VAddr addr) {
return monitor.ReadAndMark<u32>(core_index, addr, [&]() -> u32 { return memory.Read32(addr); });
return monitor.ReadAndMark<u32>(core_index, addr, [=]() -> u32 { return memory.Read32(addr); });
}
u64 DynarmicExclusiveMonitor::ExclusiveRead64(std::size_t core_index, VAddr addr) {
return monitor.ReadAndMark<u64>(core_index, addr, [&]() -> u64 { return memory.Read64(addr); });
return monitor.ReadAndMark<u64>(core_index, addr, [=]() -> u64 { return memory.Read64(addr); });
}
u128 DynarmicExclusiveMonitor::ExclusiveRead128(std::size_t core_index, VAddr addr) {
return monitor.ReadAndMark<u128>(core_index, addr, [&]() -> u128 {
return monitor.ReadAndMark<u128>(core_index, addr, [=]() -> u128 {
u128 result;
result[0] = memory.Read64(addr);
result[1] = memory.Read64(addr + 8);
@@ -41,31 +46,31 @@ void DynarmicExclusiveMonitor::ClearExclusive(std::size_t core_index) {
}
bool DynarmicExclusiveMonitor::ExclusiveWrite8(std::size_t core_index, VAddr vaddr, u8 value) {
return monitor.DoExclusiveOperation<u8>(core_index, vaddr, [&](u8 expected) -> bool {
return monitor.DoExclusiveOperation<u8>(core_index, vaddr, [=](u8 expected) -> bool {
return memory.WriteExclusive8(vaddr, value, expected);
});
}
bool DynarmicExclusiveMonitor::ExclusiveWrite16(std::size_t core_index, VAddr vaddr, u16 value) {
return monitor.DoExclusiveOperation<u16>(core_index, vaddr, [&](u16 expected) -> bool {
return monitor.DoExclusiveOperation<u16>(core_index, vaddr, [=](u16 expected) -> bool {
return memory.WriteExclusive16(vaddr, value, expected);
});
}
bool DynarmicExclusiveMonitor::ExclusiveWrite32(std::size_t core_index, VAddr vaddr, u32 value) {
return monitor.DoExclusiveOperation<u32>(core_index, vaddr, [&](u32 expected) -> bool {
return monitor.DoExclusiveOperation<u32>(core_index, vaddr, [=](u32 expected) -> bool {
return memory.WriteExclusive32(vaddr, value, expected);
});
}
bool DynarmicExclusiveMonitor::ExclusiveWrite64(std::size_t core_index, VAddr vaddr, u64 value) {
return monitor.DoExclusiveOperation<u64>(core_index, vaddr, [&](u64 expected) -> bool {
return monitor.DoExclusiveOperation<u64>(core_index, vaddr, [=](u64 expected) -> bool {
return memory.WriteExclusive64(vaddr, value, expected);
});
}
bool DynarmicExclusiveMonitor::ExclusiveWrite128(std::size_t core_index, VAddr vaddr, u128 value) {
return monitor.DoExclusiveOperation<u128>(core_index, vaddr, [&](u128 expected) -> bool {
return monitor.DoExclusiveOperation<u128>(core_index, vaddr, [=](u128 expected) -> bool {
return memory.WriteExclusive128(vaddr, value, expected);
});
}
-13
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2016 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -16,16 +13,6 @@ namespace IPC {
/// Size of the command buffer area, in 32-bit words.
constexpr std::size_t COMMAND_BUFFER_LENGTH = 0x100 / sizeof(u32);
/// Must match bitfields
constexpr std::size_t MAX_BUFFER_DESCRIPTORS = 16;
constexpr std::size_t MAX_INCOMING_MOVE_HANDLERS = 16;
constexpr std::size_t MAX_INCOMING_COPY_HANDLERS = 16;
/// Doesn't need to match bitfields but usually not big enough
constexpr std::size_t MAX_OUTGOING_COPY_OBJECTS = 16;
constexpr std::size_t MAX_OUTGOING_MOVE_OBJECTS = 16;
constexpr std::size_t MAX_OUTGOING_DOMAIN_OBJECTS = 16;
enum class ControlCommand : u32 {
ConvertSessionToDomain = 0,
ConvertDomainToSession = 1,
+12 -6
View File
@@ -128,12 +128,10 @@ Result SessionRequestManager::HandleDomainSyncRequest(Kernel::KServerSession* se
return ResultSuccess;
}
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_, Kernel::KServerSession* server_session_, Kernel::KThread* thread_)
: server_session(server_session_)
, thread(thread_)
, kernel{kernel_}
, memory{memory_}
{
HLERequestContext::HLERequestContext(Kernel::KernelCore& kernel_, Core::Memory::Memory& memory_,
Kernel::KServerSession* server_session_,
Kernel::KThread* thread_)
: server_session(server_session_), thread(thread_), kernel{kernel_}, memory{memory_} {
cmd_buf[0] = 0;
}
@@ -157,6 +155,9 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
}
if (incoming) {
// Populate the object lists with the data in the IPC request.
incoming_copy_handles.reserve(handle_descriptor_header->num_handles_to_copy);
incoming_move_handles.reserve(handle_descriptor_header->num_handles_to_move);
for (u32 handle = 0; handle < handle_descriptor_header->num_handles_to_copy; ++handle) {
incoming_copy_handles.push_back(rp.Pop<Handle>());
}
@@ -171,6 +172,11 @@ void HLERequestContext::ParseCommandBuffer(u32_le* src_cmdbuf, bool incoming) {
}
}
buffer_x_descriptors.reserve(command_header->num_buf_x_descriptors);
buffer_a_descriptors.reserve(command_header->num_buf_a_descriptors);
buffer_b_descriptors.reserve(command_header->num_buf_b_descriptors);
buffer_w_descriptors.reserve(command_header->num_buf_w_descriptors);
for (u32 i = 0; i < command_header->num_buf_x_descriptors; ++i) {
buffer_x_descriptors.push_back(rp.PopRaw<IPC::BufferDescriptorX>());
}
+26 -26
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -14,7 +11,6 @@
#include <string>
#include <type_traits>
#include <vector>
#include <boost/container/static_vector.hpp>
#include "common/assert.h"
#include "common/common_types.h"
@@ -185,7 +181,8 @@ private:
*/
class HLERequestContext {
public:
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory, Kernel::KServerSession* session, Kernel::KThread* thread);
explicit HLERequestContext(Kernel::KernelCore& kernel, Core::Memory::Memory& memory,
Kernel::KServerSession* session, Kernel::KThread* thread);
~HLERequestContext();
/// Returns a pointer to the IPC command buffer for this request.
@@ -236,19 +233,19 @@ public:
return data_payload_offset;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorX, 16>& BufferDescriptorX() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorX>& BufferDescriptorX() const {
return buffer_x_descriptors;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorA() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorA() const {
return buffer_a_descriptors;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorABW, 16>& BufferDescriptorB() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorABW>& BufferDescriptorB() const {
return buffer_b_descriptors;
}
[[nodiscard]] const boost::container::static_vector<IPC::BufferDescriptorC, 16>& BufferDescriptorC() const {
[[nodiscard]] const std::vector<IPC::BufferDescriptorC>& BufferDescriptorC() const {
return buffer_c_descriptors;
}
@@ -402,35 +399,38 @@ private:
Kernel::KHandleTable* client_handle_table{};
Kernel::KThread* thread{};
boost::container::static_vector<IPC::BufferDescriptorX, IPC::MAX_BUFFER_DESCRIPTORS> buffer_x_descriptors;
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_a_descriptors;
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_b_descriptors;
boost::container::static_vector<IPC::BufferDescriptorABW, IPC::MAX_BUFFER_DESCRIPTORS> buffer_w_descriptors;
boost::container::static_vector<IPC::BufferDescriptorC, IPC::MAX_BUFFER_DESCRIPTORS> buffer_c_descriptors;
boost::container::static_vector<Handle, IPC::MAX_INCOMING_MOVE_HANDLERS> incoming_move_handles;
boost::container::static_vector<Handle, IPC::MAX_INCOMING_COPY_HANDLERS> incoming_copy_handles;
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_MOVE_OBJECTS> outgoing_move_objects;
boost::container::static_vector<Kernel::KAutoObject*, IPC::MAX_OUTGOING_COPY_OBJECTS> outgoing_copy_objects;
boost::container::static_vector<SessionRequestHandlerPtr, IPC::MAX_OUTGOING_DOMAIN_OBJECTS> outgoing_domain_objects;
std::vector<Handle> incoming_move_handles;
std::vector<Handle> incoming_copy_handles;
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
std::vector<Kernel::KAutoObject*> outgoing_move_objects;
std::vector<Kernel::KAutoObject*> outgoing_copy_objects;
std::vector<SessionRequestHandlerPtr> outgoing_domain_objects;
std::optional<IPC::CommandHeader> command_header;
std::optional<IPC::HandleDescriptorHeader> handle_descriptor_header;
std::optional<IPC::DataPayloadHeader> data_payload_header;
std::optional<IPC::DomainMessageHeader> domain_message_header;
std::weak_ptr<SessionRequestManager> manager{};
Kernel::KernelCore& kernel;
Core::Memory::Memory& memory;
std::vector<IPC::BufferDescriptorX> buffer_x_descriptors;
std::vector<IPC::BufferDescriptorABW> buffer_a_descriptors;
std::vector<IPC::BufferDescriptorABW> buffer_b_descriptors;
std::vector<IPC::BufferDescriptorABW> buffer_w_descriptors;
std::vector<IPC::BufferDescriptorC> buffer_c_descriptors;
u64 pid{};
u32_le command{};
u64 pid{};
u32 write_size{};
u32 data_payload_offset{};
u32 handles_offset{};
u32 domain_offset{};
bool is_deferred = false;
std::weak_ptr<SessionRequestManager> manager{};
bool is_deferred{false};
Kernel::KernelCore& kernel;
Core::Memory::Memory& memory;
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_a{};
mutable std::array<Common::ScratchBuffer<u8>, 3> read_buffer_data_x{};
};
} // namespace Service
+3 -3
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
@@ -20,7 +20,6 @@
#include "common/settings.h"
#include "core/arm/arm_interface.h"
#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/k_page_table.h"
#include "core/hle/kernel/k_process.h"
#include "core/hle/result.h"
@@ -125,7 +124,8 @@ json GetFullDataAuto(const std::string& timestamp, u64 title_id, Core::System& s
}
template <bool read_value, typename DescriptorType>
json GetHLEBufferDescriptorData(const boost::container::static_vector<DescriptorType, IPC::MAX_BUFFER_DESCRIPTORS>& buffer, Core::Memory::Memory& memory) {
json GetHLEBufferDescriptorData(const std::vector<DescriptorType>& buffer,
Core::Memory::Memory& memory) {
auto buffer_out = json::array();
for (const auto& desc : buffer) {
auto entry = json{
-4
View File
@@ -169,8 +169,6 @@ if ("x86_64" IN_LIST ARCHITECTURE)
backend/x64/emit_x64_vector.cpp
backend/x64/emit_x64_vector_floating_point.cpp
backend/x64/emit_x64_vector_saturation.cpp
backend/x64/exclusive_monitor.cpp
backend/x64/exclusive_monitor_friend.h
backend/x64/host_feature.h
backend/x64/hostloc.h
backend/x64/jitstate_info.h
@@ -231,7 +229,6 @@ if ("arm64" IN_LIST ARCHITECTURE)
backend/arm64/emit_arm64_vector_floating_point.cpp
backend/arm64/emit_arm64_vector_saturation.cpp
backend/arm64/emit_context.h
backend/arm64/exclusive_monitor.cpp
backend/arm64/fastmem.h
backend/arm64/fpsr_manager.cpp
backend/arm64/fpsr_manager.h
@@ -278,7 +275,6 @@ if ("riscv64" IN_LIST ARCHITECTURE)
backend/riscv64/emit_riscv64_vector.cpp
backend/riscv64/emit_riscv64.cpp
backend/riscv64/emit_riscv64.h
backend/riscv64/exclusive_monitor.cpp
backend/riscv64/reg_alloc.cpp
backend/riscv64/reg_alloc.h
backend/riscv64/stack_layout.h
@@ -135,12 +135,9 @@ static void* EmitExclusiveWriteCallTrampoline(oaknut::CodeGenerator& code, const
oaknut::Label l_addr, l_this;
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, T value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr,
[&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
})
? 0
: 1;
return conf.global_monitor->DoExclusiveOperation<T>(conf.processor_id, vaddr, [&](T expected) -> bool {
return (conf.callbacks->*callback)(vaddr, value, expected);
}) ? 0 : 1;
};
void* target = code.xptr<void*>();
@@ -300,12 +297,9 @@ static void* EmitExclusiveWrite128CallTrampoline(oaknut::CodeGenerator& code, co
oaknut::Label l_addr, l_this;
auto fn = [](const A64::UserConfig& conf, A64::VAddr vaddr, Vector value) -> u32 {
return conf.global_monitor->DoExclusiveOperation<Vector>(conf.processor_id, vaddr,
[&](Vector expected) -> bool {
return conf.callbacks->MemoryWriteExclusive128(vaddr, value, expected);
})
? 0
: 1;
return conf.global_monitor->DoExclusiveOperation<Vector>(conf.processor_id, vaddr, [&](Vector expected) -> bool {
return conf.callbacks->MemoryWriteExclusive128(vaddr, value, expected);
}) ? 0 : 1;
};
void* target = code.xptr<void*>();
@@ -1,61 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2022 MerryMage
* SPDX-License-Identifier: 0BSD
*/
#include "dynarmic/interface/exclusive_monitor.h"
#include <algorithm>
#include "common/assert.h"
namespace Dynarmic {
ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count)
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {}
size_t ExclusiveMonitor::GetProcessorCount() const {
return exclusive_addresses.size();
}
void ExclusiveMonitor::Lock() {
lock.Lock();
}
void ExclusiveMonitor::Unlock() {
lock.Unlock();
}
bool ExclusiveMonitor::CheckAndClear(std::size_t processor_id, VAddr address) {
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
Lock();
if (exclusive_addresses[processor_id] != masked_address) {
Unlock();
return false;
}
for (VAddr& other_address : exclusive_addresses) {
if (other_address == masked_address) {
other_address = INVALID_EXCLUSIVE_ADDRESS;
}
}
return true;
}
void ExclusiveMonitor::Clear() {
Lock();
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
Unlock();
}
void ExclusiveMonitor::ClearProcessor(std::size_t processor_id) {
Lock();
exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS;
Unlock();
}
} // namespace Dynarmic
@@ -1,54 +0,0 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#include "dynarmic/interface/exclusive_monitor.h"
#include <algorithm>
namespace Dynarmic {
ExclusiveMonitor::ExclusiveMonitor(std::size_t processor_count)
: exclusive_addresses(processor_count, INVALID_EXCLUSIVE_ADDRESS), exclusive_values(processor_count) {}
size_t ExclusiveMonitor::GetProcessorCount() const {
return exclusive_addresses.size();
}
void ExclusiveMonitor::Lock() {
lock.Lock();
}
void ExclusiveMonitor::Unlock() {
lock.Unlock();
}
bool ExclusiveMonitor::CheckAndClear(size_t processor_id, VAddr address) {
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
Lock();
if (exclusive_addresses[processor_id] != masked_address) {
Unlock();
return false;
}
for (VAddr& other_address : exclusive_addresses) {
if (other_address == masked_address) {
other_address = INVALID_EXCLUSIVE_ADDRESS;
}
}
return true;
}
void ExclusiveMonitor::Clear() {
Lock();
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
Unlock();
}
void ExclusiveMonitor::ClearProcessor(size_t processor_id) {
Lock();
exclusive_addresses[processor_id] = INVALID_EXCLUSIVE_ADDRESS;
Unlock();
}
} // namespace Dynarmic
@@ -14,13 +14,13 @@
#include <fmt/format.h>
#include <fmt/ostream.h>
#include "dynarmic/mcl/integer_of_size.hpp"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/a32_emit_x64.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/devirtualize.h"
#include "dynarmic/backend/x64/emit_x64_memory.h"
#include "dynarmic/backend/x64/exclusive_monitor_friend.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "dynarmic/backend/x64/perf_map.h"
#include "dynarmic/interface/exclusive_monitor.h"
@@ -174,67 +174,35 @@ void A32EmitX64::EmitA32ClearExclusive(A32EmitContext&, IR::Inst*) {
}
void A32EmitX64::EmitA32ExclusiveReadMemory8(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
} else {
EmitExclusiveReadMemory<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
}
EmitExclusiveReadMemoryInline<8, &A32::UserCallbacks::MemoryRead8>(ctx, inst);
}
void A32EmitX64::EmitA32ExclusiveReadMemory16(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
} else {
EmitExclusiveReadMemory<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
}
EmitExclusiveReadMemoryInline<16, &A32::UserCallbacks::MemoryRead16>(ctx, inst);
}
void A32EmitX64::EmitA32ExclusiveReadMemory32(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
} else {
EmitExclusiveReadMemory<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
}
EmitExclusiveReadMemoryInline<32, &A32::UserCallbacks::MemoryRead32>(ctx, inst);
}
void A32EmitX64::EmitA32ExclusiveReadMemory64(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
} else {
EmitExclusiveReadMemory<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
}
EmitExclusiveReadMemoryInline<64, &A32::UserCallbacks::MemoryRead64>(ctx, inst);
}
void A32EmitX64::EmitA32ExclusiveWriteMemory8(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<8, &A32::UserCallbacks::MemoryWriteExclusive8>(ctx, inst);
} else {
EmitExclusiveWriteMemory<8, &A32::UserCallbacks::MemoryWriteExclusive8>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<8, &A32::UserCallbacks::MemoryWriteExclusive8>(ctx, inst);
}
void A32EmitX64::EmitA32ExclusiveWriteMemory16(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<16, &A32::UserCallbacks::MemoryWriteExclusive16>(ctx, inst);
} else {
EmitExclusiveWriteMemory<16, &A32::UserCallbacks::MemoryWriteExclusive16>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<16, &A32::UserCallbacks::MemoryWriteExclusive16>(ctx, inst);
}
void A32EmitX64::EmitA32ExclusiveWriteMemory32(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<32, &A32::UserCallbacks::MemoryWriteExclusive32>(ctx, inst);
} else {
EmitExclusiveWriteMemory<32, &A32::UserCallbacks::MemoryWriteExclusive32>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<32, &A32::UserCallbacks::MemoryWriteExclusive32>(ctx, inst);
}
void A32EmitX64::EmitA32ExclusiveWriteMemory64(A32EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<64, &A32::UserCallbacks::MemoryWriteExclusive64>(ctx, inst);
} else {
EmitExclusiveWriteMemory<64, &A32::UserCallbacks::MemoryWriteExclusive64>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<64, &A32::UserCallbacks::MemoryWriteExclusive64>(ctx, inst);
}
void A32EmitX64::EmitCheckMemoryAbort(A32EmitContext& ctx, IR::Inst* inst, Xbyak::Label* end) {
@@ -14,13 +14,13 @@
#include <fmt/format.h>
#include <fmt/ostream.h>
#include "dynarmic/mcl/integer_of_size.hpp"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/a64_emit_x64.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/devirtualize.h"
#include "dynarmic/backend/x64/emit_x64_memory.h"
#include "dynarmic/backend/x64/exclusive_monitor_friend.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "dynarmic/backend/x64/perf_map.h"
#include "dynarmic/common/spin_lock_x64.h"
#include "dynarmic/interface/exclusive_monitor.h"
@@ -330,83 +330,43 @@ void A64EmitX64::EmitA64ClearExclusive(A64EmitContext&, IR::Inst*) {
}
void A64EmitX64::EmitA64ExclusiveReadMemory8(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
} else {
EmitExclusiveReadMemory<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
}
EmitExclusiveReadMemoryInline<8, &A64::UserCallbacks::MemoryRead8>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveReadMemory16(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
} else {
EmitExclusiveReadMemory<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
}
EmitExclusiveReadMemoryInline<16, &A64::UserCallbacks::MemoryRead16>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveReadMemory32(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
} else {
EmitExclusiveReadMemory<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
}
EmitExclusiveReadMemoryInline<32, &A64::UserCallbacks::MemoryRead32>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveReadMemory64(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
} else {
EmitExclusiveReadMemory<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
}
EmitExclusiveReadMemoryInline<64, &A64::UserCallbacks::MemoryRead64>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveReadMemory128(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveReadMemoryInline<128, &A64::UserCallbacks::MemoryRead128>(ctx, inst);
} else {
EmitExclusiveReadMemory<128, &A64::UserCallbacks::MemoryRead128>(ctx, inst);
}
EmitExclusiveReadMemoryInline<128, &A64::UserCallbacks::MemoryRead128>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveWriteMemory8(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<8, &A64::UserCallbacks::MemoryWriteExclusive8>(ctx, inst);
} else {
EmitExclusiveWriteMemory<8, &A64::UserCallbacks::MemoryWriteExclusive8>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<8, &A64::UserCallbacks::MemoryWriteExclusive8>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveWriteMemory16(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<16, &A64::UserCallbacks::MemoryWriteExclusive16>(ctx, inst);
} else {
EmitExclusiveWriteMemory<16, &A64::UserCallbacks::MemoryWriteExclusive16>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<16, &A64::UserCallbacks::MemoryWriteExclusive16>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveWriteMemory32(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<32, &A64::UserCallbacks::MemoryWriteExclusive32>(ctx, inst);
} else {
EmitExclusiveWriteMemory<32, &A64::UserCallbacks::MemoryWriteExclusive32>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<32, &A64::UserCallbacks::MemoryWriteExclusive32>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveWriteMemory64(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<64, &A64::UserCallbacks::MemoryWriteExclusive64>(ctx, inst);
} else {
EmitExclusiveWriteMemory<64, &A64::UserCallbacks::MemoryWriteExclusive64>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<64, &A64::UserCallbacks::MemoryWriteExclusive64>(ctx, inst);
}
void A64EmitX64::EmitA64ExclusiveWriteMemory128(A64EmitContext& ctx, IR::Inst* inst) {
if (conf.fastmem_exclusive_access) {
EmitExclusiveWriteMemoryInline<128, &A64::UserCallbacks::MemoryWriteExclusive128>(ctx, inst);
} else {
EmitExclusiveWriteMemory<128, &A64::UserCallbacks::MemoryWriteExclusive128>(ctx, inst);
}
EmitExclusiveWriteMemoryInline<128, &A64::UserCallbacks::MemoryWriteExclusive128>(ctx, inst);
}
void A64EmitX64::EmitCheckMemoryAbort(A64EmitContext&, IR::Inst* inst, Xbyak::Label* end) {
@@ -11,7 +11,7 @@
#include <algorithm>
#include "common/common_types.h"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/block_of_code.h"
@@ -26,7 +26,7 @@
#include "common/assert.h"
#include "dynarmic/mcl/bit.hpp"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/a32_jitstate.h"
#include "dynarmic/backend/x64/abi.h"
@@ -15,7 +15,7 @@
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/callback.h"
#include "dynarmic/backend/x64/constant_pool.h"
@@ -12,7 +12,7 @@
#include <vector>
#include "common/common_types.h"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
namespace Dynarmic::Backend::X64 {
@@ -15,7 +15,7 @@
#include "common/common_types.h"
#include <ankerl/unordered_dense.h>
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
namespace Dynarmic::Backend::X64 {
@@ -20,7 +20,7 @@
#include <boost/container/stable_vector.hpp>
#include <boost/container/small_vector.hpp>
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/mcl/bit.hpp"
#include "dynarmic/backend/exception_handler.h"
#include "dynarmic/backend/x64/reg_alloc.h"
@@ -13,7 +13,7 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "dynarmic/mcl/integer_of_size.hpp"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/block_of_code.h"
@@ -230,12 +230,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) {
code.mfence();
}
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr);
});
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr) -> T {
return conf.global_monitor->ReadAndMark<T>(conf.processor_id, vaddr, [&]() -> T {
return (conf.callbacks->*callback)(vaddr);
});
});
code.ZeroExtendFrom(bitsize, code.ABI_RETURN);
} else {
const Xbyak::Xmm result = ctx.reg_alloc.ScratchXmm(code);
@@ -250,12 +249,11 @@ void AxxEmitX64::EmitExclusiveReadMemory(AxxEmitContext& ctx, IR::Inst* inst) {
if (ordered) {
code.mfence();
}
code.CallLambda(
[](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
return (conf.callbacks->*callback)(vaddr);
});
code.CallLambda([](AxxUserConfig& conf, Axx::VAddr vaddr, Vector& ret) {
ret = conf.global_monitor->ReadAndMark<Vector>(conf.processor_id, vaddr, [&]() -> Vector {
return (conf.callbacks->*callback)(vaddr);
});
});
code.movups(result, xword[rsp + ABI_SHADOW_SPACE]);
ctx.reg_alloc.ReleaseStackSpace(code, 16 + ABI_SHADOW_SPACE);
@@ -320,11 +318,12 @@ void AxxEmitX64::EmitExclusiveWriteMemory(AxxEmitContext& ctx, IR::Inst* inst) {
template<std::size_t bitsize, auto callback>
void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* inst) {
ASSERT(conf.global_monitor && conf.fastmem_pointer);
if (!exception_handler.SupportsFastmem()) {
ASSERT(conf.global_monitor);
if (!conf.fastmem_exclusive_access || !exception_handler.SupportsFastmem()) {
EmitExclusiveReadMemory<bitsize, callback>(ctx, inst);
return;
}
ASSERT(conf.fastmem_pointer);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
constexpr bool ordered = true;
@@ -344,10 +343,10 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
const auto wrapped_fn = read_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value_idx)];
EmitExclusiveLock(code, conf, tmp, tmp2.cvt32());
EmitExclusiveLock(code, conf, tmp2.cvt32());
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)], u8(1));
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(conf.global_monitor->exclusive_addresses.data() + conf.processor_id));
code.mov(qword[tmp], vaddr);
const auto fastmem_marker = ShouldFastmem(ctx, inst);
@@ -381,10 +380,10 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
code.call(wrapped_fn);
}
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorValuePointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(conf.global_monitor->exclusive_values.data() + conf.processor_id));
EmitWriteMemoryMov<bitsize>(code, tmp, value_idx, false);
EmitExclusiveUnlock(code, conf, tmp, tmp2.cvt32());
EmitExclusiveUnlock(code, conf, tmp2.cvt32());
if constexpr (bitsize == 128) {
ctx.reg_alloc.DefineValue(code, inst, Xbyak::Xmm{value_idx});
@@ -397,11 +396,12 @@ void AxxEmitX64::EmitExclusiveReadMemoryInline(AxxEmitContext& ctx, IR::Inst* in
template<std::size_t bitsize, auto callback>
void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* inst) {
ASSERT(conf.global_monitor && conf.fastmem_pointer);
if (!exception_handler.SupportsFastmem()) {
ASSERT(conf.global_monitor);
if (!conf.fastmem_exclusive_access || !exception_handler.SupportsFastmem()) {
EmitExclusiveWriteMemory<bitsize, callback>(ctx, inst);
return;
}
ASSERT(conf.fastmem_pointer);
auto args = ctx.reg_alloc.GetArgumentInfo(inst);
constexpr bool ordered = true;
@@ -425,7 +425,7 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
const auto wrapped_fn = exclusive_write_fallbacks[std::make_tuple(ordered, bitsize, vaddr.getIdx(), value.getIdx())];
EmitExclusiveLock(code, conf, tmp, tmp2.cvt32());
EmitExclusiveLock(code, conf, tmp2.cvt32());
SharedLabel end = ctx.GenSharedLabel();
@@ -433,14 +433,14 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
code.movzx(tmp.cvt32(), code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)]);
code.test(tmp.cvt8(), tmp.cvt8());
code.je(*end, code.T_NEAR);
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(conf.global_monitor->exclusive_addresses.data() + conf.processor_id));
code.cmp(qword[tmp], vaddr);
code.jne(*end, code.T_NEAR);
EmitExclusiveTestAndClear(code, conf, vaddr, tmp, rax);
code.mov(code.byte[code.ABI_JIT_PTR + offsetof(AxxJitState, exclusive_state)], u8(0));
code.mov(tmp, std::bit_cast<u64>(GetExclusiveMonitorValuePointer(conf.global_monitor, conf.processor_id)));
code.mov(tmp, std::bit_cast<u64>(conf.global_monitor->exclusive_values.data() + conf.processor_id));
if constexpr (bitsize == 128) {
code.mov(rax, qword[tmp + 0]);
@@ -519,7 +519,7 @@ void AxxEmitX64::EmitExclusiveWriteMemoryInline(AxxEmitContext& ctx, IR::Inst* i
}
code.L(*end);
EmitExclusiveUnlock(code, conf, tmp, eax);
EmitExclusiveUnlock(code, conf, eax);
ctx.reg_alloc.DefineValue(code, inst, status);
EmitCheckMemoryAbort(ctx, inst);
}
@@ -9,11 +9,11 @@
#pragma once
#include <bit>
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/a32_emit_x64.h"
#include "dynarmic/backend/x64/a64_emit_x64.h"
#include "dynarmic/backend/x64/exclusive_monitor_friend.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "dynarmic/common/spin_lock_x64.h"
#include "dynarmic/interface/exclusive_monitor.h"
#include "dynarmic/ir/acc_type.h"
@@ -344,43 +344,36 @@ const void* EmitWriteMemoryMov(BlockOfCode& code, const Xbyak::RegExp& addr, int
}
template<typename UserConfig>
void EmitExclusiveLock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg64 pointer, Xbyak::Reg32 tmp) {
if (conf.HasOptimization(OptimizationFlag::Unsafe_IgnoreGlobalMonitor)) {
return;
void EmitExclusiveLock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg32 tmp) {
if (!conf.HasOptimization(OptimizationFlag::Unsafe_IgnoreGlobalMonitor)) {
u64 const slp = std::bit_cast<u64>(std::addressof(conf.global_monitor->lock.storage));
EmitSpinLockLock(code, dword[slp], tmp, code.HasHostFeature(HostFeature::WAITPKG));
}
code.mov(pointer, std::bit_cast<u64>(GetExclusiveMonitorLockPointer(conf.global_monitor)));
EmitSpinLockLock(code, pointer, tmp, code.HasHostFeature(HostFeature::WAITPKG));
}
template<typename UserConfig>
void EmitExclusiveUnlock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg64 pointer, Xbyak::Reg32 tmp) {
if (conf.HasOptimization(OptimizationFlag::Unsafe_IgnoreGlobalMonitor)) {
return;
void EmitExclusiveUnlock(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg32 tmp) {
if (!conf.HasOptimization(OptimizationFlag::Unsafe_IgnoreGlobalMonitor)) {
u64 const slp = std::bit_cast<u64>(std::addressof(conf.global_monitor->lock.storage));
EmitSpinLockUnlock(code, dword[slp], tmp);
}
code.mov(pointer, std::bit_cast<u64>(GetExclusiveMonitorLockPointer(conf.global_monitor)));
EmitSpinLockUnlock(code, pointer, tmp);
}
template<typename UserConfig>
void EmitExclusiveTestAndClear(BlockOfCode& code, const UserConfig& conf, Xbyak::Reg64 vaddr, Xbyak::Reg64 pointer, Xbyak::Reg64 tmp) {
if (conf.HasOptimization(OptimizationFlag::Unsafe_IgnoreGlobalMonitor)) {
return;
}
code.mov(tmp, 0xDEAD'DEAD'DEAD'DEAD);
const size_t processor_count = GetExclusiveMonitorProcessorCount(conf.global_monitor);
for (size_t processor_index = 0; processor_index < processor_count; processor_index++) {
if (processor_index == conf.processor_id) {
continue;
if (!conf.HasOptimization(OptimizationFlag::Unsafe_IgnoreGlobalMonitor)) {
code.mov(tmp, 0xDEAD'DEAD'DEAD'DEAD);
static_assert(ExclusiveMonitor::MAX_NUM_CPU_CORES == 4);
for (size_t i = 0; i < ExclusiveMonitor::MAX_NUM_CPU_CORES; i++) {
if (i != conf.processor_id) {
Xbyak::Label ok;
code.mov(pointer, std::bit_cast<u64>(conf.global_monitor->exclusive_addresses.data() + i));
code.cmp(qword[pointer], vaddr);
code.jne(ok, code.T_NEAR);
code.mov(qword[pointer], tmp);
code.L(ok);
}
}
Xbyak::Label ok;
code.mov(pointer, std::bit_cast<u64>(GetExclusiveMonitorAddressPointer(conf.global_monitor, processor_index)));
code.cmp(qword[pointer], vaddr);
code.jne(ok, code.T_NEAR);
code.mov(qword[pointer], tmp);
code.L(ok);
}
}
@@ -16,7 +16,7 @@
#include "dynarmic/mcl/bit.hpp"
#include "common/common_types.h"
#include "dynarmic/mcl/function_info.hpp"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/block_of_code.h"
@@ -15,7 +15,7 @@
#include "common/assert.h"
#include "dynarmic/mcl/function_info.hpp"
#include "dynarmic/mcl/integer_of_size.hpp"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/block_of_code.h"
@@ -10,7 +10,7 @@
#include <bitset>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
namespace Dynarmic::Backend::X64 {
@@ -9,7 +9,7 @@
#pragma once
#include "common/assert.h"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
namespace Dynarmic::Backend::X64 {
@@ -15,7 +15,7 @@
#include "dynarmic/backend/x64/hostloc.h"
#include "common/assert.h"
#include <bit>
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/reg_alloc.h"
@@ -14,7 +14,7 @@
#include "boost/container/small_vector.hpp"
#include "common/common_types.h"
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include <boost/container/static_vector.hpp>
#include <boost/container/flat_set.hpp>
#include <boost/pool/pool_alloc.hpp>
@@ -0,0 +1,13 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
// You must ensure this matches with src/common/x64/xbyak.h on root dir
#include <ankerl/unordered_dense.h>
#include <boost/unordered_map.hpp>
#define XBYAK_STD_UNORDERED_SET ankerl::unordered_dense::set
#define XBYAK_STD_UNORDERED_MAP ankerl::unordered_dense::map
#define XBYAK_STD_UNORDERED_MULTIMAP boost::unordered_multimap
#include <xbyak/xbyak.h>
#include <xbyak/xbyak_util.h>
@@ -8,7 +8,7 @@
#include <mutex>
#include <optional>
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
#include "dynarmic/backend/x64/abi.h"
#include "dynarmic/backend/x64/hostloc.h"
@@ -22,26 +22,23 @@ static const auto default_cg_mode = nullptr; //Allow RWE
namespace Dynarmic {
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg) {
Xbyak::Label start, loop;
code.jmp(start, code.T_NEAR);
code.L(loop);
/// @brief Emits a lock path for a given spinlock
/// @arg ptr Operand must be a dword[ptr]
/// @arg waitpkg Whetever or not the "UMWAIT" instruction can be used
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Address ptr, Xbyak::Reg32 tmp, bool waitpkg) {
// TODO: this is because we lack regalloc - so better to be safe :(
// TODO: really involve regalloc when we require a 64 bit disp temporal... aside from the one we got handed of course
if (waitpkg) {
// TODO: this is because we lack regalloc - so better to be safe :(
code.push(Xbyak::util::rax);
code.push(Xbyak::util::rbx);
code.push(Xbyak::util::rdx);
Xbyak::Label start, loop;
code.jmp(start, code.T_NEAR);
code.L(loop);
code.push(Xbyak::util::eax);
code.push(Xbyak::util::ebx);
code.push(Xbyak::util::edx);
// TODO: This clobbers EAX and EDX did we tell the regalloc?
// ARM ptr for address-monitoring
// XBYAK BUG: code.umonitor(ptr); see issue #255
// replace once xbyak has been fixed
code.db(0xF3);
if (ptr.getIdx() >= 8) code.db(0x41);
code.db(0x0F); code.db(0xAE);
code.db(uint8_t((3 << 6) | ((6 & 7) << 3) | (ptr.getIdx() & 7)));
code.mov(Xbyak::util::eax, ptr);
code.umonitor(Xbyak::util::eax);
// tmp.bit[0] = 0: C0.1 | Slow Wakup | Better Savings
// tmp.bit[0] = 1: C0.2 | Fast Wakup | Lesser Savings
// edx:eax is implicitly used as a 64-bit deadline timestamp
@@ -55,22 +52,64 @@ void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32
code.umwait(Xbyak::util::ebx);
// CF == 1 if we hit the OS-timeout in IA32_UMWAIT_CONTROL without a write
// CF == 0 if we exited the wait for any other reason
code.pop(Xbyak::util::rdx);
code.pop(Xbyak::util::rbx);
code.pop(Xbyak::util::rax);
code.pop(Xbyak::util::edx);
code.pop(Xbyak::util::ebx);
code.pop(Xbyak::util::eax);
code.L(start);
code.mov(tmp, 1);
if (ptr.is64bitDisp()) {
// if tmp is on eax, use ebx, otherwise use eax!
auto const other_tmp = tmp.cvt32() == Xbyak::util::eax
? Xbyak::util::rbx
: Xbyak::util::rax;
code.push(other_tmp);
code.mov(other_tmp, ptr.getDisp());
/*code.lock();*/ code.xchg(code.dword[other_tmp], tmp);
code.pop(other_tmp);
} else {
/*code.lock();*/ code.xchg(ptr, tmp);
}
code.test(tmp, tmp);
code.jnz(loop, code.T_NEAR);
} else {
Xbyak::Label start, loop;
code.jmp(start, code.T_NEAR);
code.L(loop);
code.pause();
code.L(start);
code.mov(tmp, 1);
if (ptr.is64bitDisp()) {
// if tmp is on eax, use ebx, otherwise use eax!
auto const other_tmp = tmp.cvt32() == Xbyak::util::eax
? Xbyak::util::rbx
: Xbyak::util::rax;
code.push(other_tmp);
code.mov(other_tmp, ptr.getDisp());
/*code.lock();*/ code.xchg(code.dword[other_tmp], tmp);
code.pop(other_tmp);
} else {
/*code.lock();*/ code.xchg(ptr, tmp);
}
code.test(tmp, tmp);
code.jnz(loop, code.T_NEAR);
}
code.L(start);
code.mov(tmp, 1);
/*code.lock();*/ code.xchg(code.dword[ptr], tmp);
code.test(tmp, tmp);
code.jnz(loop, code.T_NEAR);
}
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp) {
// ptr operand must be a dword[ptr]
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Address ptr, Xbyak::Reg32 tmp) {
code.xor_(tmp, tmp);
code.xchg(code.dword[ptr], tmp);
if (ptr.is64bitDisp()) {
// if tmp is on eax, use ebx, otherwise use eax!
auto const other_tmp = tmp.cvt32() == Xbyak::util::eax
? Xbyak::util::rbx
: Xbyak::util::rax;
code.push(other_tmp);
code.mov(other_tmp, ptr.getDisp());
/*code.lock();*/ code.xchg(code.dword[other_tmp], tmp);
code.pop(other_tmp);
} else {
/*code.lock();*/ code.xchg(ptr, tmp);
}
code.mfence();
}
@@ -93,11 +132,12 @@ void SpinLockImpl::Initialize() noexcept {
Xbyak::Reg64 const ABI_PARAM1 = Backend::X64::HostLocToReg64(Backend::X64::ABI_PARAM1);
code.align();
lock = code.getCurr<void (*)(volatile int*)>();
EmitSpinLockLock(code, ABI_PARAM1, code.eax, false);
EmitSpinLockLock(code, code.dword[ABI_PARAM1], code.eax, false);
code.ret();
code.align();
unlock = code.getCurr<void (*)(volatile int*)>();
EmitSpinLockUnlock(code, ABI_PARAM1, code.eax);
EmitSpinLockUnlock(code, code.dword[ABI_PARAM1], code.eax);
code.ret();
}
@@ -8,11 +8,11 @@
#pragma once
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
namespace Dynarmic {
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp, bool waitpkg);
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Reg64 ptr, Xbyak::Reg32 tmp);
void EmitSpinLockLock(Xbyak::CodeGenerator& code, Xbyak::Address ptr, Xbyak::Reg32 tmp, bool waitpkg);
void EmitSpinLockUnlock(Xbyak::CodeGenerator& code, Xbyak::Address ptr, Xbyak::Reg32 tmp);
} // namespace Dynarmic
@@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
/* This file is part of the dynarmic project.
* Copyright (c) 2018 MerryMage
* SPDX-License-Identifier: 0BSD
@@ -20,68 +23,71 @@ using Vector = std::array<std::uint64_t, 2>;
class ExclusiveMonitor {
public:
/// @param processor_count Maximum number of processors using this global
/// exclusive monitor. Each processor must have a
/// unique id.
explicit ExclusiveMonitor(size_t processor_count);
size_t GetProcessorCount() const;
explicit ExclusiveMonitor() noexcept {
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
}
/// Marks a region containing [address, address+size) to be exclusive to
/// processor processor_id.
template<typename T, typename Function>
T ReadAndMark(size_t processor_id, VAddr address, Function op) {
/// processor index.
template<typename T, typename F>
[[nodiscard]] inline T ReadAndMark(std::size_t index, VAddr address, F f) {
static_assert(std::is_trivially_copyable_v<T>);
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
Lock();
exclusive_addresses[processor_id] = masked_address;
const T value = op();
std::memcpy(exclusive_values[processor_id].data(), &value, sizeof(T));
Unlock();
lock.Lock();
exclusive_addresses[index] = masked_address;
T const value = f();
std::memcpy(exclusive_values[index].data(), std::addressof(value), sizeof(T));
lock.Unlock();
return value;
}
/// Checks to see if processor processor_id has exclusive access to the
[[nodiscard]] inline bool CheckAndClear(std::size_t index, VAddr address) {
const VAddr masked_address = address & RESERVATION_GRANULE_MASK;
if (exclusive_addresses[index] != masked_address)
return false;
for (VAddr& other_address : exclusive_addresses)
if (other_address == masked_address)
other_address = INVALID_EXCLUSIVE_ADDRESS;
return true;
}
/// Checks to see if processor index has exclusive access to the
/// specified region. If it does, executes the operation then clears
/// the exclusive state for processors if their exclusive region(s)
/// contain [address, address+size).
template<typename T, typename Function>
bool DoExclusiveOperation(size_t processor_id, VAddr address, Function op) {
template<typename T, typename F>
[[nodiscard]] inline bool DoExclusiveOperation(std::size_t index, VAddr address, F&& f) {
static_assert(std::is_trivially_copyable_v<T>);
if (!CheckAndClear(processor_id, address)) {
return false;
bool result = false;
lock.Lock();
if (CheckAndClear(index, address)) {
T saved_value{};
std::memcpy(std::addressof(saved_value), exclusive_values[index].data(), sizeof(T));
result = f(saved_value);
}
T saved_value;
std::memcpy(&saved_value, exclusive_values[processor_id].data(), sizeof(T));
const bool result = op(saved_value);
Unlock();
lock.Unlock();
return result;
}
/// Unmark everything.
void Clear();
inline void Clear() {
lock.Lock();
std::fill(exclusive_addresses.begin(), exclusive_addresses.end(), INVALID_EXCLUSIVE_ADDRESS);
lock.Unlock();
}
/// Unmark processor id
void ClearProcessor(size_t processor_id);
private:
bool CheckAndClear(size_t processor_id, VAddr address);
void Lock();
void Unlock();
friend volatile int* GetExclusiveMonitorLockPointer(ExclusiveMonitor*);
friend size_t GetExclusiveMonitorProcessorCount(ExclusiveMonitor*);
friend VAddr* GetExclusiveMonitorAddressPointer(ExclusiveMonitor*, size_t index);
friend Vector* GetExclusiveMonitorValuePointer(ExclusiveMonitor*, size_t index);
inline void ClearProcessor(size_t index) {
lock.Lock();
exclusive_addresses[index] = INVALID_EXCLUSIVE_ADDRESS;
lock.Unlock();
}
static constexpr VAddr RESERVATION_GRANULE_MASK = 0xFFFF'FFFF'FFFF'FFFFull;
static constexpr VAddr INVALID_EXCLUSIVE_ADDRESS = 0xDEAD'DEAD'DEAD'DEADull;
static constexpr size_t MAX_NUM_CPU_CORES = 4; // Sync with src/core/hardware_properties
boost::container::static_vector<VAddr, MAX_NUM_CPU_CORES> exclusive_addresses;
boost::container::static_vector<Vector, MAX_NUM_CPU_CORES> exclusive_values;
std::array<VAddr, MAX_NUM_CPU_CORES> exclusive_addresses;
std::array<Vector, MAX_NUM_CPU_CORES> exclusive_values;
SpinLock lock;
};
+1 -1
View File
@@ -14,7 +14,7 @@
#include <utility>
#include <catch2/catch_test_macros.hpp>
#include "common/x64/xbyak.h"
#include "dynarmic/backend/x64/xbyak.h"
TEST_CASE("Host CPU supports", "[a64]") {
using Cpu = Xbyak::util::Cpu;
-260
View File
@@ -13,267 +13,7 @@
#include <variant>
#ifdef ARCHITECTURE_x86_64
#include <type_traits>
#include <bitset>
#include <initializer_list>
#include "common/assert.h"
// xbyak hates human beings
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wconversion"
#pragma GCC diagnostic ignored "-Wshadow"
#endif
#ifdef __clang__
#pragma clang diagnostic ignored "-Wconversion"
#pragma clang diagnostic ignored "-Wshadow"
#endif
#include "common/x64/xbyak.h"
namespace Common::X64 {
constexpr size_t RegToIndex(const Xbyak::Reg& reg) {
using Kind = Xbyak::Reg::Kind;
ASSERT((reg.getKind() & (Kind::REG | Kind::XMM)) != 0 && "RegSet only support GPRs and XMM registers.");
ASSERT(reg.getIdx() < 16 && "RegSet only supports XXM0-15.");
return size_t(reg.getIdx()) + (reg.getKind() == Kind::REG ? 0 : 16);
}
constexpr Xbyak::Reg64 IndexToReg64(size_t reg_index) {
ASSERT(reg_index < 16);
return Xbyak::Reg64(int(reg_index));
}
constexpr Xbyak::Xmm IndexToXmm(size_t reg_index) {
ASSERT(reg_index >= 16 && reg_index < 32);
return Xbyak::Xmm(int(reg_index - 16));
}
constexpr Xbyak::Reg IndexToReg(size_t reg_index) {
if (reg_index < 16) {
return IndexToReg64(reg_index);
} else {
return IndexToXmm(reg_index);
}
}
constexpr std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
size_t bits = 0;
for (const Xbyak::Reg& reg : regs)
bits |= size_t{1} << RegToIndex(reg);
return {bits};
}
constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
constexpr inline Xbyak::Reg ABI_JIT_REG = Xbyak::util::rbx;
#ifdef _WIN32
// Microsoft x64 ABI
constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
});
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rsi,
Xbyak::util::rdi,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
// XMMs
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
constexpr size_t ABI_SHADOW_SPACE = 0x20;
#else
// System V x86-64 ABI
constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
constexpr inline std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rcx,
Xbyak::util::rdx,
Xbyak::util::rdi,
Xbyak::util::rsi,
Xbyak::util::r8,
Xbyak::util::r9,
Xbyak::util::r10,
Xbyak::util::r11,
// XMMs
Xbyak::util::xmm0,
Xbyak::util::xmm1,
Xbyak::util::xmm2,
Xbyak::util::xmm3,
Xbyak::util::xmm4,
Xbyak::util::xmm5,
Xbyak::util::xmm6,
Xbyak::util::xmm7,
Xbyak::util::xmm8,
Xbyak::util::xmm9,
Xbyak::util::xmm10,
Xbyak::util::xmm11,
Xbyak::util::xmm12,
Xbyak::util::xmm13,
Xbyak::util::xmm14,
Xbyak::util::xmm15,
});
constexpr inline std::bitset<32> ABI_ALL_CALLEE_SAVED = BuildRegSet({
// GPRs
Xbyak::util::rbx,
Xbyak::util::rbp,
Xbyak::util::r12,
Xbyak::util::r13,
Xbyak::util::r14,
Xbyak::util::r15,
});
constexpr size_t ABI_SHADOW_SPACE = 0;
#endif
struct ABIFrameInfo {
s32 subtraction;
s32 xmm_offset;
};
inline ABIFrameInfo ABI_CalculateFrameSize(std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size) {
const auto count = (regs & ABI_ALL_GPRS).count();
rsp_alignment -= count * 8;
size_t subtraction = 0;
const auto xmm_count = (regs & ABI_ALL_XMMS).count();
if (xmm_count) {
// If we have any XMMs to save, we must align the stack here.
subtraction = rsp_alignment & 0xF;
}
subtraction += 0x10 * xmm_count;
size_t xmm_base_subtraction = subtraction;
subtraction += needed_frame_size;
subtraction += ABI_SHADOW_SPACE;
// Final alignment.
rsp_alignment -= subtraction;
subtraction += rsp_alignment & 0xF;
return ABIFrameInfo{
s32(subtraction),
s32(subtraction - xmm_base_subtraction)
};
}
inline size_t ABI_PushRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
for (size_t i = 0; i < regs.size(); ++i)
if (regs[i] && ABI_ALL_GPRS[i])
code.push(IndexToReg64(i));
if (frame_info.subtraction != 0)
code.sub(code.rsp, frame_info.subtraction);
for (size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_XMMS[i]) {
code.movaps(code.xword[code.rsp + frame_info.xmm_offset], IndexToXmm(i));
frame_info.xmm_offset += 0x10;
}
}
return ABI_SHADOW_SPACE;
}
inline void ABI_PopRegistersAndAdjustStack(Xbyak::CodeGenerator& code, std::bitset<32> regs, size_t rsp_alignment, size_t needed_frame_size = 0) {
auto frame_info = ABI_CalculateFrameSize(regs, rsp_alignment, needed_frame_size);
for (size_t i = 0; i < regs.size(); ++i) {
if (regs[i] && ABI_ALL_XMMS[i]) {
code.movaps(IndexToXmm(i), code.xword[code.rsp + frame_info.xmm_offset]);
frame_info.xmm_offset += 0x10;
}
}
if (frame_info.subtraction != 0)
code.add(code.rsp, frame_info.subtraction);
// GPRs need to be popped in reverse order
for (size_t j = 0; j < regs.size(); ++j) {
const size_t i = regs.size() - j - 1;
if (regs[i] && ABI_ALL_GPRS[i]) {
code.pop(IndexToReg64(i));
}
}
}
// Constants for use with cmpps/cmpss
enum {
CMP_EQ = 0,
CMP_LT = 1,
CMP_LE = 2,
CMP_UNORD = 3,
CMP_NEQ = 4,
CMP_NLT = 5,
CMP_NLE = 6,
CMP_ORD = 7,
};
constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) {
const u64 distance = target - (ref + 5);
return !(distance >= 0x8000'0000ULL && distance <= ~0x8000'0000ULL);
}
inline bool IsWithin2G(const Xbyak::CodeGenerator& code, uintptr_t target) {
return IsWithin2G(uintptr_t(code.getCurr()), target);
}
template <typename T>
inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) {
static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer.");
size_t addr = size_t(f);
if (IsWithin2G(code, addr)) {
code.call(f);
} else {
// ABI_RETURN is a safe temp register to use before a call
code.mov(ABI_RETURN, addr);
code.call(ABI_RETURN);
}
}
} // namespace Common::X64
#endif
#include "common/assert.h"
@@ -217,12 +217,6 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_UNORM_BLOCK, 0, ASTC_2D_6X5_UNORM) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ASTC_6x5_SRGB_BLOCK, 0, ASTC_2D_6X5_SRGB) \
SURFACE_FORMAT_ELEM(VK_FORMAT_E5B9G9R9_UFLOAT_PACK32, 0, E5B9G9R9_FLOAT) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_UNORM_BLOCK, 0, ETC2_RGB_UNORM) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_UNORM_BLOCK, 0, ETC2_RGBA_UNORM) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_UNORM_BLOCK, 0, ETC2_RGB_PTA_UNORM) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8_SRGB_BLOCK, 0, ETC2_RGB_SRGB) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A8_SRGB_BLOCK, 0, ETC2_RGBA_SRGB) \
SURFACE_FORMAT_ELEM(VK_FORMAT_ETC2_R8G8B8A1_SRGB_BLOCK, 0, ETC2_RGB_PTA_SRGB) \
/* Depth formats */ \
SURFACE_FORMAT_ELEM(VK_FORMAT_D32_SFLOAT, usage_attachable, D32_FLOAT) \
SURFACE_FORMAT_ELEM(VK_FORMAT_D16_UNORM, usage_attachable, D16_UNORM) \
@@ -259,8 +253,8 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
break;
}
}
// Transcode on hardware that doesn't support BCn natively
if (!device.IsOptimalBcnSupported() && VideoCore::Surface::IsPixelFormatBCn(pixel_format)) {
// Transcode on hardware that doesn't support BCn natively
if (pixel_format == PixelFormat::BC4_SNORM) {
tuple.format = VK_FORMAT_R8_SNORM;
} else if (pixel_format == PixelFormat::BC4_UNORM) {
@@ -276,9 +270,6 @@ FormatInfo SurfaceFormat(const Device& device, FormatType format_type, bool with
} else {
tuple.format = VK_FORMAT_A8B8G8R8_UNORM_PACK32;
}
} else if (!device.IsOptimalEtc2Supported() && VideoCore::Surface::IsPixelFormatETC2(pixel_format)) {
// Transcode on hardware that doesn't support ETC2 natively
tuple.format = is_srgb ? VK_FORMAT_A8B8G8R8_SRGB_PACK32 : VK_FORMAT_A8B8G8R8_UNORM_PACK32;
}
bool const attachable = (tuple.usage & usage_attachable) != 0;
bool const storage = (tuple.usage & usage_storage) != 0;
+1 -18
View File
@@ -1,4 +1,4 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
@@ -336,20 +336,6 @@ bool IsPixelFormatBCn(PixelFormat format) {
}
}
bool IsPixelFormatETC2(PixelFormat format) {
switch (format) {
case PixelFormat::ETC2_RGB_UNORM:
case PixelFormat::ETC2_RGBA_UNORM:
case PixelFormat::ETC2_RGB_PTA_UNORM:
case PixelFormat::ETC2_RGB_SRGB:
case PixelFormat::ETC2_RGBA_SRGB:
case PixelFormat::ETC2_RGB_PTA_SRGB:
return true;
default:
return false;
}
}
bool IsPixelFormatSRGB(PixelFormat format) {
switch (format) {
case PixelFormat::A8B8G8R8_SRGB:
@@ -358,9 +344,6 @@ bool IsPixelFormatSRGB(PixelFormat format) {
case PixelFormat::BC2_SRGB:
case PixelFormat::BC3_SRGB:
case PixelFormat::BC7_SRGB:
case PixelFormat::ETC2_RGB_SRGB:
case PixelFormat::ETC2_RGBA_SRGB:
case PixelFormat::ETC2_RGB_PTA_SRGB:
case PixelFormat::ASTC_2D_4X4_SRGB:
case PixelFormat::ASTC_2D_8X8_SRGB:
case PixelFormat::ASTC_2D_8X5_SRGB:
+2 -7
View File
@@ -111,12 +111,6 @@ namespace VideoCore::Surface {
PIXEL_FORMAT_ELEM(ASTC_2D_6X5_UNORM, 6, 5, 128) \
PIXEL_FORMAT_ELEM(ASTC_2D_6X5_SRGB, 6, 5, 128) \
PIXEL_FORMAT_ELEM(E5B9G9R9_FLOAT, 1, 1, 32) \
PIXEL_FORMAT_ELEM(ETC2_RGB_UNORM, 4, 4, 64) \
PIXEL_FORMAT_ELEM(ETC2_RGBA_UNORM, 4, 4, 128) \
PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_UNORM, 4, 4, 64) \
PIXEL_FORMAT_ELEM(ETC2_RGB_SRGB, 4, 4, 64) \
PIXEL_FORMAT_ELEM(ETC2_RGBA_SRGB, 4, 4, 128) \
PIXEL_FORMAT_ELEM(ETC2_RGB_PTA_SRGB, 4, 4, 64) \
/* Depth formats */ \
PIXEL_FORMAT_ELEM(D32_FLOAT, 1, 1, 32) \
PIXEL_FORMAT_ELEM(D16_UNORM, 1, 1, 16) \
@@ -187,6 +181,8 @@ constexpr u32 BitsPerBlock(PixelFormat format) noexcept {
}
}
#undef PIXEL_FORMAT_LIST
/// Returns the sizer in bytes of the specified pixel format
constexpr u32 BytesPerBlock(PixelFormat pixel_format) {
return BitsPerBlock(pixel_format) / CHAR_BIT;
@@ -202,7 +198,6 @@ SurfaceType GetFormatType(PixelFormat pixel_format);
bool HasAlpha(PixelFormat pixel_format);
bool IsPixelFormatASTC(PixelFormat format);
bool IsPixelFormatBCn(PixelFormat format);
bool IsPixelFormatETC2(PixelFormat format);
bool IsPixelFormatSRGB(PixelFormat format);
bool IsPixelFormatInteger(PixelFormat format);
bool IsPixelFormatSignedInteger(PixelFormat format);
@@ -191,20 +191,6 @@ PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red,
return PixelFormat::BC6H_SFLOAT;
case Hash(TextureFormat::BC6H_U16, FLOAT):
return PixelFormat::BC6H_UFLOAT;
/* ETC2 */
case Hash(TextureFormat::ETC2_RGB, UNORM, LINEAR):
return PixelFormat::ETC2_RGB_UNORM;
case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, LINEAR):
return PixelFormat::ETC2_RGB_PTA_UNORM;
case Hash(TextureFormat::ETC2_RGBA, UNORM, LINEAR):
return PixelFormat::ETC2_RGBA_UNORM;
case Hash(TextureFormat::ETC2_RGB, UNORM, SRGB):
return PixelFormat::ETC2_RGB_SRGB;
case Hash(TextureFormat::ETC2_RGB_PTA, UNORM, SRGB):
return PixelFormat::ETC2_RGB_PTA_SRGB;
case Hash(TextureFormat::ETC2_RGBA, UNORM, SRGB):
return PixelFormat::ETC2_RGBA_SRGB;
/* ASTC */
case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR):
return PixelFormat::ASTC_2D_4X4_UNORM;
case Hash(TextureFormat::ASTC_2D_4X4, UNORM, SRGB):
+204 -6
View File
@@ -1,6 +1,3 @@
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
@@ -20,9 +17,210 @@ struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::str
using VideoCore::Surface::PixelFormat;
const string_view name = [format] {
switch (format) {
#define PIXEL_FORMAT_ELEM(NAME, ...) case PixelFormat::NAME: return #NAME;
PIXEL_FORMAT_LIST
#undef PIXEL_FORMAT_ELEM
case PixelFormat::A8B8G8R8_UNORM:
return "A8B8G8R8_UNORM";
case PixelFormat::A8B8G8R8_SNORM:
return "A8B8G8R8_SNORM";
case PixelFormat::A8B8G8R8_SINT:
return "A8B8G8R8_SINT";
case PixelFormat::A8B8G8R8_UINT:
return "A8B8G8R8_UINT";
case PixelFormat::R5G6B5_UNORM:
return "R5G6B5_UNORM";
case PixelFormat::B5G6R5_UNORM:
return "B5G6R5_UNORM";
case PixelFormat::A1R5G5B5_UNORM:
return "A1R5G5B5_UNORM";
case PixelFormat::A2B10G10R10_UNORM:
return "A2B10G10R10_UNORM";
case PixelFormat::A2B10G10R10_UINT:
return "A2B10G10R10_UINT";
case PixelFormat::A2R10G10B10_UNORM:
return "A2R10G10B10_UNORM";
case PixelFormat::A1B5G5R5_UNORM:
return "A1B5G5R5_UNORM";
case PixelFormat::A5B5G5R1_UNORM:
return "A5B5G5R1_UNORM";
case PixelFormat::R8_UNORM:
return "R8_UNORM";
case PixelFormat::R8_SNORM:
return "R8_SNORM";
case PixelFormat::R8_SINT:
return "R8_SINT";
case PixelFormat::R8_UINT:
return "R8_UINT";
case PixelFormat::R16G16B16A16_FLOAT:
return "R16G16B16A16_FLOAT";
case PixelFormat::R16G16B16A16_UNORM:
return "R16G16B16A16_UNORM";
case PixelFormat::R16G16B16A16_SNORM:
return "R16G16B16A16_SNORM";
case PixelFormat::R16G16B16A16_SINT:
return "R16G16B16A16_SINT";
case PixelFormat::R16G16B16A16_UINT:
return "R16G16B16A16_UINT";
case PixelFormat::B10G11R11_FLOAT:
return "B10G11R11_FLOAT";
case PixelFormat::R32G32B32A32_UINT:
return "R32G32B32A32_UINT";
case PixelFormat::BC1_RGBA_UNORM:
return "BC1_RGBA_UNORM";
case PixelFormat::BC2_UNORM:
return "BC2_UNORM";
case PixelFormat::BC3_UNORM:
return "BC3_UNORM";
case PixelFormat::BC4_UNORM:
return "BC4_UNORM";
case PixelFormat::BC4_SNORM:
return "BC4_SNORM";
case PixelFormat::BC5_UNORM:
return "BC5_UNORM";
case PixelFormat::BC5_SNORM:
return "BC5_SNORM";
case PixelFormat::BC7_UNORM:
return "BC7_UNORM";
case PixelFormat::BC6H_UFLOAT:
return "BC6H_UFLOAT";
case PixelFormat::BC6H_SFLOAT:
return "BC6H_SFLOAT";
case PixelFormat::ASTC_2D_4X4_UNORM:
return "ASTC_2D_4X4_UNORM";
case PixelFormat::B8G8R8A8_UNORM:
return "B8G8R8A8_UNORM";
case PixelFormat::R32G32B32A32_FLOAT:
return "R32G32B32A32_FLOAT";
case PixelFormat::R32G32B32A32_SINT:
return "R32G32B32A32_SINT";
case PixelFormat::R32G32_FLOAT:
return "R32G32_FLOAT";
case PixelFormat::R32G32_SINT:
return "R32G32_SINT";
case PixelFormat::R32_FLOAT:
return "R32_FLOAT";
case PixelFormat::R16_FLOAT:
return "R16_FLOAT";
case PixelFormat::R16_UNORM:
return "R16_UNORM";
case PixelFormat::R16_SNORM:
return "R16_SNORM";
case PixelFormat::R16_UINT:
return "R16_UINT";
case PixelFormat::R16_SINT:
return "R16_SINT";
case PixelFormat::R16G16_UNORM:
return "R16G16_UNORM";
case PixelFormat::R16G16_FLOAT:
return "R16G16_FLOAT";
case PixelFormat::R16G16_UINT:
return "R16G16_UINT";
case PixelFormat::R16G16_SINT:
return "R16G16_SINT";
case PixelFormat::R16G16_SNORM:
return "R16G16_SNORM";
case PixelFormat::R32G32B32_FLOAT:
return "R32G32B32_FLOAT";
case PixelFormat::A8B8G8R8_SRGB:
return "A8B8G8R8_SRGB";
case PixelFormat::R8G8_UNORM:
return "R8G8_UNORM";
case PixelFormat::R8G8_SNORM:
return "R8G8_SNORM";
case PixelFormat::R8G8_SINT:
return "R8G8_SINT";
case PixelFormat::R8G8_UINT:
return "R8G8_UINT";
case PixelFormat::R32G32_UINT:
return "R32G32_UINT";
case PixelFormat::R16G16B16X16_FLOAT:
return "R16G16B16X16_FLOAT";
case PixelFormat::R32_UINT:
return "R32_UINT";
case PixelFormat::R32_SINT:
return "R32_SINT";
case PixelFormat::ASTC_2D_8X8_UNORM:
return "ASTC_2D_8X8_UNORM";
case PixelFormat::ASTC_2D_8X5_UNORM:
return "ASTC_2D_8X5_UNORM";
case PixelFormat::ASTC_2D_5X4_UNORM:
return "ASTC_2D_5X4_UNORM";
case PixelFormat::B8G8R8A8_SRGB:
return "B8G8R8A8_SRGB";
case PixelFormat::BC1_RGBA_SRGB:
return "BC1_RGBA_SRGB";
case PixelFormat::BC2_SRGB:
return "BC2_SRGB";
case PixelFormat::BC3_SRGB:
return "BC3_SRGB";
case PixelFormat::BC7_SRGB:
return "BC7_SRGB";
case PixelFormat::A4B4G4R4_UNORM:
return "A4B4G4R4_UNORM";
case PixelFormat::G4R4_UNORM:
return "G4R4_UNORM";
case PixelFormat::ASTC_2D_4X4_SRGB:
return "ASTC_2D_4X4_SRGB";
case PixelFormat::ASTC_2D_8X8_SRGB:
return "ASTC_2D_8X8_SRGB";
case PixelFormat::ASTC_2D_8X5_SRGB:
return "ASTC_2D_8X5_SRGB";
case PixelFormat::ASTC_2D_5X4_SRGB:
return "ASTC_2D_5X4_SRGB";
case PixelFormat::ASTC_2D_5X5_UNORM:
return "ASTC_2D_5X5_UNORM";
case PixelFormat::ASTC_2D_5X5_SRGB:
return "ASTC_2D_5X5_SRGB";
case PixelFormat::ASTC_2D_10X8_UNORM:
return "ASTC_2D_10X8_UNORM";
case PixelFormat::ASTC_2D_10X8_SRGB:
return "ASTC_2D_10X8_SRGB";
case PixelFormat::ASTC_2D_6X6_UNORM:
return "ASTC_2D_6X6_UNORM";
case PixelFormat::ASTC_2D_6X6_SRGB:
return "ASTC_2D_6X6_SRGB";
case PixelFormat::ASTC_2D_10X6_UNORM:
return "ASTC_2D_10X6_UNORM";
case PixelFormat::ASTC_2D_10X6_SRGB:
return "ASTC_2D_10X6_SRGB";
case PixelFormat::ASTC_2D_10X5_UNORM:
return "ASTC_2D_10X5_UNORM";
case PixelFormat::ASTC_2D_10X5_SRGB:
return "ASTC_2D_10X5_SRGB";
case PixelFormat::ASTC_2D_10X10_UNORM:
return "ASTC_2D_10X10_UNORM";
case PixelFormat::ASTC_2D_10X10_SRGB:
return "ASTC_2D_10X10_SRGB";
case PixelFormat::ASTC_2D_12X10_UNORM:
return "ASTC_2D_12X10_UNORM";
case PixelFormat::ASTC_2D_12X10_SRGB:
return "ASTC_2D_12X10_SRGB";
case PixelFormat::ASTC_2D_12X12_UNORM:
return "ASTC_2D_12X12_UNORM";
case PixelFormat::ASTC_2D_12X12_SRGB:
return "ASTC_2D_12X12_SRGB";
case PixelFormat::ASTC_2D_8X6_UNORM:
return "ASTC_2D_8X6_UNORM";
case PixelFormat::ASTC_2D_8X6_SRGB:
return "ASTC_2D_8X6_SRGB";
case PixelFormat::ASTC_2D_6X5_UNORM:
return "ASTC_2D_6X5_UNORM";
case PixelFormat::ASTC_2D_6X5_SRGB:
return "ASTC_2D_6X5_SRGB";
case PixelFormat::E5B9G9R9_FLOAT:
return "E5B9G9R9_FLOAT";
case PixelFormat::D32_FLOAT:
return "D32_FLOAT";
case PixelFormat::D16_UNORM:
return "D16_UNORM";
case PixelFormat::X8_D24_UNORM:
return "X8_D24_UNORM";
case PixelFormat::S8_UINT:
return "S8_UINT";
case PixelFormat::D24_UNORM_S8_UINT:
return "D24_UNORM_S8_UINT";
case PixelFormat::S8_UINT_D24_UNORM:
return "S8_UINT_D24_UNORM";
case PixelFormat::D32_FLOAT_S8_UINT:
return "D32_FLOAT_S8_UINT";
case PixelFormat::MaxDepthStencilFormat:
case PixelFormat::Invalid:
return "Invalid";
@@ -363,11 +363,6 @@ public:
return features.features.textureCompressionBC;
}
/// Returns true if ETC2 is natively supported.
bool IsOptimalEtc2Supported() const {
return features.features.textureCompressionETC2;
}
/// Returns true if descriptor aliasing is natively supported.
bool IsDescriptorAliasingSupported() const {
return GetDriverID() != VK_DRIVER_ID_QUALCOMM_PROPRIETARY;