mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-07 04:05:14 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
448c4ec797 | ||
|
|
08fad8fb0d | ||
|
|
29083a0b41 | ||
|
|
a1f4ca873c | ||
|
|
c3b0e4820b | ||
|
|
bb96955e05 | ||
|
|
16bb64aaab | ||
|
|
b9e53b6c46 | ||
|
|
616aa46c6b | ||
|
|
c98fb7ae45 | ||
|
|
6c5abc8819 | ||
|
|
f36726ecf6 | ||
|
|
6d20ae5dc6 | ||
|
|
e469031c40 | ||
|
|
b9a8c383fd | ||
|
|
57df4c6d61 | ||
|
|
c5cfb45f48 | ||
|
|
b6417153d7 | ||
|
|
599b579192 | ||
|
|
9ed0572fa4 | ||
|
|
00b0222129 | ||
|
|
32cc98856f | ||
|
|
dee7441795 | ||
|
|
f6a6f2eac8 | ||
|
|
c10e139964 | ||
|
|
379b200d05 | ||
|
|
b4cde28137 | ||
|
|
3f88150581 | ||
|
|
e95076f321 | ||
|
|
e2a16c53b6 | ||
|
|
ee89b7a794 | ||
|
|
5ac9b45528 | ||
|
|
6a94011b00 | ||
|
|
104fabc11f | ||
|
|
15b2eb48e5 | ||
|
|
62c446a5f9 | ||
|
|
d57a034220 | ||
|
|
18fe049976 | ||
|
|
c6e09ddec5 | ||
|
|
05ee4c363b | ||
|
|
6f2f4a9bc0 | ||
|
|
f699726409 | ||
|
|
a2b8fedba5 | ||
|
|
ed2336c4f2 | ||
|
|
748c3182a7 | ||
|
|
dfc454873c | ||
|
|
1b06a06a4e | ||
|
|
b2ad8d73ac | ||
|
|
fec078f924 | ||
|
|
38f1cded2c | ||
|
|
a313253510 | ||
|
|
7b2b2a3d07 | ||
|
|
910a25b0ed | ||
|
|
75bae81331 | ||
|
|
02abbd216a | ||
|
|
8b7a7e91c9 | ||
|
|
130f82a310 | ||
|
|
18e0f2636f | ||
|
|
2b9029623e | ||
|
|
6798afe91f | ||
|
|
4fa8522d9e | ||
|
|
58272ffc47 | ||
|
|
4c275ecc08 | ||
|
|
294ec80aaf | ||
|
|
e78d3d1a80 | ||
|
|
262389f4ea | ||
|
|
341b811642 | ||
|
|
32744d074e | ||
|
|
5f91bbe28d | ||
|
|
69035348f5 | ||
|
|
be46db10e4 | ||
|
|
aa2c18d8cc | ||
|
|
0a7f0a3441 | ||
|
|
ca2b04b309 | ||
|
|
376892793f | ||
|
|
61f73cf0bf | ||
|
|
9716a4f0cf | ||
|
|
35f3777797 | ||
|
|
d73b79470e | ||
|
|
676d9cb665 | ||
|
|
993b4513d9 | ||
|
|
2958744777 | ||
|
|
2e8aacffe6 | ||
|
|
2101914c9d | ||
|
|
97c4ba018b | ||
|
|
525d50f07e | ||
|
|
5cec8ddca0 | ||
|
|
39b2be15ab | ||
|
|
83bf79ab42 | ||
|
|
1f97a2baa9 | ||
|
|
0c2344166c | ||
|
|
0bf30e2024 | ||
|
|
933cdf76b4 | ||
|
|
36a77bb396 | ||
|
|
1936ebc59c | ||
|
|
9838309560 | ||
|
|
37e2210f64 | ||
|
|
71c32c0135 | ||
|
|
1f0bc49e54 | ||
|
|
718f7ef8e6 | ||
|
|
edd1dfdfe8 | ||
|
|
4243ed19a4 | ||
|
|
16467c0fb7 | ||
|
|
0b7587768d | ||
|
|
d115a57fe6 | ||
|
|
4c74478610 | ||
|
|
77a111cf19 | ||
|
|
7e551e5f88 | ||
|
|
9d44dfdca6 | ||
|
|
a7858f4d37 | ||
|
|
6b3b10f8ae | ||
|
|
9d5fa64f68 | ||
|
|
8b3bbd0ea1 | ||
|
|
cc477b6964 | ||
|
|
7a64bba8c4 | ||
|
|
2d342d4662 | ||
|
|
7b41808806 | ||
|
|
912d019ad8 | ||
|
|
1f9405b880 | ||
|
|
211e7bf0f0 | ||
|
|
822a08f271 | ||
|
|
6cba775d4e | ||
|
|
8b5873061a | ||
|
|
37a33bf127 | ||
|
|
d70c91ddc6 | ||
|
|
370f36c8f7 | ||
|
|
42bb27b1fb | ||
|
|
f522303837 | ||
|
|
3d09a55715 | ||
|
|
c103f54774 | ||
|
|
dc7437b8c6 | ||
|
|
b45503f93c | ||
|
|
8cf1b0263d | ||
|
|
7ecaf24e8b | ||
|
|
52f7ea5433 | ||
|
|
023a32ea26 | ||
|
|
36980131e0 | ||
|
|
b2eb13fa1c | ||
|
|
27b6497bee | ||
|
|
66ee89ec8a | ||
|
|
bee73199e1 | ||
|
|
0e6db843ef | ||
|
|
b45538f01e | ||
|
|
f8cf98d378 | ||
|
|
796c5ccbc9 | ||
|
|
b157a5a0fb | ||
|
|
783ceb2d56 | ||
|
|
d1fc65daaa | ||
|
|
94f464e1a4 | ||
|
|
dfb15aaeb9 | ||
|
|
565f0e6af3 | ||
|
|
b03554bb7d | ||
|
|
7b8cb5107a | ||
|
|
cbb20ef6c6 | ||
|
|
298481f9af | ||
|
|
0d108cd8fd | ||
|
|
490d7e57d7 | ||
|
|
fe77c3ef06 | ||
|
|
eb02afe952 | ||
|
|
b8dc63754b | ||
|
|
df8f1850ae | ||
|
|
e282fd2221 | ||
|
|
3147f0d84f | ||
|
|
4c02c9f037 | ||
|
|
83b4188569 | ||
|
|
82b04d9886 | ||
|
|
36fdd7f6e2 | ||
|
|
19e45544fb |
No files matched your search
@@ -33,3 +33,9 @@
|
||||
[submodule "External/jemalloc"]
|
||||
path = External/jemalloc
|
||||
url = https://github.com/FEX-Emu/jemalloc.git
|
||||
[submodule "External/fmt"]
|
||||
path = External/fmt
|
||||
url = https://github.com/fmtlib/fmt.git
|
||||
[submodule "External/drm-headers"]
|
||||
path = External/drm-headers
|
||||
url = https://github.com/FEX-Emu/drm-headers.git
|
||||
@@ -116,6 +116,8 @@ include_directories(External/jemalloc/pregen/include/)
|
||||
add_subdirectory(External/cpp-optparse/)
|
||||
include_directories(External/cpp-optparse/)
|
||||
|
||||
add_subdirectory(External/fmt/)
|
||||
|
||||
add_subdirectory(External/imgui/)
|
||||
include_directories(External/imgui/)
|
||||
|
||||
|
||||
+2
-2
@@ -267,7 +267,7 @@ function(AddObject Name Type)
|
||||
add_dependencies(${Name} IR_INC)
|
||||
add_dependencies(${Name} CONFIG_INC)
|
||||
|
||||
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
|
||||
target_link_libraries(${Name} pthread vixl dl fmt::fmt xxhash FEX_jemalloc)
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
set_target_properties(${Name} PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(${Name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
@@ -306,7 +306,7 @@ endfunction()
|
||||
|
||||
function(AddLibrary Name Type)
|
||||
add_library(${Name} ${Type} $<TARGET_OBJECTS:${PROJECT_NAME}_object>)
|
||||
target_link_libraries(${Name} pthread vixl dl xxhash FEX_jemalloc)
|
||||
target_link_libraries(${Name} pthread vixl dl fmt::fmt xxhash FEX_jemalloc)
|
||||
set_target_properties(${Name} PROPERTIES OUTPUT_NAME FEXCore)
|
||||
set_target_properties(${Name} PROPERTIES C_VISIBILITY_PRESET hidden)
|
||||
set_target_properties(${Name} PROPERTIES CXX_VISIBILITY_PRESET hidden)
|
||||
|
||||
+19
-11
@@ -6,10 +6,13 @@
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace FEXCore::Paths {
|
||||
std::string CachePath;
|
||||
std::string EntryCache;
|
||||
std::unique_ptr<std::string> CachePath;
|
||||
std::unique_ptr<std::string> EntryCache;
|
||||
|
||||
void InitializePaths() {
|
||||
CachePath = std::make_unique<std::string>();
|
||||
EntryCache = std::make_unique<std::string>();
|
||||
|
||||
char const *HomeDir = getenv("HOME");
|
||||
|
||||
if (!HomeDir) {
|
||||
@@ -22,29 +25,34 @@ namespace FEXCore::Paths {
|
||||
|
||||
char *XDGDataDir = getenv("XDG_DATA_DIR");
|
||||
if (XDGDataDir) {
|
||||
CachePath = XDGDataDir;
|
||||
*CachePath = XDGDataDir;
|
||||
}
|
||||
else {
|
||||
if (HomeDir) {
|
||||
CachePath = HomeDir;
|
||||
*CachePath = HomeDir;
|
||||
}
|
||||
}
|
||||
|
||||
CachePath += "/.fex-emu/";
|
||||
EntryCache = CachePath + "/EntryCache/";
|
||||
*CachePath += "/.fex-emu/";
|
||||
*EntryCache = *CachePath + "/EntryCache/";
|
||||
|
||||
// Ensure the folder structure is created for our Data
|
||||
if (!std::filesystem::exists(EntryCache) &&
|
||||
!std::filesystem::create_directories(EntryCache)) {
|
||||
LogMan::Msg::D("Couldn't create EntryCache directory: '%s'", EntryCache.c_str());
|
||||
if (!std::filesystem::exists(*EntryCache) &&
|
||||
!std::filesystem::create_directories(*EntryCache)) {
|
||||
LogMan::Msg::D("Couldn't create EntryCache directory: '%s'", EntryCache->c_str());
|
||||
}
|
||||
}
|
||||
|
||||
void ShutdownPaths() {
|
||||
CachePath.reset();
|
||||
EntryCache.reset();
|
||||
}
|
||||
|
||||
std::string GetCachePath() {
|
||||
return CachePath;
|
||||
return *CachePath;
|
||||
}
|
||||
|
||||
std::string GetEntryCachePath() {
|
||||
return EntryCache;
|
||||
return *EntryCache;
|
||||
}
|
||||
}
|
||||
+1
@@ -3,6 +3,7 @@
|
||||
|
||||
namespace FEXCore::Paths {
|
||||
void InitializePaths();
|
||||
void ShutdownPaths();
|
||||
std::string GetCachePath();
|
||||
std::string GetEntryCachePath();
|
||||
}
|
||||
+1
-1
@@ -83,7 +83,7 @@ namespace FEXCore::Config {
|
||||
return ConfigFile;
|
||||
}
|
||||
|
||||
std::string GetApplicationConfig(std::string &Filename, bool Global) {
|
||||
std::string GetApplicationConfig(const std::string &Filename, bool Global) {
|
||||
std::string ConfigFile = GetConfigDirectory(Global);
|
||||
if (!Global &&
|
||||
!std::filesystem::exists(ConfigFile) &&
|
||||
|
||||
+12
-4
@@ -14,6 +14,10 @@ namespace FEXCore::Context {
|
||||
IR::InstallOpcodeHandlers(Mode);
|
||||
}
|
||||
|
||||
void ShutdownStaticTables() {
|
||||
FEXCore::Paths::ShutdownPaths();
|
||||
}
|
||||
|
||||
FEXCore::Context::Context *CreateNewContext() {
|
||||
return new FEXCore::Context::Context{};
|
||||
}
|
||||
@@ -50,8 +54,8 @@ namespace FEXCore::Context {
|
||||
CTX->Step();
|
||||
}
|
||||
|
||||
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP) {
|
||||
CTX->CompileBlock(CTX->ParentThread->CurrentFrame, GuestRIP);
|
||||
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
Thread->CTX->CompileBlock(Thread->CurrentFrame, GuestRIP);
|
||||
}
|
||||
|
||||
FEXCore::Context::ExitReason RunUntilExit(FEXCore::Context::Context *CTX) {
|
||||
@@ -149,8 +153,12 @@ namespace FEXCore::Context {
|
||||
CTX->AOTIRLoader = CacheReader;
|
||||
}
|
||||
|
||||
bool WriteAOTIR(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter) {
|
||||
return CTX->WriteAOTIRCache(CacheWriter);
|
||||
void SetAOTIRWriter(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter) {
|
||||
CTX->AOTIRWriter = CacheWriter;
|
||||
}
|
||||
|
||||
void FinalizeAOTIRCache(FEXCore::Context::Context *CTX) {
|
||||
CTX->FinalizeAOTIRCache();
|
||||
}
|
||||
|
||||
void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
|
||||
|
||||
+27
-16
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/JitSymbols.h"
|
||||
#include "Interface/Core/CPUID.h"
|
||||
#include "Interface/Core/Frontend.h"
|
||||
@@ -9,6 +10,7 @@
|
||||
#include "Interface/IR/Passes/RegisterAllocationPass.h"
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/Core/CPUBackend.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/Event.h>
|
||||
#include <stdint.h>
|
||||
|
||||
@@ -20,7 +22,9 @@
|
||||
#include <optional>
|
||||
#include <ostream>
|
||||
#include <set>
|
||||
#include <shared_mutex>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
|
||||
namespace FEXCore {
|
||||
class ThunkHandler;
|
||||
@@ -52,14 +56,6 @@ namespace FEXCore::Context {
|
||||
MODE_SINGLESTEP = 1,
|
||||
};
|
||||
|
||||
struct AOTIRCaptureCacheEntry {
|
||||
uint64_t start;
|
||||
uint64_t len;
|
||||
uint64_t crc;
|
||||
IR::IRListView *IR;
|
||||
IR::RegisterAllocationData *RAData;
|
||||
};
|
||||
|
||||
struct AOTIRInlineEntry {
|
||||
uint64_t GuestHash;
|
||||
uint64_t GuestLength;
|
||||
@@ -67,8 +63,8 @@ namespace FEXCore::Context {
|
||||
/* RAData followed by IRData */
|
||||
uint8_t InlineData[0];
|
||||
|
||||
IR::RegisterAllocationData *GetRAData();
|
||||
IR::IRListView *GetIRData();
|
||||
IR::RegisterAllocationData *GetRAData();
|
||||
IR::IRListView *GetIRData();
|
||||
};
|
||||
|
||||
struct AOTIRInlineIndexEntry {
|
||||
@@ -85,6 +81,13 @@ namespace FEXCore::Context {
|
||||
AOTIRInlineEntry *GetInlineEntry(uint64_t DataOffset);
|
||||
};
|
||||
|
||||
struct AOTIRCaptureCacheEntry {
|
||||
std::unique_ptr<std::ostream> Stream;
|
||||
std::map<uint64_t, uint64_t> Index;
|
||||
|
||||
void AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t Start, uint64_t Length, uint64_t Hash, FEXCore::IR::IRListView *IRList, FEXCore::IR::RegisterAllocationData *RAData);
|
||||
};
|
||||
|
||||
struct Context {
|
||||
friend class FEXCore::HLE::SyscallHandler;
|
||||
#ifdef JIT_ARM64
|
||||
@@ -121,7 +124,7 @@ namespace FEXCore::Context {
|
||||
FEX_CONFIG_OPT(DumpIR, DUMPIR);
|
||||
} Config;
|
||||
|
||||
using IntCallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
|
||||
using IntCallbackReturn = FEX_NAKED void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
|
||||
IntCallbackReturn InterpreterCallbackReturn;
|
||||
|
||||
FEXCore::HostFeatures HostFeatures;
|
||||
@@ -154,7 +157,8 @@ namespace FEXCore::Context {
|
||||
|
||||
std::unordered_map<std::string, AOTIRCacheEntry> AOTIRCache;
|
||||
std::function<int(const std::string&)> AOTIRLoader;
|
||||
std::unordered_map<std::string, std::map<uint64_t, AOTIRCaptureCacheEntry>> AOTIRCaptureCache;
|
||||
std::function<std::unique_ptr<std::ostream>(const std::string&)> AOTIRWriter;
|
||||
std::unordered_map<std::string, AOTIRCaptureCacheEntry> AOTIRCaptureCache;
|
||||
|
||||
struct AddrToFileEntry {
|
||||
uint64_t Start;
|
||||
@@ -181,7 +185,7 @@ namespace FEXCore::Context {
|
||||
|
||||
bool InitCore(FEXCore::CodeLoader *Loader);
|
||||
FEXCore::Context::ExitReason RunUntilExit();
|
||||
int GetProgramStatus();
|
||||
int GetProgramStatus() const;
|
||||
bool IsPaused() const { return !Running; }
|
||||
void Pause();
|
||||
void Run();
|
||||
@@ -192,7 +196,7 @@ namespace FEXCore::Context {
|
||||
void StopThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
void SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event);
|
||||
|
||||
bool GetGdbServerStatus() { return (bool)DebugServer; }
|
||||
bool GetGdbServerStatus() const { return DebugServer != nullptr; }
|
||||
void StartGdbServer();
|
||||
void StopGdbServer();
|
||||
void HandleCallback(uint64_t RIP);
|
||||
@@ -222,7 +226,7 @@ namespace FEXCore::Context {
|
||||
void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
|
||||
|
||||
bool LoadAOTIRCache(int streamfd);
|
||||
bool WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
|
||||
void FinalizeAOTIRCache();
|
||||
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer);
|
||||
|
||||
// Used for thread creation from syscalls
|
||||
@@ -257,7 +261,6 @@ namespace FEXCore::Context {
|
||||
void NotifyPause();
|
||||
|
||||
void AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr, uint64_t Start, uint64_t Length);
|
||||
|
||||
FEXCore::CodeLoader *LocalLoader{};
|
||||
|
||||
// Entry Cache
|
||||
@@ -265,6 +268,14 @@ namespace FEXCore::Context {
|
||||
std::mutex ExitMutex;
|
||||
std::unique_ptr<GdbServer> DebugServer;
|
||||
|
||||
std::shared_mutex AOTIRCacheLock;
|
||||
std::shared_mutex AOTIRCaptureCacheWriteoutLock;
|
||||
std::atomic<bool> AOTIRCaptureCacheWriteoutFlusing;
|
||||
|
||||
std::queue<std::function<void()>> AOTIRCaptureCacheWriteoutQueue;
|
||||
void AOTIRCaptureCacheWriteoutQueue_Flush();
|
||||
void AOTIRCaptureCacheWriteoutQueue_Append(const std::function<void()> &fn);
|
||||
|
||||
bool StartPaused = false;
|
||||
FEX_CONFIG_OPT(AppFilename, APP_FILENAME);
|
||||
};
|
||||
|
||||
+1
-1
@@ -90,7 +90,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h() {
|
||||
(0 << 16) | // Reserved
|
||||
(0 << 17) | // Process-context identifiers
|
||||
(1 << 18) | // Prefetching from memory mapped device
|
||||
(0 << 19) | // SSE4.1
|
||||
(1 << 19) | // SSE4.1
|
||||
(0 << 20) | // SSE4.2
|
||||
(0 << 21) | // X2APIC
|
||||
(1 << 22) | // MOVBE
|
||||
|
||||
+156
-91
@@ -52,8 +52,6 @@ namespace FEXCore::CPU {
|
||||
}
|
||||
}
|
||||
|
||||
static std::mutex AOTIRCacheLock;
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct ThreadLocalData {
|
||||
FEXCore::Core::InternalThreadState* Thread;
|
||||
@@ -147,6 +145,53 @@ namespace DefaultFallbackCore {
|
||||
}
|
||||
|
||||
namespace FEXCore::Context {
|
||||
void Context::AOTIRCaptureCacheWriteoutQueue_Flush() {
|
||||
{
|
||||
std::shared_lock lk{AOTIRCaptureCacheWriteoutLock};
|
||||
if (AOTIRCaptureCacheWriteoutQueue.size() == 0) {
|
||||
AOTIRCaptureCacheWriteoutFlusing.store(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
AOTIRCaptureCacheWriteoutLock.lock();
|
||||
std::function<void()> fn = std::move(AOTIRCaptureCacheWriteoutQueue.front());
|
||||
bool MaybeEmpty = false;
|
||||
AOTIRCaptureCacheWriteoutQueue.pop();
|
||||
MaybeEmpty = AOTIRCaptureCacheWriteoutQueue.size() == 0;
|
||||
AOTIRCaptureCacheWriteoutLock.unlock();
|
||||
|
||||
fn();
|
||||
if (MaybeEmpty) {
|
||||
std::shared_lock lk{AOTIRCaptureCacheWriteoutLock};
|
||||
if (AOTIRCaptureCacheWriteoutQueue.size() == 0) {
|
||||
AOTIRCaptureCacheWriteoutFlusing.store(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOGMAN_MSG_A("Must never get here");
|
||||
}
|
||||
|
||||
void Context::AOTIRCaptureCacheWriteoutQueue_Append(const std::function<void()> &fn) {
|
||||
bool Flush = false;
|
||||
|
||||
{
|
||||
std::unique_lock lk{AOTIRCaptureCacheWriteoutLock};
|
||||
AOTIRCaptureCacheWriteoutQueue.push(fn);
|
||||
if (AOTIRCaptureCacheWriteoutQueue.size() > 10000) {
|
||||
Flush = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool test_val = false;
|
||||
if (Flush && AOTIRCaptureCacheWriteoutFlusing.compare_exchange_strong(test_val, true)) {
|
||||
AOTIRCaptureCacheWriteoutQueue_Flush();
|
||||
}
|
||||
}
|
||||
|
||||
Context::Context() {
|
||||
#ifdef BLOCKSTATS
|
||||
BlockData = std::make_unique<FEXCore::BlockSamplingData>();
|
||||
@@ -177,14 +222,6 @@ namespace FEXCore::Context {
|
||||
Threads.clear();
|
||||
}
|
||||
|
||||
// AOTIRCaptureCache needs manual clear
|
||||
for (auto &Mod: AOTIRCaptureCache) {
|
||||
for (auto &Entry: Mod.second) {
|
||||
delete Entry.second.IR;
|
||||
FEXCore::Allocator::free(Entry.second.RAData);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &Mod: AOTIRCache) {
|
||||
FEXCore::Allocator::munmap(Mod.second.mapping, Mod.second.size);
|
||||
}
|
||||
@@ -282,7 +319,7 @@ namespace FEXCore::Context {
|
||||
// Tell all the threads that they should pause
|
||||
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
|
||||
for (auto &Thread : Threads) {
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE);
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Pause);
|
||||
if (Thread->RunningEvents.Running.load()) {
|
||||
// Only attempt to stop this thread if it is running
|
||||
tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
|
||||
@@ -303,7 +340,7 @@ namespace FEXCore::Context {
|
||||
// Spin up all the threads
|
||||
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
|
||||
for (auto &Thread : Threads) {
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN);
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Return);
|
||||
Thread->RunningEvents.WaitingToStart.store(true);
|
||||
}
|
||||
|
||||
@@ -383,7 +420,7 @@ namespace FEXCore::Context {
|
||||
|
||||
void Context::StopThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
if (Thread->RunningEvents.Running.exchange(false)) {
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::SIGNALEVENT_STOP);
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Stop);
|
||||
tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
|
||||
}
|
||||
}
|
||||
@@ -411,7 +448,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
int Context::GetProgramStatus() {
|
||||
int Context::GetProgramStatus() const {
|
||||
return ParentThread->StatusCode;
|
||||
}
|
||||
|
||||
@@ -442,8 +479,6 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
void Context::InitializeThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
InitializeThreadData(Thread);
|
||||
|
||||
// This will create the execution thread but it won't actually start executing
|
||||
ExecutionThreadHandler *Arg = reinterpret_cast<ExecutionThreadHandler*>(FEXCore::Allocator::malloc(sizeof(ExecutionThreadHandler)));
|
||||
Arg->This = this;
|
||||
@@ -485,21 +520,23 @@ namespace FEXCore::Context {
|
||||
// Create CPU backend
|
||||
switch (Config.Core) {
|
||||
case FEXCore::Config::CONFIG_INTERPRETER:
|
||||
State->CPUBackend.reset(FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread));
|
||||
State->CPUBackend = FEXCore::CPU::CreateInterpreterCore(this, State, CompileThread);
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_IRJIT:
|
||||
State->PassManager->InsertRegisterAllocationPass(DoSRA);
|
||||
|
||||
#if (_M_X86_64 && JIT_X86_64)
|
||||
State->CPUBackend.reset(FEXCore::CPU::CreateX86JITCore(this, State, CompileThread));
|
||||
State->CPUBackend = FEXCore::CPU::CreateX86JITCore(this, State, CompileThread);
|
||||
#elif (_M_ARM_64 && JIT_ARM64)
|
||||
State->CPUBackend.reset(FEXCore::CPU::CreateArm64JITCore(this, State, CompileThread));
|
||||
State->CPUBackend = FEXCore::CPU::CreateArm64JITCore(this, State, CompileThread);
|
||||
#else
|
||||
ERROR_AND_DIE("FEXCore has been compiled without a viable JIT core");
|
||||
#endif
|
||||
|
||||
break;
|
||||
case FEXCore::Config::CONFIG_CUSTOM: State->CPUBackend.reset(CustomCPUFactory(this, State)); break;
|
||||
case FEXCore::Config::CONFIG_CUSTOM:
|
||||
State->CPUBackend = CustomCPUFactory(this, State);
|
||||
break;
|
||||
default: ERROR_AND_DIE("Unknown core configuration");
|
||||
}
|
||||
}
|
||||
@@ -522,6 +559,7 @@ namespace FEXCore::Context {
|
||||
Thread->ThreadManager.parent_tid = ParentTID;
|
||||
|
||||
InitializeCompiler(Thread, false);
|
||||
InitializeThreadData(Thread);
|
||||
|
||||
return Thread;
|
||||
}
|
||||
@@ -764,7 +802,6 @@ namespace FEXCore::Context {
|
||||
LogMan::Msg::I("two:\n %s", out2.str().c_str());
|
||||
LOGMAN_MSG_A("Parsed ir doesn't match\n");
|
||||
}
|
||||
delete reparsed;
|
||||
}
|
||||
}
|
||||
// Run the passmanager over the IR from the dispatcher
|
||||
@@ -824,6 +861,28 @@ namespace FEXCore::Context {
|
||||
return (IR::IRListView *)&InlineData[Offset];
|
||||
}
|
||||
|
||||
void AOTIRCaptureCacheEntry::AppendAOTIRCaptureCache(uint64_t GuestRIP, uint64_t Start, uint64_t Length, uint64_t Hash, FEXCore::IR::IRListView *IRList, FEXCore::IR::RegisterAllocationData *RAData) {
|
||||
auto Inserted = Index.emplace(GuestRIP, Stream->tellp());
|
||||
|
||||
if (Inserted.second) {
|
||||
//GuestHash
|
||||
Stream->write((char*)&Hash, sizeof(Hash));
|
||||
|
||||
//GuestLength
|
||||
Stream->write((char*)&Length, sizeof(Length));
|
||||
|
||||
// RAData (inline)
|
||||
// In file, IsShared is always set
|
||||
auto Shared = RAData->IsShared;
|
||||
RAData->IsShared = true;
|
||||
Stream->write((char*)RAData, RAData->Size(RAData->MapCount));
|
||||
RAData->IsShared = Shared;
|
||||
|
||||
// IRData (inline)
|
||||
IRList->Serialize(*Stream);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<void *, FEXCore::IR::IRListView *, FEXCore::Core::DebugData *, FEXCore::IR::RegisterAllocationData *, bool, uint64_t, uint64_t> Context::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
FEXCore::IR::IRListView *IRList {};
|
||||
FEXCore::Core::DebugData *DebugData {};
|
||||
@@ -848,7 +907,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
std::shared_lock lk(AOTIRCacheLock);
|
||||
auto file = AddrToFile.lower_bound(GuestRIP);
|
||||
if (file != AddrToFile.begin()) {
|
||||
--file;
|
||||
@@ -860,7 +919,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
if (IRList == nullptr && Config.AOTIRLoad) {
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
std::shared_lock lk(AOTIRCacheLock);
|
||||
auto file = AddrToFile.lower_bound(GuestRIP);
|
||||
if (file != AddrToFile.begin()) {
|
||||
--file;
|
||||
@@ -939,31 +998,44 @@ namespace FEXCore::Context {
|
||||
|
||||
bool Context::LoadAOTIRCache(int streamfd) {
|
||||
uint64_t tag;
|
||||
|
||||
if (!readAll(streamfd, (char*)&tag, sizeof(tag)) || tag != 0xDEADBEEFC0D30003)
|
||||
|
||||
if (!readAll(streamfd, (char*)&tag, sizeof(tag)) || tag != 0xDEADBEEFC0D30004)
|
||||
return false;
|
||||
|
||||
|
||||
std::string Module;
|
||||
uint64_t ModSize;
|
||||
|
||||
uint64_t IndexSize;
|
||||
|
||||
lseek(streamfd, -sizeof(ModSize), SEEK_END);
|
||||
|
||||
if (!readAll(streamfd, (char*)&ModSize, sizeof(ModSize)))
|
||||
return false;
|
||||
|
||||
Module.resize(ModSize);
|
||||
|
||||
lseek(streamfd, -sizeof(ModSize) - ModSize, SEEK_END);
|
||||
|
||||
if (!readAll(streamfd, (char*)&Module[0], Module.size()))
|
||||
return false;
|
||||
|
||||
lseek(streamfd, -sizeof(ModSize) - ModSize - sizeof(IndexSize), SEEK_END);
|
||||
|
||||
if (!readAll(streamfd, (char*)&IndexSize, sizeof(IndexSize)))
|
||||
return false;
|
||||
|
||||
struct stat fileinfo;
|
||||
if (fstat(streamfd, &fileinfo) < 0)
|
||||
return false;
|
||||
size_t Size = (fileinfo.st_size + 4095) & ~4095;
|
||||
|
||||
size_t IndexOffset = fileinfo.st_size - IndexSize -sizeof(ModSize) - ModSize - sizeof(IndexSize);
|
||||
|
||||
void *FilePtr = FEXCore::Allocator::mmap(nullptr, Size, PROT_READ, MAP_SHARED, streamfd, 0);
|
||||
|
||||
if (FilePtr == MAP_FAILED)
|
||||
return false;
|
||||
|
||||
auto Array = (AOTIRInlineIndex *)((char*)FilePtr + sizeof(tag) + sizeof(ModSize) + ((ModSize+31) & ~31));
|
||||
auto Array = (AOTIRInlineIndex *)((char*)FilePtr + IndexOffset);
|
||||
|
||||
AOTIRCache.insert({Module, {Array, FilePtr, Size}});
|
||||
|
||||
@@ -974,83 +1046,53 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
void Context::WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
std::shared_lock lk(AOTIRCacheLock);
|
||||
for( const auto &File: FilesWithCode) {
|
||||
Writer(File.first, File.second);
|
||||
}
|
||||
}
|
||||
|
||||
bool Context::WriteAOTIRCache(std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter) {
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
void Context::FinalizeAOTIRCache() {
|
||||
AOTIRCaptureCacheWriteoutQueue_Flush();
|
||||
|
||||
bool rv = true;
|
||||
std::unique_lock lk(AOTIRCacheLock);
|
||||
|
||||
for (auto AOTModule: AOTIRCaptureCache) {
|
||||
if (AOTModule.second.size() == 0) {
|
||||
for (auto &AOTModule: AOTIRCaptureCache) {
|
||||
if (!AOTModule.second.Stream) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto stream = CacheWriter(AOTModule.first);
|
||||
if (!*stream) {
|
||||
rv = false;
|
||||
}
|
||||
uint64_t tag = 0xDEADBEEFC0D30003;
|
||||
stream->write((char*)&tag, sizeof(tag));
|
||||
|
||||
auto ModSize = AOTModule.first.size();
|
||||
stream->write((char*)&ModSize, sizeof(ModSize));
|
||||
stream->write((char*)&AOTModule.first[0], ModSize);
|
||||
auto &stream = AOTModule.second.Stream;
|
||||
|
||||
auto Skip = ((ModSize + 31) & ~31) - ModSize;
|
||||
// pad to 32 bytes
|
||||
char Zero = 0;
|
||||
for (int i = 0; i < Skip; i++)
|
||||
while(stream->tellp() & 31)
|
||||
stream->write(&Zero, 1);
|
||||
|
||||
// AOTIRInlineIndex
|
||||
|
||||
auto FnCount = AOTModule.second.Index.size();
|
||||
size_t DataBase = -stream->tellp();
|
||||
|
||||
auto FnCount = AOTModule.second.size();
|
||||
stream->write((char*)&FnCount, sizeof(FnCount));
|
||||
|
||||
size_t DataBase = sizeof(FnCount) + sizeof(DataBase) + FnCount * sizeof(AOTIRInlineIndexEntry);
|
||||
stream->write((char*)&DataBase, sizeof(DataBase));
|
||||
|
||||
size_t DataOffset = 0;
|
||||
for (auto entry: AOTModule.second) {
|
||||
for (auto entry: AOTModule.second.Index) {
|
||||
//AOTIRInlineIndexEntry
|
||||
|
||||
// GuestStart
|
||||
stream->write((char*)&entry.first, sizeof(entry.first));
|
||||
|
||||
// DataOffset
|
||||
stream->write((char*)&DataOffset, sizeof(DataOffset));
|
||||
|
||||
|
||||
DataOffset += sizeof(entry.second.crc);
|
||||
DataOffset += sizeof(entry.second.len);
|
||||
|
||||
DataOffset += entry.second.RAData->Size(entry.second.RAData->MapCount);
|
||||
|
||||
DataOffset += entry.second.IR->GetInlineSize();
|
||||
stream->write((char*)&entry.second, sizeof(entry.second));
|
||||
}
|
||||
|
||||
// AOTIRInlineEntry
|
||||
for (auto entry: AOTModule.second) {
|
||||
//GuestHash
|
||||
stream->write((char*)&entry.second.crc, sizeof(entry.second.crc));
|
||||
|
||||
//GuestLength
|
||||
stream->write((char*)&entry.second.len, sizeof(entry.second.len));
|
||||
|
||||
// RAData (inline)
|
||||
stream->write((char*)entry.second.RAData, entry.second.RAData->Size(entry.second.RAData->MapCount));
|
||||
|
||||
// IRData (inline)
|
||||
entry.second.IR->Serialize(*stream);
|
||||
}
|
||||
// End of file header
|
||||
auto IndexSize = FnCount * sizeof(AOTIRInlineIndexEntry) + sizeof(DataBase) + sizeof(FnCount);
|
||||
stream->write((char*)&IndexSize, sizeof(IndexSize));
|
||||
stream->write((char*)&AOTModule.first[0], ModSize);
|
||||
stream->write((char*)&ModSize, sizeof(ModSize));
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void Context::CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
@@ -1061,7 +1103,7 @@ namespace FEXCore::Context {
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
uintptr_t Context::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
@@ -1135,26 +1177,49 @@ namespace FEXCore::Context {
|
||||
|
||||
// Insert to caches if we generated IR
|
||||
if (GeneratedIR) {
|
||||
Core::LocalIREntry Entry = {StartAddr, Length, decltype(Entry.IR)(IRList), decltype(Entry.RAData)(RAData), decltype(Entry.DebugData)(DebugData)};
|
||||
Thread->LocalIRCache.insert({GuestRIP, std::move(Entry)});
|
||||
|
||||
// Add to AOT cache if aot generation is enabled
|
||||
if ((Config.AOTIRCapture() || Config.AOTIRGenerate()) && RAData) {
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
|
||||
RAData->IsShared = true;
|
||||
IRList->SetShared(true);
|
||||
|
||||
auto hash = XXH3_64bits((void*)StartAddr, Length);
|
||||
|
||||
std::shared_lock lk(AOTIRCacheLock);
|
||||
|
||||
auto file = AddrToFile.lower_bound(StartAddr);
|
||||
if (file != AddrToFile.begin()) {
|
||||
--file;
|
||||
if (file->second.Start <= StartAddr && (file->second.Start + file->second.Len) >= (StartAddr + Length)) {
|
||||
AOTIRCaptureCache[file->second.fileid].insert({GuestRIP - file->second.Start + file->second.Offset, {StartAddr - file->second.Start + file->second.Offset, Length, hash, IRList, RAData}});
|
||||
auto LocalRIP = GuestRIP - file->second.Start + file->second.Offset;
|
||||
auto LocalStartAddr = StartAddr - file->second.Start + file->second.Offset;
|
||||
auto fileid = file->second.fileid;
|
||||
AOTIRCaptureCacheWriteoutQueue_Append([this, LocalRIP, LocalStartAddr, Length, hash, IRList, RAData, fileid]() {
|
||||
auto *AotFile = &AOTIRCaptureCache[fileid];
|
||||
|
||||
if (!AotFile->Stream) {
|
||||
AotFile->Stream = AOTIRWriter(fileid);
|
||||
uint64_t tag = 0xDEADBEEFC0D30004;
|
||||
AotFile->Stream->write((char*)&tag, sizeof(tag));
|
||||
}
|
||||
AotFile->AppendAOTIRCaptureCache(LocalRIP, LocalStartAddr, Length, hash, IRList, RAData);
|
||||
delete IRList;
|
||||
FEXCore::Allocator::free(RAData);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.AOTIRGenerate()) {
|
||||
// cleanup memory and early exit here -- we're not running the application
|
||||
|
||||
if (DecrementRefCount)
|
||||
--Thread->CompileBlockReentrantRefCount;
|
||||
|
||||
Thread->CPUBackend->ClearCache();
|
||||
|
||||
return (uintptr_t)CodePtr;
|
||||
}
|
||||
}
|
||||
|
||||
// Add to thread local ir cache
|
||||
Core::LocalIREntry Entry = {StartAddr, Length, decltype(Entry.IR)(IRList), decltype(Entry.RAData)(RAData), decltype(Entry.DebugData)(DebugData)};
|
||||
Thread->LocalIRCache.insert({GuestRIP, std::move(Entry)});
|
||||
}
|
||||
|
||||
if (DecrementRefCount)
|
||||
@@ -1303,7 +1368,7 @@ namespace FEXCore::Context {
|
||||
fileid += Config.ABILocalFlags ? "L" : "l";
|
||||
fileid += Config.ABINoPF ? "p" : "P";
|
||||
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
std::unique_lock lk(AOTIRCacheLock);
|
||||
|
||||
AddrToFile.insert({ Base, { Base, Size, Offset, fileid, filename, nullptr, false} });
|
||||
|
||||
@@ -1318,13 +1383,13 @@ namespace FEXCore::Context {
|
||||
}
|
||||
|
||||
void Context::RemoveNamedRegion(uintptr_t Base, uintptr_t Size) {
|
||||
std::lock_guard<std::mutex> lk(AOTIRCacheLock);
|
||||
std::unique_lock lk(AOTIRCacheLock);
|
||||
// TODO: Support partial removing
|
||||
AddrToFile.erase(Base);
|
||||
}
|
||||
|
||||
void ConfigureAOTGen(FEXCore::Context::Context *CTX, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
CTX->ParentThread->FrontendDecoder->SetExternalBranches(ExternalBranches);
|
||||
CTX->ParentThread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
|
||||
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
|
||||
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Interface/Context/Context.h"
|
||||
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <bit>
|
||||
#include <cmath>
|
||||
|
||||
#include "aarch64/assembler-aarch64.h"
|
||||
@@ -103,7 +104,7 @@ Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::
|
||||
ldr(x0, &l_PagePtr);
|
||||
|
||||
// Mask the address by the virtual address size so we can check for aliases
|
||||
if (__builtin_popcountl(VirtualMemorySize) == 1) {
|
||||
if (std::popcount(VirtualMemorySize) == 1) {
|
||||
and_(x3, RipReg, Thread->LookupCache->GetVirtualMemorySize() - 1);
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -175,16 +175,23 @@ bool Dispatcher::HandleGuestSignal(int Signal, void *info, void *ucontext, Guest
|
||||
|
||||
// siginfo_t
|
||||
siginfo_t *HostSigInfo = reinterpret_cast<siginfo_t*>(info);
|
||||
guest_siginfo->si_signo = Signal;
|
||||
switch (Signal) {
|
||||
case SIGSEGV:
|
||||
case SIGBUS:
|
||||
guest_siginfo->si_code = HostSigInfo->si_code;
|
||||
guest_siginfo->si_errno = HostSigInfo->si_errno;
|
||||
// Macro expansion to get the si_addr
|
||||
guest_siginfo->si_addr = HostSigInfo->si_addr;
|
||||
break;
|
||||
default: LogMan::Msg::D("Unhandled siginfo_t signal: %d", Signal); break;
|
||||
if (HostSigInfo->si_code == SI_USER) {
|
||||
// If the signal was a user signal then we need to pass this struct through unaltered
|
||||
// Guest might be doing something with it
|
||||
*guest_siginfo = *HostSigInfo;
|
||||
}
|
||||
else {
|
||||
guest_siginfo->si_signo = Signal;
|
||||
switch (Signal) {
|
||||
case SIGSEGV:
|
||||
case SIGBUS:
|
||||
guest_siginfo->si_code = HostSigInfo->si_code;
|
||||
guest_siginfo->si_errno = HostSigInfo->si_errno;
|
||||
// Macro expansion to get the si_addr
|
||||
guest_siginfo->si_addr = HostSigInfo->si_addr;
|
||||
break;
|
||||
default: LogMan::Msg::D("Unhandled siginfo_t signal: %d", Signal); break;
|
||||
}
|
||||
}
|
||||
|
||||
Frame->State.gregs[X86State::REG_RSI] = SigInfoLocation;
|
||||
@@ -254,7 +261,7 @@ bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
FEXCore::Core::SignalEvent SignalReason = ThreadState->SignalReason.load();
|
||||
auto Frame = ThreadState->CurrentFrame;
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_PAUSE) {
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::Pause) {
|
||||
// Store our thread state so we can come back to this
|
||||
StoreThreadState(Signal, ucontext);
|
||||
|
||||
@@ -279,11 +286,11 @@ bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
// We use this to track if it is safe to clear cache
|
||||
++SignalHandlerRefCounter;
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_STOP) {
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::Stop) {
|
||||
// Our thread is stopping
|
||||
// We don't care about anything at this point
|
||||
// Set the stack to our starting location when we entered the core and get out safely
|
||||
@@ -304,18 +311,18 @@ bool Dispatcher::HandleSignalPause(int Signal, void *info, void *ucontext) {
|
||||
ArchHelpers::Context::SetPc(ucontext, ThreadStopHandlerAddress);
|
||||
}
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::SIGNALEVENT_RETURN) {
|
||||
if (SignalReason == FEXCore::Core::SignalEvent::Return) {
|
||||
RestoreThreadState(ucontext);
|
||||
|
||||
// Ref count our faults
|
||||
// We use this to track if it is safe to clear cache
|
||||
--SignalHandlerRefCounter;
|
||||
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SIGNALEVENT_NONE);
|
||||
ThreadState->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -344,7 +351,7 @@ void Dispatcher::RemoveCodeBuffer(uint8_t* start_to_remove) {
|
||||
}
|
||||
}
|
||||
|
||||
bool Dispatcher::IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher) {
|
||||
bool Dispatcher::IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher) const {
|
||||
for (auto [start, end] : CodeBuffers) {
|
||||
if (Address >= start && Address < end) {
|
||||
return true;
|
||||
|
||||
@@ -54,8 +54,8 @@ public:
|
||||
|
||||
void RemoveCodeBuffer(uint8_t* start);
|
||||
|
||||
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true);
|
||||
bool IsAddressInDispatcher(uint64_t Address) {
|
||||
bool IsAddressInJITCode(uint64_t Address, bool IncludeDispatcher = true) const;
|
||||
bool IsAddressInDispatcher(uint64_t Address) const {
|
||||
return Address >= Start && Address < End;
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,7 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::Inte
|
||||
|
||||
{
|
||||
ReturnPtr = getCurr<FEXCore::Context::Context::IntCallbackReturn>();
|
||||
// using CallbackReturn = __attribute__((naked)) void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
|
||||
// using CallbackReturn = FEX_NAKED void(*)(FEXCore::Core::InternalThreadState *Thread, volatile void *Host_RSP);
|
||||
|
||||
// rdi = thread
|
||||
// rsi = rsp
|
||||
@@ -293,7 +293,7 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, FEXCore::Core::Inte
|
||||
|
||||
#if ENABLE_JITSYMBOLS
|
||||
std::string Name = "Dispatch_" + std::to_string(::gettid());
|
||||
CTX->Symbols.Register(Start, End-Start, Name);
|
||||
CTX->Symbols.Register(reinterpret_cast<void*>(Start), End-Start, Name);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
+50
-51
@@ -131,7 +131,7 @@ uint8_t Decoder::ReadByte() {
|
||||
return Byte;
|
||||
}
|
||||
|
||||
uint8_t Decoder::PeekByte(uint8_t Offset) {
|
||||
uint8_t Decoder::PeekByte(uint8_t Offset) const {
|
||||
uint8_t Byte = InstStream[InstructionSize + Offset];
|
||||
return Byte;
|
||||
}
|
||||
@@ -197,9 +197,9 @@ void Decoder::DecodeModRM_16(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
}
|
||||
}
|
||||
|
||||
Operand->TypeSIB.Type = DecodedOperand::TYPE_SIB;
|
||||
Operand->TypeSIB.Scale = 1;
|
||||
Operand->TypeSIB.Offset = Literal;
|
||||
Operand->Type = DecodedOperand::OpType::SIB;
|
||||
Operand->Data.SIB.Scale = 1;
|
||||
Operand->Data.SIB.Offset = Literal;
|
||||
|
||||
// Only called when ModRM.mod != 0b11
|
||||
struct Encodings {
|
||||
@@ -238,8 +238,8 @@ void Decoder::DecodeModRM_16(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
|
||||
uint8_t LookupIndex = ModRM.mod << 3 | ModRM.rm;
|
||||
auto it = Lookup[LookupIndex];
|
||||
Operand->TypeSIB.Base = it.Base;
|
||||
Operand->TypeSIB.Index = it.Index;
|
||||
Operand->Data.SIB.Base = it.Base;
|
||||
Operand->Data.SIB.Index = it.Index;
|
||||
}
|
||||
|
||||
void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModRMDecoded ModRM) {
|
||||
@@ -277,12 +277,12 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
}
|
||||
|
||||
// SIB
|
||||
Operand->TypeSIB.Type = DecodedOperand::TYPE_SIB;
|
||||
Operand->TypeSIB.Scale = 1 << SIB.scale;
|
||||
Operand->Type = DecodedOperand::OpType::SIB;
|
||||
Operand->Data.SIB.Scale = 1 << SIB.scale;
|
||||
|
||||
// The invalid encoding types are described at Table 1-12. "promoted nsigned is always non-zero"
|
||||
Operand->TypeSIB.Index = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_X ? 1 : 0, SIB.index, false, false, false, false, 0b100);
|
||||
Operand->TypeSIB.Base = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, SIB.base, false, false, false, false, ModRM.mod == 0 ? 0b101 : 16);
|
||||
Operand->Data.SIB.Index = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_X ? 1 : 0, SIB.index, false, false, false, false, 0b100);
|
||||
Operand->Data.SIB.Base = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, SIB.base, false, false, false, false, ModRM.mod == 0 ? 0b101 : 16);
|
||||
|
||||
uint64_t Literal {0};
|
||||
LOGMAN_THROW_A(Displacement <= 4, "Number of bytes should be <= 4 for literal src");
|
||||
@@ -291,7 +291,7 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
if (Displacement == 1) {
|
||||
Literal = static_cast<int8_t>(Literal);
|
||||
}
|
||||
Operand->TypeSIB.Offset = Literal;
|
||||
Operand->Data.SIB.Offset = Literal;
|
||||
}
|
||||
else if (ModRM.mod == 0) {
|
||||
// Explained in Table 1-14. "Operand Addressing Using ModRM and SIB Bytes"
|
||||
@@ -300,13 +300,13 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
uint32_t Literal;
|
||||
Literal = ReadData(4);
|
||||
|
||||
Operand->TypeRIPLiteral.Type = DecodedOperand::TYPE_RIP_RELATIVE;
|
||||
Operand->TypeRIPLiteral.Literal.u = Literal;
|
||||
Operand->Type = DecodedOperand::OpType::RIPRelative;
|
||||
Operand->Data.RIPLiteral.Value.u = Literal;
|
||||
}
|
||||
else {
|
||||
// Register-direct addressing
|
||||
Operand->TypeGPR.Type = DecodedOperand::TYPE_GPR_DIRECT;
|
||||
Operand->TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
|
||||
Operand->Type = DecodedOperand::OpType::GPRDirect;
|
||||
Operand->Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -318,9 +318,9 @@ void Decoder::DecodeModRM_64(X86Tables::DecodedOperand *Operand, X86Tables::ModR
|
||||
}
|
||||
Displacement = DisplacementSize;
|
||||
|
||||
Operand->TypeGPRIndirect.Type = DecodedOperand::TYPE_GPR_INDIRECT;
|
||||
Operand->TypeGPRIndirect.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
|
||||
Operand->TypeGPRIndirect.Displacement = Literal;
|
||||
Operand->Type = DecodedOperand::OpType::GPRIndirect;
|
||||
Operand->Data.GPRIndirect.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, false, false, false, false);
|
||||
Operand->Data.GPRIndirect.Displacement = Literal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,9 +460,9 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ||
|
||||
HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RDX)) {
|
||||
// Some instructions hardcode their destination as RAX
|
||||
CurrentDest->TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
CurrentDest->TypeGPR.HighBits = false;
|
||||
CurrentDest->TypeGPR.GPR = HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ? FEXCore::X86State::REG_RAX : FEXCore::X86State::REG_RDX;
|
||||
CurrentDest->Type = DecodedOperand::OpType::GPR;
|
||||
CurrentDest->Data.GPR.HighBits = false;
|
||||
CurrentDest->Data.GPR.GPR = HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ? FEXCore::X86State::REG_RAX : FEXCore::X86State::REG_RDX;
|
||||
CurrentDest = &DecodeInst->Src[0];
|
||||
}
|
||||
|
||||
@@ -473,11 +473,11 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
// This also means that the destination is always a GPR on these ones
|
||||
// ADDITIONALLY:
|
||||
// If there is a REX prefix then that allows extended GPR usage
|
||||
CurrentDest->TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
DecodeInst->Dest.TypeGPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM;
|
||||
CurrentDest->TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, Op & 0b111, Is8BitDest, HasREX, false, false);
|
||||
CurrentDest->Type = DecodedOperand::OpType::GPR;
|
||||
DecodeInst->Dest.Data.GPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM;
|
||||
CurrentDest->Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, Op & 0b111, Is8BitDest, HasREX, false, false);
|
||||
|
||||
if (CurrentDest->TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
if (CurrentDest->Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -501,20 +501,20 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
ModRM.Hex = DecodeInst->ModRM;
|
||||
|
||||
// Decode the GPR source first
|
||||
GPR.TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
GPR.TypeGPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM;
|
||||
GPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_R ? 1 : 0, ModRM.reg, GPR8Bit, HasREX, HasXMMGPR, HasMMGPR);
|
||||
GPR.Type = DecodedOperand::OpType::GPR;
|
||||
GPR.Data.GPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM;
|
||||
GPR.Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_R ? 1 : 0, ModRM.reg, GPR8Bit, HasREX, HasXMMGPR, HasMMGPR);
|
||||
|
||||
if (GPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
if (GPR.Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
return false;
|
||||
|
||||
// ModRM.mod == 0b11 == Register
|
||||
// ModRM.Mod != 0b11 == Register-direct addressing
|
||||
if (ModRM.mod == 0b11) {
|
||||
NonGPR.TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
NonGPR.TypeGPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM;
|
||||
NonGPR.TypeGPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, NonGPR8Bit, HasREX, HasXMMNonGPR, HasMMNonGPR);
|
||||
if (NonGPR.TypeGPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
NonGPR.Type = DecodedOperand::OpType::GPR;
|
||||
NonGPR.Data.GPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM;
|
||||
NonGPR.Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, ModRM.rm, NonGPR8Bit, HasREX, HasXMMNonGPR, HasMMNonGPR);
|
||||
if (NonGPR.Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
@@ -540,25 +540,24 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
}
|
||||
|
||||
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_SRC_RAX)) {
|
||||
DecodeInst->Src[CurrentSrc].TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
DecodeInst->Src[CurrentSrc].TypeGPR.HighBits = false;
|
||||
DecodeInst->Src[CurrentSrc].TypeGPR.GPR = FEXCore::X86State::REG_RAX;
|
||||
DecodeInst->Src[CurrentSrc].Type = DecodedOperand::OpType::GPR;
|
||||
DecodeInst->Src[CurrentSrc].Data.GPR.HighBits = false;
|
||||
DecodeInst->Src[CurrentSrc].Data.GPR.GPR = FEXCore::X86State::REG_RAX;
|
||||
++CurrentSrc;
|
||||
}
|
||||
else if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_SRC_RCX)) {
|
||||
DecodeInst->Src[CurrentSrc].TypeGPR.Type = DecodedOperand::TYPE_GPR;
|
||||
DecodeInst->Src[CurrentSrc].TypeGPR.HighBits = false;
|
||||
DecodeInst->Src[CurrentSrc].TypeGPR.GPR = FEXCore::X86State::REG_RCX;
|
||||
DecodeInst->Src[CurrentSrc].Type = DecodedOperand::OpType::GPR;
|
||||
DecodeInst->Src[CurrentSrc].Data.GPR.HighBits = false;
|
||||
DecodeInst->Src[CurrentSrc].Data.GPR.GPR = FEXCore::X86State::REG_RCX;
|
||||
++CurrentSrc;
|
||||
}
|
||||
|
||||
if (Bytes != 0) {
|
||||
LOGMAN_THROW_A(Bytes <= 8, "Number of bytes should be <= 8 for literal src");
|
||||
|
||||
DecodeInst->Src[CurrentSrc].TypeLiteral.Size = Bytes;
|
||||
DecodeInst->Src[CurrentSrc].Data.Literal.Size = Bytes;
|
||||
|
||||
uint64_t Literal {0};
|
||||
Literal = ReadData(Bytes);
|
||||
uint64_t Literal = ReadData(Bytes);
|
||||
|
||||
if ((Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SRC_SEXT) ||
|
||||
(DecodeFlags::GetSizeDstFlags(DecodeInst->Flags) == DecodeFlags::SIZE_64BIT && Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SRC_SEXT64BIT)) {
|
||||
@@ -571,12 +570,12 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op)
|
||||
else {
|
||||
Literal = static_cast<int32_t>(Literal);
|
||||
}
|
||||
DecodeInst->Src[CurrentSrc].TypeLiteral.Size = DestSize;
|
||||
DecodeInst->Src[CurrentSrc].Data.Literal.Size = DestSize;
|
||||
}
|
||||
|
||||
Bytes = 0;
|
||||
DecodeInst->Src[CurrentSrc].TypeLiteral.Type = DecodedOperand::TYPE_LITERAL;
|
||||
DecodeInst->Src[CurrentSrc].TypeLiteral.Literal = Literal;
|
||||
DecodeInst->Src[CurrentSrc].Type = DecodedOperand::OpType::Literal;
|
||||
DecodeInst->Src[CurrentSrc].Data.Literal.Value = Literal;
|
||||
}
|
||||
|
||||
LOGMAN_THROW_A(Bytes == 0, "Inst at 0x%lx: 0x%04x '%s' Had an instruction of size %d with %d remaining", DecodeInst->PC, DecodeInst->OP, DecodeInst->TableInfo->Name, InstructionSize, Bytes);
|
||||
@@ -927,8 +926,8 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
|
||||
|
||||
}
|
||||
|
||||
if (DecodeInst->Dest.TypeNone.Type == FEXCore::X86Tables::DecodedOperand::TYPE_GPR) {
|
||||
assert(DecodeInst->Dest.TypeGPR.GPR != 255);
|
||||
if (DecodeInst->Dest.IsGPR()) {
|
||||
assert(DecodeInst->Dest.Data.GPR.GPR != 255);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -950,14 +949,14 @@ void Decoder::BranchTargetInMultiblockRange() {
|
||||
// auto RIPOffset = LoadSource(Op, Op->Src[0], Op->Flags);
|
||||
// auto RIPTargetConst = _Constant(Op->PC + Op->InstSize);
|
||||
// Target offset is PC + InstSize + Literal
|
||||
LOGMAN_THROW_A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
|
||||
LOGMAN_THROW_A(DecodeInst->Src[0].IsLiteral(), "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Data.Literal.Value;
|
||||
break;
|
||||
}
|
||||
case 0xE9:
|
||||
case 0xEB: // Both are unconditional JMP instructions
|
||||
LOGMAN_THROW_A(DecodeInst->Src[0].TypeNone.Type == DecodedOperand::TYPE_LITERAL, "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].TypeLiteral.Literal;
|
||||
LOGMAN_THROW_A(DecodeInst->Src[0].IsLiteral(), "Had wrong operand type");
|
||||
TargetRIP = DecodeInst->PC + DecodeInst->InstSize + DecodeInst->Src[0].Data.Literal.Value;
|
||||
Conditional = false;
|
||||
break;
|
||||
case 0xE8: // Call - Immediate target, We don't want to inline calls
|
||||
|
||||
+2
-2
@@ -26,7 +26,7 @@ public:
|
||||
Decoder(FEXCore::Context::Context *ctx);
|
||||
bool DecodeInstructionsAtEntry(uint8_t const* InstStream, uint64_t PC);
|
||||
|
||||
std::vector<DecodedBlocks> const *GetDecodedBlocks() {
|
||||
std::vector<DecodedBlocks> const *GetDecodedBlocks() const {
|
||||
return &Blocks;
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ private:
|
||||
void BranchTargetInMultiblockRange();
|
||||
|
||||
uint8_t ReadByte();
|
||||
uint8_t PeekByte(uint8_t Offset);
|
||||
uint8_t PeekByte(uint8_t Offset) const;
|
||||
uint64_t ReadData(uint8_t Size);
|
||||
void SkipBytes(uint8_t Size) { InstructionSize += Size; }
|
||||
bool NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op);
|
||||
|
||||
+74
-90
@@ -15,15 +15,19 @@ $end_info$
|
||||
#include <optional>
|
||||
#include "Common/NetStream.h"
|
||||
#include "Common/SoftFloat.h"
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <netdb.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
|
||||
#include "GdbServer.h"
|
||||
#include <FEXCore/Core/CodeLoader.h>
|
||||
@@ -34,12 +38,12 @@ namespace FEXCore
|
||||
|
||||
void GdbServer::Break(int signal) {
|
||||
std::lock_guard lk(sendMutex);
|
||||
if (!CommsStream) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << "S" << std::setfill('0') << std::setw(2) << std::hex << signal;
|
||||
|
||||
if (CommsStream)
|
||||
SendPacket(*CommsStream, ss.str());
|
||||
const auto str = fmt::format("S{:02x}", signal);
|
||||
SendPacket(*CommsStream, str);
|
||||
}
|
||||
|
||||
GdbServer::GdbServer(FEXCore::Context::Context *ctx) : CTX(ctx) {
|
||||
@@ -65,7 +69,7 @@ GdbServer::GdbServer(FEXCore::Context::Context *ctx) : CTX(ctx) {
|
||||
StartThread();
|
||||
}
|
||||
|
||||
static int calculateChecksum(std::string &packet) {
|
||||
static int calculateChecksum(const std::string &packet) {
|
||||
unsigned char checksum = 0;
|
||||
for (const char &c : packet) {
|
||||
checksum += c;
|
||||
@@ -99,11 +103,9 @@ static std::string encodeHex(unsigned char *data, size_t length) {
|
||||
}
|
||||
|
||||
static std::string getThreadName(uint32_t ThreadID) {
|
||||
std::fstream fs;
|
||||
std::ostringstream ThreadFile;
|
||||
ThreadFile << "/proc/" << getpid() << "/task/" << ThreadID << "/comm";
|
||||
const auto ThreadFile = fmt::format("/proc/{}/task/{}/comm", getpid(), ThreadID);
|
||||
std::fstream fs(ThreadFile, std::fstream::in | std::fstream::binary);
|
||||
|
||||
fs.open(ThreadFile.str(), std::fstream::in | std::fstream::binary);
|
||||
if (fs.is_open()) {
|
||||
std::string ThreadName;
|
||||
fs >> ThreadName;
|
||||
@@ -135,7 +137,7 @@ std::string GdbServer::ReadPacket(std::iostream &stream) {
|
||||
switch(c) {
|
||||
case '$': // start of packet
|
||||
if (packet.size() != 0)
|
||||
LogMan::Msg::E("Dropping unexpected data: \"%s\"", packet.c_str());
|
||||
LogMan::Msg::EFmt("Dropping unexpected data: \"{}\"", packet);
|
||||
|
||||
// clear any existing data, must have been a mistake.
|
||||
packet = std::string();
|
||||
@@ -156,7 +158,7 @@ std::string GdbServer::ReadPacket(std::iostream &stream) {
|
||||
if (calculateChecksum(packet) == expected_checksum) {
|
||||
return packet;
|
||||
} else {
|
||||
LogMan::Msg::E("Received Invalid Packet: $%s#%02x %c%c", packet.c_str(), expected_checksum);
|
||||
LogMan::Msg::EFmt("Received Invalid Packet: ${}#{:02x}", packet, expected_checksum);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -169,10 +171,10 @@ std::string GdbServer::ReadPacket(std::iostream &stream) {
|
||||
return "";
|
||||
}
|
||||
|
||||
static std::string escapePacket(std::string packet) {
|
||||
static std::string escapePacket(const std::string& packet) {
|
||||
std::ostringstream ss;
|
||||
|
||||
for(auto &c : packet) {
|
||||
for(const auto &c : packet) {
|
||||
switch (c) {
|
||||
case '$':
|
||||
case '#':
|
||||
@@ -191,13 +193,11 @@ static std::string escapePacket(std::string packet) {
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
void GdbServer::SendPacket(std::ostream &stream, std::string packet) {
|
||||
auto escaped = escapePacket(packet);
|
||||
std::ostringstream ss;
|
||||
void GdbServer::SendPacket(std::ostream &stream, const std::string& packet) {
|
||||
const auto escaped = escapePacket(packet);
|
||||
const auto str = fmt::format("${}#{:02x}", escaped, calculateChecksum(escaped));
|
||||
|
||||
ss << '$' << escaped << '#';
|
||||
ss << std::setfill('0') << std::setw(2) << std::hex << (int)calculateChecksum(escaped);
|
||||
stream << ss.str() << std::flush;
|
||||
stream << str << std::flush;
|
||||
}
|
||||
|
||||
void GdbServer::SendACK(std::ostream &stream, bool NACK) {
|
||||
@@ -218,7 +218,7 @@ void GdbServer::SendACK(std::ostream &stream, bool NACK) {
|
||||
}
|
||||
}
|
||||
|
||||
struct __attribute__((packed)) GDBContextDefinition {
|
||||
struct FEX_PACKED GDBContextDefinition {
|
||||
uint64_t gregs[16];
|
||||
uint64_t rip;
|
||||
uint32_t eflags;
|
||||
@@ -279,7 +279,7 @@ std::string GdbServer::readRegs() {
|
||||
return encodeHex((unsigned char *)&GDB, sizeof(GDBContextDefinition));
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::readReg(std::string& packet) {
|
||||
GdbServer::HandledPacketType GdbServer::readReg(const std::string& packet) {
|
||||
size_t addr;
|
||||
auto ss = std::istringstream(packet);
|
||||
ss.get(); // Drop first letter
|
||||
@@ -357,7 +357,7 @@ GdbServer::HandledPacketType GdbServer::readReg(std::string& packet) {
|
||||
return {encodeHex((unsigned char *)(&Empty), sizeof(uint32_t)), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
|
||||
LogMan::Msg::E("Unknown GDB register 0x%lx", addr);
|
||||
LogMan::Msg::EFmt("Unknown GDB register 0x{:x}", addr);
|
||||
return {"E00", HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@ std::string buildTargetXML() {
|
||||
return xml.str();
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::handleXfer(std::string &packet) {
|
||||
GdbServer::HandledPacketType GdbServer::handleXfer(const std::string &packet) {
|
||||
std::string object;
|
||||
std::string rw;
|
||||
std::string annex;
|
||||
@@ -548,10 +548,9 @@ GdbServer::HandledPacketType GdbServer::handleXfer(std::string &packet) {
|
||||
|
||||
static size_t CheckMemMapping(uint64_t Address, size_t Size) {
|
||||
uint64_t AddressEnd = Address + Size;
|
||||
|
||||
std::fstream fs;
|
||||
fs.open("/proc/self/maps", std::fstream::in | std::fstream::binary);
|
||||
std::fstream fs("/proc/self/maps", std::fstream::in | std::fstream::binary);
|
||||
std::string Line;
|
||||
|
||||
while (std::getline(fs, Line)) {
|
||||
if (fs.eof()) break;
|
||||
uint64_t Begin, End;
|
||||
@@ -568,32 +567,29 @@ static size_t CheckMemMapping(uint64_t Address, size_t Size) {
|
||||
}
|
||||
}
|
||||
|
||||
fs.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::handleProgramOffsets() {
|
||||
std::fstream fs;
|
||||
fs.open("/proc/self/maps", std::fstream::in | std::fstream::binary);
|
||||
std::fstream fs("/proc/self/maps", std::fstream::in | std::fstream::binary);
|
||||
std::string Line;
|
||||
std::string const &RuntimeExecutable = Filename();
|
||||
|
||||
while (std::getline(fs, Line)) {
|
||||
uint64_t Begin, End;
|
||||
char Filename[255];
|
||||
if (sscanf(Line.c_str(), "%lx-%lx %*c%*c%*c%*c %*x %*x:%*x %*d%s", &Begin, &End, Filename) == 3) {
|
||||
if (RuntimeExecutable == Filename) {
|
||||
std::ostringstream ss;
|
||||
ss << "Text=" << std::hex << Begin << ";Data=" << std::hex << Begin << ";Bss=" << std::hex << Begin;
|
||||
ss << std::flush;
|
||||
return {ss.str(), HandledPacketType::TYPE_ACK};
|
||||
auto str = fmt::format("Text={:x};Data={:x};Bss={:x}", Begin, Begin, Begin);
|
||||
return {std::move(str), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
}
|
||||
}
|
||||
fs.close();
|
||||
|
||||
return {"Text=0;Data=0;Bss=0", HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::handleMemory(std::string &packet) {
|
||||
GdbServer::HandledPacketType GdbServer::handleMemory(const std::string &packet) {
|
||||
bool write;
|
||||
size_t addr;
|
||||
size_t length;
|
||||
@@ -634,8 +630,8 @@ GdbServer::HandledPacketType GdbServer::handleMemory(std::string &packet) {
|
||||
}
|
||||
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
|
||||
auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
|
||||
GdbServer::HandledPacketType GdbServer::handleQuery(const std::string &packet) {
|
||||
const auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
|
||||
|
||||
if (match("qSupported")) {
|
||||
return {"PacketSize=5000;xmlRegisters=i386;qXfer:exec-file:read+;qXfer:features:read+;", HandledPacketType::TYPE_ACK};
|
||||
@@ -693,8 +689,8 @@ GdbServer::HandledPacketType GdbServer::handleQuery(std::string &packet) {
|
||||
return {"", HandledPacketType::TYPE_UNKNOWN};
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
|
||||
auto match = [&](std::string str) -> std::optional<std::istringstream> {
|
||||
GdbServer::HandledPacketType GdbServer::handleV(const std::string& packet) {
|
||||
const auto match = [&](const std::string& str) -> std::optional<std::istringstream> {
|
||||
if (packet.rfind(str, 0) == 0) {
|
||||
auto ss = std::istringstream(packet);
|
||||
ss.seekg(str.size());
|
||||
@@ -703,18 +699,11 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
auto F = [](int result) {
|
||||
std::ostringstream ss;
|
||||
ss << "F" << std::hex << result;
|
||||
return ss.str(); };
|
||||
auto F_error = [&]() {
|
||||
std::ostringstream ss;
|
||||
ss << "F-1," << std::hex << errno;
|
||||
return ss.str(); };
|
||||
auto F_data = [&](int result, std::string data) {
|
||||
std::ostringstream ss;
|
||||
ss << "F" << std::hex << result << ";" << data;
|
||||
return ss.str(); };
|
||||
const auto F = [](int result) { return fmt::format("F{:x}", result); };
|
||||
const auto F_error = [] { return fmt::format("F-1,{:x}", errno); };
|
||||
const auto F_data = [](int result, const std::string& data) {
|
||||
return fmt::format("F{:x};{}", result, data);
|
||||
};
|
||||
|
||||
std::optional<std::istringstream> ss;
|
||||
if((ss = match("vFile:open:"))) {
|
||||
@@ -736,11 +725,11 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
|
||||
return {F(pid == 0 ? 0 : -1), HandledPacketType::TYPE_ACK}; // Only support the common filesystem
|
||||
}
|
||||
if((ss = match("vFile:close:"))) {
|
||||
int fd;
|
||||
*ss >> std::hex >> fd;
|
||||
close(fd);
|
||||
return {F(0), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
int fd;
|
||||
*ss >> std::hex >> fd;
|
||||
close(fd);
|
||||
return {F(0), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
if((ss = match("vFile:pread:"))) {
|
||||
int fd, count, offset;
|
||||
|
||||
@@ -777,7 +766,7 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
|
||||
}
|
||||
|
||||
if (ss->fail()) {
|
||||
return {"E00", HandledPacketType::TYPE_ACK};
|
||||
return {"E00", HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
@@ -787,27 +776,25 @@ GdbServer::HandledPacketType GdbServer::handleV(std::string& packet) {
|
||||
}
|
||||
case 's': {
|
||||
CTX->Step();
|
||||
SendPacketPair({"OK", HandledPacketType::TYPE_ACK});
|
||||
std::ostringstream ss;
|
||||
ss << "T05thread:" << std::setfill('0') << std::setw(2) << std::hex << getpid() << ";core:2c;";
|
||||
|
||||
SendPacketPair({ss.str(), HandledPacketType::TYPE_ACK});
|
||||
SendPacketPair({"OK", HandledPacketType::TYPE_ACK});
|
||||
auto str = fmt::format("T05thread:{:02x};core:2c;", getpid());
|
||||
SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK});
|
||||
return {"OK", HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
case 't':
|
||||
// This thread isn't part of the thread pool
|
||||
CTX->Stop(false /* Ignore current thread */);
|
||||
return {"OK", HandledPacketType::TYPE_ACK};
|
||||
return {"OK", HandledPacketType::TYPE_ACK};
|
||||
default:
|
||||
return {"E00", HandledPacketType::TYPE_ACK};
|
||||
return {"E00", HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
|
||||
}
|
||||
return {"", HandledPacketType::TYPE_ACK};
|
||||
return {"", HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::handleThreadOp(std::string &packet) {
|
||||
auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
|
||||
GdbServer::HandledPacketType GdbServer::handleThreadOp(const std::string &packet) {
|
||||
const auto match = [&](const char *str) -> bool { return packet.rfind(str, 0) == 0; };
|
||||
|
||||
if (match("Hc")) {
|
||||
// Sets thread to this ID for stepping
|
||||
@@ -823,7 +810,7 @@ GdbServer::HandledPacketType GdbServer::handleThreadOp(std::string &packet) {
|
||||
if (match("Hg")) {
|
||||
// Sets thread for "other" operations
|
||||
auto ss = std::istringstream(packet);
|
||||
ss.seekg(std::string("Hg").size());
|
||||
ss.seekg(std::string_view("Hg").size());
|
||||
ss >> std::hex >> CurrentDebuggingThread;
|
||||
|
||||
// This must return quick otherwise IDA complains
|
||||
@@ -834,7 +821,7 @@ GdbServer::HandledPacketType GdbServer::handleThreadOp(std::string &packet) {
|
||||
return {"", HandledPacketType::TYPE_UNKNOWN};
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::handleBreakpoint(std::string &packet) {
|
||||
GdbServer::HandledPacketType GdbServer::handleBreakpoint(const std::string &packet) {
|
||||
auto ss = std::istringstream(packet);
|
||||
|
||||
bool Set{};
|
||||
@@ -850,17 +837,15 @@ GdbServer::HandledPacketType GdbServer::handleBreakpoint(std::string &packet) {
|
||||
return {"OK", HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
|
||||
GdbServer::HandledPacketType GdbServer::ProcessPacket(std::string &packet) {
|
||||
GdbServer::HandledPacketType GdbServer::ProcessPacket(const std::string &packet) {
|
||||
switch (packet[0]) {
|
||||
case '?': {
|
||||
// Indicates the reason that the thread has stopped
|
||||
// Behaviour changes if the target is in non-stop mode
|
||||
// Binja doesn't support S response here
|
||||
//return {"S00", HandledPacketType::TYPE_ACK};
|
||||
std::ostringstream ss;
|
||||
ss << "T00thread:" << std::setfill('0') << std::setw(2) << std::hex << getpid() << ";core:2c;";
|
||||
|
||||
return {ss.str(), HandledPacketType::TYPE_ACK};
|
||||
auto str = fmt::format("T00thread:{:02x};core:2c;", getpid());
|
||||
return {std::move(str), HandledPacketType::TYPE_ACK};
|
||||
}
|
||||
case 'g':
|
||||
return {readRegs(), HandledPacketType::TYPE_ACK};
|
||||
@@ -890,14 +875,14 @@ GdbServer::HandledPacketType GdbServer::ProcessPacket(std::string &packet) {
|
||||
}
|
||||
}
|
||||
|
||||
void GdbServer::SendPacketPair(HandledPacketType response) {
|
||||
void GdbServer::SendPacketPair(const HandledPacketType& response) {
|
||||
std::lock_guard lk(sendMutex);
|
||||
if (response.TypeResponse == HandledPacketType::TYPE_ACK ||
|
||||
response.TypeResponse == HandledPacketType::TYPE_ONLYACK) {
|
||||
SendACK(*CommsStream, false);
|
||||
}
|
||||
else if (response.TypeResponse == HandledPacketType::TYPE_NACK ||
|
||||
response.TypeResponse == HandledPacketType::TYPE_ONLYNACK) {
|
||||
response.TypeResponse == HandledPacketType::TYPE_ONLYNACK) {
|
||||
SendACK(*CommsStream, true);
|
||||
}
|
||||
|
||||
@@ -905,8 +890,8 @@ void GdbServer::SendPacketPair(HandledPacketType response) {
|
||||
SendPacket(*CommsStream, "");
|
||||
}
|
||||
else if (response.TypeResponse != HandledPacketType::TYPE_ONLYNACK &&
|
||||
response.TypeResponse != HandledPacketType::TYPE_ONLYACK &&
|
||||
response.TypeResponse != HandledPacketType::TYPE_NONE) {
|
||||
response.TypeResponse != HandledPacketType::TYPE_ONLYACK &&
|
||||
response.TypeResponse != HandledPacketType::TYPE_NONE) {
|
||||
SendPacket(*CommsStream, response.Response);
|
||||
}
|
||||
}
|
||||
@@ -927,7 +912,7 @@ void GdbServer::GdbServerLoop() {
|
||||
response = ProcessPacket(packet);
|
||||
SendPacketPair(response);
|
||||
if (response.TypeResponse == HandledPacketType::TYPE_UNKNOWN) {
|
||||
LogMan::Msg::D("Unknown packet %s", packet.c_str());
|
||||
LogMan::Msg::DFmt("Unknown packet {}", packet);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -943,13 +928,12 @@ void GdbServer::GdbServerLoop() {
|
||||
break;
|
||||
case '\x03': { // ASCII EOT
|
||||
CTX->Pause();
|
||||
std::ostringstream ss;
|
||||
ss << "T02thread:" << std::setfill('0') << std::setw(2) << std::hex << getpid() << ";core:2c;";
|
||||
SendPacketPair({ss.str(), HandledPacketType::TYPE_ACK});
|
||||
auto str = fmt::format("T02thread:{:02x};core:2c;", getpid());
|
||||
SendPacketPair({std::move(str), HandledPacketType::TYPE_ACK});
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LogMan::Msg::D("GdbServer: Unexpected byte %c (%02x)", c, c);
|
||||
LogMan::Msg::DFmt("GdbServer: Unexpected byte {} ({:02x})", static_cast<char>(c), c);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,7 +987,7 @@ std::unique_ptr<std::iostream> GdbServer::OpenSocket() {
|
||||
|
||||
// Block until a connection arrives
|
||||
|
||||
LogMan::Msg::I("GdbServer, waiting for connection on localhost:8086");
|
||||
LogMan::Msg::IFmt("GdbServer, waiting for connection on localhost:8086");
|
||||
listen(sockfd, 1);
|
||||
|
||||
new_fd = accept(sockfd, (struct sockaddr *)&their_addr, &addr_size);
|
||||
|
||||
+10
-10
@@ -30,7 +30,7 @@ private:
|
||||
std::unique_ptr<std::iostream> OpenSocket();
|
||||
void StartThread();
|
||||
std::string ReadPacket(std::iostream &stream);
|
||||
void SendPacket(std::ostream &stream, std::string packet);
|
||||
void SendPacket(std::ostream &stream, const std::string& packet);
|
||||
|
||||
void SendACK(std::ostream &stream, bool NACK);
|
||||
|
||||
@@ -47,18 +47,18 @@ private:
|
||||
ResponseType TypeResponse{};
|
||||
};
|
||||
|
||||
void SendPacketPair(HandledPacketType packetPair);
|
||||
HandledPacketType ProcessPacket(std::string &packet);
|
||||
HandledPacketType handleQuery(std::string &packet);
|
||||
HandledPacketType handleXfer(std::string &packet);
|
||||
HandledPacketType handleMemory(std::string &packet);
|
||||
HandledPacketType handleV(std::string& packet);
|
||||
HandledPacketType handleThreadOp(std::string &packet);
|
||||
HandledPacketType handleBreakpoint(std::string &packet);
|
||||
void SendPacketPair(const HandledPacketType& packetPair);
|
||||
HandledPacketType ProcessPacket(const std::string &packet);
|
||||
HandledPacketType handleQuery(const std::string &packet);
|
||||
HandledPacketType handleXfer(const std::string &packet);
|
||||
HandledPacketType handleMemory(const std::string &packet);
|
||||
HandledPacketType handleV(const std::string& packet);
|
||||
HandledPacketType handleThreadOp(const std::string &packet);
|
||||
HandledPacketType handleBreakpoint(const std::string &packet);
|
||||
HandledPacketType handleProgramOffsets();
|
||||
|
||||
std::string readRegs();
|
||||
HandledPacketType readReg(std::string& packet);
|
||||
HandledPacketType readReg(const std::string& packet);
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
std::unique_ptr<FEXCore::Threads::Thread> gdbServerThread;
|
||||
|
||||
@@ -115,8 +115,8 @@ void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR:
|
||||
return reinterpret_cast<void*>(InterpreterExecution);
|
||||
}
|
||||
|
||||
FEXCore::CPU::CPUBackend *CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return new InterpreterCore(ctx, Thread, CompileThread);
|
||||
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return std::make_unique<InterpreterCore>(ctx, Thread, CompileThread);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
@@ -11,6 +13,6 @@ namespace FEXCore::Core {
|
||||
namespace FEXCore::CPU {
|
||||
class CPUBackend;
|
||||
|
||||
FEXCore::CPU::CPUBackend *CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
|
||||
}
|
||||
+285
-166
@@ -10,15 +10,18 @@
|
||||
#include "Interface/Core/DebugData.h"
|
||||
#include "Interface/Core/InternalThreadState.h"
|
||||
#include "Interface/Core/Interpreter/InterpreterClass.h"
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <FEXCore/Core/CPUBackend.h>
|
||||
#include <FEXCore/HLE/SyscallHandler.h>
|
||||
#include <FEXCore/IR/IR.h>
|
||||
#include <FEXCore/IR/IntrusiveIRList.h>
|
||||
#include <FEXCore/Utils/BitUtils.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include "Interface/HLE/Thunks/Thunks.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
@@ -411,15 +414,15 @@ static void StopThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
Thread->CTX->StopThread(Thread);
|
||||
|
||||
LOGMAN_MSG_A("unreachable");
|
||||
__builtin_unreachable();
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
[[noreturn]]
|
||||
static void SignalReturn(FEXCore::Core::InternalThreadState *Thread) {
|
||||
Thread->CTX->SignalThread(Thread, FEXCore::Core::SIGNALEVENT_RETURN);
|
||||
Thread->CTX->SignalThread(Thread, FEXCore::Core::SignalEvent::Return);
|
||||
|
||||
LOGMAN_MSG_A("unreachable");
|
||||
__builtin_unreachable();
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
template<IR::IROps Op>
|
||||
@@ -1215,7 +1218,41 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_VDUPELEMENT: {
|
||||
auto Op = IROp->C<IR::IROp_VDupElement>();
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
LOGMAN_THROW_A(OpSize <= 16, "OpSize is too large for VDupElement: %d", OpSize);
|
||||
if (OpSize == 16) {
|
||||
__uint128_t SourceMask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
|
||||
uint64_t Shift = Op->Header.ElementSize * Op->Index * 8;
|
||||
if (Op->Header.ElementSize == 8)
|
||||
SourceMask = ~0ULL;
|
||||
|
||||
__uint128_t Src = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[0]);
|
||||
Src >>= Shift;
|
||||
Src &= SourceMask;
|
||||
for (size_t i = 0; i < Elements; ++i) {
|
||||
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(GDP) + (Op->Header.ElementSize * i)),
|
||||
&Src, Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
uint64_t SourceMask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
|
||||
uint64_t Shift = Op->Header.ElementSize * Op->Index * 8;
|
||||
if (Op->Header.ElementSize == 8)
|
||||
SourceMask = ~0ULL;
|
||||
|
||||
uint64_t Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
|
||||
Src >>= Shift;
|
||||
Src &= SourceMask;
|
||||
for (size_t i = 0; i < Elements; ++i) {
|
||||
memcpy(reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(GDP) + (Op->Header.ElementSize * i)),
|
||||
&Src, Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_ENTRYPOINTOFFSET: {
|
||||
auto Op = IROp->C<IR::IROp_EntrypointOffset>();
|
||||
GD = Entry + Op->Offset;
|
||||
@@ -1573,10 +1610,10 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
uint8_t Mask = OpSize * 8 - 1;
|
||||
switch (OpSize) {
|
||||
case 4:
|
||||
GD = static_cast<int32_t>(Src1) << (Src2 & Mask);
|
||||
GD = static_cast<uint32_t>(Src1) << (Src2 & Mask);
|
||||
break;
|
||||
case 8:
|
||||
GD = static_cast<int64_t>(Src1) << (Src2 & Mask);
|
||||
GD = static_cast<uint64_t>(Src1) << (Src2 & Mask);
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unknown LSHL Size: %d\n", OpSize); break;
|
||||
};
|
||||
@@ -1866,23 +1903,23 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
case IR::OP_POPCOUNT: {
|
||||
auto Op = IROp->C<IR::IROp_Popcount>();
|
||||
uint64_t Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
|
||||
GD = __builtin_popcountl(Src);
|
||||
GD = std::popcount(Src);
|
||||
break;
|
||||
}
|
||||
case IR::OP_FINDLSB: {
|
||||
auto Op = IROp->C<IR::IROp_FindLSB>();
|
||||
uint64_t Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
|
||||
uint64_t Result = __builtin_ffsll(Src);
|
||||
uint64_t Result = FindFirstSetBit(Src);
|
||||
GD = Result - 1;
|
||||
break;
|
||||
}
|
||||
case IR::OP_FINDMSB: {
|
||||
auto Op = IROp->C<IR::IROp_FindMSB>();
|
||||
switch (OpSize) {
|
||||
case 1: GD = ((24 + OpSize * 8) - __builtin_clz(*GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 2: GD = ((16 + OpSize * 8) - __builtin_clz(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 4: GD = (OpSize * 8 - __builtin_clz(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 8: GD = (OpSize * 8 - __builtin_clzll(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 1: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 2: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 4: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
case 8: GD = (OpSize * 8 - std::countl_zero(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]))) - 1; break;
|
||||
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
|
||||
}
|
||||
break;
|
||||
@@ -1890,9 +1927,9 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
case IR::OP_REV: {
|
||||
auto Op = IROp->C<IR::IROp_Rev>();
|
||||
switch (OpSize) {
|
||||
case 2: GD = __builtin_bswap16(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0])); break;
|
||||
case 4: GD = __builtin_bswap32(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0])); break;
|
||||
case 8: GD = __builtin_bswap64(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0])); break;
|
||||
case 2: GD = BSwap16(*GetSrc<uint16_t*>(SSAData, Op->Header.Args[0])); break;
|
||||
case 4: GD = BSwap32(*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0])); break;
|
||||
case 8: GD = BSwap64(*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0])); break;
|
||||
default: LOGMAN_MSG_A("Unknown REV size: %d", OpSize); break;
|
||||
}
|
||||
break;
|
||||
@@ -1902,34 +1939,22 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
auto Src = *GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]);
|
||||
if (Src)
|
||||
GD = __builtin_ctz(Src);
|
||||
else
|
||||
GD = sizeof(Src) * 8;
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
auto Src = *GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]);
|
||||
if (Src)
|
||||
GD = __builtin_ctz(Src);
|
||||
else
|
||||
GD = sizeof(Src) * 8;
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
auto Src = *GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
|
||||
if (Src)
|
||||
GD = __builtin_ctz(Src);
|
||||
else
|
||||
GD = sizeof(Src) * 8;
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
auto Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
|
||||
if (Src)
|
||||
GD = __builtin_ctzll(Src);
|
||||
else
|
||||
GD = sizeof(Src) * 8;
|
||||
GD = std::countr_zero(Src);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
|
||||
@@ -1940,37 +1965,23 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
auto Op = IROp->C<IR::IROp_CountLeadingZeroes>();
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
uint32_t Src = *GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]);
|
||||
Src <<= 24;
|
||||
if (Src)
|
||||
GD = __builtin_clz(Src);
|
||||
else
|
||||
GD = 8;
|
||||
auto Src = *GetSrc<uint8_t*>(SSAData, Op->Header.Args[0]);
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
uint32_t Src = *GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]);
|
||||
Src <<= 16;
|
||||
if (Src)
|
||||
GD = __builtin_clz(Src);
|
||||
else
|
||||
GD = 16;
|
||||
auto Src = *GetSrc<uint16_t*>(SSAData, Op->Header.Args[0]);
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
auto Src = *GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
|
||||
if (Src)
|
||||
GD = __builtin_clz(Src);
|
||||
else
|
||||
GD = sizeof(Src) * 8;
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
auto Src = *GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
|
||||
if (Src)
|
||||
GD = __builtin_clzll(Src);
|
||||
else
|
||||
GD = sizeof(Src) * 8;
|
||||
GD = std::countl_zero(Src);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown size: %d", OpSize); break;
|
||||
@@ -2543,6 +2554,16 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, &Dst, 16);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VBIC: {
|
||||
auto Op = IROp->C<IR::IROp_VBic>();
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[0]);
|
||||
__uint128_t Src2 = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[1]);
|
||||
|
||||
__uint128_t Dst = Src1 & ~Src2;
|
||||
memcpy(GDP, &Dst, 16);
|
||||
break;
|
||||
}
|
||||
|
||||
case IR::OP_VXOR: {
|
||||
auto Op = IROp->C<IR::IROp_VXor>();
|
||||
__uint128_t Src1 = *GetSrc<__uint128_t*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -2988,6 +3009,24 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VUMINV: {
|
||||
auto Op = IROp->C<IR::IROp_VUMinV>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
uint8_t Tmp[16];
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto current, auto a) { return std::min(current, a); };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_REDUCE_1SRC_OP(1, uint8_t, Func, ~0)
|
||||
DO_VECTOR_REDUCE_1SRC_OP(2, uint16_t, Func, ~0)
|
||||
DO_VECTOR_REDUCE_1SRC_OP(4, uint32_t, Func, ~0U)
|
||||
DO_VECTOR_REDUCE_1SRC_OP(8, uint64_t, Func, ~0ULL)
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
memcpy(GDP, Tmp, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VURAVG: {
|
||||
auto Op = IROp->C<IR::IROp_VURAvg>();
|
||||
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -3023,6 +3062,24 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VPOPCOUNT: {
|
||||
auto Op = IROp->C<IR::IROp_VPopcount>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
uint8_t Tmp[16];
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a) { return std::popcount(a); };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_OP(1, uint8_t, Func)
|
||||
DO_VECTOR_1SRC_OP(2, uint16_t, Func)
|
||||
DO_VECTOR_1SRC_OP(4, uint32_t, Func)
|
||||
DO_VECTOR_1SRC_OP(8, uint64_t, Func)
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VFMUL: {
|
||||
auto Op = IROp->C<IR::IROp_VFMul>();
|
||||
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -3292,22 +3349,6 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VECTOR_UTOF: {
|
||||
auto Op = IROp->C<IR::IROp_Vector_UToF>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
uint8_t Tmp[16];
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, float, uint32_t, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, double, uint64_t, Func, 0, 0)
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VECTOR_STOF: {
|
||||
auto Op = IROp->C<IR::IROp_Vector_SToF>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -3324,22 +3365,6 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VECTOR_FTOZU: {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToZU>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
uint8_t Tmp[16];
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, uint32_t, float, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, uint64_t, double, Func, 0, 0)
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VECTOR_FTOZS: {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -3347,7 +3372,7 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
auto Func = [](auto a, auto min, auto max) { return std::trunc(a); };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
|
||||
@@ -3356,22 +3381,6 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VECTOR_FTOU: {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToU>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
uint8_t Tmp[16];
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, uint32_t, float, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, uint64_t, double, Func, 0, 0)
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VECTOR_FTOS: {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToS>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -3379,7 +3388,7 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func = [](auto a, auto min, auto max) { return a; };
|
||||
auto Func = [](auto a, auto min, auto max) { return std::nearbyint(a); };
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_2TYPE_OP(4, int32_t, float, Func, 0, 0)
|
||||
DO_VECTOR_1SRC_2TYPE_OP(8, int64_t, double, Func, 0, 0)
|
||||
@@ -3502,6 +3511,28 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, Op->Header.Size);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VUABDL: {
|
||||
auto Op = IROp->C<IR::IROp_VUABDL>();
|
||||
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(SSAData, Op->Header.Args[1]);
|
||||
|
||||
uint8_t Tmp[16];
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
auto Func8 = [](auto a, auto b) { return std::abs((int16_t)a - (int16_t)b); };
|
||||
auto Func16 = [](auto a, auto b) { return std::abs((int32_t)a - (int32_t)b); };
|
||||
auto Func32 = [](auto a, auto b) { return std::abs((int64_t)a - (int64_t)b); };
|
||||
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_2SRC_2TYPE_OP(2, uint16_t, uint8_t, Func8)
|
||||
DO_VECTOR_2SRC_2TYPE_OP(4, uint32_t, uint16_t, Func16)
|
||||
DO_VECTOR_2SRC_2TYPE_OP(8, uint64_t, uint32_t, Func32)
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VSXTL: {
|
||||
auto Op = IROp->C<IR::IROp_VSXTL>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -3822,6 +3853,64 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VUNZIP2:
|
||||
case IR::OP_VUNZIP: {
|
||||
auto Op = IROp->C<IR::IROp_VUnZip>();
|
||||
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
void *Src2 = GetSrc<void*>(SSAData, Op->Header.Args[1]);
|
||||
uint8_t Tmp[16];
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
unsigned Start = IROp->Op == IR::OP_VUNZIP ? 0 : 1;
|
||||
Elements >>= 1;
|
||||
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
auto *Dst_d = reinterpret_cast<uint8_t*>(Tmp);
|
||||
auto *Src1_d = reinterpret_cast<uint8_t*>(Src1);
|
||||
auto *Src2_d = reinterpret_cast<uint8_t*>(Src2);
|
||||
for (unsigned i = 0; i < Elements; ++i) {
|
||||
Dst_d[i] = Src1_d[Start + (i * 2)];
|
||||
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
auto *Dst_d = reinterpret_cast<uint16_t*>(Tmp);
|
||||
auto *Src1_d = reinterpret_cast<uint16_t*>(Src1);
|
||||
auto *Src2_d = reinterpret_cast<uint16_t*>(Src2);
|
||||
for (unsigned i = 0; i < Elements; ++i) {
|
||||
Dst_d[i] = Src1_d[Start + (i * 2)];
|
||||
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
auto *Dst_d = reinterpret_cast<uint32_t*>(Tmp);
|
||||
auto *Src1_d = reinterpret_cast<uint32_t*>(Src1);
|
||||
auto *Src2_d = reinterpret_cast<uint32_t*>(Src2);
|
||||
for (unsigned i = 0; i < Elements; ++i) {
|
||||
Dst_d[i] = Src1_d[Start + (i * 2)];
|
||||
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
auto *Dst_d = reinterpret_cast<uint64_t*>(Tmp);
|
||||
auto *Src1_d = reinterpret_cast<uint64_t*>(Src1);
|
||||
auto *Src2_d = reinterpret_cast<uint64_t*>(Src2);
|
||||
for (unsigned i = 0; i < Elements; ++i) {
|
||||
Dst_d[i] = Src1_d[Start + (i * 2)];
|
||||
Dst_d[Elements+i] = Src2_d[Start + (i * 2)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
|
||||
case IR::OP_VINSELEMENT: {
|
||||
auto Op = IROp->C<IR::IROp_VInsElement>();
|
||||
void *Src1 = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
@@ -4204,6 +4293,10 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
|
||||
uint64_t Offset = Op->Index * Op->Header.ElementSize * 8;
|
||||
__uint128_t Mask = (1ULL << (Op->Header.ElementSize * 8)) - 1;
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
Mask = ~0ULL;
|
||||
}
|
||||
Src2 = Src2 & Mask;
|
||||
Mask <<= Offset;
|
||||
Mask = ~Mask;
|
||||
__uint128_t Dst = Src1 & Mask;
|
||||
@@ -4240,78 +4333,57 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_FLOAT_FROMGPR_U: {
|
||||
auto Op = IROp->C<IR::IROp_Float_FromGPR_U>();
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0404: { // Float <- int32_t
|
||||
float Dst = (float)*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // Float <- int64_t
|
||||
float Dst = (float)*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case 0x0804: { // Double <- int32_t
|
||||
double Dst = (double)*GetSrc<uint32_t*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
case 0x0808: { // Double <- int64_t
|
||||
double Dst = (double)*GetSrc<uint64_t*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_FLOAT_TOGPR_ZS: {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZS>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
int64_t Dst = (int64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
}
|
||||
else {
|
||||
int32_t Dst = (int32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_FLOAT_TOGPR_ZU: {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZU>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
uint64_t Dst = (uint64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
}
|
||||
else {
|
||||
uint32_t Dst = (uint32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0804: { // int64_t <- float
|
||||
int64_t Dst = (int64_t)std::trunc(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0808: { // int64_t <- double
|
||||
int64_t Dst = (int64_t)std::trunc(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0404: { // int32_t <- float
|
||||
int32_t Dst = (int32_t)std::trunc(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // int32_t <- double
|
||||
int32_t Dst = (int32_t)std::trunc(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_FLOAT_TOGPR_S: {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
int64_t Dst = (int64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
}
|
||||
else {
|
||||
int32_t Dst = (int32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IR::OP_FLOAT_TOGPR_U: {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_U>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
uint64_t Dst = (uint64_t)*GetSrc<double*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
}
|
||||
else {
|
||||
uint32_t Dst = (uint32_t)*GetSrc<float*>(SSAData, Op->Header.Args[0]);
|
||||
memcpy(GDP, &Dst, Op->Header.ElementSize);
|
||||
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0804: { // int64_t <- float
|
||||
int64_t Dst = (int64_t)std::nearbyint(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0808: { // int64_t <- double
|
||||
int64_t Dst = (int64_t)std::nearbyint(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0404: { // int32_t <- float
|
||||
int32_t Dst = (int32_t)std::nearbyint(*GetSrc<float*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // int32_t <- double
|
||||
int32_t Dst = (int32_t)std::nearbyint(*GetSrc<double*>(SSAData, Op->Header.Args[0]));
|
||||
memcpy(GDP, &Dst, IROp->Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4363,6 +4435,53 @@ void InterpreterOps::InterpretIR(FEXCore::Core::InternalThreadState *Thread, uin
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_VECTOR_FTOI: {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToI>();
|
||||
void *Src = GetSrc<void*>(SSAData, Op->Header.Args[0]);
|
||||
uint8_t Tmp[16]{};
|
||||
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
auto Func_Nearest = [](auto a) { return std::rint(a); };
|
||||
auto Func_Neg = [](auto a) { return std::floor(a); };
|
||||
auto Func_Pos = [](auto a) { return std::ceil(a); };
|
||||
auto Func_Trunc = [](auto a) { return std::trunc(a); };
|
||||
auto Func_Host = [](auto a) { return std::rint(a); };
|
||||
|
||||
switch (Op->Round) {
|
||||
case FEXCore::IR::Round_Nearest.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_OP(4, float, Func_Nearest)
|
||||
DO_VECTOR_1SRC_OP(8, double, Func_Nearest)
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Negative_Infinity.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_OP(4, float, Func_Neg)
|
||||
DO_VECTOR_1SRC_OP(8, double, Func_Neg)
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Positive_Infinity.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_OP(4, float, Func_Pos)
|
||||
DO_VECTOR_1SRC_OP(8, double, Func_Pos)
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Towards_Zero.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_OP(4, float, Func_Trunc)
|
||||
DO_VECTOR_1SRC_OP(8, double, Func_Trunc)
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Host.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
DO_VECTOR_1SRC_OP(4, float, Func_Host)
|
||||
DO_VECTOR_1SRC_OP(8, double, Func_Host)
|
||||
}
|
||||
break;
|
||||
}
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
break;
|
||||
}
|
||||
case IR::OP_FCMP: {
|
||||
auto Op = IROp->C<IR::IROp_FCmp>();
|
||||
uint32_t ResultFlags{};
|
||||
|
||||
+28
-15
@@ -969,34 +969,49 @@ DEF_OP(VExtractToGPR) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_ZU) {
|
||||
LogMan::Msg::D("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_ZS) {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZS>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
fcvtzs(GetReg<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
aarch64::Register Dst{};
|
||||
aarch64::VRegister Src{};
|
||||
if (Op->SrcElementSize == 8) {
|
||||
Src = GetSrc(Op->Header.Args[0].ID()).D();
|
||||
}
|
||||
else {
|
||||
fcvtzs(GetReg<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()).S());
|
||||
Src = GetSrc(Op->Header.Args[0].ID()).S();
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_U) {
|
||||
LogMan::Msg::D("Unimplemented");
|
||||
if (IROp->Size == 8) {
|
||||
Dst = GetReg<RA_64>(Node);
|
||||
}
|
||||
else {
|
||||
Dst = GetReg<RA_32>(Node);
|
||||
}
|
||||
|
||||
fcvtzs(Dst, Src);
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
|
||||
aarch64::Register Dst{};
|
||||
aarch64::VRegister Src{};
|
||||
if (Op->SrcElementSize == 8) {
|
||||
frinti(VTMP1.D(), GetSrc(Op->Header.Args[0].ID()).D());
|
||||
fcvtzs(GetReg<RA_64>(Node), VTMP1.D());
|
||||
Src = VTMP1.D();
|
||||
}
|
||||
else {
|
||||
frinti(VTMP1.S(), GetSrc(Op->Header.Args[0].ID()).S());
|
||||
fcvtzs(GetReg<RA_32>(Node), VTMP1.S());
|
||||
Src = VTMP1.S();
|
||||
}
|
||||
|
||||
if (IROp->Size == 8) {
|
||||
Dst = GetReg<RA_64>(Node);
|
||||
}
|
||||
else {
|
||||
Dst = GetReg<RA_32>(Node);
|
||||
}
|
||||
|
||||
fcvtzs(Dst, Src);
|
||||
}
|
||||
|
||||
DEF_OP(FCmp) {
|
||||
@@ -1087,9 +1102,7 @@ void Arm64JITCore::RegisterALUHandlers() {
|
||||
REGISTER_OP(SBFE, Sbfe);
|
||||
REGISTER_OP(SELECT, Select);
|
||||
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
|
||||
REGISTER_OP(FLOAT_TOGPR_ZU, Float_ToGPR_ZU);
|
||||
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
|
||||
REGISTER_OP(FLOAT_TOGPR_U, Float_ToGPR_U);
|
||||
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
|
||||
REGISTER_OP(FCMP, FCmp);
|
||||
|
||||
|
||||
@@ -56,10 +56,6 @@ DEF_OP(VCastFromGPR) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_U) {
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
@@ -99,19 +95,6 @@ DEF_OP(Float_FToF) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_UToF) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_UToF>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
ucvtf(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
case 8:
|
||||
ucvtf(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_SToF) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_SToF>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -125,19 +108,6 @@ DEF_OP(Vector_SToF) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToZU) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToZU>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
fcvtzu(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
case 8:
|
||||
fcvtzu(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToZS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -151,21 +121,6 @@ DEF_OP(Vector_FToZS) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToU) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToU>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
frinti(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
fcvtzu(GetDst(Node).V4S(), GetDst(Node).V4S());
|
||||
break;
|
||||
case 8:
|
||||
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
fcvtzu(GetDst(Node).V2D(), GetDst(Node).V2D());
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unknown castGPR element size: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToS>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -198,21 +153,74 @@ DEF_OP(Vector_FToF) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToI) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToI>();
|
||||
switch (Op->Round) {
|
||||
case FEXCore::IR::Round_Nearest.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
frintn(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
case 8:
|
||||
frintn(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Negative_Infinity.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
frintm(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
case 8:
|
||||
frintm(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Positive_Infinity.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
frintp(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
case 8:
|
||||
frintp(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Towards_Zero.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
frintz(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
case 8:
|
||||
frintz(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::Round_Host.Val:
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
frinti(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S());
|
||||
break;
|
||||
case 8:
|
||||
frinti(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterConversionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
|
||||
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
|
||||
REGISTER_OP(FLOAT_FTOF, Float_FToF);
|
||||
REGISTER_OP(VECTOR_UTOF, Vector_UToF);
|
||||
REGISTER_OP(VECTOR_STOF, Vector_SToF);
|
||||
REGISTER_OP(VECTOR_FTOZU, Vector_FToZU);
|
||||
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
|
||||
REGISTER_OP(VECTOR_FTOU, Vector_FToU);
|
||||
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
|
||||
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
|
||||
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+23
-18
@@ -23,6 +23,7 @@ $end_info$
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Core/UContext.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include "Interface/Core/Interpreter/InterpreterOps.h"
|
||||
|
||||
#include <sys/mman.h>
|
||||
@@ -575,7 +576,7 @@ Arm64JITCore::~Arm64JITCore() {
|
||||
FreeCodeBuffer(InitialCodeBuffer);
|
||||
}
|
||||
|
||||
IR::PhysicalRegister Arm64JITCore::GetPhys(uint32_t Node) {
|
||||
IR::PhysicalRegister Arm64JITCore::GetPhys(uint32_t Node) const {
|
||||
auto PhyReg = RAData->GetNodeRegister(Node);
|
||||
|
||||
LOGMAN_THROW_A(!PhyReg.IsInvalid(), "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
|
||||
@@ -584,7 +585,7 @@ IR::PhysicalRegister Arm64JITCore::GetPhys(uint32_t Node) {
|
||||
}
|
||||
|
||||
template<>
|
||||
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_32>(uint32_t Node) {
|
||||
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_32>(uint32_t Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
if (Reg.Class == IR::GPRFixedClass.Val) {
|
||||
@@ -594,11 +595,12 @@ aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_32>(uint32_t Node) {
|
||||
} else {
|
||||
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
template<>
|
||||
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_64>(uint32_t Node) {
|
||||
aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_64>(uint32_t Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
if (Reg.Class == IR::GPRFixedClass.Val) {
|
||||
@@ -608,22 +610,23 @@ aarch64::Register Arm64JITCore::GetReg<Arm64JITCore::RA_64>(uint32_t Node) {
|
||||
} else {
|
||||
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_32>(uint32_t Node) {
|
||||
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_32>(uint32_t Node) const {
|
||||
uint32_t Reg = GetPhys(Node).Reg;
|
||||
return RA32Pair[Reg];
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_64>(uint32_t Node) {
|
||||
std::pair<aarch64::Register, aarch64::Register> Arm64JITCore::GetSrcPair<Arm64JITCore::RA_64>(uint32_t Node) const {
|
||||
uint32_t Reg = GetPhys(Node).Reg;
|
||||
return RA64Pair[Reg];
|
||||
}
|
||||
|
||||
aarch64::VRegister Arm64JITCore::GetSrc(uint32_t Node) {
|
||||
aarch64::VRegister Arm64JITCore::GetSrc(uint32_t Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
if (Reg.Class == IR::FPRFixedClass.Val) {
|
||||
@@ -633,10 +636,11 @@ aarch64::VRegister Arm64JITCore::GetSrc(uint32_t Node) {
|
||||
} else {
|
||||
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
aarch64::VRegister Arm64JITCore::GetDst(uint32_t Node) {
|
||||
aarch64::VRegister Arm64JITCore::GetDst(uint32_t Node) const {
|
||||
auto Reg = GetPhys(Node);
|
||||
|
||||
if (Reg.Class == IR::FPRFixedClass.Val) {
|
||||
@@ -646,10 +650,11 @@ aarch64::VRegister Arm64JITCore::GetDst(uint32_t Node) {
|
||||
} else {
|
||||
LOGMAN_THROW_A(false, "Unexpected Class: %d", Reg.Class);
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
|
||||
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
|
||||
|
||||
if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) {
|
||||
@@ -663,7 +668,7 @@ bool Arm64JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_
|
||||
}
|
||||
}
|
||||
|
||||
bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
|
||||
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
|
||||
|
||||
if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) {
|
||||
@@ -677,18 +682,18 @@ bool Arm64JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode,
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::IR::RegisterClassType Arm64JITCore::GetRegClass(uint32_t Node) {
|
||||
FEXCore::IR::RegisterClassType Arm64JITCore::GetRegClass(uint32_t Node) const {
|
||||
return FEXCore::IR::RegisterClassType {GetPhys(Node).Class};
|
||||
}
|
||||
|
||||
|
||||
bool Arm64JITCore::IsFPR(uint32_t Node) {
|
||||
bool Arm64JITCore::IsFPR(uint32_t Node) const {
|
||||
auto Class = GetRegClass(Node);
|
||||
|
||||
return Class == IR::FPRClass || Class == IR::FPRFixedClass;
|
||||
}
|
||||
|
||||
bool Arm64JITCore::IsGPR(uint32_t Node) {
|
||||
bool Arm64JITCore::IsGPR(uint32_t Node) const {
|
||||
auto Class = GetRegClass(Node);
|
||||
|
||||
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
|
||||
@@ -888,7 +893,7 @@ uint64_t Arm64JITCore::ExitFunctionLink(Arm64JITCore *core, FEXCore::Core::CpuSt
|
||||
return HostCode;
|
||||
}
|
||||
|
||||
FEXCore::CPU::CPUBackend *CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return new Arm64JITCore(ctx, Thread, CompileThread);
|
||||
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return std::make_unique<Arm64JITCore>(ctx, Thread, CompileThread);
|
||||
}
|
||||
}
|
||||
+22
-19
@@ -96,35 +96,35 @@ private:
|
||||
constexpr static uint8_t RA_FPR = 2;
|
||||
|
||||
template<uint8_t RAType>
|
||||
aarch64::Register GetReg(uint32_t Node);
|
||||
aarch64::Register GetReg(uint32_t Node) const;
|
||||
|
||||
template<>
|
||||
aarch64::Register GetReg<RA_32>(uint32_t Node);
|
||||
aarch64::Register GetReg<RA_32>(uint32_t Node) const;
|
||||
template<>
|
||||
aarch64::Register GetReg<RA_64>(uint32_t Node);
|
||||
aarch64::Register GetReg<RA_64>(uint32_t Node) const;
|
||||
|
||||
template<uint8_t RAType>
|
||||
std::pair<aarch64::Register, aarch64::Register> GetSrcPair(uint32_t Node);
|
||||
std::pair<aarch64::Register, aarch64::Register> GetSrcPair(uint32_t Node) const;
|
||||
|
||||
template<>
|
||||
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_32>(uint32_t Node);
|
||||
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_32>(uint32_t Node) const;
|
||||
template<>
|
||||
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_64>(uint32_t Node);
|
||||
std::pair<aarch64::Register, aarch64::Register> GetSrcPair<RA_64>(uint32_t Node) const;
|
||||
|
||||
aarch64::VRegister GetSrc(uint32_t Node);
|
||||
aarch64::VRegister GetDst(uint32_t Node);
|
||||
aarch64::VRegister GetSrc(uint32_t Node) const;
|
||||
aarch64::VRegister GetDst(uint32_t Node) const;
|
||||
|
||||
FEXCore::IR::RegisterClassType GetRegClass(uint32_t Node);
|
||||
FEXCore::IR::RegisterClassType GetRegClass(uint32_t Node) const;
|
||||
|
||||
IR::PhysicalRegister GetPhys(uint32_t Node);
|
||||
IR::PhysicalRegister GetPhys(uint32_t Node) const;
|
||||
|
||||
bool IsFPR(uint32_t Node);
|
||||
bool IsGPR(uint32_t Node);
|
||||
bool IsFPR(uint32_t Node) const;
|
||||
bool IsGPR(uint32_t Node) const;
|
||||
|
||||
MemOperand GenerateMemOperand(uint8_t AccessSize, aarch64::Register Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale);
|
||||
|
||||
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr);
|
||||
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value);
|
||||
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr) const;
|
||||
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const;
|
||||
|
||||
struct LiveRange {
|
||||
uint32_t Begin;
|
||||
@@ -240,7 +240,6 @@ private:
|
||||
DEF_OP(VExtractToGPR);
|
||||
DEF_OP(Float_ToGPR_ZU);
|
||||
DEF_OP(Float_ToGPR_ZS);
|
||||
DEF_OP(Float_ToGPR_U);
|
||||
DEF_OP(Float_ToGPR_S);
|
||||
DEF_OP(FCmp);
|
||||
|
||||
@@ -277,16 +276,13 @@ private:
|
||||
///< Conversion ops
|
||||
DEF_OP(VInsGPR);
|
||||
DEF_OP(VCastFromGPR);
|
||||
DEF_OP(Float_FromGPR_U);
|
||||
DEF_OP(Float_FromGPR_S);
|
||||
DEF_OP(Float_FToF);
|
||||
DEF_OP(Vector_UToF);
|
||||
DEF_OP(Vector_SToF);
|
||||
DEF_OP(Vector_FToZU);
|
||||
DEF_OP(Vector_FToZS);
|
||||
DEF_OP(Vector_FToU);
|
||||
DEF_OP(Vector_FToS);
|
||||
DEF_OP(Vector_FToF);
|
||||
DEF_OP(Vector_FToI);
|
||||
|
||||
///< Flag ops
|
||||
DEF_OP(GetHostFlag);
|
||||
@@ -336,6 +332,7 @@ private:
|
||||
DEF_OP(SplatVector4);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
DEF_OP(VOr);
|
||||
DEF_OP(VXor);
|
||||
DEF_OP(VAdd);
|
||||
@@ -346,8 +343,10 @@ private:
|
||||
DEF_OP(VSQSub);
|
||||
DEF_OP(VAddP);
|
||||
DEF_OP(VAddV);
|
||||
DEF_OP(VUMinV);
|
||||
DEF_OP(VURAvg);
|
||||
DEF_OP(VAbs);
|
||||
DEF_OP(VPopcount);
|
||||
DEF_OP(VFAdd);
|
||||
DEF_OP(VFAddP);
|
||||
DEF_OP(VFSub);
|
||||
@@ -367,6 +366,8 @@ private:
|
||||
DEF_OP(VSMax);
|
||||
DEF_OP(VZip);
|
||||
DEF_OP(VZip2);
|
||||
DEF_OP(VUnZip);
|
||||
DEF_OP(VUnZip2);
|
||||
DEF_OP(VBSL);
|
||||
DEF_OP(VCMPEQ);
|
||||
DEF_OP(VCMPEQZ);
|
||||
@@ -389,6 +390,7 @@ private:
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
DEF_OP(VSRI);
|
||||
@@ -411,6 +413,7 @@ private:
|
||||
DEF_OP(VSMull);
|
||||
DEF_OP(VUMull2);
|
||||
DEF_OP(VSMull2);
|
||||
DEF_OP(VUABDL);
|
||||
DEF_OP(VTBL1);
|
||||
|
||||
///< Encryption ops
|
||||
|
||||
@@ -5,6 +5,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Interface/Core/JIT/Arm64/JITClass.h"
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
@@ -554,7 +555,8 @@ MemOperand Arm64JITCore::GenerateMemOperand(uint8_t AccessSize, aarch64::Registe
|
||||
}
|
||||
}
|
||||
}
|
||||
__builtin_unreachable();
|
||||
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
DEF_OP(LoadMem) {
|
||||
|
||||
@@ -127,6 +127,11 @@ DEF_OP(VAnd) {
|
||||
and_(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
|
||||
}
|
||||
|
||||
DEF_OP(VBic) {
|
||||
auto Op = IROp->C<IR::IROp_VBic>();
|
||||
bic(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
|
||||
}
|
||||
|
||||
DEF_OP(VOr) {
|
||||
auto Op = IROp->C<IR::IROp_VOr>();
|
||||
orr(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
|
||||
@@ -337,6 +342,21 @@ DEF_OP(VAddV) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUMinV) {
|
||||
auto Op = IROp->C<IR::IROp_VUMinV>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
// Vector
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
uminv(GetDst(Node).VCast(Op->Header.ElementSize * 8, 1), GetSrc(Op->Header.Args[0].ID()).VCast(OpSize * 8, Elements));
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VURAvg) {
|
||||
auto Op = IROp->C<IR::IROp_VURAvg>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -380,6 +400,30 @@ DEF_OP(VAbs) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VPopcount) {
|
||||
auto Op = IROp->C<IR::IROp_VPopcount>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
if (OpSize == 8) {
|
||||
// Scalar
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
cnt(GetDst(Node).V8B(), GetSrc(Op->Header.Args[0].ID()).V8B());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Vector
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1:
|
||||
cnt(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B());
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VFAdd) {
|
||||
auto Op = IROp->C<IR::IROp_VFAdd>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -949,6 +993,92 @@ DEF_OP(VZip2) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUnZip) {
|
||||
auto Op = IROp->C<IR::IROp_VUnZip>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
if (OpSize == 8) {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
uzp1(GetDst(Node).V8B(), GetSrc(Op->Header.Args[0].ID()).V8B(), GetSrc(Op->Header.Args[1].ID()).V8B());
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
uzp1(GetDst(Node).V4H(), GetSrc(Op->Header.Args[0].ID()).V4H(), GetSrc(Op->Header.Args[1].ID()).V4H());
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uzp1(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
uzp1(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
uzp1(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), GetSrc(Op->Header.Args[1].ID()).V8H());
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uzp1(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uzp1(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUnZip2) {
|
||||
auto Op = IROp->C<IR::IROp_VUnZip2>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
if (OpSize == 8) {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
uzp2(GetDst(Node).V8B(), GetSrc(Op->Header.Args[0].ID()).V8B(), GetSrc(Op->Header.Args[1].ID()).V8B());
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
uzp2(GetDst(Node).V4H(), GetSrc(Op->Header.Args[0].ID()).V4H(), GetSrc(Op->Header.Args[1].ID()).V4H());
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uzp2(GetDst(Node).V2S(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
uzp2(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), GetSrc(Op->Header.Args[1].ID()).V16B());
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
uzp2(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), GetSrc(Op->Header.Args[1].ID()).V8H());
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uzp2(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), GetSrc(Op->Header.Args[1].ID()).V4S());
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uzp2(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), GetSrc(Op->Header.Args[1].ID()).V2D());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VBSL) {
|
||||
auto Op = IROp->C<IR::IROp_VBSL>();
|
||||
if (IROp->Size == 16) {
|
||||
@@ -1648,6 +1778,25 @@ DEF_OP(VExtractElement) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VDupElement) {
|
||||
auto Op = IROp->C<IR::IROp_VDupElement>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1:
|
||||
dup(GetDst(Node).V16B(), GetSrc(Op->Header.Args[0].ID()).V16B(), Op->Index);
|
||||
break;
|
||||
case 2:
|
||||
dup(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8H(), Op->Index);
|
||||
break;
|
||||
case 4:
|
||||
dup(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4S(), Op->Index);
|
||||
break;
|
||||
case 8:
|
||||
dup(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2D(), Op->Index);
|
||||
break;
|
||||
default: LOGMAN_MSG_A("Unhandled DupElementSize: %d", Op->Header.ElementSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VExtr) {
|
||||
auto Op = IROp->C<IR::IROp_VExtr>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -2135,6 +2284,25 @@ DEF_OP(VSMull2) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUABDL) {
|
||||
auto Op = IROp->C<IR::IROp_VUABDL>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 2: {
|
||||
uabdl(GetDst(Node).V8H(), GetSrc(Op->Header.Args[0].ID()).V8B(), GetSrc(Op->Header.Args[1].ID()).V8B());
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
uabdl(GetDst(Node).V4S(), GetSrc(Op->Header.Args[0].ID()).V4H(), GetSrc(Op->Header.Args[1].ID()).V4H());
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
uabdl(GetDst(Node).V2D(), GetSrc(Op->Header.Args[0].ID()).V2S(), GetSrc(Op->Header.Args[1].ID()).V2S());
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize >> 1); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VTBL1) {
|
||||
auto Op = IROp->C<IR::IROp_VTBL1>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -2163,6 +2331,7 @@ void Arm64JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(SPLATVECTOR4, SplatVector4);
|
||||
REGISTER_OP(VMOV, VMov);
|
||||
REGISTER_OP(VAND, VAnd);
|
||||
REGISTER_OP(VBIC, VBic);
|
||||
REGISTER_OP(VOR, VOr);
|
||||
REGISTER_OP(VXOR, VXor);
|
||||
REGISTER_OP(VADD, VAdd);
|
||||
@@ -2173,8 +2342,10 @@ void Arm64JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VSQSUB, VSQSub);
|
||||
REGISTER_OP(VADDP, VAddP);
|
||||
REGISTER_OP(VADDV, VAddV);
|
||||
REGISTER_OP(VUMINV, VUMinV);
|
||||
REGISTER_OP(VURAVG, VURAvg);
|
||||
REGISTER_OP(VABS, VAbs);
|
||||
REGISTER_OP(VPOPCOUNT, VPopcount);
|
||||
REGISTER_OP(VFADD, VFAdd);
|
||||
REGISTER_OP(VFADDP, VFAddP);
|
||||
REGISTER_OP(VFSUB, VFSub);
|
||||
@@ -2194,6 +2365,8 @@ void Arm64JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VSMAX, VSMax);
|
||||
REGISTER_OP(VZIP, VZip);
|
||||
REGISTER_OP(VZIP2, VZip2);
|
||||
REGISTER_OP(VUNZIP, VUnZip);
|
||||
REGISTER_OP(VUNZIP2, VUnZip2);
|
||||
REGISTER_OP(VBSL, VBSL);
|
||||
REGISTER_OP(VCMPEQ, VCMPEQ);
|
||||
REGISTER_OP(VCMPEQZ, VCMPEQZ);
|
||||
@@ -2216,6 +2389,7 @@ void Arm64JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VINSELEMENT, VInsElement);
|
||||
REGISTER_OP(VINSSCALARELEMENT, VInsScalarElement);
|
||||
REGISTER_OP(VEXTRACTELEMENT, VExtractElement);
|
||||
REGISTER_OP(VDUPELEMENT, VDupElement);
|
||||
REGISTER_OP(VEXTR, VExtr);
|
||||
REGISTER_OP(VSLI, VSLI);
|
||||
REGISTER_OP(VSRI, VSRI);
|
||||
@@ -2239,6 +2413,7 @@ void Arm64JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VSMULL, VSMull);
|
||||
REGISTER_OP(VUMULL2, VUMull2);
|
||||
REGISTER_OP(VSMULL2, VSMull2);
|
||||
REGISTER_OP(VUABDL, VUABDL);
|
||||
REGISTER_OP(VTBL1, VTBL1);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
@@ -11,6 +13,6 @@ struct InternalThreadState;
|
||||
namespace FEXCore::CPU {
|
||||
class CPUBackend;
|
||||
|
||||
FEXCore::CPU::CPUBackend *CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
FEXCore::CPU::CPUBackend *CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread);
|
||||
}
|
||||
+42
-23
@@ -1108,42 +1108,63 @@ DEF_OP(VExtractToGPR) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_ZU) {
|
||||
LogMan::Msg::D("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_ZS) {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_ZS>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
cvttsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
}
|
||||
else {
|
||||
cvttss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_U) {
|
||||
LogMan::Msg::D("Unimplemented");
|
||||
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0804: // int64_t <- float
|
||||
cvttss2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
case 0x0808: // int64_t <- double
|
||||
cvttsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
case 0x0404: // int32_t <- float
|
||||
cvttss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
case 0x0408: // int32_t <- double
|
||||
cvttsd2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_ToGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_ToGPR_S>();
|
||||
if (Op->Header.ElementSize == 8) {
|
||||
cvtsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
}
|
||||
else {
|
||||
cvtss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
uint16_t Conv = (IROp->Size << 8) | Op->SrcElementSize;
|
||||
switch (Conv) {
|
||||
case 0x0804: // int64_t <- float
|
||||
cvtss2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
case 0x0808: // int64_t <- double
|
||||
cvtsd2si(GetDst<RA_64>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
case 0x0404: // int32_t <- float
|
||||
cvtss2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
case 0x0408: // int32_t <- double
|
||||
cvtsd2si(GetDst<RA_32>(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(FCmp) {
|
||||
auto Op = IROp->C<IR::IROp_FCmp>();
|
||||
|
||||
if (Op->ElementSize == 4) {
|
||||
ucomiss(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
if (Op->Flags & (1 << IR::FCMP_FLAG_UNORDERED)) {
|
||||
if (Op->ElementSize == 4) {
|
||||
ucomiss(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
}
|
||||
else {
|
||||
ucomisd(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
ucomisd(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
if (Op->ElementSize == 4) {
|
||||
comiss(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
}
|
||||
else {
|
||||
comisd(GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
}
|
||||
}
|
||||
mov (rdx, 0);
|
||||
|
||||
@@ -1217,9 +1238,7 @@ void X86JITCore::RegisterALUHandlers() {
|
||||
REGISTER_OP(SBFE, Sbfe);
|
||||
REGISTER_OP(SELECT, Select);
|
||||
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
|
||||
REGISTER_OP(FLOAT_TOGPR_ZU, Float_ToGPR_ZU);
|
||||
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
|
||||
REGISTER_OP(FLOAT_TOGPR_U, Float_ToGPR_U);
|
||||
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
|
||||
REGISTER_OP(FCMP, FCmp);
|
||||
#undef REGISTER_OP
|
||||
|
||||
@@ -56,10 +56,6 @@ DEF_OP(VCastFromGPR) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_U) {
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
|
||||
uint16_t Conv = (Op->Header.ElementSize << 8) | Op->SrcElementSize;
|
||||
@@ -99,10 +95,6 @@ DEF_OP(Float_FToF) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_UToF) {
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Vector_SToF) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_SToF>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -125,10 +117,6 @@ DEF_OP(Vector_SToF) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToZU) {
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToZS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToZS>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -142,10 +130,6 @@ DEF_OP(Vector_FToZS) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToU) {
|
||||
LOGMAN_MSG_A("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToS) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToS>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -176,21 +160,50 @@ DEF_OP(Vector_FToF) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Vector_FToI) {
|
||||
auto Op = IROp->C<IR::IROp_Vector_FToI>();
|
||||
uint8_t RoundMode{};
|
||||
|
||||
switch (Op->Round) {
|
||||
case FEXCore::IR::Round_Nearest.Val:
|
||||
RoundMode = 0b0000'0'0'00;
|
||||
break;
|
||||
case FEXCore::IR::Round_Negative_Infinity.Val:
|
||||
RoundMode = 0b0000'0'0'01;
|
||||
break;
|
||||
case FEXCore::IR::Round_Positive_Infinity.Val:
|
||||
RoundMode = 0b0000'0'0'10;
|
||||
break;
|
||||
case FEXCore::IR::Round_Towards_Zero.Val:
|
||||
RoundMode = 0b0000'0'0'11;
|
||||
break;
|
||||
case FEXCore::IR::Round_Host.Val:
|
||||
RoundMode = 0b0000'0'1'00;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 4:
|
||||
roundps(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), RoundMode);
|
||||
break;
|
||||
case 8:
|
||||
roundpd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), RoundMode);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void X86JITCore::RegisterConversionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_U, Float_FromGPR_U);
|
||||
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
|
||||
REGISTER_OP(FLOAT_FTOF, Float_FToF);
|
||||
REGISTER_OP(VECTOR_UTOF, Vector_UToF);
|
||||
REGISTER_OP(VECTOR_STOF, Vector_SToF);
|
||||
REGISTER_OP(VECTOR_FTOZU, Vector_FToZU);
|
||||
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
|
||||
REGISTER_OP(VECTOR_FTOU, Vector_FToU);
|
||||
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
|
||||
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
|
||||
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+34
-34
@@ -411,7 +411,7 @@ void X86JITCore::ClearCache() {
|
||||
}
|
||||
}
|
||||
|
||||
IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) {
|
||||
IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) const {
|
||||
auto PhyReg = RAData->GetNodeRegister(Node);
|
||||
|
||||
LOGMAN_THROW_A(PhyReg.Raw != 255, "Couldn't Allocate register for node: ssa%d. Class: %d", Node, PhyReg.Class);
|
||||
@@ -419,98 +419,98 @@ IR::PhysicalRegister X86JITCore::GetPhys(uint32_t Node) {
|
||||
return PhyReg;
|
||||
}
|
||||
|
||||
bool X86JITCore::IsFPR(uint32_t Node) {
|
||||
bool X86JITCore::IsFPR(uint32_t Node) const {
|
||||
return RAData->GetNodeRegister(Node).Class == IR::FPRClass.Val;
|
||||
}
|
||||
|
||||
bool X86JITCore::IsGPR(uint32_t Node) {
|
||||
bool X86JITCore::IsGPR(uint32_t Node) const {
|
||||
return RAData->GetNodeRegister(Node).Class == IR::GPRClass.Val;
|
||||
}
|
||||
|
||||
template<uint8_t RAType>
|
||||
Xbyak::Reg X86JITCore::GetSrc(uint32_t Node) {
|
||||
Xbyak::Reg X86JITCore::GetSrc(uint32_t Node) const {
|
||||
// rax, rcx, rdx, rsi, r8, r9,
|
||||
// r10
|
||||
// Callee Saved
|
||||
// rbx, rbp, r12, r13, r14, r15
|
||||
auto PhyReg = GetPhys(Node);
|
||||
if (RAType == RA_64)
|
||||
if constexpr (RAType == RA_64)
|
||||
return RA64[PhyReg.Reg].cvt64();
|
||||
else if (RAType == RA_XMM)
|
||||
else if constexpr (RAType == RA_XMM)
|
||||
return RAXMM[PhyReg.Reg];
|
||||
else if (RAType == RA_32)
|
||||
else if constexpr (RAType == RA_32)
|
||||
return RA64[PhyReg.Reg].cvt32();
|
||||
else if (RAType == RA_16)
|
||||
else if constexpr (RAType == RA_16)
|
||||
return RA64[PhyReg.Reg].cvt16();
|
||||
else if (RAType == RA_8)
|
||||
else if constexpr (RAType == RA_8)
|
||||
return RA64[PhyReg.Reg].cvt8();
|
||||
}
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_64>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_64>(uint32_t Node) const;
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_32>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_32>(uint32_t Node) const;
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_16>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_16>(uint32_t Node) const;
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_8>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetSrc<X86JITCore::RA_8>(uint32_t Node) const;
|
||||
|
||||
Xbyak::Xmm X86JITCore::GetSrc(uint32_t Node) {
|
||||
Xbyak::Xmm X86JITCore::GetSrc(uint32_t Node) const {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
return RAXMM_x[PhyReg.Reg];
|
||||
}
|
||||
|
||||
template<uint8_t RAType>
|
||||
Xbyak::Reg X86JITCore::GetDst(uint32_t Node) {
|
||||
Xbyak::Reg X86JITCore::GetDst(uint32_t Node) const {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
if (RAType == RA_64)
|
||||
if constexpr (RAType == RA_64)
|
||||
return RA64[PhyReg.Reg].cvt64();
|
||||
else if (RAType == RA_XMM)
|
||||
else if constexpr (RAType == RA_XMM)
|
||||
return RAXMM[PhyReg.Reg];
|
||||
else if (RAType == RA_32)
|
||||
else if constexpr (RAType == RA_32)
|
||||
return RA64[PhyReg.Reg].cvt32();
|
||||
else if (RAType == RA_16)
|
||||
else if constexpr (RAType == RA_16)
|
||||
return RA64[PhyReg.Reg].cvt16();
|
||||
else if (RAType == RA_8)
|
||||
else if constexpr (RAType == RA_8)
|
||||
return RA64[PhyReg.Reg].cvt8();
|
||||
}
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_64>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_64>(uint32_t Node) const;
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_32>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_32>(uint32_t Node) const;
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_16>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_16>(uint32_t Node) const;
|
||||
|
||||
template
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_8>(uint32_t Node);
|
||||
Xbyak::Reg X86JITCore::GetDst<X86JITCore::RA_8>(uint32_t Node) const;
|
||||
|
||||
template<uint8_t RAType>
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair(uint32_t Node) {
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair(uint32_t Node) const {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
if (RAType == RA_64)
|
||||
if constexpr (RAType == RA_64)
|
||||
return RA64Pair[PhyReg.Reg];
|
||||
else if (RAType == RA_32)
|
||||
else if constexpr (RAType == RA_32)
|
||||
return {RA64Pair[PhyReg.Reg].first.cvt32(), RA64Pair[PhyReg.Reg].second.cvt32()};
|
||||
}
|
||||
|
||||
template
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_64>(uint32_t Node);
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_64>(uint32_t Node) const;
|
||||
|
||||
template
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_32>(uint32_t Node);
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> X86JITCore::GetSrcPair<X86JITCore::RA_32>(uint32_t Node) const;
|
||||
|
||||
Xbyak::Xmm X86JITCore::GetDst(uint32_t Node) {
|
||||
Xbyak::Xmm X86JITCore::GetDst(uint32_t Node) const {
|
||||
auto PhyReg = GetPhys(Node);
|
||||
return RAXMM_x[PhyReg.Reg];
|
||||
}
|
||||
|
||||
bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
|
||||
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
|
||||
|
||||
if (OpHeader->Op == IR::IROps::OP_INLINECONSTANT) {
|
||||
@@ -524,7 +524,7 @@ bool X86JITCore::IsInlineConstant(const IR::OrderedNodeWrapper& WNode, uint64_t*
|
||||
}
|
||||
}
|
||||
|
||||
bool X86JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) {
|
||||
bool X86JITCore::IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const {
|
||||
auto OpHeader = IR->GetOp<IR::IROp_Header>(WNode);
|
||||
|
||||
if (OpHeader->Op == IR::IROps::OP_INLINEENTRYPOINTOFFSET) {
|
||||
@@ -764,7 +764,7 @@ uint64_t X86JITCore::ExitFunctionLink(X86JITCore *core, FEXCore::Core::CpuStateF
|
||||
return HostCode;
|
||||
}
|
||||
|
||||
FEXCore::CPU::CPUBackend *CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return new X86JITCore(ctx, Thread, AllocateNewCodeBuffer(CompileThread ? X86JITCore::MAX_CODE_SIZE : X86JITCore::INITIAL_CODE_SIZE), CompileThread);
|
||||
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread, bool CompileThread) {
|
||||
return std::make_unique<X86JITCore>(ctx, Thread, AllocateNewCodeBuffer(CompileThread ? X86JITCore::MAX_CODE_SIZE : X86JITCore::INITIAL_CODE_SIZE), CompileThread);
|
||||
}
|
||||
}
|
||||
+19
-16
@@ -112,26 +112,26 @@ private:
|
||||
constexpr static uint8_t RA_64 = 3;
|
||||
constexpr static uint8_t RA_XMM = 4;
|
||||
|
||||
IR::PhysicalRegister GetPhys(uint32_t Node);
|
||||
IR::PhysicalRegister GetPhys(uint32_t Node) const;
|
||||
|
||||
bool IsFPR(uint32_t Node);
|
||||
bool IsGPR(uint32_t Node);
|
||||
bool IsFPR(uint32_t Node) const;
|
||||
bool IsGPR(uint32_t Node) const;
|
||||
|
||||
template<uint8_t RAType>
|
||||
Xbyak::Reg GetSrc(uint32_t Node);
|
||||
Xbyak::Reg GetSrc(uint32_t Node) const;
|
||||
template<uint8_t RAType>
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> GetSrcPair(uint32_t Node);
|
||||
std::pair<Xbyak::Reg, Xbyak::Reg> GetSrcPair(uint32_t Node) const;
|
||||
|
||||
template<uint8_t RAType>
|
||||
Xbyak::Reg GetDst(uint32_t Node);
|
||||
Xbyak::Reg GetDst(uint32_t Node) const;
|
||||
|
||||
Xbyak::Xmm GetSrc(uint32_t Node);
|
||||
Xbyak::Xmm GetDst(uint32_t Node);
|
||||
Xbyak::Xmm GetSrc(uint32_t Node) const;
|
||||
Xbyak::Xmm GetDst(uint32_t Node) const;
|
||||
|
||||
Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale);
|
||||
Xbyak::RegExp GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) const;
|
||||
|
||||
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr);
|
||||
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value);
|
||||
bool IsInlineConstant(const IR::OrderedNodeWrapper& Node, uint64_t* Value = nullptr) const;
|
||||
bool IsInlineEntrypointOffset(const IR::OrderedNodeWrapper& WNode, uint64_t* Value) const;
|
||||
|
||||
IR::RegisterAllocationPass *RAPass;
|
||||
FEXCore::IR::RegisterAllocationData *RAData;
|
||||
@@ -241,9 +241,7 @@ private:
|
||||
DEF_OP(Sbfe);
|
||||
DEF_OP(Select);
|
||||
DEF_OP(VExtractToGPR);
|
||||
DEF_OP(Float_ToGPR_ZU);
|
||||
DEF_OP(Float_ToGPR_ZS);
|
||||
DEF_OP(Float_ToGPR_U);
|
||||
DEF_OP(Float_ToGPR_S);
|
||||
DEF_OP(FCmp);
|
||||
DEF_OP(F80Cmp);
|
||||
@@ -281,16 +279,14 @@ private:
|
||||
///< Conversion ops
|
||||
DEF_OP(VInsGPR);
|
||||
DEF_OP(VCastFromGPR);
|
||||
DEF_OP(Float_FromGPR_U);
|
||||
DEF_OP(Float_FromGPR_S);
|
||||
DEF_OP(Float_FToF);
|
||||
DEF_OP(Vector_UToF);
|
||||
DEF_OP(Vector_SToF);
|
||||
DEF_OP(Vector_FToZU);
|
||||
DEF_OP(Vector_FToZS);
|
||||
DEF_OP(Vector_FToU);
|
||||
DEF_OP(Vector_FToS);
|
||||
DEF_OP(Vector_FToF);
|
||||
DEF_OP(Vector_FToI);
|
||||
|
||||
///< Flag ops
|
||||
DEF_OP(GetHostFlag);
|
||||
@@ -333,6 +329,7 @@ private:
|
||||
DEF_OP(SplatVector);
|
||||
DEF_OP(VMov);
|
||||
DEF_OP(VAnd);
|
||||
DEF_OP(VBic);
|
||||
DEF_OP(VOr);
|
||||
DEF_OP(VXor);
|
||||
DEF_OP(VAdd);
|
||||
@@ -343,8 +340,10 @@ private:
|
||||
DEF_OP(VSQSub);
|
||||
DEF_OP(VAddP);
|
||||
DEF_OP(VAddV);
|
||||
DEF_OP(VUMinV);
|
||||
DEF_OP(VURAvg);
|
||||
DEF_OP(VAbs);
|
||||
DEF_OP(VPopcount);
|
||||
DEF_OP(VFAdd);
|
||||
DEF_OP(VFAddP);
|
||||
DEF_OP(VFSub);
|
||||
@@ -364,6 +363,8 @@ private:
|
||||
DEF_OP(VSMax);
|
||||
DEF_OP(VZip);
|
||||
DEF_OP(VZip2);
|
||||
DEF_OP(VUnZip);
|
||||
DEF_OP(VUnZip2);
|
||||
DEF_OP(VBSL);
|
||||
DEF_OP(VCMPEQ);
|
||||
DEF_OP(VCMPEQZ);
|
||||
@@ -386,6 +387,7 @@ private:
|
||||
DEF_OP(VInsElement);
|
||||
DEF_OP(VInsScalarElement);
|
||||
DEF_OP(VExtractElement);
|
||||
DEF_OP(VDupElement);
|
||||
DEF_OP(VExtr);
|
||||
DEF_OP(VSLI);
|
||||
DEF_OP(VSRI);
|
||||
@@ -408,6 +410,7 @@ private:
|
||||
DEF_OP(VSMull);
|
||||
DEF_OP(VUMull2);
|
||||
DEF_OP(VSMull2);
|
||||
DEF_OP(VUABDL);
|
||||
DEF_OP(VTBL1);
|
||||
|
||||
///< Encryption ops
|
||||
|
||||
@@ -425,7 +425,7 @@ DEF_OP(StoreFlag) {
|
||||
mov(byte [STATE + (offsetof(FEXCore::Core::CPUState, flags[0]) + Op->Flag)], al);
|
||||
}
|
||||
|
||||
Xbyak::RegExp X86JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) {
|
||||
Xbyak::RegExp X86JITCore::GenerateModRM(Xbyak::Reg Base, IR::OrderedNodeWrapper Offset, IR::MemOffsetType OffsetType, uint8_t OffsetScale) const {
|
||||
if (Offset.IsInvalid()) {
|
||||
return Base;
|
||||
} else {
|
||||
|
||||
+15
-13
@@ -12,6 +12,10 @@ static void PrintValue(uint64_t Value) {
|
||||
LogMan::Msg::D("Value: 0x%lx", Value);
|
||||
}
|
||||
|
||||
static void PrintVectorValue(uint64_t Value, uint64_t ValueUpper) {
|
||||
LogMan::Msg::D("Value: 0x%016lx'%016lx", ValueUpper, Value);
|
||||
}
|
||||
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(FEXCore::IR::IROp_Header *IROp, uint32_t Node)
|
||||
|
||||
DEF_OP(Fence) {
|
||||
@@ -119,24 +123,22 @@ DEF_OP(SetRoundingMode) {
|
||||
DEF_OP(Print) {
|
||||
auto Op = IROp->C<IR::IROp_Print>();
|
||||
|
||||
for (auto &Reg : RA64)
|
||||
push(Reg);
|
||||
PushRegs();
|
||||
if (IsGPR(Op->Header.Args[0].ID())) {
|
||||
mov (rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
auto NumPush = RA64.size();
|
||||
if (NumPush & 1)
|
||||
sub(rsp, 8); // Align
|
||||
mov(rax, reinterpret_cast<uintptr_t>(PrintValue));
|
||||
}
|
||||
else {
|
||||
pextrq(rdi, GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
pextrq(rsi, GetSrc(Op->Header.Args[0].ID()), 1);
|
||||
|
||||
mov (rdi, GetSrc<RA_64>(Op->Header.Args[0].ID()));
|
||||
|
||||
mov(rax, reinterpret_cast<uintptr_t>(PrintValue));
|
||||
mov(rax, reinterpret_cast<uintptr_t>(PrintVectorValue));
|
||||
}
|
||||
|
||||
call(rax);
|
||||
|
||||
if (NumPush & 1)
|
||||
add(rsp, 8); // Align
|
||||
|
||||
for (uint32_t i = RA64.size(); i > 0; --i)
|
||||
pop(RA64[i - 1]);
|
||||
PopRegs();
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
|
||||
+265
-3
@@ -139,6 +139,14 @@ DEF_OP(VAnd) {
|
||||
vpand(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
}
|
||||
|
||||
DEF_OP(VBic) {
|
||||
auto Op = IROp->C<IR::IROp_VBic>();
|
||||
// This doesn't map directly to ARM
|
||||
vpcmpeqd(xmm15, xmm15, xmm15);
|
||||
vpxor(xmm15, GetSrc(Op->Header.Args[1].ID()), xmm15);
|
||||
vpand(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), xmm15);
|
||||
}
|
||||
|
||||
DEF_OP(VOr) {
|
||||
auto Op = IROp->C<IR::IROp_VOr>();
|
||||
vpor(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
@@ -355,6 +363,23 @@ DEF_OP(VAddV) {
|
||||
movaps(Dest, xmm15);
|
||||
}
|
||||
|
||||
DEF_OP(VUMinV) {
|
||||
auto Op = IROp->C<IR::IROp_VUMinV>();
|
||||
|
||||
auto Src = GetSrc(Op->Header.Args[0].ID());
|
||||
auto Dest = GetDst(Node);
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 2: {
|
||||
phminposuw(Dest, Src);
|
||||
// Extract the upper bits which are zero, overwriting position
|
||||
pextrw(eax, Dest, 2);
|
||||
pinsrw(Dest, eax, 1);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VURAvg) {
|
||||
auto Op = IROp->C<IR::IROp_VURAvg>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
@@ -393,6 +418,32 @@ DEF_OP(VAbs) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VPopcount) {
|
||||
auto Op = IROp->C<IR::IROp_VPopcount>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
// This only supports 8bit popcount on 8byte to 16byte registers
|
||||
|
||||
auto Src = GetSrc(Op->Header.Args[0].ID());
|
||||
auto Dest = GetDst(Node);
|
||||
vpxor(xmm15, xmm15, xmm15);
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
|
||||
// This is disgustingly bad on x86-64 but we only need it for compatibility
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
for (size_t i = 0; i < Elements; ++i) {
|
||||
pextrb(eax, Src, i);
|
||||
popcnt(eax, eax);
|
||||
pinsrb(xmm15, eax, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
|
||||
movaps(Dest, xmm15);
|
||||
}
|
||||
|
||||
DEF_OP(VFAdd) {
|
||||
auto Op = IROp->C<IR::IROp_VFAdd>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -933,6 +984,112 @@ DEF_OP(VZip2) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUnZip) {
|
||||
auto Op = IROp->C<IR::IROp_VUnZip>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
if (OpSize == 8) {
|
||||
LOGMAN_MSG_A("Unsupported registersize on VunZip");
|
||||
}
|
||||
else {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
// Shuffle low bits
|
||||
mov(rax, 0x0E'0C'0A'08'06'04'02'00); // Lower
|
||||
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
|
||||
vmovq(xmm15, rax);
|
||||
pinsrq(xmm15, rcx, 1);
|
||||
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
|
||||
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
|
||||
// movlhps back to combine
|
||||
vmovlhps(GetDst(Node), xmm14, xmm13);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// Shuffle low bits
|
||||
mov(rax, 0x0D'0C'09'08'05'04'01'00); // Lower
|
||||
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
|
||||
vmovq(xmm15, rax);
|
||||
pinsrq(xmm15, rcx, 1);
|
||||
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
|
||||
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
|
||||
// movlhps back to combine
|
||||
vmovlhps(GetDst(Node), xmm14, xmm13);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
vshufps(GetDst(Node),
|
||||
GetSrc(Op->Header.Args[0].ID()),
|
||||
GetSrc(Op->Header.Args[1].ID()),
|
||||
0b10'00'10'00);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
vshufpd(GetDst(Node),
|
||||
GetSrc(Op->Header.Args[0].ID()),
|
||||
GetSrc(Op->Header.Args[1].ID()),
|
||||
0b0'0);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUnZip2) {
|
||||
auto Op = IROp->C<IR::IROp_VUnZip2>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
|
||||
|
||||
if (OpSize == 8) {
|
||||
LOGMAN_MSG_A("Unsupported registersize on VunZip");
|
||||
}
|
||||
else {
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
// Shuffle low bits
|
||||
mov(rax, 0x0F'0D'0B'09'07'05'03'01); // Lower
|
||||
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
|
||||
vmovq(xmm15, rax);
|
||||
pinsrq(xmm15, rcx, 1);
|
||||
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
|
||||
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
|
||||
// movlhps back to combine
|
||||
vmovlhps(GetDst(Node), xmm14, xmm13);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// Shuffle low bits
|
||||
mov(rax, 0x0F'0E'0B'0A'07'06'03'02); // Lower
|
||||
mov(rcx, 0x80'80'80'80'80'80'80'80); // Upper
|
||||
vmovq(xmm15, rax);
|
||||
pinsrq(xmm15, rcx, 1);
|
||||
vpshufb(xmm14, GetSrc(Op->Header.Args[0].ID()), xmm15);
|
||||
vpshufb(xmm13, GetSrc(Op->Header.Args[1].ID()), xmm15);
|
||||
// movlhps back to combine
|
||||
vmovlhps(GetDst(Node), xmm14, xmm13);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
vshufps(GetDst(Node),
|
||||
GetSrc(Op->Header.Args[0].ID()),
|
||||
GetSrc(Op->Header.Args[1].ID()),
|
||||
0b11'01'11'01);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
vshufpd(GetDst(Node),
|
||||
GetSrc(Op->Header.Args[0].ID()),
|
||||
GetSrc(Op->Header.Args[1].ID()),
|
||||
0b1'1);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DEF_OP(VBSL) {
|
||||
auto Op = IROp->C<IR::IROp_VBSL>();
|
||||
vpand(xmm0, GetSrc(Op->Header.Args[0].ID()), GetSrc(Op->Header.Args[1].ID()));
|
||||
@@ -1407,6 +1564,61 @@ DEF_OP(VExtractElement) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VDupElement) {
|
||||
auto Op = IROp->C<IR::IROp_VDupElement>();
|
||||
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
// First extract the index
|
||||
pextrb(eax, GetSrc(Op->Header.Args[0].ID()), Op->Index);
|
||||
// Insert it in to the first element of the destination
|
||||
pinsrb(GetDst(Node), eax, 0);
|
||||
pinsrb(GetDst(Node), eax, 1);
|
||||
// Shuffle low elements
|
||||
vpshuflw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
// Insert element in to the first upper 64bit element
|
||||
pinsrb(GetDst(Node), eax, 8);
|
||||
pinsrb(GetDst(Node), eax, 9);
|
||||
// Shuffle high elements
|
||||
vpshufhw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
// First extract the index
|
||||
pextrw(eax, GetSrc(Op->Header.Args[0].ID()), Op->Index);
|
||||
// Insert it in to the first element of the destination
|
||||
pinsrw(GetDst(Node), eax, 0);
|
||||
// Shuffle low elements
|
||||
vpshuflw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
// Insert element in to the first upper 64bit element
|
||||
pinsrw(GetDst(Node), eax, 4);
|
||||
// Shuffle high elements
|
||||
vpshufhw(GetDst(Node), GetSrc(Op->Header.Args[0].ID()), 0);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
vpshufd(GetDst(Node),
|
||||
GetSrc(Op->Header.Args[0].ID()),
|
||||
(Op->Index << 0) |
|
||||
(Op->Index << 2) |
|
||||
(Op->Index << 4) |
|
||||
(Op->Index << 6));
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
vshufpd(GetDst(Node),
|
||||
GetSrc(Op->Header.Args[0].ID()),
|
||||
GetSrc(Op->Header.Args[0].ID()),
|
||||
(Op->Index << 0) |
|
||||
(Op->Index << 1));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
DEF_OP(VExtr) {
|
||||
auto Op = IROp->C<IR::IROp_VExtr>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -1460,14 +1672,36 @@ DEF_OP(VUShrI) {
|
||||
|
||||
DEF_OP(VSShrI) {
|
||||
auto Op = IROp->C<IR::IROp_VSShrI>();
|
||||
movapd(GetDst(Node), GetSrc(Op->Header.Args[0].ID()));
|
||||
auto Dest = GetDst(Node);
|
||||
movapd(Dest, GetSrc(Op->Header.Args[0].ID()));
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 1: {
|
||||
// This isn't a native instruction on x86
|
||||
uint8_t OpSize = IROp->Size;
|
||||
uint8_t Elements = OpSize / Op->Header.ElementSize;
|
||||
for (int i = 0; i < Elements; ++i) {
|
||||
pextrb(eax, Dest, i);
|
||||
movsx(eax, al);
|
||||
sar(al, Op->BitShift);
|
||||
pinsrb(Dest, eax, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
psraw(GetDst(Node), Op->BitShift);
|
||||
psraw(Dest, Op->BitShift);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
psrad(GetDst(Node), Op->BitShift);
|
||||
psrad(Dest, Op->BitShift);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
// This isn't a native instruction on x86
|
||||
for (int i = 0; i < 2; ++i) {
|
||||
pextrq(rax, Dest, i);
|
||||
sar(rax, Op->BitShift);
|
||||
pinsrq(Dest, rax, i);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
@@ -1872,6 +2106,27 @@ DEF_OP(VSMull2) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUABDL) {
|
||||
auto Op = IROp->C<IR::IROp_VUABDL>();
|
||||
switch (Op->Header.ElementSize) {
|
||||
case 2: {
|
||||
pmovzxbw(xmm14, GetSrc(Op->Header.Args[0].ID()));
|
||||
pmovzxbw(xmm15, GetSrc(Op->Header.Args[1].ID()));
|
||||
vpsubw(GetDst(Node), xmm14, xmm15);
|
||||
vpabsw(GetDst(Node), GetDst(Node));
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
pmovzxwd(xmm14, GetSrc(Op->Header.Args[0].ID()));
|
||||
pmovzxwd(xmm15, GetSrc(Op->Header.Args[1].ID()));
|
||||
vpsubd(GetDst(Node), xmm14, xmm15);
|
||||
vpabsd(GetDst(Node), GetDst(Node));
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A("Unknown Element Size: %d", Op->Header.ElementSize); break;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VTBL1) {
|
||||
auto Op = IROp->C<IR::IROp_VTBL1>();
|
||||
uint8_t OpSize = IROp->Size;
|
||||
@@ -1901,6 +2156,7 @@ void X86JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(SPLATVECTOR4, SplatVector);
|
||||
REGISTER_OP(VMOV, VMov);
|
||||
REGISTER_OP(VAND, VAnd);
|
||||
REGISTER_OP(VBIC, VBic);
|
||||
REGISTER_OP(VOR, VOr);
|
||||
REGISTER_OP(VXOR, VXor);
|
||||
REGISTER_OP(VADD, VAdd);
|
||||
@@ -1911,8 +2167,10 @@ void X86JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VSQSUB, VSQSub);
|
||||
REGISTER_OP(VADDP, VAddP);
|
||||
REGISTER_OP(VADDV, VAddV);
|
||||
REGISTER_OP(VUMINV, VUMinV);
|
||||
REGISTER_OP(VURAVG, VURAvg);
|
||||
REGISTER_OP(VABS, VAbs);
|
||||
REGISTER_OP(VPOPCOUNT, VPopcount);
|
||||
REGISTER_OP(VFADD, VFAdd);
|
||||
REGISTER_OP(VFADDP, VFAddP);
|
||||
REGISTER_OP(VFSUB, VFSub);
|
||||
@@ -1932,6 +2190,8 @@ void X86JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VSMAX, VSMax);
|
||||
REGISTER_OP(VZIP, VZip);
|
||||
REGISTER_OP(VZIP2, VZip2);
|
||||
REGISTER_OP(VUNZIP, VUnZip);
|
||||
REGISTER_OP(VUNZIP2, VUnZip2);
|
||||
REGISTER_OP(VBSL, VBSL);
|
||||
REGISTER_OP(VCMPEQ, VCMPEQ);
|
||||
REGISTER_OP(VCMPEQZ, VCMPEQZ);
|
||||
@@ -1954,6 +2214,7 @@ void X86JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VINSELEMENT, VInsElement);
|
||||
REGISTER_OP(VINSSCALARELEMENT, VInsScalarElement);
|
||||
REGISTER_OP(VEXTRACTELEMENT, VExtractElement);
|
||||
REGISTER_OP(VDUPELEMENT, VDupElement);
|
||||
REGISTER_OP(VEXTR, VExtr);
|
||||
REGISTER_OP(VSLI, VSLI);
|
||||
REGISTER_OP(VSRI, VSRI);
|
||||
@@ -1977,6 +2238,7 @@ void X86JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VSMULL, VSMull);
|
||||
REGISTER_OP(VUMULL2, VUMull2);
|
||||
REGISTER_OP(VSMULL2, VSMull2);
|
||||
REGISTER_OP(VUABDL, VUABDL);
|
||||
REGISTER_OP(VTBL1, VTBL1);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
+2
-2
@@ -98,8 +98,8 @@ public:
|
||||
|
||||
void HintUsedRange(uint64_t Address, uint64_t Size);
|
||||
|
||||
uintptr_t GetL1Pointer() { return L1Pointer; }
|
||||
uintptr_t GetPagePointer() { return PagePointer; }
|
||||
uintptr_t GetL1Pointer() const { return L1Pointer; }
|
||||
uintptr_t GetPagePointer() const { return PagePointer; }
|
||||
uintptr_t GetVirtualMemorySize() const { return VirtualMemSize; }
|
||||
|
||||
constexpr static size_t L1_ENTRIES = 1 * 1024 * 1024; // Must be a power of 2
|
||||
|
||||
+629
-351
File diff suppressed because it is too large.
Load diff
+30
-27
@@ -105,7 +105,7 @@ public:
|
||||
|
||||
void ResetWorkingList();
|
||||
void ResetDecodeFailure() { DecodeFailure = false; }
|
||||
bool HadDecodeFailure() { return DecodeFailure; }
|
||||
bool HadDecodeFailure() const { return DecodeFailure; }
|
||||
|
||||
void BeginFunction(uint64_t RIP, std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
|
||||
void Finalize();
|
||||
@@ -260,12 +260,6 @@ public:
|
||||
template<size_t ElementSize>
|
||||
void PSUBQOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void PMINUOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void PMAXUOp(OpcodeArgs);
|
||||
void PMINSWOp(OpcodeArgs);
|
||||
void PMAXSWOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void MOVMSKOp(OpcodeArgs);
|
||||
void MOVMSKOpOne(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
@@ -275,10 +269,6 @@ public:
|
||||
void PSHUFBOp(OpcodeArgs);
|
||||
template<size_t ElementSize, bool HalfSize, bool Low>
|
||||
void PSHUFDOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void PCMPEQOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void PCMPGTOp(OpcodeArgs);
|
||||
void MOVDOp(OpcodeArgs);
|
||||
template<size_t ElementSize, bool Scalar, uint32_t SrcIndex>
|
||||
void PSRLDOp(OpcodeArgs);
|
||||
@@ -297,21 +287,21 @@ public:
|
||||
template<size_t ElementSize>
|
||||
void PAVGOp(OpcodeArgs);
|
||||
void MOVDDUPOp(OpcodeArgs);
|
||||
template<size_t DstElementSize, bool Signed>
|
||||
template<size_t DstElementSize>
|
||||
void CVTGPR_To_FPR(OpcodeArgs);
|
||||
template<size_t SrcElementSize, bool Signed, bool HostRoundingMode>
|
||||
template<size_t SrcElementSize, bool HostRoundingMode>
|
||||
void CVTFPR_To_GPR(OpcodeArgs);
|
||||
template<size_t SrcElementSize, bool Signed, bool Widen>
|
||||
template<size_t SrcElementSize, bool Widen>
|
||||
void Vector_CVT_Int_To_Float(OpcodeArgs);
|
||||
template<size_t DstElementSize, size_t SrcElementSize>
|
||||
void Scalar_CVT_Float_To_Float(OpcodeArgs);
|
||||
template<size_t DstElementSize, size_t SrcElementSize>
|
||||
void Vector_CVT_Float_To_Float(OpcodeArgs);
|
||||
template<size_t SrcElementSize, bool Signed, bool Narrow, bool HostRoundingMode>
|
||||
template<size_t SrcElementSize, bool Narrow, bool HostRoundingMode>
|
||||
void Vector_CVT_Float_To_Int(OpcodeArgs);
|
||||
template<size_t SrcElementSize, bool Signed, bool Widen>
|
||||
void MMX_To_XMM_Vector_CVT_Int_To_Float(OpcodeArgs);
|
||||
template<size_t SrcElementSize, bool Signed, bool Narrow, bool HostRoundingMode>
|
||||
template<size_t SrcElementSize, bool Narrow, bool HostRoundingMode>
|
||||
void XMM_To_MMX_Vector_CVT_Float_To_Int(OpcodeArgs);
|
||||
void MASKMOVOp(OpcodeArgs);
|
||||
void MOVBetweenGPR_FPR(OpcodeArgs);
|
||||
@@ -325,17 +315,13 @@ public:
|
||||
void ANDNOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void PINSROp(OpcodeArgs);
|
||||
void InsertPSOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void PExtrOp(OpcodeArgs);
|
||||
template<size_t ElementSize, bool Signed>
|
||||
void PMULOp(OpcodeArgs);
|
||||
|
||||
template<size_t ElementSize>
|
||||
void PSIGN(OpcodeArgs);
|
||||
|
||||
template<size_t ElementSize>
|
||||
void PABS(OpcodeArgs);
|
||||
|
||||
// X87 Ops
|
||||
template<size_t width>
|
||||
void FLD(OpcodeArgs);
|
||||
@@ -470,6 +456,23 @@ public:
|
||||
void AESDecLastOp(OpcodeArgs);
|
||||
void AESKeyGenAssist(OpcodeArgs);
|
||||
|
||||
template<size_t ElementSize, size_t DstElementSize, bool Signed>
|
||||
void ExtendVectorElements(OpcodeArgs);
|
||||
template<size_t ElementSize, bool Scalar>
|
||||
void VectorRound(OpcodeArgs);
|
||||
|
||||
template<size_t ElementSize>
|
||||
void VectorBlend(OpcodeArgs);
|
||||
|
||||
template<size_t ElementSize>
|
||||
void VectorVariableBlend(OpcodeArgs);
|
||||
void PTestOp(OpcodeArgs);
|
||||
void PHMINPOSUWOp(OpcodeArgs);
|
||||
template<size_t ElementSize>
|
||||
void DPPOp(OpcodeArgs);
|
||||
|
||||
void MPSADBWOp(OpcodeArgs);
|
||||
|
||||
void UnimplementedOp(OpcodeArgs);
|
||||
|
||||
#undef OpcodeArgs
|
||||
@@ -494,8 +497,8 @@ private:
|
||||
void StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, FEXCore::X86Tables::DecodedOperand const& Operand, OrderedNode *const Src, int8_t Align);
|
||||
void StoreResult(FEXCore::IR::RegisterClassType Class, FEXCore::X86Tables::DecodedOp Op, OrderedNode *const Src, int8_t Align);
|
||||
|
||||
uint8_t GetDstSize(FEXCore::X86Tables::DecodedOp Op);
|
||||
uint8_t GetSrcSize(FEXCore::X86Tables::DecodedOp Op);
|
||||
uint8_t GetDstSize(FEXCore::X86Tables::DecodedOp Op) const;
|
||||
uint8_t GetSrcSize(FEXCore::X86Tables::DecodedOp Op) const;
|
||||
|
||||
template<unsigned BitOffset>
|
||||
void SetRFLAG(OrderedNode *Value);
|
||||
@@ -525,12 +528,12 @@ private:
|
||||
OrderedNode * GetX87Top();
|
||||
void SetX87Top(OrderedNode *Value);
|
||||
|
||||
bool DestIsLockedMem(FEXCore::X86Tables::DecodedOp Op) {
|
||||
return Op->Dest.TypeNone.Type !=FEXCore::X86Tables::DecodedOperand::TYPE_GPR && (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK);
|
||||
bool DestIsLockedMem(FEXCore::X86Tables::DecodedOp Op) const {
|
||||
return DestIsMem(Op) && (Op->Flags & FEXCore::X86Tables::DecodeFlags::FLAG_LOCK) != 0;
|
||||
}
|
||||
|
||||
bool DestIsMem(FEXCore::X86Tables::DecodedOp Op) {
|
||||
return Op->Dest.TypeNone.Type !=FEXCore::X86Tables::DecodedOperand::TYPE_GPR;
|
||||
bool DestIsMem(FEXCore::X86Tables::DecodedOp Op) const {
|
||||
return !Op->Dest.IsGPR();
|
||||
}
|
||||
|
||||
void CreateJumpBlocks(std::vector<FEXCore::Frontend::Decoder::DecodedBlocks> const *Blocks);
|
||||
|
||||
@@ -41,10 +41,10 @@ void InitializeH0F38Tables() {
|
||||
{OPD(PF_38_NONE, 0x0B), 1, X86InstInfo{"PMULHRSW", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x0B), 1, X86InstInfo{"PMULHRSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(PF_38_66, 0x10), 1, X86InstInfo{"PBLENDVB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x14), 1, X86InstInfo{"BLENDVPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x15), 1, X86InstInfo{"BLENDVPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x17), 1, X86InstInfo{"PTEST", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x10), 1, X86InstInfo{"PBLENDVB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x14), 1, X86InstInfo{"BLENDVPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x15), 1, X86InstInfo{"BLENDVPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x17), 1, X86InstInfo{"PTEST", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_NONE, 0x1C), 1, X86InstInfo{"PABSB", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x1C), 1, X86InstInfo{"PABSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_NONE, 0x1D), 1, X86InstInfo{"PABSW", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
|
||||
@@ -52,34 +52,34 @@ void InitializeH0F38Tables() {
|
||||
{OPD(PF_38_NONE, 0x1E), 1, X86InstInfo{"PABSD", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x1E), 1, X86InstInfo{"PABSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(PF_38_66, 0x20), 1, X86InstInfo{"PMOVSXBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x21), 1, X86InstInfo{"PMOVSXBD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x22), 1, X86InstInfo{"PMOVSXBQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x23), 1, X86InstInfo{"PMOVSXWD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x24), 1, X86InstInfo{"PMOVSXWQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x25), 1, X86InstInfo{"PMOVSXDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x28), 1, X86InstInfo{"PMULDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x29), 1, X86InstInfo{"PCMPEQQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x20), 1, X86InstInfo{"PMOVSXBW", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x21), 1, X86InstInfo{"PMOVSXBD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x22), 1, X86InstInfo{"PMOVSXBQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_16BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x23), 1, X86InstInfo{"PMOVSXWD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x24), 1, X86InstInfo{"PMOVSXWQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x25), 1, X86InstInfo{"PMOVSXDQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x28), 1, X86InstInfo{"PMULDQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x29), 1, X86InstInfo{"PCMPEQQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x2A), 1, X86InstInfo{"MOVNTDQA", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x2B), 1, X86InstInfo{"PACKUSDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x2B), 1, X86InstInfo{"PACKUSDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(PF_38_66, 0x30), 1, X86InstInfo{"PMOVZXBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x31), 1, X86InstInfo{"PMOVZXBD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x32), 1, X86InstInfo{"PMOVZXBQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x33), 1, X86InstInfo{"PMOVZXWD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x34), 1, X86InstInfo{"PMOVZXWQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x35), 1, X86InstInfo{"PMOVZXDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x38), 1, X86InstInfo{"PMINSB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x39), 1, X86InstInfo{"PMINSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3A), 1, X86InstInfo{"PMINUW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3B), 1, X86InstInfo{"PMINUD", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3C), 1, X86InstInfo{"PMAXSB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3D), 1, X86InstInfo{"PMAXSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3E), 1, X86InstInfo{"PMAXUW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3F), 1, X86InstInfo{"PMAXUD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x30), 1, X86InstInfo{"PMOVZXBW", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x31), 1, X86InstInfo{"PMOVZXBD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x32), 1, X86InstInfo{"PMOVZXBQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_16BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x33), 1, X86InstInfo{"PMOVZXWD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x34), 1, X86InstInfo{"PMOVZXWQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x35), 1, X86InstInfo{"PMOVZXDQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x38), 1, X86InstInfo{"PMINSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x39), 1, X86InstInfo{"PMINSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3A), 1, X86InstInfo{"PMINUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3B), 1, X86InstInfo{"PMINUD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3C), 1, X86InstInfo{"PMAXSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3D), 1, X86InstInfo{"PMAXSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3E), 1, X86InstInfo{"PMAXUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x3F), 1, X86InstInfo{"PMAXUD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(PF_38_66, 0x40), 1, X86InstInfo{"PMULLD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x41), 1, X86InstInfo{"PHMINPOSUW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x40), 1, X86InstInfo{"PMULLD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0x41), 1, X86InstInfo{"PHMINPOSUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(PF_38_66, 0xDB), 1, X86InstInfo{"AESIMC", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(PF_38_66, 0xDC), 1, X86InstInfo{"AESENC", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
@@ -16,26 +16,26 @@ void InitializeH0F3ATables(Context::OperatingMode Mode) {
|
||||
|
||||
const U16U8InfoStruct H0F3ATable[] = {
|
||||
{OPD(0, PF_3A_NONE, 0x0F), 1, X86InstInfo{"PALIGNR", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_MMX, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x08), 1, X86InstInfo{"ROUNDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x09), 1, X86InstInfo{"ROUNDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0A), 1, X86InstInfo{"ROUNDSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0B), 1, X86InstInfo{"ROUNDSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0C), 1, X86InstInfo{"BLENDPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0D), 1, X86InstInfo{"BLENDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0E), 1, X86InstInfo{"PBLENDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x08), 1, X86InstInfo{"ROUNDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x09), 1, X86InstInfo{"ROUNDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0A), 1, X86InstInfo{"ROUNDSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0B), 1, X86InstInfo{"ROUNDSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0C), 1, X86InstInfo{"BLENDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0D), 1, X86InstInfo{"BLENDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0E), 1, X86InstInfo{"PBLENDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x0F), 1, X86InstInfo{"PALIGNR", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(0, PF_3A_66, 0x14), 1, X86InstInfo{"PEXTRB", TYPE_INST, GenFlagsSizes(SIZE_8BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x15), 1, X86InstInfo{"PEXTRW", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x16), 1, X86InstInfo{"PEXTRD", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x17), 1, X86InstInfo{"EXTRACTPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x17), 1, X86InstInfo{"EXTRACTPS", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(0, PF_3A_66, 0x20), 1, X86InstInfo{"PINSRB", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_8BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_SRC_GPR, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x21), 1, X86InstInfo{"INSERTPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x20), 1, X86InstInfo{"PINSRB", TYPE_INST, GenFlagsDstSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_SRC_GPR, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x21), 1, X86InstInfo{"INSERTPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x22), 1, X86InstInfo{"PINSRD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS | FLAGS_SF_SRC_GPR, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x40), 1, X86InstInfo{"DPPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x41), 1, X86InstInfo{"DPPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x42), 1, X86InstInfo{"MPSADBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x40), 1, X86InstInfo{"DPPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x41), 1, X86InstInfo{"DPPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x42), 1, X86InstInfo{"MPSADBW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(0, PF_3A_66, 0x44), 1, X86InstInfo{"PCLMULQDQ", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{OPD(0, PF_3A_66, 0x60), 1, X86InstInfo{"PCMPESTRM", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
+168
-99
@@ -60,6 +60,12 @@
|
||||
"constexpr static uint8_t ROUND_MODE_TOWARDS_ZERO = 3",
|
||||
"constexpr static uint8_t ROUND_MODE_FLUSH_TO_ZERO = 1 << 2",
|
||||
|
||||
"static constexpr FEXCore::IR::RoundType Round_Nearest {ROUND_MODE_NEAREST}",
|
||||
"static constexpr FEXCore::IR::RoundType Round_Negative_Infinity {ROUND_MODE_NEGATIVE_INFINITY}",
|
||||
"static constexpr FEXCore::IR::RoundType Round_Positive_Infinity {ROUND_MODE_POSITIVE_INFINITY}",
|
||||
"static constexpr FEXCore::IR::RoundType Round_Towards_Zero {ROUND_MODE_TOWARDS_ZERO} /* Truncate */",
|
||||
"static constexpr FEXCore::IR::RoundType Round_Host {ROUND_MODE_TOWARDS_ZERO + 1}",
|
||||
|
||||
"constexpr static FEXCore::IR::MemOffsetType MEM_OFFSET_SXTX {0};",
|
||||
"constexpr static FEXCore::IR::MemOffsetType MEM_OFFSET_UXTW {1};",
|
||||
"constexpr static FEXCore::IR::MemOffsetType MEM_OFFSET_SXTW {2};"
|
||||
@@ -1477,24 +1483,6 @@
|
||||
]
|
||||
},
|
||||
|
||||
"Float_ToGPR_U": {
|
||||
"Desc": ["Moves the scalar element to a GPR with conversion",
|
||||
"Converts the 32bit or 64bit float to an unsigned integer",
|
||||
"Rounding mode determined by host flag's rounding mode"
|
||||
],
|
||||
"OpClass": "ALU",
|
||||
"HasDest": true,
|
||||
"DestClass": "GPR",
|
||||
"DestSize": "ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Scalar"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"Float_ToGPR_S": {
|
||||
"Desc": ["Moves the scalar element to a GPR with conversion",
|
||||
"Converts the 32bit or 64bit float to an signed integer",
|
||||
@@ -1503,30 +1491,16 @@
|
||||
"OpClass": "ALU",
|
||||
"HasDest": true,
|
||||
"DestClass": "GPR",
|
||||
"DestSize": "ElementSize",
|
||||
"DestSize": "DestElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Scalar"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"Float_ToGPR_ZU": {
|
||||
"Desc": ["Moves the scalar element to a GPR with conversion",
|
||||
"Converts the 32bit or 64bit float to an unsigned integer rounding towards zero (Truncating)"
|
||||
],
|
||||
"OpClass": "ALU",
|
||||
"HasDest": true,
|
||||
"DestClass": "GPR",
|
||||
"DestSize": "ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Scalar"
|
||||
"HelperArgs": [
|
||||
"uint8_t", "DestElementSize"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "ElementSize"
|
||||
"uint8_t", "SrcElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1537,13 +1511,16 @@
|
||||
"OpClass": "ALU",
|
||||
"HasDest": true,
|
||||
"DestClass": "GPR",
|
||||
"DestSize": "ElementSize",
|
||||
"DestSize": "DestElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Scalar"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "DestElementSize"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "ElementSize"
|
||||
"uint8_t", "SrcElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
@@ -1664,6 +1641,23 @@
|
||||
]
|
||||
},
|
||||
|
||||
"VBic": {
|
||||
"OpClass": "Vector",
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Vector1",
|
||||
"Vector2"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"VOr": {
|
||||
"OpClass": "Vector",
|
||||
"HasDest": true,
|
||||
@@ -1809,8 +1803,8 @@
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Vector1",
|
||||
"Vector2"
|
||||
"VectorLower",
|
||||
"VectorUpper"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
@@ -1837,6 +1831,25 @@
|
||||
]
|
||||
},
|
||||
|
||||
"VUMinV": {
|
||||
"OpClass": "Vector",
|
||||
"Desc": ["Does a horizontal vector unsigned minimum of elements across the source vector",
|
||||
"Result is a zero extended scalar"
|
||||
],
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Vector"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"VURAvg": {
|
||||
"OpClass": "Vector",
|
||||
"Desc": ["Does an unsigned rounded average", "dst_elem = (src1_elem + src2_elem + 1) >> 1"],
|
||||
@@ -1873,6 +1886,24 @@
|
||||
]
|
||||
},
|
||||
|
||||
"VPopcount": {
|
||||
"OpClass": "Vector",
|
||||
"Desc": ["Does a popcount for each element of the register"
|
||||
],
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Vector"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"VFAdd": {
|
||||
"OpClass": "Vector",
|
||||
"HasDest": true,
|
||||
@@ -1899,8 +1930,8 @@
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Vector1",
|
||||
"Vector2"
|
||||
"VectorLow",
|
||||
"VectorHigh"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
@@ -2192,6 +2223,40 @@
|
||||
]
|
||||
},
|
||||
|
||||
"VUnZip": {
|
||||
"OpClass": "Vector",
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Lower",
|
||||
"Upper"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"VUnZip2": {
|
||||
"OpClass": "Vector",
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Lower",
|
||||
"Upper"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"VBSL": {
|
||||
"Desc": ["Does a vector bitwise select.",
|
||||
"If the bit in the field is 1 then the corresponding bit is pulled from VectorTrue",
|
||||
@@ -2583,6 +2648,26 @@
|
||||
]
|
||||
},
|
||||
|
||||
"VDupElement": {
|
||||
"Desc": ["Duplicates one element from the source register across the whole register"],
|
||||
"OpClass": "Vector",
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Vector"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "Index"
|
||||
]
|
||||
},
|
||||
|
||||
"VExtr": {
|
||||
"Desc": ["Concats two vector registers together and extracts a full width register from the element index",
|
||||
"Index is an element index. So it is offset by ElementSize argument",
|
||||
@@ -2935,27 +3020,6 @@
|
||||
]
|
||||
},
|
||||
|
||||
"Float_FromGPR_U": {
|
||||
"OpClass": "Conv",
|
||||
"Desc": ["Scalar op: Converts unsigned GPR to Scalar float",
|
||||
"Zeroes the upper bits of the vector register"
|
||||
],
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "DstElementSize",
|
||||
"NumElements": "1",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"GPR"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "DstElementSize"
|
||||
],
|
||||
"Args": [
|
||||
"uint8_t", "SrcElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"Float_FromGPR_S": {
|
||||
"OpClass": "Conv",
|
||||
"Desc": ["Scalar op: Converts signed GPR to Scalar float",
|
||||
@@ -3032,25 +3096,6 @@
|
||||
]
|
||||
},
|
||||
|
||||
"Vector_FToU": {
|
||||
"OpClass": "Conv",
|
||||
"Desc": ["Vector op: Converts float to unsigned integer",
|
||||
"Rounding mode determined by host rounding mode"
|
||||
],
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Vector"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"Vector_FToS": {
|
||||
"OpClass": "Conv",
|
||||
"Desc": ["Vector op: Converts float to signed integer, rounding towards zero",
|
||||
@@ -3070,23 +3115,6 @@
|
||||
]
|
||||
},
|
||||
|
||||
"Vector_FToZU": {
|
||||
"OpClass": "Conv",
|
||||
"Desc": "Vector op: Converts float to unsigned integer, rounding towards zero",
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Vector"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"Vector_FToZS": {
|
||||
"OpClass": "Conv",
|
||||
"Desc": "Vector op: Converts float to signed integer, rounding towards zero",
|
||||
@@ -3124,6 +3152,28 @@
|
||||
]
|
||||
},
|
||||
|
||||
"Vector_FToI": {
|
||||
"OpClass": "Conv",
|
||||
"Desc": ["Vector op: Rounds float to integral",
|
||||
"Rounding mode determined by argument"
|
||||
],
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize",
|
||||
"SSAArgs": "1",
|
||||
"SSANames": [
|
||||
"Vector"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
],
|
||||
"Args":[
|
||||
"FEXCore::IR::RoundType", "Round"
|
||||
]
|
||||
},
|
||||
|
||||
"VUMul": {
|
||||
"OpClass": "Vector",
|
||||
"HasDest": true,
|
||||
@@ -3231,6 +3281,25 @@
|
||||
]
|
||||
},
|
||||
|
||||
"VUABDL": {
|
||||
"OpClass": "Vector",
|
||||
"Desc": ["Unsigned Absolute Difference Long"
|
||||
],
|
||||
"HasDest": true,
|
||||
"DestClass": "FPR",
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / (ElementSize << 1)",
|
||||
"SSAArgs": "2",
|
||||
"SSANames": [
|
||||
"Vector1",
|
||||
"Vector2"
|
||||
],
|
||||
"HelperArgs": [
|
||||
"uint8_t", "RegisterSize",
|
||||
"uint8_t", "ElementSize"
|
||||
]
|
||||
},
|
||||
|
||||
"VTBL1": {
|
||||
"Desc": ["Does a vector table lookup from one register in to the destination",
|
||||
"Lookup is byte sized per byte element.",
|
||||
|
||||
+13
-2
@@ -37,7 +37,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, CondClassType Arg) {
|
||||
std::array<std::string, 22> CondNames = {
|
||||
static constexpr std::array<std::string_view, 22> CondNames = {
|
||||
"EQ",
|
||||
"NEQ",
|
||||
"UGE",
|
||||
@@ -66,7 +66,7 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, MemOffsetType Arg) {
|
||||
std::array<std::string, 3> Names = {
|
||||
static constexpr std::array<std::string_view, 3> Names = {
|
||||
"SXTX",
|
||||
"UXTW",
|
||||
"SXTW",
|
||||
@@ -154,6 +154,17 @@ static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const*
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintArg(std::stringstream *out, [[maybe_unused]] IRListView const* IR, FEXCore::IR::RoundType Arg) {
|
||||
switch (Arg) {
|
||||
case FEXCore::IR::Round_Nearest: *out << "Nearest"; break;
|
||||
case FEXCore::IR::Round_Negative_Infinity: *out << "-Inf"; break;
|
||||
case FEXCore::IR::Round_Positive_Infinity: *out << "+Inf"; break;
|
||||
case FEXCore::IR::Round_Towards_Zero: *out << "Towards Zero"; break;
|
||||
case FEXCore::IR::Round_Host: *out << "Host"; break;
|
||||
default: *out << "<Unknown Round Type>"; break;
|
||||
}
|
||||
}
|
||||
|
||||
void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData) {
|
||||
auto HeaderOp = IR->GetHeader();
|
||||
|
||||
|
||||
+106
-108
@@ -66,7 +66,8 @@ std::string DecodeErrorToString(DecodeFailure Failure) {
|
||||
case DecodeFailure::DECODE_INVALID_CONDFLAG: return "Invalid Conditional name";
|
||||
case DecodeFailure::DECODE_INVALID_MEMOFFSETTYPE: return "Invalid Memory Offset Type";
|
||||
case DecodeFailure::DECODE_INVALID_FENCETYPE: return "Invalid Fence Type";
|
||||
};
|
||||
}
|
||||
return "Unknown Error";
|
||||
}
|
||||
|
||||
std::unordered_map<std::string_view, FEXCore::IR::IROps> NameToOpMap;
|
||||
@@ -74,22 +75,22 @@ std::unordered_map<std::string_view, FEXCore::IR::IROps> NameToOpMap;
|
||||
class IRParser: public FEXCore::IR::IREmitter {
|
||||
public:
|
||||
template<typename Type>
|
||||
std::pair<DecodeFailure, Type> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, Type> DecodeValue(const std::string &Arg) {
|
||||
return {DecodeFailure::DECODE_UNKNOWN_TYPE, {}};
|
||||
}
|
||||
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, uint8_t> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, uint8_t> DecodeValue(const std::string &Arg) {
|
||||
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
|
||||
|
||||
uint8_t Result = strtoul(&Arg.at(1), nullptr, 0);
|
||||
if (errno == ERANGE) return {DecodeFailure::DECODE_INVALIDRANGE, 0};
|
||||
return {DecodeFailure::DECODE_OKAY, Result};
|
||||
}
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, bool> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, bool> DecodeValue(const std::string &Arg) {
|
||||
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
|
||||
|
||||
uint8_t Result = strtoul(&Arg.at(1), nullptr, 0);
|
||||
@@ -98,7 +99,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, uint16_t> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, uint16_t> DecodeValue(const std::string &Arg) {
|
||||
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
|
||||
|
||||
uint16_t Result = strtoul(&Arg.at(1), nullptr, 0);
|
||||
@@ -107,7 +108,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, uint32_t> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, uint32_t> DecodeValue(const std::string &Arg) {
|
||||
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
|
||||
|
||||
uint32_t Result = strtoul(&Arg.at(1), nullptr, 0);
|
||||
@@ -116,7 +117,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, uint64_t> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, uint64_t> DecodeValue(const std::string &Arg) {
|
||||
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
|
||||
|
||||
uint64_t Result = strtoull(&Arg.at(1), nullptr, 0);
|
||||
@@ -125,7 +126,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, int64_t> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, int64_t> DecodeValue(const std::string &Arg) {
|
||||
if (Arg.at(0) != '#') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
|
||||
|
||||
int64_t Result = (int64_t)strtoull(&Arg.at(1), nullptr, 0);
|
||||
@@ -134,7 +135,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, IR::SHA256Sum> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, IR::SHA256Sum> DecodeValue(const std::string &Arg) {
|
||||
IR::SHA256Sum Result;
|
||||
|
||||
if (Arg.at(0) != 's' || Arg.at(1) != 'h' || Arg.at(2) != 'a' || Arg.at(3) != '2' || Arg.at(4) != '5' || Arg.at(5) != '6' || Arg.at(6) != ':')
|
||||
@@ -165,7 +166,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, FEXCore::IR::RegisterClassType> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, FEXCore::IR::RegisterClassType> DecodeValue(const std::string &Arg) {
|
||||
if (Arg == "GPR") {
|
||||
return {DecodeFailure::DECODE_OKAY, FEXCore::IR::GPRClass};
|
||||
}
|
||||
@@ -183,7 +184,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, FEXCore::IR::TypeDefinition> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, FEXCore::IR::TypeDefinition> DecodeValue(const std::string &Arg) {
|
||||
uint8_t Size{}, Elements{1};
|
||||
int NumArgs = sscanf(Arg.c_str(), "i%hhdv%hhd", &Size, &Elements);
|
||||
|
||||
@@ -195,8 +196,8 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, FEXCore::IR::CondClassType> DecodeValue(std::string &Arg) {
|
||||
std::array<std::string, 22> CondNames = {
|
||||
std::pair<DecodeFailure, FEXCore::IR::CondClassType> DecodeValue(const std::string &Arg) {
|
||||
static constexpr std::array<std::string_view, 22> CondNames = {
|
||||
"EQ",
|
||||
"NEQ",
|
||||
"UGE",
|
||||
@@ -230,8 +231,8 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, FEXCore::IR::MemOffsetType> DecodeValue(std::string &Arg) {
|
||||
std::array<std::string, 3> Names = {
|
||||
std::pair<DecodeFailure, FEXCore::IR::MemOffsetType> DecodeValue(const std::string &Arg) {
|
||||
static constexpr std::array<std::string_view, 3> Names = {
|
||||
"SXTX",
|
||||
"UXTW",
|
||||
"SXTW",
|
||||
@@ -246,8 +247,8 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, FEXCore::IR::FenceType> DecodeValue(std::string &Arg) {
|
||||
std::array<std::string, 3> Names = {
|
||||
std::pair<DecodeFailure, FEXCore::IR::FenceType> DecodeValue(const std::string &Arg) {
|
||||
static constexpr std::array<std::string_view, 3> Names = {
|
||||
"Loads",
|
||||
"Stores",
|
||||
"LoadStores",
|
||||
@@ -262,23 +263,22 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
template<>
|
||||
std::pair<DecodeFailure, OrderedNode*> DecodeValue(std::string &Arg) {
|
||||
std::pair<DecodeFailure, OrderedNode*> DecodeValue(const std::string &Arg) {
|
||||
if (Arg.at(0) != '%') return {DecodeFailure::DECODE_INVALIDCHAR, 0};
|
||||
|
||||
// Strip off the type qualifier from the ssa value
|
||||
size_t ArgEnd = std::string::npos;
|
||||
std::string SSAName = trim(Arg);
|
||||
ArgEnd = SSAName.find_first_of(" ");
|
||||
const size_t ArgEnd = SSAName.find_first_of(' ');
|
||||
|
||||
if (ArgEnd != std::string::npos) {
|
||||
SSAName = SSAName.substr(0, ArgEnd);
|
||||
}
|
||||
SSAName = SSAName.substr(0, ArgEnd);
|
||||
}
|
||||
|
||||
// Forward declarations may make this not succed
|
||||
// Forward declarations may make this not succed
|
||||
auto Op = SSANameMapper.find(SSAName);
|
||||
if (Op == SSANameMapper.end()) {
|
||||
if (Op == SSANameMapper.end()) {
|
||||
return {DecodeFailure::DECODE_UNKNOWN_SSA, nullptr};
|
||||
}
|
||||
}
|
||||
|
||||
return {DecodeFailure::DECODE_OKAY, Op->second};
|
||||
}
|
||||
@@ -302,21 +302,21 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
|
||||
IRParser(std::istream *text) {
|
||||
InitializeStaticTables();
|
||||
|
||||
|
||||
std::string TmpLine;
|
||||
while (!text->eof()) {
|
||||
std::getline(*text, TmpLine);
|
||||
if (text->eof()) {
|
||||
break;
|
||||
}
|
||||
if (text->eof()) {
|
||||
break;
|
||||
}
|
||||
if (text->fail()) {
|
||||
LogMan::Msg::E("Failed to getline on line: %ld", Lines.size());
|
||||
LogMan::Msg::EFmt("Failed to getline on line: {}", Lines.size());
|
||||
return;
|
||||
}
|
||||
Lines.emplace_back(TmpLine);
|
||||
}
|
||||
|
||||
ResetWorkingList();
|
||||
ResetWorkingList();
|
||||
Loaded = Parse();
|
||||
}
|
||||
|
||||
@@ -327,11 +327,11 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
|
||||
|
||||
bool Parse() {
|
||||
auto CheckPrintError = [&](LineDefinition &Def, DecodeFailure Failure) -> bool {
|
||||
const auto CheckPrintError = [&](const LineDefinition &Def, DecodeFailure Failure) -> bool {
|
||||
if (Failure != DecodeFailure::DECODE_OKAY) {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("Value Couldn't be decoded due to %s", DecodeErrorToString(Failure).c_str());
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("Value Couldn't be decoded due to {}", DecodeErrorToString(Failure));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -339,13 +339,13 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
};
|
||||
|
||||
// String parse every line for our definitions
|
||||
for (size_t i = 0; i < Lines.size(); ++i) {
|
||||
std::string Line = Lines[i];
|
||||
for (size_t i = 0; i < Lines.size(); ++i) {
|
||||
std::string Line = Lines[i];
|
||||
LineDefinition Def{};
|
||||
CurrentDef = &Def;
|
||||
CurrentDef = &Def;
|
||||
Def.LineNumber = i;
|
||||
|
||||
Line = trim(Line);
|
||||
Line = trim(Line);
|
||||
|
||||
// Skip empty lines
|
||||
if (Line.empty()) {
|
||||
@@ -359,35 +359,37 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
size_t CurrentPos{};
|
||||
// Let's see if this node is assigning something first
|
||||
if (Line[0] == '%') {
|
||||
// Let's see if this node is assigning something first
|
||||
if (Line[0] == '%') {
|
||||
size_t DefinitionEnd = std::string::npos;
|
||||
if ((DefinitionEnd = Line.find_first_of("=", CurrentPos)) != std::string::npos) {
|
||||
if ((DefinitionEnd = Line.find_first_of('=', CurrentPos)) != std::string::npos) {
|
||||
Def.Definition = Line.substr(0, DefinitionEnd);
|
||||
Def.Definition = trim(Def.Definition);
|
||||
Def.HasDefinition = true;
|
||||
CurrentPos = DefinitionEnd + 1; // +1 to ensure we go past then assignment
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Error on Line: %d", i);
|
||||
LogMan::Msg::E("%s", Lines[i].c_str());
|
||||
LogMan::Msg::E("SSA declaration without assignment");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", i);
|
||||
LogMan::Msg::EFmt("{}", Lines[i]);
|
||||
LogMan::Msg::EFmt("SSA declaration without assignment");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we are pulling in some IR from the IR Printer
|
||||
// Prints (%ssa%d) at the start of lines without a definition
|
||||
if (Line[0] == '(') {
|
||||
size_t DefinitionEnd = std::string::npos;
|
||||
if ((DefinitionEnd = Line.find_first_of(")", CurrentPos)) != std::string::npos) {
|
||||
if ((DefinitionEnd = Line.find_first_of(')', CurrentPos)) != std::string::npos) {
|
||||
size_t SSAEnd = std::string::npos;
|
||||
if ((SSAEnd = Line.find_last_of(" ", DefinitionEnd)) != std::string::npos) {
|
||||
if ((SSAEnd = Line.find_last_of(' ', DefinitionEnd)) != std::string::npos) {
|
||||
std::string Type = Line.substr(SSAEnd + 1, DefinitionEnd - SSAEnd - 1);
|
||||
Type = trim(Type);
|
||||
|
||||
auto DefinitionSize = DecodeValue<FEXCore::IR::TypeDefinition>(Type);
|
||||
if (!CheckPrintError(Def, DefinitionSize.first)) return false;
|
||||
if (!CheckPrintError(Def, DefinitionSize.first)) {
|
||||
return false;
|
||||
}
|
||||
Def.Size = DefinitionSize.second;
|
||||
}
|
||||
|
||||
@@ -396,9 +398,9 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
CurrentPos = DefinitionEnd + 1;
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Error on Line: %d", i);
|
||||
LogMan::Msg::E("%s", Lines[i].c_str());
|
||||
LogMan::Msg::E("SSA value with numbered SSA provided but no closing parentheses");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", i);
|
||||
LogMan::Msg::EFmt("{}", Lines[i]);
|
||||
LogMan::Msg::EFmt("SSA value with numbered SSA provided but no closing parentheses");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -406,7 +408,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
if (Def.HasDefinition) {
|
||||
// Let's check if we have a size declared with this variable
|
||||
size_t NameEnd = std::string::npos;
|
||||
if ((NameEnd = Def.Definition.find_first_of(" ")) != std::string::npos) {
|
||||
if ((NameEnd = Def.Definition.find_first_of(' ')) != std::string::npos) {
|
||||
std::string Type = Def.Definition.substr(NameEnd + 1);
|
||||
Type = trim(Type);
|
||||
Def.Definition = trim(Def.Definition.substr(0, NameEnd));
|
||||
@@ -417,9 +419,9 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
if (Def.Definition == "%Invalid") {
|
||||
LogMan::Msg::E("Error on Line: %d", i);
|
||||
LogMan::Msg::E("%s", Lines[i].c_str());
|
||||
LogMan::Msg::E("Definition tried to define reserved %Invalid ssa node");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", i);
|
||||
LogMan::Msg::EFmt("{}", Lines[i]);
|
||||
LogMan::Msg::EFmt("Definition tried to define reserved %Invalid ssa node");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -436,9 +438,9 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
else {
|
||||
if (RemainingLine.empty()) {
|
||||
LogMan::Msg::E("Error on Line: %d", i);
|
||||
LogMan::Msg::E("%s", Lines[i].c_str());
|
||||
LogMan::Msg::E("Line without an IROp?");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", i);
|
||||
LogMan::Msg::EFmt("{}", Lines[i]);
|
||||
LogMan::Msg::EFmt("Line without an IROp?");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -455,12 +457,10 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
else {
|
||||
while (!RemainingLine.empty()) {
|
||||
size_t ArgEnd = std::string::npos;
|
||||
ArgEnd = RemainingLine.find_first_of(",");
|
||||
const size_t ArgEnd = RemainingLine.find(',');
|
||||
std::string Arg = trim(RemainingLine.substr(0, ArgEnd));
|
||||
|
||||
std::string Arg = RemainingLine.substr(0, ArgEnd);
|
||||
Arg = trim(Arg);
|
||||
Def.Args.emplace_back(Arg);
|
||||
Def.Args.emplace_back(std::move(Arg));
|
||||
|
||||
RemainingLine.erase(0, ArgEnd+1); // +1 to ensure we go past the ','
|
||||
if (ArgEnd == std::string::npos)
|
||||
@@ -469,17 +469,17 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
}
|
||||
|
||||
Defs.emplace_back(Def);
|
||||
}
|
||||
CurrentDef = &Defs.emplace_back(std::move(Def));
|
||||
}
|
||||
|
||||
// Ensure all of the ops are real ops
|
||||
for(size_t i = 0; i < Defs.size(); ++i) {
|
||||
auto &Def = Defs[i];
|
||||
auto Op = NameToOpMap.find(Def.IROp);
|
||||
if (Op == NameToOpMap.end()) {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("IROp '%s' doesn't exist", Def.IROp.c_str());
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("IROp '{}' doesn't exist", Def.IROp);
|
||||
return false;
|
||||
}
|
||||
Def.OpEnum = Op->second;
|
||||
@@ -489,11 +489,11 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
IRPair<IROp_IRHeader> IRHeader;
|
||||
{
|
||||
auto &Def = Defs[0];
|
||||
CurrentDef = &Def;
|
||||
CurrentDef = &Def;
|
||||
if (Def.OpEnum != FEXCore::IR::IROps::OP_IRHEADER) {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("First op needs to be IRHeader. Was '%s'", Def.IROp.c_str());
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("First op needs to be IRHeader. Was '{}'", Def.IROp);
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -507,14 +507,14 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
SetWriteCursor(nullptr); // isolate the header from everything following
|
||||
|
||||
// Initialize SSANameMapper with Invalid value
|
||||
SSANameMapper["%Invalid"] = Invalid();
|
||||
SSANameMapper.insert_or_assign("%Invalid", Invalid());
|
||||
|
||||
// Spin through the blocks and generate basic block ops
|
||||
for(size_t i = 0; i < Defs.size(); ++i) {
|
||||
auto &Def = Defs[i];
|
||||
if (Def.OpEnum == FEXCore::IR::IROps::OP_CODEBLOCK) {
|
||||
auto CodeBlock = _CodeBlock(InvalidNode, InvalidNode);
|
||||
SSANameMapper[Def.Definition] = CodeBlock.Node;
|
||||
SSANameMapper.insert_or_assign(Def.Definition, CodeBlock.Node);
|
||||
Def.Node = CodeBlock.Node;
|
||||
|
||||
if (i == 1) {
|
||||
@@ -532,23 +532,22 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
FEXCore::IR::IROp_CodeBlock *CurrentBlockOp{};
|
||||
for(size_t i = 1; i < Defs.size(); ++i) {
|
||||
auto &Def = Defs[i];
|
||||
CurrentDef = &Def;
|
||||
|
||||
CurrentDef = &Def;
|
||||
|
||||
switch (Def.OpEnum) {
|
||||
// Special handled
|
||||
case FEXCore::IR::IROps::OP_IRHEADER:
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("IRHEADER used in the middle of the block!");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("IRHEADER used in the middle of the block!");
|
||||
return false; // only one OP_IRHEADER allowed per block
|
||||
|
||||
case FEXCore::IR::IROps::OP_CODEBLOCK: {
|
||||
SetWriteCursor(nullptr); // isolate from previous block
|
||||
if (CurrentBlock != nullptr) {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("CodeBlock being used inside of already existing codeblock!");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("CodeBlock being used inside of already existing codeblock!");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -560,15 +559,16 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
|
||||
case FEXCore::IR::IROps::OP_BEGINBLOCK: {
|
||||
if (CurrentBlock == nullptr) {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("EndBlock being used outside of a block!");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("EndBlock being used outside of a block!");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
|
||||
|
||||
if (!CheckPrintError(Def, Adjust.first)) return false;
|
||||
if (!CheckPrintError(Def, Adjust.first)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Def.Node = _BeginBlock(Adjust.second);
|
||||
CurrentBlockOp->Begin = Def.Node->Wrapped(DualListData.ListBegin());
|
||||
@@ -577,15 +577,16 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
|
||||
case FEXCore::IR::IROps::OP_ENDBLOCK: {
|
||||
if (CurrentBlock == nullptr) {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("EndBlock being used outside of a block!");
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("EndBlock being used outside of a block!");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto Adjust = DecodeValue<OrderedNode*>(Def.Args[0]);
|
||||
|
||||
if (!CheckPrintError(Def, Adjust.first)) return false;
|
||||
if (!CheckPrintError(Def, Adjust.first)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Def.Node = _EndBlock(Adjust.second);
|
||||
CurrentBlockOp->Last = Def.Node->Wrapped(DualListData.ListBegin());
|
||||
@@ -597,20 +598,18 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
case FEXCore::IR::IROps::OP_DUMMY: {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("Dummy op must not be used");
|
||||
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("Dummy op must not be used");
|
||||
break;
|
||||
}
|
||||
#define IROP_PARSER_SWITCH_HELPERS
|
||||
#include <FEXCore/IR/IRDefines.inc>
|
||||
default: {
|
||||
LogMan::Msg::E("Error on Line: %d", Def.LineNumber);
|
||||
LogMan::Msg::E("%s", Lines[Def.LineNumber].c_str());
|
||||
LogMan::Msg::E("Unhandled Op enum '%s' in parser", Def.IROp.c_str());
|
||||
LogMan::Msg::EFmt("Error on Line: {}", Def.LineNumber);
|
||||
LogMan::Msg::EFmt("{}", Lines[Def.LineNumber]);
|
||||
LogMan::Msg::EFmt("Unhandled Op enum '{}' in parser", Def.IROp);
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -624,7 +623,7 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
IROp->Size = Def.Size.Bytes();
|
||||
IROp->ElementSize = 0;
|
||||
}
|
||||
SSANameMapper[Def.Definition] = Def.Node;
|
||||
SSANameMapper.insert_or_assign(Def.Definition, Def.Node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,11 +631,11 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
}
|
||||
|
||||
void InitializeStaticTables() {
|
||||
if (NameToOpMap.size() == 0) {
|
||||
if (NameToOpMap.empty()) {
|
||||
for (FEXCore::IR::IROps Op = FEXCore::IR::IROps::OP_DUMMY;
|
||||
Op <= FEXCore::IR::IROps::OP_LAST;
|
||||
Op = static_cast<FEXCore::IR::IROps>(static_cast<uint32_t>(Op) + 1)) {
|
||||
NameToOpMap[FEXCore::IR::GetName(Op)] = Op;
|
||||
NameToOpMap.insert_or_assign(FEXCore::IR::GetName(Op), Op);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -644,13 +643,12 @@ class IRParser: public FEXCore::IR::IREmitter {
|
||||
|
||||
} // anon namespace
|
||||
|
||||
IREmitter* Parse(std::istream *in) {
|
||||
auto parser = new IRParser(in);
|
||||
std::unique_ptr<IREmitter> Parse(std::istream *in) {
|
||||
auto parser = std::make_unique<IRParser>(in);
|
||||
|
||||
if (parser->Loaded) {
|
||||
return parser;
|
||||
} else {
|
||||
delete parser;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
+367
-312
@@ -19,15 +19,6 @@ $end_info$
|
||||
|
||||
namespace FEXCore::IR {
|
||||
|
||||
class ConstProp final : public FEXCore::IR::Pass {
|
||||
std::unordered_map<uint64_t, OrderedNode*> ConstPool;
|
||||
std::map<OrderedNode*, uint64_t> AddressgenConsts;
|
||||
public:
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
bool InlineConstants;
|
||||
ConstProp(bool DoInlineConstants) : InlineConstants(DoInlineConstants) { }
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
uint64_t getMask(T Op) {
|
||||
uint64_t NumBits = Op->Header.Size * 8;
|
||||
@@ -69,8 +60,7 @@ static bool IsImmMemory(uint64_t imm, uint8_t AccessSize) {
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<MemOffsetType, uint8_t, OrderedNode*, OrderedNode*> MemExtendedAddressing(IREmitter *IREmit, uint8_t AccessSize, IROp_Header* AddressHeader) {
|
||||
|
||||
static std::tuple<MemOffsetType, uint8_t, OrderedNode*, OrderedNode*> MemExtendedAddressing(IREmitter *IREmit, uint8_t AccessSize, IROp_Header* AddressHeader) {
|
||||
auto Src0Header = IREmit->GetOpHeader(AddressHeader->Args[0]);
|
||||
if (Src0Header->Size == 8) {
|
||||
//Try to optimize: Base + MUL(Offset, Scale)
|
||||
@@ -124,7 +114,7 @@ std::tuple<MemOffsetType, uint8_t, OrderedNode*, OrderedNode*> MemExtendedAddres
|
||||
return { MEM_OFFSET_SXTX, 1, IREmit->UnwrapNode(AddressHeader->Args[0]), IREmit->UnwrapNode(AddressHeader->Args[1]) };
|
||||
}
|
||||
|
||||
OrderedNodeWrapper RemoveUselessMasking(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t mask) {
|
||||
static OrderedNodeWrapper RemoveUselessMasking(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t mask) {
|
||||
#if 1 // HOTFIX: We need to clear up the meaning of opsize and dest size. See #594
|
||||
return src;
|
||||
#else
|
||||
@@ -151,7 +141,7 @@ OrderedNodeWrapper RemoveUselessMasking(IREmitter *IREmit, OrderedNodeWrapper sr
|
||||
#endif
|
||||
}
|
||||
|
||||
bool IsBfeAlreadyDone(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t Width) {
|
||||
static bool IsBfeAlreadyDone(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t Width) {
|
||||
auto IROp = IREmit->GetOpHeader(src);
|
||||
if (IROp->Op == OP_BFE) {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
@@ -162,31 +152,55 @@ bool IsBfeAlreadyDone(IREmitter *IREmit, OrderedNodeWrapper src, uint64_t Width)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConstProp::Run(IREmitter *IREmit) {
|
||||
class ConstProp final : public FEXCore::IR::Pass {
|
||||
public:
|
||||
explicit ConstProp(bool DoInlineConstants) : InlineConstants(DoInlineConstants) { }
|
||||
|
||||
bool Run(IREmitter *IREmit) override;
|
||||
|
||||
bool InlineConstants;
|
||||
|
||||
private:
|
||||
bool HandleConstantPools(IREmitter *IREmit, const IRListView& CurrentIR);
|
||||
void CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& CurrentIR);
|
||||
void FCMPOptimization(IREmitter *IREmit, const IRListView& CurrentIR);
|
||||
void LoadMemStoreMemImmediatePooling(IREmitter *IREmit, const IRListView& CurrentIR);
|
||||
bool ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& CurrentIR,
|
||||
OrderedNode* CodeNode, IROp_Header* IROp);
|
||||
bool ConstantPropagation(IREmitter *IREmit, const IRListView& CurrentIR,
|
||||
OrderedNode* CodeNode, IROp_Header* IROp);
|
||||
bool ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR);
|
||||
|
||||
std::unordered_map<uint64_t, OrderedNode*> ConstPool;
|
||||
std::map<OrderedNode*, uint64_t> AddressgenConsts;
|
||||
};
|
||||
|
||||
bool ConstProp::HandleConstantPools(IREmitter *IREmit, const IRListView& CurrentIR) {
|
||||
bool Changed = false;
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
auto OriginalWriteCursor = IREmit->GetWriteCursor();
|
||||
|
||||
{
|
||||
|
||||
// constants are pooled per block
|
||||
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IROp->Op == OP_CONSTANT) {
|
||||
auto Op = IROp->C<IR::IROp_Constant>();
|
||||
if (ConstPool.count(Op->Constant)) {
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, ConstPool[Op->Constant]);
|
||||
Changed = true;
|
||||
} else {
|
||||
ConstPool[Op->Constant] = CodeNode;
|
||||
}
|
||||
// constants are pooled per block
|
||||
for (auto [BlockNode, BlockHeader] : CurrentIR.GetBlocks()) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IROp->Op == OP_CONSTANT) {
|
||||
auto Op = IROp->C<IR::IROp_Constant>();
|
||||
if (ConstPool.count(Op->Constant)) {
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, ConstPool[Op->Constant]);
|
||||
Changed = true;
|
||||
} else {
|
||||
ConstPool[Op->Constant] = CodeNode;
|
||||
}
|
||||
}
|
||||
ConstPool.clear();
|
||||
}
|
||||
ConstPool.clear();
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
// Code motion around selects
|
||||
// Moves unary ops that depend on a select before the select, if both inputs are constants
|
||||
// assumes that unary ops without side effects on constants will be constprop'd
|
||||
void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& CurrentIR) {
|
||||
// Code motion around selects
|
||||
// Moves unary ops that depend on a select before the select, if both inputs are constants
|
||||
// assumes that unary ops without side effects on constants will be constprop'd
|
||||
@@ -243,9 +257,9 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// FCMP optimization
|
||||
|
||||
void ConstProp::FCMPOptimization(IREmitter *IREmit, const IRListView& CurrentIR) {
|
||||
// Make all FCMPs set no flags
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
if (IROp->Op == OP_FCMP) {
|
||||
@@ -266,10 +280,11 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// LoadMem / StoreMem imm pooling
|
||||
// If imms are close by, use address gen to generate the values instead of using a new imm
|
||||
// LoadMem / StoreMem imm pooling
|
||||
// If imms are close by, use address gen to generate the values instead of using a new imm
|
||||
void ConstProp::LoadMemStoreMemImmediatePooling(IREmitter *IREmit, const IRListView& CurrentIR) {
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IROp->Op == OP_LOADMEM || IROp->Op == OP_STOREMEM) {
|
||||
@@ -293,152 +308,163 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
}
|
||||
AddressgenConsts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
// zext / masking elimination
|
||||
switch (IROp->Op) {
|
||||
// Generic handling
|
||||
case OP_OR:
|
||||
case OP_XOR:
|
||||
case OP_NOT:
|
||||
case OP_ADD:
|
||||
case OP_SUB:
|
||||
case OP_MUL:
|
||||
case OP_UMUL:
|
||||
case OP_DIV:
|
||||
case OP_UDIV:
|
||||
case OP_LSHR:
|
||||
case OP_ASHR:
|
||||
case OP_LSHL:
|
||||
case OP_ROR: {
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp));
|
||||
if (newArg.ID() != IROp->Args[i].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& CurrentIR,
|
||||
OrderedNode* CodeNode, IROp_Header* IROp) {
|
||||
bool Changed = false;
|
||||
|
||||
case OP_AND: {
|
||||
// if AND's arguments are imms, they are masking
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto mask = getMask(IROp);
|
||||
uint64_t imm = 0;
|
||||
if (IREmit->IsValueConstant(IROp->Args[i^1], &imm))
|
||||
mask = imm;
|
||||
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], imm);
|
||||
|
||||
if (newArg.ID() != IROp->Args[i].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_BFE: {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
|
||||
// Is this value already BFE'd?
|
||||
if (IsBfeAlreadyDone(IREmit, IROp->Args[0], Op->Width)) {
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
|
||||
//printf("Removed BFE once \n");
|
||||
break;
|
||||
}
|
||||
|
||||
// Is this value already ZEXT'd?
|
||||
if (Op->lsb == 0) {
|
||||
//LoadMem, LoadMemTSO & LoadContext ZExt
|
||||
auto source = IROp->Args[0];
|
||||
auto sourceHeader = IREmit->GetOpHeader(source);
|
||||
|
||||
if (Op->Width >= (sourceHeader->Size*8) &&
|
||||
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)
|
||||
) {
|
||||
//printf("Eliminated needless zext bfe\n");
|
||||
// Load mem / load ctx zexts, no need to vmem
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// BFE does implicit masking, remove any masks leading to this, if possible
|
||||
uint64_t imm = 1ULL << (Op->Width-1);
|
||||
imm = (imm-1) *2 + 1;
|
||||
imm <<= Op->lsb;
|
||||
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
|
||||
|
||||
if (newArg.ID() != IROp->Args[0].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
|
||||
switch (IROp->Op) {
|
||||
// Generic handling
|
||||
case OP_OR:
|
||||
case OP_XOR:
|
||||
case OP_NOT:
|
||||
case OP_ADD:
|
||||
case OP_SUB:
|
||||
case OP_MUL:
|
||||
case OP_UMUL:
|
||||
case OP_DIV:
|
||||
case OP_UDIV:
|
||||
case OP_LSHR:
|
||||
case OP_ASHR:
|
||||
case OP_LSHL:
|
||||
case OP_ROR: {
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp));
|
||||
if (newArg.ID() != IROp->Args[i].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_SBFE: {
|
||||
auto Op = IROp->C<IR::IROp_Sbfe>();
|
||||
case OP_AND: {
|
||||
// if AND's arguments are imms, they are masking
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto mask = getMask(IROp);
|
||||
uint64_t imm = 0;
|
||||
if (IREmit->IsValueConstant(IROp->Args[i^1], &imm))
|
||||
mask = imm;
|
||||
|
||||
// BFE does implicit masking
|
||||
uint64_t imm = 1ULL << (Op->Width-1);
|
||||
imm = (imm-1) *2 + 1;
|
||||
imm <<= Op->lsb;
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], imm);
|
||||
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
|
||||
|
||||
if (newArg.ID() != IROp->Args[0].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
|
||||
if (newArg.ID() != IROp->Args[i].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_BFE: {
|
||||
auto Op = IROp->C<IR::IROp_Bfe>();
|
||||
|
||||
// Is this value already BFE'd?
|
||||
if (IsBfeAlreadyDone(IREmit, IROp->Args[0], Op->Width)) {
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(IROp->Args[0]));
|
||||
//printf("Removed BFE once \n");
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_VFADD:
|
||||
case OP_VFSUB:
|
||||
case OP_VFMUL:
|
||||
case OP_VFDIV:
|
||||
case OP_FCMP: {
|
||||
auto flopSize = IROp->Size;
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto argHeader = IREmit->GetOpHeader(IROp->Args[i]);
|
||||
|
||||
if (argHeader->Op == OP_VMOV) {
|
||||
auto source = argHeader->Args[0];
|
||||
auto sourceHeader = IREmit->GetOpHeader(source);
|
||||
if (sourceHeader->Size >= flopSize) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(source));
|
||||
//printf("VMOV bypassed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_VMOV: {
|
||||
// elim from load mem
|
||||
// Is this value already ZEXT'd?
|
||||
if (Op->lsb == 0) {
|
||||
//LoadMem, LoadMemTSO & LoadContext ZExt
|
||||
auto source = IROp->Args[0];
|
||||
auto sourceHeader = IREmit->GetOpHeader(source);
|
||||
|
||||
if (IROp->Size >= sourceHeader->Size &&
|
||||
if (Op->Width >= (sourceHeader->Size*8) &&
|
||||
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)
|
||||
) {
|
||||
//printf("Eliminated needless zext VMOV\n");
|
||||
) {
|
||||
//printf("Eliminated needless zext bfe\n");
|
||||
// Load mem / load ctx zexts, no need to vmem
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
|
||||
} else if (IROp->Size == sourceHeader->Size) {
|
||||
// VMOV of same size
|
||||
//printf("printf vmov of same size?!\n");
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
|
||||
// BFE does implicit masking, remove any masks leading to this, if possible
|
||||
uint64_t imm = 1ULL << (Op->Width-1);
|
||||
imm = (imm-1) *2 + 1;
|
||||
imm <<= Op->lsb;
|
||||
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
|
||||
|
||||
if (newArg.ID() != IROp->Args[0].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// constprop + some more per instruction logic
|
||||
case OP_SBFE: {
|
||||
auto Op = IROp->C<IR::IROp_Sbfe>();
|
||||
|
||||
// BFE does implicit masking
|
||||
uint64_t imm = 1ULL << (Op->Width-1);
|
||||
imm = (imm-1) *2 + 1;
|
||||
imm <<= Op->lsb;
|
||||
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[0], imm);
|
||||
|
||||
if (newArg.ID() != IROp->Args[0].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->UnwrapNode(newArg));
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_VFADD:
|
||||
case OP_VFSUB:
|
||||
case OP_VFMUL:
|
||||
case OP_VFDIV:
|
||||
case OP_FCMP: {
|
||||
auto flopSize = IROp->Size;
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto argHeader = IREmit->GetOpHeader(IROp->Args[i]);
|
||||
|
||||
if (argHeader->Op == OP_VMOV) {
|
||||
auto source = argHeader->Args[0];
|
||||
auto sourceHeader = IREmit->GetOpHeader(source);
|
||||
if (sourceHeader->Size >= flopSize) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(source));
|
||||
//printf("VMOV bypassed\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_VMOV: {
|
||||
// elim from load mem
|
||||
auto source = IROp->Args[0];
|
||||
auto sourceHeader = IREmit->GetOpHeader(source);
|
||||
|
||||
if (IROp->Size >= sourceHeader->Size &&
|
||||
(sourceHeader->Op == OP_LOADMEM || sourceHeader->Op == OP_LOADMEMTSO || sourceHeader->Op == OP_LOADCONTEXT)
|
||||
) {
|
||||
//printf("Eliminated needless zext VMOV\n");
|
||||
// Load mem / load ctx zexts, no need to vmem
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
|
||||
} else if (IROp->Size == sourceHeader->Size) {
|
||||
// VMOV of same size
|
||||
//printf("printf vmov of same size?!\n");
|
||||
IREmit->ReplaceAllUsesWith(CodeNode, CurrentIR.GetNode(source));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return Changed;
|
||||
}
|
||||
|
||||
// constprop + some more per instruction logic
|
||||
bool ConstProp::ConstantPropagation(IREmitter *IREmit, const IRListView& CurrentIR,
|
||||
OrderedNode* CodeNode, IROp_Header* IROp) {
|
||||
bool Changed = false;
|
||||
|
||||
switch (IROp->Op) {
|
||||
/*
|
||||
case OP_UMUL:
|
||||
@@ -490,7 +516,6 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
auto AddressHeader = IREmit->GetOpHeader(Op->Header.Args[0]);
|
||||
|
||||
if (AddressHeader->Op == OP_ADD && AddressHeader->Size == 8) {
|
||||
|
||||
auto [OffsetType, OffsetScale, Arg0, Arg1] = MemExtendedAddressing(IREmit, Op->Size, AddressHeader);
|
||||
|
||||
Op->OffsetType = OffsetType;
|
||||
@@ -530,7 +555,6 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
uint64_t NewConstant = (Constant1 + Constant2) & getMask(Op) ;
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
Changed = true;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -711,9 +735,9 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
uint64_t NewConstant = (Constant1 * Constant2) & getMask(Op);
|
||||
IREmit->ReplaceWithConstant(CodeNode, NewConstant);
|
||||
Changed = true;
|
||||
} else if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2) && __builtin_popcountl(Constant2) == 1) {
|
||||
} else if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2) && std::popcount(Constant2) == 1) {
|
||||
if (IROp->Size == 4 || IROp->Size == 8) {
|
||||
uint64_t amt = __builtin_ctzl(Constant2);
|
||||
uint64_t amt = std::countr_zero(Constant2);
|
||||
IREmit->SetWriteCursor(CodeNode);
|
||||
auto shift = IREmit->_Lshl(CurrentIR.GetNode(Op->Header.Args[0]), IREmit->_Constant(amt));
|
||||
shift.first->Header.Size = IROp->Size; // force Lshl to be the same size as the original Mul
|
||||
@@ -753,187 +777,218 @@ bool ConstProp::Run(IREmitter *IREmit) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// constant inlining
|
||||
if (InlineConstants) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
switch(IROp->Op) {
|
||||
case OP_LSHR:
|
||||
case OP_ASHR:
|
||||
case OP_ROR:
|
||||
case OP_LSHL:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_Lshr>();
|
||||
return Changed;
|
||||
}
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR) {
|
||||
bool Changed = false;
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
switch(IROp->Op) {
|
||||
case OP_LSHR:
|
||||
case OP_ASHR:
|
||||
case OP_ROR:
|
||||
case OP_LSHL:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_Lshr>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
// this shouldn't be here, but rather on the emitter themselves or the constprop transformation?
|
||||
if (IROp->Size <=4)
|
||||
Constant2 &= 31;
|
||||
else
|
||||
Constant2 &= 63;
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_ADD:
|
||||
case OP_SUB:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_Add>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
// this shouldn't be here, but rather on the emitter themselves or the constprop transformation?
|
||||
if (IROp->Size <=4)
|
||||
Constant2 &= 31;
|
||||
else
|
||||
Constant2 &= 63;
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_SELECT:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_Select>();
|
||||
|
||||
uint64_t Constant1{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant1)) {
|
||||
if (IsImmAddSub(Constant1)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
case OP_ADD:
|
||||
case OP_SUB:
|
||||
uint64_t Constant2{};
|
||||
uint64_t Constant3{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[2], &Constant2) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[3], &Constant3) &&
|
||||
Constant2 == 1 &&
|
||||
Constant3 == 0)
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_Add>();
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
|
||||
}
|
||||
|
||||
case OP_SELECT:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_Select>();
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t Constant1{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant1)) {
|
||||
if (IsImmAddSub(Constant1)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
case OP_CONDJUMP:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_CondJump>();
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
uint64_t Constant2{};
|
||||
uint64_t Constant3{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[2], &Constant2) &&
|
||||
IREmit->IsValueConstant(Op->Header.Args[3], &Constant3) &&
|
||||
Constant2 == 1 &&
|
||||
Constant3 == 0)
|
||||
{
|
||||
case OP_EXITFUNCTION:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_ExitFunction>();
|
||||
|
||||
uint64_t Constant{};
|
||||
if (IREmit->IsValueConstant(Op->NewRIP, &Constant)) {
|
||||
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant));
|
||||
|
||||
Changed = true;
|
||||
} else {
|
||||
auto NewRIP = IREmit->GetOpHeader(Op->NewRIP);
|
||||
if (NewRIP->Op == OP_ENTRYPOINTOFFSET) {
|
||||
auto EO = NewRIP->C<IR::IROp_EntrypointOffset>();
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(EO->Offset, EO->Header.Size));
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_OR:
|
||||
case OP_XOR:
|
||||
case OP_AND:
|
||||
{
|
||||
auto Op = IROp->CW<IR::IROp_Or>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmLogical(Constant2, IROp->Size * 8)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LOADMEM:
|
||||
{
|
||||
auto Op = IROp->CW<IR::IROp_LoadMem>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmMemory(Constant2, Op->Size)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_STOREMEM:
|
||||
{
|
||||
auto Op = IROp->CW<IR::IROp_StoreMem>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[2], &Constant2)) {
|
||||
if (IsImmMemory(Constant2, Op->Size)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_CONDJUMP:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_CondJump>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_EXITFUNCTION:
|
||||
{
|
||||
auto Op = IROp->C<IR::IROp_ExitFunction>();
|
||||
|
||||
uint64_t Constant{};
|
||||
if (IREmit->IsValueConstant(Op->NewRIP, &Constant)) {
|
||||
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant));
|
||||
|
||||
Changed = true;
|
||||
} else {
|
||||
auto NewRIP = IREmit->GetOpHeader(Op->NewRIP);
|
||||
if (NewRIP->Op == OP_ENTRYPOINTOFFSET) {
|
||||
auto EO = NewRIP->C<IR::IROp_EntrypointOffset>();
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineEntrypointOffset(EO->Offset, EO->Header.Size));
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_OR:
|
||||
case OP_XOR:
|
||||
case OP_AND:
|
||||
{
|
||||
auto Op = IROp->CW<IR::IROp_Or>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmLogical(Constant2, IROp->Size * 8)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_LOADMEM:
|
||||
{
|
||||
auto Op = IROp->CW<IR::IROp_LoadMem>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[1], &Constant2)) {
|
||||
if (IsImmMemory(Constant2, Op->Size)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OP_STOREMEM:
|
||||
{
|
||||
auto Op = IROp->CW<IR::IROp_StoreMem>();
|
||||
|
||||
uint64_t Constant2{};
|
||||
if (Op->OffsetType == MEM_OFFSET_SXTX && IREmit->IsValueConstant(Op->Header.Args[2], &Constant2)) {
|
||||
if (IsImmMemory(Constant2, Op->Size)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: break;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IREmit->SetWriteCursor(OriginalWriteCursor);
|
||||
return Changed;
|
||||
}
|
||||
|
||||
bool ConstProp::Run(IREmitter *IREmit) {
|
||||
bool Changed = false;
|
||||
auto CurrentIR = IREmit->ViewIR();
|
||||
auto OriginalWriteCursor = IREmit->GetWriteCursor();
|
||||
|
||||
if (HandleConstantPools(IREmit, CurrentIR)) {
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
CodeMotionAroundSelects(IREmit, CurrentIR);
|
||||
FCMPOptimization(IREmit, CurrentIR);
|
||||
LoadMemStoreMemImmediatePooling(IREmit, CurrentIR);
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
if (ZextAndMaskingElimination(IREmit, CurrentIR, CodeNode, IROp)) {
|
||||
Changed = true;
|
||||
}
|
||||
if (ConstantPropagation(IREmit, CurrentIR, CodeNode, IROp)) {
|
||||
Changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (InlineConstants && ConstantInlining(IREmit, CurrentIR)) {
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
IREmit->SetWriteCursor(OriginalWriteCursor);
|
||||
return Changed;
|
||||
}
|
||||
|
||||
|
||||
+45
-4
@@ -3,6 +3,7 @@
|
||||
#include <sys/mman.h>
|
||||
#include <jemalloc/jemalloc.h>
|
||||
#include <memory>
|
||||
#include <malloc.h>
|
||||
|
||||
extern "C" {
|
||||
extern void *__libc_malloc(size_t size);
|
||||
@@ -16,6 +17,15 @@ extern "C" {
|
||||
|
||||
extern mmap_hook_type __mmap_hook;
|
||||
extern munmap_hook_type __munmap_hook;
|
||||
|
||||
static FEXCore::Allocator::MALLOC_Hook global_malloc {::__libc_malloc};
|
||||
static FEXCore::Allocator::REALLOC_Hook global_realloc {::__libc_realloc};
|
||||
static FEXCore::Allocator::FREE_Hook global_free {::__libc_free};
|
||||
|
||||
// Override the global functions
|
||||
FEX_DEFAULT_VISIBILITY void *malloc(size_t size) { return global_malloc(size); }
|
||||
FEX_DEFAULT_VISIBILITY void *realloc(void *ptr, size_t size) { return global_realloc(ptr, size); }
|
||||
FEX_DEFAULT_VISIBILITY void free(void *ptr) { return global_free(ptr); }
|
||||
}
|
||||
|
||||
namespace FEXCore::Allocator {
|
||||
@@ -25,6 +35,10 @@ namespace FEXCore::Allocator {
|
||||
REALLOC_Hook realloc {::__libc_realloc};
|
||||
FREE_Hook free {::__libc_free};
|
||||
|
||||
using GLIBC_MALLOC_Hook = void*(*)(size_t, const void *caller);
|
||||
using GLIBC_REALLOC_Hook = void*(*)(void*, size_t, const void *caller);
|
||||
using GLIBC_FREE_Hook = void(*)(void*, const void *caller);
|
||||
|
||||
std::unique_ptr<Alloc::HostAllocator> Alloc64{};
|
||||
|
||||
void *FEX_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) {
|
||||
@@ -57,6 +71,8 @@ namespace FEXCore::Allocator {
|
||||
return ::je_free(ptr);
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
void SetupHooks() {
|
||||
Alloc64.reset(Alloc::OSAllocator::Create64BitAllocator());
|
||||
__mmap_hook = FEX_mmap;
|
||||
@@ -66,12 +82,37 @@ namespace FEXCore::Allocator {
|
||||
FEXCore::Allocator::malloc = ::je_malloc;
|
||||
FEXCore::Allocator::realloc = ::je_realloc;
|
||||
FEXCore::Allocator::free = ::je_free;
|
||||
|
||||
global_malloc = ::je_malloc;
|
||||
global_realloc = ::je_realloc;
|
||||
global_free = ::je_free;
|
||||
|
||||
__malloc_hook = FEXCore::Allocator::FEX_malloc_hook;
|
||||
__realloc_hook = FEXCore::Allocator::FEX_realloc_hook;
|
||||
__free_hook = FEXCore::Allocator::FEX_free_hook;
|
||||
}
|
||||
|
||||
void ClearHooks() {
|
||||
__mmap_hook = ::mmap;
|
||||
__munmap_hook = ::munmap;
|
||||
FEXCore::Allocator::mmap = ::mmap;
|
||||
FEXCore::Allocator::munmap = ::munmap;
|
||||
FEXCore::Allocator::malloc = ::__libc_malloc;
|
||||
FEXCore::Allocator::realloc = ::__libc_realloc;
|
||||
FEXCore::Allocator::free = ::__libc_free;
|
||||
|
||||
global_malloc = ::__libc_malloc;
|
||||
global_realloc = ::__libc_realloc;
|
||||
global_free = ::__libc_free;
|
||||
|
||||
// Reset's glibc hooks
|
||||
__malloc_hook = 0;
|
||||
__realloc_hook = 0;
|
||||
__free_hook = 0;
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
// Override the global functions
|
||||
void *malloc(size_t size) { return FEXCore::Allocator::malloc(size); }
|
||||
void *realloc(void *ptr, size_t size) { return FEXCore::Allocator::realloc(ptr, size); }
|
||||
void free(void *ptr) { return FEXCore::Allocator::free(ptr); }
|
||||
}
|
||||
+19
-1
@@ -37,7 +37,17 @@ void UnInstallHandlers() { Handlers.clear(); }
|
||||
Handler(Buffer);
|
||||
}
|
||||
|
||||
__builtin_trap();
|
||||
FEX_TRAP_EXECUTION;
|
||||
}
|
||||
|
||||
void MFmt(const char *fmt, const fmt::format_args& args) {
|
||||
auto msg = fmt::vformat(fmt, args);
|
||||
|
||||
for (auto& Handler : Handlers) {
|
||||
Handler(msg.c_str());
|
||||
}
|
||||
|
||||
FEX_TRAP_EXECUTION;
|
||||
}
|
||||
} // namespace Throw
|
||||
|
||||
@@ -67,5 +77,13 @@ void M(DebugLevels Level, const char *fmt, va_list args) {
|
||||
}
|
||||
}
|
||||
|
||||
void MFmtImpl(DebugLevels level, const char* fmt, const fmt::format_args& args) {
|
||||
const auto msg = fmt::vformat(fmt, args);
|
||||
|
||||
for (auto& Handler : Handlers) {
|
||||
Handler(level, msg.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Msg
|
||||
} // namespace LogMan
|
||||
+31
-29
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <list>
|
||||
@@ -54,15 +56,15 @@ namespace Type {
|
||||
#undef P
|
||||
}
|
||||
|
||||
__attribute__((visibility("default"))) std::string GetDataDirectory();
|
||||
__attribute__((visibility("default"))) std::string GetConfigDirectory(bool Global);
|
||||
__attribute__((visibility("default"))) std::string GetConfigFileLocation();
|
||||
__attribute__((visibility("default"))) std::string GetApplicationConfig(std::string &Filename, bool Global);
|
||||
FEX_DEFAULT_VISIBILITY std::string GetDataDirectory();
|
||||
FEX_DEFAULT_VISIBILITY std::string GetConfigDirectory(bool Global);
|
||||
FEX_DEFAULT_VISIBILITY std::string GetConfigFileLocation();
|
||||
FEX_DEFAULT_VISIBILITY std::string GetApplicationConfig(const std::string &Filename, bool Global);
|
||||
|
||||
using LayerValue = std::list<std::string>;
|
||||
using LayerOptions = std::unordered_map<ConfigOption, LayerValue>;
|
||||
|
||||
class __attribute__((visibility("default"))) Layer {
|
||||
class FEX_DEFAULT_VISIBILITY Layer {
|
||||
public:
|
||||
explicit Layer(const LayerType _Type);
|
||||
virtual ~Layer();
|
||||
@@ -94,57 +96,57 @@ namespace Type {
|
||||
}
|
||||
|
||||
void Set(ConfigOption Option, std::string Data) {
|
||||
OptionMap[Option].emplace_back(Data);
|
||||
OptionMap[Option].emplace_back(std::move(Data));
|
||||
}
|
||||
|
||||
void EraseSet(ConfigOption Option, std::string Data) {
|
||||
OptionMap.erase(Option);
|
||||
OptionMap[Option].emplace_back(Data);
|
||||
Erase(Option);
|
||||
Set(Option, std::move(Data));
|
||||
}
|
||||
|
||||
void Erase(ConfigOption Option) {
|
||||
OptionMap.erase(Option);
|
||||
}
|
||||
|
||||
const LayerType GetLayerType() const { return Type; }
|
||||
const LayerOptions &GetOptionMap() { return OptionMap; }
|
||||
LayerType GetLayerType() const { return Type; }
|
||||
const LayerOptions &GetOptionMap() const { return OptionMap; }
|
||||
|
||||
protected:
|
||||
const LayerType Type;
|
||||
LayerOptions OptionMap;
|
||||
};
|
||||
|
||||
__attribute__((visibility("default"))) void Initialize();
|
||||
__attribute__((visibility("default"))) void Shutdown();
|
||||
FEX_DEFAULT_VISIBILITY void Initialize();
|
||||
FEX_DEFAULT_VISIBILITY void Shutdown();
|
||||
|
||||
__attribute__((visibility("default"))) void Load();
|
||||
__attribute__((visibility("default"))) void ReloadMetaLayer();
|
||||
FEX_DEFAULT_VISIBILITY void Load();
|
||||
FEX_DEFAULT_VISIBILITY void ReloadMetaLayer();
|
||||
|
||||
__attribute__((visibility("default"))) void AddLayer(std::unique_ptr<FEXCore::Config::Layer> _Layer);
|
||||
FEX_DEFAULT_VISIBILITY void AddLayer(std::unique_ptr<FEXCore::Config::Layer> _Layer);
|
||||
|
||||
__attribute__((visibility("default"))) bool Exists(ConfigOption Option);
|
||||
__attribute__((visibility("default"))) std::optional<LayerValue*> All(ConfigOption Option);
|
||||
__attribute__((visibility("default"))) std::optional<std::string*> Get(ConfigOption Option);
|
||||
FEX_DEFAULT_VISIBILITY bool Exists(ConfigOption Option);
|
||||
FEX_DEFAULT_VISIBILITY std::optional<LayerValue*> All(ConfigOption Option);
|
||||
FEX_DEFAULT_VISIBILITY std::optional<std::string*> Get(ConfigOption Option);
|
||||
|
||||
__attribute__((visibility("default"))) void Set(ConfigOption Option, std::string Data);
|
||||
__attribute__((visibility("default"))) void Erase(ConfigOption Option);
|
||||
__attribute__((visibility("default"))) void EraseSet(ConfigOption Option, std::string Data);
|
||||
FEX_DEFAULT_VISIBILITY void Set(ConfigOption Option, std::string Data);
|
||||
FEX_DEFAULT_VISIBILITY void Erase(ConfigOption Option);
|
||||
FEX_DEFAULT_VISIBILITY void EraseSet(ConfigOption Option, std::string Data);
|
||||
|
||||
template<typename T>
|
||||
class __attribute__((visibility("default"))) Value {
|
||||
class FEX_DEFAULT_VISIBILITY Value {
|
||||
public:
|
||||
template <typename TT = T,
|
||||
typename std::enable_if<!std::is_same<TT, std::string>::value, int>::type = 0>
|
||||
Value(FEXCore::Config::ConfigOption _Option, T Default)
|
||||
: Option {_Option} {
|
||||
ValueData = FEXCore::Config::Value<T>::GetIfExists(Option, Default);
|
||||
ValueData = GetIfExists(Option, Default);
|
||||
}
|
||||
|
||||
template <typename TT = T,
|
||||
typename std::enable_if<std::is_same<TT, std::string>::value, int>::type = 0>
|
||||
Value(FEXCore::Config::ConfigOption _Option, T Default)
|
||||
: Option {_Option} {
|
||||
ValueData = FEXCore::Config::Value<T>::GetIfExists(Option, Default);
|
||||
ValueData = GetIfExists(Option, Default);
|
||||
GetListIfExists(Option, &AppendList);
|
||||
}
|
||||
|
||||
@@ -156,7 +158,7 @@ namespace Type {
|
||||
ERROR_AND_DIE("FEXCore::Config::Value has no value");
|
||||
}
|
||||
|
||||
ValueData = FEXCore::Config::Value<T>::Get(Option);
|
||||
ValueData = Get(Option);
|
||||
}
|
||||
|
||||
template <typename TT = T,
|
||||
@@ -167,13 +169,13 @@ namespace Type {
|
||||
ERROR_AND_DIE("FEXCore::Config::Value has no value");
|
||||
}
|
||||
|
||||
ValueData = FEXCore::Config::Value<T>::GetIfExists(Option);
|
||||
ValueData = GetIfExists(Option);
|
||||
GetListIfExists(Option, &AppendList);
|
||||
}
|
||||
|
||||
operator T() { return ValueData; }
|
||||
T operator()() { return ValueData; }
|
||||
Value<T>(T Value) { ValueData = Value; }
|
||||
operator T() const { return ValueData; }
|
||||
T operator()() const { return ValueData; }
|
||||
Value<T>(T Value) { ValueData = std::move(Value); }
|
||||
std::list<T> &All() { return AppendList; }
|
||||
|
||||
private:
|
||||
|
||||
+6
-3
@@ -6,7 +6,10 @@ $end_info$
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
namespace FEXCore {
|
||||
@@ -85,8 +88,8 @@ class LLVMCore;
|
||||
virtual void ClearCache() {}
|
||||
virtual void CopyNecessaryDataForCompileThread(CPUBackend *Original) {}
|
||||
|
||||
using AsmDispatch = __attribute__((naked)) void(*)(FEXCore::Core::CpuStateFrame *Frame);
|
||||
using JITCallback = __attribute__((naked)) void(*)(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP);
|
||||
using AsmDispatch = FEX_NAKED void(*)(FEXCore::Core::CpuStateFrame *Frame);
|
||||
using JITCallback = FEX_NAKED void(*)(FEXCore::Core::CpuStateFrame *Frame, uint64_t RIP);
|
||||
|
||||
JITCallback CallbackPtr{};
|
||||
protected:
|
||||
|
||||
+5
-1
@@ -16,6 +16,10 @@ class IREmitter;
|
||||
*/
|
||||
class CodeLoader {
|
||||
public:
|
||||
using MapperFn = std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)>;
|
||||
using UnmapperFn = std::function<int(void *addr, size_t length)>;
|
||||
|
||||
virtual ~CodeLoader() = default;
|
||||
|
||||
/**
|
||||
* @brief CPU Core uses this to choose what the stack size should be for this code
|
||||
@@ -35,7 +39,7 @@ public:
|
||||
/**
|
||||
* @brief Maps and copies the executable, also sets up stack
|
||||
*/
|
||||
virtual bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) { return false; }
|
||||
virtual bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) { return false; }
|
||||
|
||||
virtual std::vector<std::string> const *GetApplicationArguments() { return nullptr; }
|
||||
virtual void GetExecveArguments(std::vector<char const*> *Args) {}
|
||||
|
||||
+44
-41
@@ -5,6 +5,7 @@
|
||||
|
||||
#include <FEXCore/Core/SignalDelegator.h>
|
||||
#include <FEXCore/Core/CPUID.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <istream>
|
||||
#include <ostream>
|
||||
@@ -45,12 +46,13 @@ namespace FEXCore::Context {
|
||||
MODE_32BIT,
|
||||
MODE_64BIT,
|
||||
};
|
||||
using CustomCPUFactoryType = std::function<FEXCore::CPU::CPUBackend* (FEXCore::Context::Context*, FEXCore::Core::InternalThreadState *Thread)>;
|
||||
using CustomCPUFactoryType = std::function<std::unique_ptr<FEXCore::CPU::CPUBackend> (FEXCore::Context::Context*, FEXCore::Core::InternalThreadState *Thread)>;
|
||||
|
||||
/**
|
||||
* @brief This initializes internal FEXCore state that is shared between contexts and requires overhead to setup
|
||||
*/
|
||||
__attribute__((visibility("default"))) void InitializeStaticTables(OperatingMode Mode = MODE_64BIT);
|
||||
FEX_DEFAULT_VISIBILITY void InitializeStaticTables(OperatingMode Mode = MODE_64BIT);
|
||||
FEX_DEFAULT_VISIBILITY void ShutdownStaticTables();
|
||||
|
||||
/**
|
||||
* @brief [[threadsafe]] Create a new FEXCore context object
|
||||
@@ -59,7 +61,7 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return a new context object
|
||||
*/
|
||||
__attribute__((visibility("default"))) FEXCore::Context::Context *CreateNewContext();
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::Context::Context *CreateNewContext();
|
||||
|
||||
/**
|
||||
* @brief Post creation context initialization
|
||||
@@ -69,14 +71,14 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return true if we managed to initialize correctly
|
||||
*/
|
||||
__attribute__((visibility("default"))) bool InitializeContext(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY bool InitializeContext(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Destroy the context object
|
||||
*
|
||||
* @param CTX
|
||||
*/
|
||||
__attribute__((visibility("default"))) void DestroyContext(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY void DestroyContext(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Allows setting up in memory code and other things prior to launchign code execution
|
||||
@@ -86,17 +88,17 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return true if we loaded code
|
||||
*/
|
||||
__attribute__((visibility("default"))) bool InitCore(FEXCore::Context::Context *CTX, FEXCore::CodeLoader *Loader);
|
||||
FEX_DEFAULT_VISIBILITY bool InitCore(FEXCore::Context::Context *CTX, FEXCore::CodeLoader *Loader);
|
||||
|
||||
__attribute__((visibility("default"))) void SetExitHandler(FEXCore::Context::Context *CTX, std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> handler);
|
||||
__attribute__((visibility("default"))) std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> GetExitHandler(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY void SetExitHandler(FEXCore::Context::Context *CTX, std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> handler);
|
||||
FEX_DEFAULT_VISIBILITY std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)> GetExitHandler(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Pauses execution on the CPU core
|
||||
*
|
||||
* Blocks until all threads have paused.
|
||||
*/
|
||||
__attribute__((visibility("default"))) void Pause(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY void Pause(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Starts (or continues) the CPU core
|
||||
@@ -105,7 +107,7 @@ namespace FEXCore::Context {
|
||||
* Use RunUntilExit() for synchonous executions
|
||||
*
|
||||
*/
|
||||
__attribute__((visibility("default"))) void Run(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY void Run(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Runs the CPU core until it exits
|
||||
@@ -117,9 +119,9 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return The ExitReason for the parentthread.
|
||||
*/
|
||||
__attribute__((visibility("default"))) ExitReason RunUntilExit(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY ExitReason RunUntilExit(FEXCore::Context::Context *CTX);
|
||||
|
||||
__attribute__((visibility("default"))) void CompileRIP(FEXCore::Context::Context *CTX, uint64_t GuestRIP);
|
||||
FEX_DEFAULT_VISIBILITY void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
|
||||
/**
|
||||
* @brief Gets the program exit status
|
||||
@@ -129,21 +131,21 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return The program exit status
|
||||
*/
|
||||
__attribute__((visibility("default"))) int GetProgramStatus(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY int GetProgramStatus(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Tells the core to shutdown
|
||||
*
|
||||
* Blocks until shutdown
|
||||
*/
|
||||
__attribute__((visibility("default"))) void Stop(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY void Stop(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Executes one instruction
|
||||
*
|
||||
* Returns once execution is complete.
|
||||
*/
|
||||
__attribute__((visibility("default"))) void Step(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY void Step(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief [[threadsafe]] Returns the ExitReason of the parent thread. Typically used for async result status
|
||||
@@ -152,7 +154,7 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return The ExitReason for the parentthread
|
||||
*/
|
||||
__attribute__((visibility("default"))) ExitReason GetExitReason(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY ExitReason GetExitReason(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief [[theadsafe]] Checks if the Context is either done working or paused(in the case of single stepping)
|
||||
@@ -163,7 +165,7 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return true if the core is done or paused
|
||||
*/
|
||||
__attribute__((visibility("default"))) bool IsDone(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY bool IsDone(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Gets a copy the CPUState of the parent thread
|
||||
@@ -171,7 +173,7 @@ namespace FEXCore::Context {
|
||||
* @param CTX The context that we created
|
||||
* @param State The state object to populate
|
||||
*/
|
||||
__attribute__((visibility("default"))) void GetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
|
||||
FEX_DEFAULT_VISIBILITY void GetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
|
||||
|
||||
/**
|
||||
* @brief Copies the CPUState provided to the parent thread
|
||||
@@ -179,7 +181,7 @@ namespace FEXCore::Context {
|
||||
* @param CTX The context that we created
|
||||
* @param State The satate object to copy from
|
||||
*/
|
||||
__attribute__((visibility("default"))) void SetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
|
||||
FEX_DEFAULT_VISIBILITY void SetCPUState(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State);
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to pass in a custom CPUBackend creation factory
|
||||
@@ -189,7 +191,7 @@ namespace FEXCore::Context {
|
||||
* @param CTX The context that we created
|
||||
* @param Factory The factory that the context will call if the DefaultCore config ise set to CUSTOM
|
||||
*/
|
||||
__attribute__((visibility("default"))) void SetCustomCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory);
|
||||
FEX_DEFAULT_VISIBILITY void SetCustomCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory);
|
||||
|
||||
/**
|
||||
* @brief Sets up memory regions on the guest for mirroring within the guest's VM space
|
||||
@@ -200,7 +202,7 @@ namespace FEXCore::Context {
|
||||
*
|
||||
* @return true when successfully mapped. false if there was an error adding
|
||||
*/
|
||||
__attribute__((visibility("default"))) bool AddVirtualMemoryMapping(FEXCore::Context::Context *CTX, uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size);
|
||||
FEX_DEFAULT_VISIBILITY bool AddVirtualMemoryMapping(FEXCore::Context::Context *CTX, uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size);
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to set a custom syscall handler
|
||||
@@ -210,29 +212,30 @@ namespace FEXCore::Context {
|
||||
* @param Syscall Which syscall ID to install a visitor to
|
||||
* @param Visitor The Visitor to install
|
||||
*/
|
||||
__attribute__((visibility("default"))) void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, uint64_t Syscall, FEXCore::HLE::SyscallVisitor *Visitor);
|
||||
FEX_DEFAULT_VISIBILITY void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, uint64_t Syscall, FEXCore::HLE::SyscallVisitor *Visitor);
|
||||
|
||||
__attribute__((visibility("default"))) void HandleCallback(FEXCore::Context::Context *CTX, uint64_t RIP);
|
||||
FEX_DEFAULT_VISIBILITY void HandleCallback(FEXCore::Context::Context *CTX, uint64_t RIP);
|
||||
|
||||
__attribute__((visibility("default"))) void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
|
||||
__attribute__((visibility("default"))) void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
|
||||
FEX_DEFAULT_VISIBILITY void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
|
||||
FEX_DEFAULT_VISIBILITY void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func);
|
||||
|
||||
__attribute__((visibility("default"))) FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
|
||||
__attribute__((visibility("default"))) void InitializeThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
__attribute__((visibility("default"))) void RunThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
__attribute__((visibility("default"))) void StopThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
__attribute__((visibility("default"))) void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
__attribute__((visibility("default"))) void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
__attribute__((visibility("default"))) void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation);
|
||||
__attribute__((visibility("default"))) void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler);
|
||||
__attribute__((visibility("default"))) FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf);
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
|
||||
FEX_DEFAULT_VISIBILITY void InitializeThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
FEX_DEFAULT_VISIBILITY void RunThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
FEX_DEFAULT_VISIBILITY void StopThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
FEX_DEFAULT_VISIBILITY void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
FEX_DEFAULT_VISIBILITY void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
FEX_DEFAULT_VISIBILITY void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation);
|
||||
FEX_DEFAULT_VISIBILITY void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler);
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf);
|
||||
|
||||
__attribute__((visibility("default"))) void AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name);
|
||||
__attribute__((visibility("default"))) void RemoveNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length);
|
||||
__attribute__((visibility("default"))) void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader);
|
||||
__attribute__((visibility("default"))) bool WriteAOTIR(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
|
||||
__attribute__((visibility("default"))) void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer);
|
||||
__attribute__((visibility("default"))) void FlushCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length);
|
||||
FEX_DEFAULT_VISIBILITY void AddNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length, uintptr_t Offset, const std::string& Name);
|
||||
FEX_DEFAULT_VISIBILITY void RemoveNamedRegion(FEXCore::Context::Context *CTX, uintptr_t Base, uintptr_t Length);
|
||||
FEX_DEFAULT_VISIBILITY void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader);
|
||||
FEX_DEFAULT_VISIBILITY void SetAOTIRWriter(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ostream>(const std::string&)> CacheWriter);
|
||||
FEX_DEFAULT_VISIBILITY void FinalizeAOTIRCache(FEXCore::Context::Context *CTX);
|
||||
FEX_DEFAULT_VISIBILITY void WriteFilesWithCode(FEXCore::Context::Context *CTX, std::function<void(const std::string& fileid, const std::string& filename)> Writer);
|
||||
FEX_DEFAULT_VISIBILITY void FlushCodeRange(FEXCore::Core::InternalThreadState *Thread, uint64_t Start, uint64_t Length);
|
||||
|
||||
__attribute__((visibility("default"))) void ConfigureAOTGen(FEXCore::Context::Context *CTX, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
|
||||
FEX_DEFAULT_VISIBILITY void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
|
||||
}
|
||||
+6
-3
@@ -1,12 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/HLE/Linux/ThreadManagement.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cstddef>
|
||||
#include <stdint.h>
|
||||
#include <string_view>
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct __attribute__((packed)) CPUState {
|
||||
struct FEX_PACKED CPUState {
|
||||
uint64_t rip; ///< Current core's RIP. May not be entirely accurate while JIT is active
|
||||
uint64_t gregs[16];
|
||||
uint64_t : 64;
|
||||
@@ -51,6 +54,6 @@ namespace FEXCore::Core {
|
||||
|
||||
constexpr uint64_t PAGE_SIZE = 4096;
|
||||
|
||||
__attribute__((visibility("default"))) std::string_view const& GetFlagName(unsigned Flag);
|
||||
__attribute__((visibility("default"))) std::string_view const& GetGRegName(unsigned Reg);
|
||||
FEX_DEFAULT_VISIBILITY std::string_view const& GetFlagName(unsigned Flag);
|
||||
FEX_DEFAULT_VISIBILITY std::string_view const& GetGRegName(unsigned Reg);
|
||||
}
|
||||
+7
-2
@@ -1,4 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <signal.h>
|
||||
@@ -7,11 +10,11 @@ namespace FEXCore {
|
||||
namespace Core {
|
||||
struct InternalThreadState;
|
||||
}
|
||||
struct __attribute__((packed)) GuestSAMask {
|
||||
struct FEX_PACKED GuestSAMask {
|
||||
uint64_t Val;
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) GuestSigAction {
|
||||
struct FEX_PACKED GuestSigAction {
|
||||
union {
|
||||
void (*handler)(int);
|
||||
void (*sigaction)(int, siginfo_t *, void*);
|
||||
@@ -27,6 +30,8 @@ namespace Core {
|
||||
|
||||
class SignalDelegator {
|
||||
public:
|
||||
virtual ~SignalDelegator() = default;
|
||||
|
||||
/**
|
||||
* @brief Registers an emulated thread's object to a TLS object
|
||||
*
|
||||
|
||||
+10
-7
@@ -1,4 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
@@ -13,7 +16,7 @@ namespace FEXCore {
|
||||
constexpr uint64_t UC_STRICT_RESTORE_SS = (1ULL << 2);
|
||||
|
||||
///< Describes the signal stack
|
||||
struct __attribute__((packed)) stack_t {
|
||||
struct FEX_PACKED stack_t {
|
||||
void *ss_sp;
|
||||
int32_t ss_flags;
|
||||
uint32_t : 32;
|
||||
@@ -21,7 +24,7 @@ namespace FEXCore {
|
||||
};
|
||||
static_assert(sizeof(FEXCore::x86_64::stack_t) == 24, "This needs to be the right size");
|
||||
|
||||
struct __attribute__((packed)) _libc_fpstate {
|
||||
struct FEX_PACKED _libc_fpstate {
|
||||
// This is in FXSAVE format
|
||||
uint16_t fcw;
|
||||
uint16_t fsw;
|
||||
@@ -65,19 +68,19 @@ namespace FEXCore {
|
||||
};
|
||||
static_assert(FEX_REG_CR2 == 22, "Oops");
|
||||
|
||||
struct __attribute__((packed)) mcontext_t {
|
||||
struct FEX_PACKED mcontext_t {
|
||||
uint64_t gregs[23];
|
||||
FEXCore::x86_64::_libc_fpstate *fpregs;
|
||||
uint64_t __reserved[8];
|
||||
};
|
||||
static_assert(sizeof(FEXCore::x86_64::mcontext_t) == 256, "This needs to be the right size");
|
||||
|
||||
struct __attribute__((packed)) sigset_t {
|
||||
struct FEX_PACKED sigset_t {
|
||||
uint64_t val[16];
|
||||
};
|
||||
static_assert(sizeof(FEXCore::x86_64::sigset_t) == 128, "This needs to be the right size");
|
||||
|
||||
struct __attribute__((packed)) ucontext_t {
|
||||
struct FEX_PACKED ucontext_t {
|
||||
uint64_t uc_flags;
|
||||
FEXCore::x86_64::ucontext_t *uc_link;
|
||||
FEXCore::x86_64::stack_t uc_stack;
|
||||
@@ -92,12 +95,12 @@ namespace FEXCore {
|
||||
}
|
||||
|
||||
namespace x86 {
|
||||
struct __attribute__((packed)) siginfo_t {
|
||||
struct FEX_PACKED siginfo_t {
|
||||
uint32_t pad[32];
|
||||
};
|
||||
static_assert(sizeof(FEXCore::x86::siginfo_t) == 128, "This needs to be the right size");
|
||||
|
||||
struct __attribute__((packed)) ucontext_t {
|
||||
struct FEX_PACKED ucontext_t {
|
||||
uint32_t pad[91];
|
||||
};
|
||||
static_assert(sizeof(FEXCore::x86::ucontext_t) == 364, "This needs to be the right size");
|
||||
|
||||
@@ -54,11 +54,11 @@ namespace FEXCore::Core {
|
||||
std::vector<DebugDataSubblock> Subblocks;
|
||||
};
|
||||
|
||||
enum SignalEvent {
|
||||
SIGNALEVENT_NONE, // If the guest uses our signal we need to know it was errant on our end
|
||||
SIGNALEVENT_PAUSE,
|
||||
SIGNALEVENT_STOP,
|
||||
SIGNALEVENT_RETURN,
|
||||
enum class SignalEvent {
|
||||
Nothing, // If the guest uses our signal we need to know it was errant on our end
|
||||
Pause,
|
||||
Stop,
|
||||
Return,
|
||||
};
|
||||
|
||||
struct LocalIREntry {
|
||||
@@ -78,7 +78,7 @@ namespace FEXCore::Core {
|
||||
} RunningEvents;
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
std::atomic<SignalEvent> SignalReason {SignalEvent::SIGNALEVENT_NONE};
|
||||
std::atomic<SignalEvent> SignalReason{SignalEvent::Nothing};
|
||||
|
||||
std::unique_ptr<FEXCore::Threads::Thread> ExecutionThread;
|
||||
Event StartRunning;
|
||||
@@ -107,7 +107,7 @@ namespace FEXCore::Core {
|
||||
alignas(16) FEXCore::Core::CpuStateFrame BaseFrameState{};
|
||||
|
||||
};
|
||||
static_assert(std::is_standard_layout<InternalThreadState>::value, "This needs to be standard layout");
|
||||
// static_assert(std::is_standard_layout<InternalThreadState>::value, "This needs to be standard layout");
|
||||
}
|
||||
|
||||
|
||||
+80
-58
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Core/Context.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
@@ -99,54 +100,72 @@ inline void PopOpAddrIf(uint32_t *Flags, uint32_t Flag) {
|
||||
|
||||
}
|
||||
|
||||
union DecodedOperand {
|
||||
enum {
|
||||
TYPE_NONE,
|
||||
TYPE_GPR,
|
||||
TYPE_GPR_DIRECT,
|
||||
TYPE_GPR_INDIRECT,
|
||||
TYPE_RIP_RELATIVE,
|
||||
TYPE_LITERAL,
|
||||
TYPE_SIB,
|
||||
struct DecodedOperand {
|
||||
enum class OpType : uint8_t {
|
||||
Nothing,
|
||||
GPR,
|
||||
GPRDirect,
|
||||
GPRIndirect,
|
||||
RIPRelative,
|
||||
Literal,
|
||||
SIB,
|
||||
};
|
||||
|
||||
struct {
|
||||
uint8_t Type;
|
||||
} TypeNone;
|
||||
bool IsNone() const {
|
||||
return Type == OpType::Nothing;
|
||||
}
|
||||
bool IsGPR() const {
|
||||
return Type == OpType::GPR;
|
||||
}
|
||||
bool IsGPRDirect() const {
|
||||
return Type == OpType::GPRDirect;
|
||||
}
|
||||
bool IsGPRIndirect() const {
|
||||
return Type == OpType::GPRIndirect;
|
||||
}
|
||||
bool IsRIPRelative() const {
|
||||
return Type == OpType::RIPRelative;
|
||||
}
|
||||
bool IsLiteral() const {
|
||||
return Type == OpType::Literal;
|
||||
}
|
||||
bool IsSIB() const {
|
||||
return Type == OpType::SIB;
|
||||
}
|
||||
|
||||
struct {
|
||||
uint8_t Type;
|
||||
bool HighBits;
|
||||
uint8_t GPR;
|
||||
} TypeGPR;
|
||||
union TypeUnion {
|
||||
struct {
|
||||
bool HighBits;
|
||||
uint8_t GPR;
|
||||
} GPR;
|
||||
|
||||
struct {
|
||||
uint8_t Type;
|
||||
uint8_t GPR;
|
||||
int32_t Displacement;
|
||||
} TypeGPRIndirect;
|
||||
struct {
|
||||
uint8_t GPR;
|
||||
int32_t Displacement;
|
||||
} GPRIndirect;
|
||||
|
||||
struct {
|
||||
uint8_t Type;
|
||||
union {
|
||||
int32_t s;
|
||||
uint32_t u;
|
||||
struct {
|
||||
union {
|
||||
int32_t s;
|
||||
uint32_t u;
|
||||
} Value;
|
||||
} RIPLiteral;
|
||||
|
||||
struct {
|
||||
uint8_t Size;
|
||||
uint64_t Value;
|
||||
} Literal;
|
||||
} TypeRIPLiteral;
|
||||
|
||||
struct {
|
||||
uint8_t Type;
|
||||
uint8_t Size;
|
||||
uint64_t Literal;
|
||||
} TypeLiteral;
|
||||
struct {
|
||||
uint8_t Index; // ~0 invalid
|
||||
uint8_t Base; // ~0 invalid
|
||||
uint32_t Scale : 8;
|
||||
int32_t Offset;
|
||||
} SIB;
|
||||
};
|
||||
|
||||
struct {
|
||||
uint8_t Type;
|
||||
uint8_t Index; // ~0 invalid
|
||||
uint8_t Base; // ~0 invalid
|
||||
uint32_t Scale : 8;
|
||||
int32_t Offset;
|
||||
} TypeSIB;
|
||||
OpType Type;
|
||||
TypeUnion Data;
|
||||
};
|
||||
|
||||
struct DecodedInst {
|
||||
@@ -418,6 +437,9 @@ struct X86InstInfo {
|
||||
// We don't care if the opcode dispatcher differs
|
||||
return true;
|
||||
}
|
||||
bool operator!=(const X86InstInfo &b) const {
|
||||
return !operator==(b);
|
||||
}
|
||||
};
|
||||
|
||||
static_assert(std::is_trivial<X86InstInfo>::value, "X86InstInfo needs to be trivial");
|
||||
@@ -455,29 +477,29 @@ constexpr size_t MAX_XOP_GROUP_TABLE_SIZE = (1 << 6);
|
||||
|
||||
constexpr size_t MAX_EVEX_TABLE_SIZE = 256;
|
||||
|
||||
extern __attribute__((visibility("default"))) X86InstInfo BaseOps[MAX_PRIMARY_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo SecondBaseOps[MAX_SECOND_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo RepModOps[MAX_REP_MOD_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo RepNEModOps[MAX_REPNE_MOD_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo OpSizeModOps[MAX_OPSIZE_MOD_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo PrimaryInstGroupOps[MAX_INST_GROUP_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo SecondInstGroupOps[MAX_INST_SECOND_GROUP_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo SecondModRMTableOps[MAX_SECOND_MODRM_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo X87Ops[MAX_X87_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo DDDNowOps[MAX_3DNOW_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo H0F38TableOps[MAX_0F_38_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo H0F3ATableOps[MAX_0F_3A_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo BaseOps[MAX_PRIMARY_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo SecondBaseOps[MAX_SECOND_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo RepModOps[MAX_REP_MOD_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo RepNEModOps[MAX_REPNE_MOD_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo OpSizeModOps[MAX_OPSIZE_MOD_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo PrimaryInstGroupOps[MAX_INST_GROUP_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo SecondInstGroupOps[MAX_INST_SECOND_GROUP_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo SecondModRMTableOps[MAX_SECOND_MODRM_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo X87Ops[MAX_X87_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo DDDNowOps[MAX_3DNOW_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo H0F38TableOps[MAX_0F_38_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo H0F3ATableOps[MAX_0F_3A_TABLE_SIZE];
|
||||
|
||||
// VEX
|
||||
extern __attribute__((visibility("default"))) X86InstInfo VEXTableOps[MAX_VEX_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo VEXTableGroupOps[MAX_VEX_GROUP_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo VEXTableOps[MAX_VEX_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo VEXTableGroupOps[MAX_VEX_GROUP_TABLE_SIZE];
|
||||
|
||||
// XOP
|
||||
extern __attribute__((visibility("default"))) X86InstInfo XOPTableOps[MAX_XOP_TABLE_SIZE];
|
||||
extern __attribute__((visibility("default"))) X86InstInfo XOPTableGroupOps[MAX_XOP_GROUP_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo XOPTableOps[MAX_XOP_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo XOPTableGroupOps[MAX_XOP_GROUP_TABLE_SIZE];
|
||||
|
||||
// EVEX
|
||||
extern __attribute__((visibility("default"))) X86InstInfo EVEXTableOps[MAX_EVEX_TABLE_SIZE];
|
||||
extern FEX_DEFAULT_VISIBILITY X86InstInfo EVEXTableOps[MAX_EVEX_TABLE_SIZE];
|
||||
|
||||
__attribute__((visibility("default"))) void InitializeInfoTables(Context::OperatingMode Mode);
|
||||
FEX_DEFAULT_VISIBILITY void InitializeInfoTables(Context::OperatingMode Mode);
|
||||
}
|
||||
@@ -7,12 +7,12 @@ namespace FEXCore::HLE {
|
||||
// Tracking relationships between thread IDs and such
|
||||
class ThreadManagement {
|
||||
public:
|
||||
uint64_t GetUID() { return UID; }
|
||||
uint64_t GetGID() { return GID; }
|
||||
uint64_t GetEUID() { return EUID; }
|
||||
uint64_t GetEGID() { return EGID; }
|
||||
uint64_t GetTID() { return TID; }
|
||||
uint64_t GetPID() { return PID; }
|
||||
uint64_t GetUID() const { return UID; }
|
||||
uint64_t GetGID() const { return GID; }
|
||||
uint64_t GetEUID() const { return EUID; }
|
||||
uint64_t GetEGID() const { return EGID; }
|
||||
uint64_t GetTID() const { return TID; }
|
||||
uint64_t GetPID() const { return PID; }
|
||||
|
||||
uint64_t UID{1000};
|
||||
uint64_t GID{1000};
|
||||
|
||||
+37
-29
@@ -1,8 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <tuple>
|
||||
|
||||
@@ -70,11 +74,10 @@ struct NodeWrapperBase final {
|
||||
Type const *GetNode(uintptr_t Base) const { return reinterpret_cast<Type*>(Base + NodeOffset); }
|
||||
|
||||
void SetOffset(uintptr_t Base, uintptr_t Value) { NodeOffset = Value - Base; }
|
||||
constexpr bool operator==(NodeWrapperBase<Type> const &rhs) const { return NodeOffset == rhs.NodeOffset; }
|
||||
constexpr bool operator!=(NodeWrapperBase<Type> const &rhs) const { return !operator==(rhs); }
|
||||
friend constexpr bool operator==(const NodeWrapperBase<Type>&, const NodeWrapperBase<Type>&) = default;
|
||||
};
|
||||
|
||||
static_assert(std::is_trivial<NodeWrapperBase<OrderedNode>>::value);
|
||||
static_assert(std::is_trivial_v<NodeWrapperBase<OrderedNode>>);
|
||||
|
||||
static_assert(sizeof(NodeWrapperBase<OrderedNode>) == sizeof(uint32_t));
|
||||
|
||||
@@ -252,76 +255,81 @@ class OrderedNode final {
|
||||
void SetUses(uint32_t Uses) { NumUses = Uses; }
|
||||
};
|
||||
|
||||
static_assert(std::is_trivial<OrderedNode>::value);
|
||||
static_assert(std::is_trivially_copyable<OrderedNode>::value);
|
||||
static_assert(std::is_trivial_v<OrderedNode>);
|
||||
static_assert(std::is_trivially_copyable_v<OrderedNode>);
|
||||
static_assert(offsetof(OrderedNode, Header) == 0);
|
||||
static_assert(sizeof(OrderedNode) == (sizeof(OrderedNodeHeader) + sizeof(uint32_t)));
|
||||
|
||||
struct RegisterClassType final {
|
||||
uint32_t Val;
|
||||
operator uint32_t() {
|
||||
constexpr operator uint32_t() const {
|
||||
return Val;
|
||||
}
|
||||
constexpr bool operator==(RegisterClassType const &rhs) const { return Val == rhs.Val; }
|
||||
constexpr bool operator!=(RegisterClassType const &rhs) const { return !operator==(rhs); }
|
||||
friend constexpr bool operator==(const RegisterClassType&, const RegisterClassType&) = default;
|
||||
};
|
||||
|
||||
struct CondClassType final {
|
||||
uint8_t Val;
|
||||
operator uint8_t() {
|
||||
constexpr operator uint8_t() const {
|
||||
return Val;
|
||||
}
|
||||
friend constexpr bool operator==(const CondClassType&, const CondClassType&) = default;
|
||||
};
|
||||
|
||||
struct MemOffsetType final {
|
||||
uint8_t Val;
|
||||
operator uint8_t() {
|
||||
constexpr operator uint8_t() const {
|
||||
return Val;
|
||||
}
|
||||
int operator ==(const MemOffsetType other) {
|
||||
return Val == other.Val;
|
||||
}
|
||||
int operator !=(const MemOffsetType other) {
|
||||
return Val != other.Val;
|
||||
}
|
||||
friend constexpr bool operator==(const MemOffsetType&, const MemOffsetType&) = default;
|
||||
};
|
||||
|
||||
struct TypeDefinition final {
|
||||
uint16_t Val;
|
||||
operator uint16_t() const {
|
||||
|
||||
constexpr operator uint16_t() const {
|
||||
return Val;
|
||||
}
|
||||
|
||||
static TypeDefinition Create(uint8_t Bytes) {
|
||||
static constexpr TypeDefinition Create(uint8_t Bytes) {
|
||||
TypeDefinition Type{};
|
||||
Type.Val = Bytes << 8;
|
||||
return Type;
|
||||
}
|
||||
|
||||
static TypeDefinition Create(uint8_t Bytes, uint8_t Elements) {
|
||||
static constexpr TypeDefinition Create(uint8_t Bytes, uint8_t Elements) {
|
||||
TypeDefinition Type{};
|
||||
Type.Val = (Bytes << 8) | (Elements & 255);
|
||||
return Type;
|
||||
}
|
||||
|
||||
uint8_t Bytes() const {
|
||||
constexpr uint8_t Bytes() const {
|
||||
return Val >> 8;
|
||||
}
|
||||
|
||||
uint8_t Elements() const {
|
||||
constexpr uint8_t Elements() const {
|
||||
return Val & 255;
|
||||
}
|
||||
|
||||
friend constexpr bool operator==(const TypeDefinition&, const TypeDefinition&) = default;
|
||||
};
|
||||
|
||||
static_assert(std::is_trivial<TypeDefinition>::value);
|
||||
static_assert(std::is_trivial_v<TypeDefinition>);
|
||||
|
||||
struct FenceType final {
|
||||
uint8_t Val;
|
||||
operator uint8_t() const {
|
||||
constexpr operator uint8_t() const {
|
||||
return Val;
|
||||
}
|
||||
constexpr bool operator==(FenceType const &rhs) const { return Val == rhs.Val; }
|
||||
constexpr bool operator!=(FenceType const &rhs) const { return !operator==(rhs); }
|
||||
friend constexpr bool operator==(const FenceType&, const FenceType&) = default;
|
||||
};
|
||||
|
||||
struct RoundType final {
|
||||
uint8_t Val;
|
||||
constexpr operator uint8_t() const {
|
||||
return Val;
|
||||
}
|
||||
friend constexpr bool operator==(const RoundType&, const RoundType&) = default;
|
||||
};
|
||||
|
||||
struct SHA256Sum final {
|
||||
@@ -383,7 +391,7 @@ public:
|
||||
return { RealNode, RealNode->Op(IRList) };
|
||||
}
|
||||
|
||||
uint32_t ID() {
|
||||
uint32_t ID() const {
|
||||
return Node.ID();
|
||||
}
|
||||
|
||||
@@ -463,8 +471,8 @@ public:
|
||||
class IRListView;
|
||||
class IREmitter;
|
||||
|
||||
__attribute__((visibility("default"))) void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData);
|
||||
__attribute__((visibility("default"))) IREmitter* Parse(std::istream *in);
|
||||
FEX_DEFAULT_VISIBILITY void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationData *RAData);
|
||||
FEX_DEFAULT_VISIBILITY std::unique_ptr<IREmitter> Parse(std::istream *in);
|
||||
|
||||
template<typename Type>
|
||||
inline uint32_t NodeWrapperBase<Type>::ID() const { return NodeOffset / sizeof(IR::OrderedNode); }
|
||||
|
||||
+24
-3
@@ -111,9 +111,15 @@ friend class FEXCore::IR::PassManager;
|
||||
IRPair<IROp_VExtractElement> _VExtractElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
|
||||
return _VExtractElement(ssa0, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VDupElement> _VDupElement(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, uint8_t Index) {
|
||||
return _VDupElement(ssa0, Index, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAnd> _VAnd(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VAnd(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VBic> _VBic(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VBic(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VOr> _VOr(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VOr(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
@@ -144,12 +150,21 @@ friend class FEXCore::IR::PassManager;
|
||||
IRPair<IROp_VAddV> _VAddV(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VAddV(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMinV> _VUMinV(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VUMinV(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VURAvg> _VURAvg(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VURAvg(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VAbs> _VAbs(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VAbs(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VPopcount> _VPopcount(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VPopcount(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VFMul> _VFMul(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VFMul(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUMin> _VUMin(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUMin(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
@@ -168,6 +183,12 @@ friend class FEXCore::IR::PassManager;
|
||||
IRPair<IROp_VZip2> _VZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VZip2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUnZip> _VUnZip(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUnZip(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUnZip2> _VUnZip2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUnZip2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VCMPEQ> _VCMPEQ(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VCMPEQ(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
@@ -294,9 +315,6 @@ friend class FEXCore::IR::PassManager;
|
||||
IRPair<IROp_Vector_FToF> _Vector_FToF(uint8_t RegisterSize, uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
|
||||
return _Vector_FToF(ssa0, SrcElementSize, RegisterSize, DstElementSize);
|
||||
}
|
||||
IRPair<IROp_Float_FromGPR_U> _Float_FromGPR_U(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
|
||||
return _Float_FromGPR_U(ssa0, SrcElementSize, DstElementSize);
|
||||
}
|
||||
IRPair<IROp_Float_FromGPR_S> _Float_FromGPR_S(uint8_t DstElementSize, uint8_t SrcElementSize, OrderedNode *ssa0) {
|
||||
return _Float_FromGPR_S(ssa0, SrcElementSize, DstElementSize);
|
||||
}
|
||||
@@ -321,6 +339,9 @@ friend class FEXCore::IR::PassManager;
|
||||
IRPair<IROp_VSMull2> _VSMull2(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VSMull2(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VUABDL> _VUABDL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0, OrderedNode *ssa1) {
|
||||
return _VUABDL(ssa0, ssa1, RegisterSize, ElementSize);
|
||||
}
|
||||
IRPair<IROp_VSXTL> _VSXTL(uint8_t RegisterSize, uint8_t ElementSize, OrderedNode *ssa0) {
|
||||
return _VSXTL(ssa0, RegisterSize, ElementSize);
|
||||
}
|
||||
|
||||
+2
-2
@@ -35,12 +35,12 @@ class DualIntrusiveAllocator final {
|
||||
FEXCore::Allocator::free(reinterpret_cast<void*>(Data));
|
||||
}
|
||||
|
||||
bool DataCheckSize(size_t Size) {
|
||||
bool DataCheckSize(size_t Size) const {
|
||||
size_t NewOffset = DataCurrentOffset + Size;
|
||||
return NewOffset <= MemorySize;
|
||||
}
|
||||
|
||||
bool ListCheckSize(size_t Size) {
|
||||
bool ListCheckSize(size_t Size) const {
|
||||
size_t NewOffset = ListCurrentOffset + Size;
|
||||
return NewOffset <= MemorySize;
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ union PhysicalRegister {
|
||||
return PhysicalRegister(InvalidClass, InvalidReg);
|
||||
}
|
||||
|
||||
bool IsInvalid() {
|
||||
bool IsInvalid() const {
|
||||
return *this == Invalid();
|
||||
}
|
||||
};
|
||||
|
||||
+8
-5
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
|
||||
@@ -11,11 +13,12 @@ namespace FEXCore::Allocator {
|
||||
using REALLOC_Hook = void*(*)(void*, size_t);
|
||||
using FREE_Hook = void(*)(void*);
|
||||
|
||||
__attribute__((visibility("default"))) extern MMAP_Hook mmap;
|
||||
__attribute__((visibility("default"))) extern MUNMAP_Hook munmap;
|
||||
__attribute__((visibility("default"))) extern MALLOC_Hook malloc;
|
||||
__attribute__((visibility("default"))) extern REALLOC_Hook realloc;
|
||||
__attribute__((visibility("default"))) extern FREE_Hook free;
|
||||
FEX_DEFAULT_VISIBILITY extern MMAP_Hook mmap;
|
||||
FEX_DEFAULT_VISIBILITY extern MUNMAP_Hook munmap;
|
||||
FEX_DEFAULT_VISIBILITY extern MALLOC_Hook malloc;
|
||||
FEX_DEFAULT_VISIBILITY extern REALLOC_Hook realloc;
|
||||
FEX_DEFAULT_VISIBILITY extern FREE_Hook free;
|
||||
|
||||
void SetupHooks();
|
||||
void ClearHooks();
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
// Header for various utilities that operate on bits and bytes.
|
||||
|
||||
#include <bit>
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
namespace FEXCore {
|
||||
|
||||
// Determines the number of bits inside of a given type.
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr size_t BitSize() noexcept {
|
||||
return sizeof(T) * CHAR_BIT;
|
||||
}
|
||||
|
||||
// Swaps the bytes of a 16-bit unsigned value.
|
||||
[[nodiscard]] inline uint16_t BSwap16(uint16_t value) noexcept {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap16(value);
|
||||
#else
|
||||
return (value >> 8) | (value << 8);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Swaps the bytes of a 32-bit unsigned value.
|
||||
[[nodiscard]] inline uint32_t BSwap32(uint32_t value) noexcept {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap32(value);
|
||||
#else
|
||||
return ((value & 0xFF000000U) >> 24) | ((value & 0x00FF0000U) >> 8) |
|
||||
((value & 0x0000FF00U) << 8) | ((value & 0x000000FFU) << 24);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Swaps the bytes of a 64-bit unsigned value.
|
||||
[[nodiscard]] inline uint64_t BSwap64(uint64_t value) noexcept {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_bswap64(value);
|
||||
#else
|
||||
return ((value & 0xFF00000000000000ULL) >> 56) | ((value & 0x00FF000000000000ULL) >> 40) |
|
||||
((value & 0x0000FF0000000000ULL) >> 24) | ((value & 0x000000FF00000000ULL) >> 8) |
|
||||
((value & 0x00000000FF000000ULL) << 8) | ((value & 0x0000000000FF0000ULL) << 24) |
|
||||
((value & 0x000000000000FF00ULL) << 40) | ((value & 0x00000000000000FFULL) << 56);
|
||||
#endif
|
||||
}
|
||||
|
||||
// Finds the first least-significant set bit within a given value.
|
||||
// Note that all returned indices are 1-based, not 0-based.
|
||||
template <typename T>
|
||||
[[nodiscard]] constexpr int FindFirstSetBit(T value) noexcept {
|
||||
static_assert(std::is_unsigned_v<T>, "Type must be unsigned.");
|
||||
|
||||
if (value == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const int trailing_zeroes = std::countr_zero(value);
|
||||
return trailing_zeroes + 1;
|
||||
}
|
||||
|
||||
} // namespace FEXCore
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
// Contains general abstractions related to compilers used to build FEX.
|
||||
|
||||
// Specifies the minimum alignment for a variable or structure field, measured in bytes.
|
||||
#define FEX_ALIGNED(alignment) __attribute__((aligned(alignment)))
|
||||
|
||||
// Allows annotating declarations with extra information.
|
||||
#define FEX_ANNOTATE(annotation_str) __attribute__((annotate(annotation_str)))
|
||||
|
||||
// Makes the attributed entity have the default DSO visibility level.
|
||||
// Compiler options can affect the visibility of symbols. This attribute
|
||||
// overrides said changes. This gives entities external linkage.
|
||||
#define FEX_DEFAULT_VISIBILITY __attribute__((visibility("default")))
|
||||
|
||||
// Indicates that the specified function doesn't need a function prologue/epilogue.
|
||||
// emitted for it by the compiler.
|
||||
#define FEX_NAKED __attribute__((naked))
|
||||
|
||||
// Specifies that a structure member or structure itself should have the smallest possible alignment.
|
||||
#define FEX_PACKED __attribute__((packed))
|
||||
|
||||
// Causes execution to exit abnormally.
|
||||
#define FEX_TRAP_EXECUTION __builtin_trap()
|
||||
|
||||
// Dictates to the compiler that the path this is on should not be reachable
|
||||
// from normal execution control flow. If normal execution does reach this,
|
||||
// then program behavior is undefined.
|
||||
#define FEX_UNREACHABLE __builtin_unreachable()
|
||||
@@ -1,10 +1,13 @@
|
||||
#pragma once
|
||||
#include "ELFContainer.h"
|
||||
#include <vector>
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/ELFContainer.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
namespace ELFLoader {
|
||||
class __attribute__((visibility("default"))) ELFSymbolDatabase final {
|
||||
class FEX_DEFAULT_VISIBILITY ELFSymbolDatabase final {
|
||||
public:
|
||||
ELFSymbolDatabase(::ELFLoader::ELFContainer *file);
|
||||
~ELFSymbolDatabase();
|
||||
|
||||
+105
-8
@@ -1,7 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <functional>
|
||||
#include <cstdarg>
|
||||
#include <sstream>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace LogMan {
|
||||
enum DebugLevels {
|
||||
@@ -18,8 +23,8 @@ constexpr DebugLevels MSG_LEVEL = INFO;
|
||||
|
||||
namespace Throw {
|
||||
using ThrowHandler = void(*)(char const *Message);
|
||||
__attribute__((visibility("default"))) void InstallHandler(ThrowHandler Handler);
|
||||
__attribute__((visibility("default"))) void UnInstallHandlers();
|
||||
FEX_DEFAULT_VISIBILITY void InstallHandler(ThrowHandler Handler);
|
||||
FEX_DEFAULT_VISIBILITY void UnInstallHandlers();
|
||||
|
||||
[[noreturn]] void M(const char *fmt, va_list args);
|
||||
|
||||
@@ -38,14 +43,32 @@ static inline void A(bool, const char*, ...) {}
|
||||
#define LOGMAN_THROW_A(pred, ...) do {} while (0)
|
||||
#endif
|
||||
|
||||
// Fmt interface
|
||||
|
||||
[[noreturn]] void MFmt(const char *fmt, const fmt::format_args& args);
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
template <typename... Args>
|
||||
static inline void AFmt(bool Value, const char *fmt, const Args&... args) {
|
||||
if (MSG_LEVEL < ASSERT || Value) {
|
||||
return;
|
||||
}
|
||||
MFmt(fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
#define LOGMAN_THROW_A_FMT(pred, ...) do { LogMan::Throw::AFmt(pred, __VA_ARGS__); } while (0)
|
||||
#else
|
||||
static inline void AFmt(bool, const char*, ...) {}
|
||||
#define LOGMAN_THROW_A_FMT(pred, ...) do {} while (0)
|
||||
#endif
|
||||
|
||||
} // namespace Throw
|
||||
|
||||
namespace Msg {
|
||||
using MsgHandler = void(*)(DebugLevels Level, char const *Message);
|
||||
__attribute__((visibility("default"))) void InstallHandler(MsgHandler Handler);
|
||||
__attribute__((visibility("default"))) void UnInstallHandlers();
|
||||
FEX_DEFAULT_VISIBILITY void InstallHandler(MsgHandler Handler);
|
||||
FEX_DEFAULT_VISIBILITY void UnInstallHandlers();
|
||||
|
||||
__attribute__((visibility("default"))) void M(DebugLevels Level, const char *fmt, va_list args);
|
||||
FEX_DEFAULT_VISIBILITY void M(DebugLevels Level, const char *fmt, va_list args);
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
static inline void A(const char *fmt, ...) {
|
||||
@@ -55,7 +78,7 @@ static inline void A(const char *fmt, ...) {
|
||||
M(ASSERT, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
__builtin_trap();
|
||||
FEX_TRAP_EXECUTION;
|
||||
}
|
||||
#define LOGMAN_MSG_A(...) do { LogMan::Msg::A(__VA_ARGS__); } while (0)
|
||||
#else
|
||||
@@ -114,7 +137,81 @@ static inline void ERR(const char *fmt, ...) {
|
||||
#define ERROR_AND_DIE(...) \
|
||||
do { \
|
||||
LogMan::Msg::E(__VA_ARGS__); \
|
||||
__builtin_trap(); \
|
||||
FEX_TRAP_EXECUTION; \
|
||||
} while(0)
|
||||
|
||||
// Fmt-capable interface.
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void MFmtImpl(DebugLevels level, const char* fmt, const fmt::format_args& args);
|
||||
|
||||
template <typename... Args>
|
||||
static inline void MFmt(DebugLevels level, const char* fmt, const Args&... args) {
|
||||
MFmtImpl(level, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static inline void EFmt(const char* fmt, const Args&... args) {
|
||||
if (MSG_LEVEL < ERROR) {
|
||||
return;
|
||||
}
|
||||
MFmtImpl(ERROR, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static inline void DFmt(const char* fmt, const Args&... args) {
|
||||
if (MSG_LEVEL < DEBUG) {
|
||||
return;
|
||||
}
|
||||
MFmtImpl(DEBUG, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static inline void IFmt(const char* fmt, const Args&... args) {
|
||||
if (MSG_LEVEL < INFO) {
|
||||
return;
|
||||
}
|
||||
MFmtImpl(INFO, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static inline void OutFmt(const char* fmt, const Args&... args) {
|
||||
MFmtImpl(STDOUT, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
static inline void ErrFmt(const char* fmt, const Args&... args) {
|
||||
MFmtImpl(STDERR, fmt, fmt::make_format_args(args...));
|
||||
}
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
template <typename... Args>
|
||||
static inline void AFmt(const char *fmt, const Args&... args) {
|
||||
if (MSG_LEVEL < ASSERT) {
|
||||
return;
|
||||
}
|
||||
MFmtImpl(ASSERT, fmt, fmt::make_format_args(args...));
|
||||
FEX_TRAP_EXECUTION;
|
||||
}
|
||||
#define LOGMAN_MSG_A_FMT(...) do { LogMan::Msg::AFmt(__VA_ARGS__); } while (0)
|
||||
#else
|
||||
template <typename... Args>
|
||||
static inline void AFmt(const char*, const Args&...) {}
|
||||
#define LOGMAN_MSG_A_FMT(...) do {} while(0)
|
||||
#endif
|
||||
|
||||
#define WARN_ONCE_FMT(...) \
|
||||
do { \
|
||||
static bool Warned{}; \
|
||||
if (!Warned) { \
|
||||
LogMan::Msg::DFmt(__VA_ARGS__); \
|
||||
Warned = true; \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define ERROR_AND_DIE_FMT(...) \
|
||||
do { \
|
||||
LogMan::Msg::EFmt(__VA_ARGS__); \
|
||||
FEX_TRAP_EXECUTION; \
|
||||
} while(0)
|
||||
|
||||
} // namespace Msg
|
||||
|
||||
+1
Submodule External/drm-headers added at 2b23749e35.
+1
Submodule External/fmt added at 7bdf0628b1.
Vendored
+1
-1
Submodule External/imgui updated: 8b412457dc...df6054ff26.
@@ -17,7 +17,7 @@
|
||||
#include <json-maker.h>
|
||||
|
||||
namespace FEX::Config {
|
||||
bool LoadConfigFile(std::vector<char> &Data, std::string Config) {
|
||||
static bool LoadConfigFile(std::vector<char> &Data, const std::string &Config) {
|
||||
std::fstream ConfigFile;
|
||||
ConfigFile.open(Config, std::ios::in);
|
||||
|
||||
@@ -72,7 +72,7 @@ namespace FEX::Config {
|
||||
return &*alloc->json_objects->emplace(alloc->json_objects->end());
|
||||
}
|
||||
|
||||
void LoadJSonConfig(std::string &Config, std::function<void(const char *Name, const char *ConfigSring)> Func) {
|
||||
static void LoadJSonConfig(const std::string &Config, std::function<void(const char *Name, const char *ConfigSring)> Func) {
|
||||
std::vector<char> Data;
|
||||
if (!LoadConfigFile(Data, Config)) {
|
||||
return;
|
||||
@@ -123,7 +123,7 @@ namespace FEX::Config {
|
||||
#include <FEXCore/Config/ConfigValues.inl>
|
||||
}};
|
||||
|
||||
static const std::map<std::string, FEXCore::Config::ConfigOption> ConfigLookup = {{
|
||||
static const std::map<std::string, FEXCore::Config::ConfigOption, std::less<>> ConfigLookup = {{
|
||||
#define OPT_BASE(type, group, enum, json, default) {#json, FEXCore::Config::ConfigOption::CONFIG_##enum},
|
||||
#include <FEXCore/Config/ConfigValues.inl>
|
||||
}};
|
||||
@@ -132,7 +132,7 @@ namespace FEX::Config {
|
||||
#include <FEXCore/Config/ConfigValues.inl>
|
||||
}};
|
||||
|
||||
void SaveLayerToJSON(std::string Filename, FEXCore::Config::Layer *const Layer) {
|
||||
void SaveLayerToJSON(const std::string& Filename, FEXCore::Config::Layer *const Layer) {
|
||||
char Buffer[4096];
|
||||
char *Dest{};
|
||||
Dest = json_objOpen(Buffer, nullptr);
|
||||
@@ -166,13 +166,13 @@ namespace FEX::Config {
|
||||
}
|
||||
|
||||
MainLoader::MainLoader()
|
||||
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN) {
|
||||
Config = FEXCore::Config::GetConfigFileLocation();
|
||||
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN)
|
||||
, Config{FEXCore::Config::GetConfigFileLocation()} {
|
||||
}
|
||||
|
||||
MainLoader::MainLoader(std::string ConfigFile)
|
||||
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN) {
|
||||
Config = ConfigFile;
|
||||
: FEX::Config::OptionMapper(FEXCore::Config::LayerType::LAYER_MAIN)
|
||||
, Config{std::move(ConfigFile)} {
|
||||
}
|
||||
|
||||
void MainLoader::Load() {
|
||||
@@ -181,7 +181,7 @@ namespace FEX::Config {
|
||||
});
|
||||
}
|
||||
|
||||
AppLoader::AppLoader(std::string Filename, bool Global)
|
||||
AppLoader::AppLoader(const std::string& Filename, bool Global)
|
||||
: FEX::Config::OptionMapper(Global ? FEXCore::Config::LayerType::LAYER_GLOBAL_APP : FEXCore::Config::LayerType::LAYER_LOCAL_APP) {
|
||||
Config = FEXCore::Config::GetApplicationConfig(Filename, Global);
|
||||
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace FEX::Config {
|
||||
|
||||
class AppLoader final : public FEX::Config::OptionMapper {
|
||||
public:
|
||||
explicit AppLoader(std::string Filename, bool Global);
|
||||
explicit AppLoader(const std::string& Filename, bool Global);
|
||||
void Load();
|
||||
|
||||
private:
|
||||
@@ -56,5 +56,5 @@ namespace FEX::Config {
|
||||
char *const *envp;
|
||||
};
|
||||
|
||||
void SaveLayerToJSON(std::string Filename, FEXCore::Config::Layer *const Layer);
|
||||
void SaveLayerToJSON(const std::string& Filename, FEXCore::Config::Layer *const Layer);
|
||||
}
|
||||
@@ -174,11 +174,11 @@ namespace HostFactory {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
return new HostCore(CTX, Thread, false);
|
||||
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
return std::make_unique<HostCore>(CTX, Thread, false);
|
||||
}
|
||||
#else
|
||||
FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
LOGMAN_MSG_A("HostCPU factory doesn't exist for this host");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
class CPUBackend;
|
||||
}
|
||||
@@ -9,6 +13,5 @@ namespace FEXCore::Core {
|
||||
}
|
||||
|
||||
namespace HostFactory {
|
||||
FEXCore::CPU::CPUBackend *CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
FEXCore::CPU::CPUBackend *CPUCreationFactoryFallback(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
std::unique_ptr<FEXCore::CPU::CPUBackend> CPUCreationFactory(FEXCore::Context::Context* CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
}
|
||||
@@ -285,7 +285,7 @@ public:
|
||||
return DB.DefaultRIP();
|
||||
}
|
||||
|
||||
bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override {
|
||||
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override {
|
||||
auto DoMMap = [Mapper](uint64_t Address, size_t Size, bool FixedNoReplace) -> void* {
|
||||
void *Result = Mapper(reinterpret_cast<void*>(Address), Size, PROT_READ | PROT_WRITE, (FixedNoReplace ? MAP_FIXED_NOREPLACE : MAP_FIXED) | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
LOGMAN_THROW_A(Result != (void*)~0ULL, "Couldn't mmap");
|
||||
|
||||
@@ -80,7 +80,8 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
return false;
|
||||
} else {
|
||||
auto Filename = get_fdpath(file.fd);
|
||||
Sections.push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0});
|
||||
Sections = std::make_unique<std::vector<LoadedSection>>();
|
||||
Sections->push_back({Base, (uintptr_t)rv, size, (off_t)off, Filename, (prot & PROT_EXEC) != 0});
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -203,7 +204,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
bool Executable;
|
||||
};
|
||||
|
||||
std::vector<LoadedSection> Sections;
|
||||
std::unique_ptr<std::vector<LoadedSection>> Sections;
|
||||
ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *AdditionalEnvp = nullptr) :
|
||||
Args {args} {
|
||||
|
||||
@@ -254,6 +255,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
}
|
||||
}
|
||||
|
||||
void FreeSections() {
|
||||
Sections.reset();
|
||||
}
|
||||
|
||||
virtual uint64_t StackSize() const override { return STACK_SIZE; }
|
||||
virtual uint64_t GetStackPointer() override { return StackPointer; }
|
||||
virtual uint64_t DefaultRIP() const override { return Entrypoint; };
|
||||
@@ -268,8 +273,7 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
uint64_t val;
|
||||
};
|
||||
|
||||
virtual bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override {
|
||||
|
||||
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override {
|
||||
for (auto Header: MainElf.phdrs) {
|
||||
if (Header.p_type == PT_GNU_STACK) {
|
||||
if (Header.p_flags & PF_X)
|
||||
|
||||
+186
-119
@@ -30,10 +30,14 @@ $end_info$
|
||||
#include <filesystem>
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
#include <thread>
|
||||
#include <queue>
|
||||
|
||||
#include <sys/sysinfo.h>
|
||||
|
||||
namespace {
|
||||
static bool SilentLog;
|
||||
static FILE *OutputFD {stderr};
|
||||
static int OutputFD {STDERR_FILENO};
|
||||
|
||||
void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
|
||||
const char *CharLevel{nullptr};
|
||||
@@ -66,15 +70,17 @@ void MsgHandler(LogMan::DebugLevels Level, char const *Message) {
|
||||
}
|
||||
|
||||
if (!SilentLog) {
|
||||
fprintf(OutputFD, "[%s] %s\n", CharLevel, Message);
|
||||
fflush(OutputFD);
|
||||
std::ostringstream Output;
|
||||
Output << "[" << CharLevel << "] " << Message << std::endl;
|
||||
write(OutputFD, Output.str().c_str(), Output.str().size());
|
||||
}
|
||||
}
|
||||
|
||||
void AssertHandler(char const *Message) {
|
||||
if (!SilentLog) {
|
||||
fprintf(OutputFD, "[ASSERT] %s\n", Message);
|
||||
fflush(OutputFD);
|
||||
std::ostringstream Output;
|
||||
Output << "[ASSERT] " << Message << std::endl;
|
||||
write(OutputFD, Output.str().c_str(), Output.str().size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,6 +166,152 @@ bool IsInterpreterInstalled() {
|
||||
std::filesystem::exists("/proc/sys/fs/binfmt_misc/FEX-x86_64");
|
||||
}
|
||||
|
||||
void AOTGenSection(FEXCore::Context::Context *CTX, ELFCodeLoader2::LoadedSection &Section) {
|
||||
|
||||
// Make sure this section is executable and big enough
|
||||
if (!Section.Executable || Section.Size < 16)
|
||||
return;
|
||||
|
||||
std::set<uintptr_t> InitialBranchTargets;
|
||||
|
||||
// Load the ELF again with symbol parsing this time
|
||||
ELFLoader::ELFContainer container{Section.Filename, "", false};
|
||||
|
||||
// Add symbols to the branch targets list
|
||||
container.AddSymbols([&](ELFLoader::ELFSymbol* sym) {
|
||||
auto Destination = sym->Address + Section.ElfBase;
|
||||
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
|
||||
return; // outside of current section, unlikely to be real code
|
||||
}
|
||||
|
||||
InitialBranchTargets.insert(Destination);
|
||||
});
|
||||
|
||||
LogMan::Msg::I("Symbol seed: %ld", InitialBranchTargets.size());
|
||||
|
||||
// Add unwind entries to the branch target list
|
||||
container.AddUnwindEntries([&](uintptr_t Entry) {
|
||||
auto Destination = Entry + Section.ElfBase;
|
||||
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
|
||||
return; // outside of current section, unlikely to be real code
|
||||
}
|
||||
|
||||
InitialBranchTargets.insert(Destination);
|
||||
});
|
||||
|
||||
LogMan::Msg::I("Symbol + Unwind seed: %ld", InitialBranchTargets.size());
|
||||
|
||||
// Scan the executable section and try to find function entries
|
||||
for (size_t Offset = 0; Offset < (Section.Size - 16); Offset++) {
|
||||
uint8_t *pCode = (uint8_t *)(Section.Base + Offset);
|
||||
|
||||
// Possible CALL <disp32>
|
||||
if (*pCode == 0xE8) {
|
||||
uintptr_t Destination = (int)(pCode[1] | (pCode[2] << 8) | (pCode[3] << 16) | (pCode[4] << 24));
|
||||
Destination += (uintptr_t)pCode + 5;
|
||||
|
||||
auto DestinationPtr = (uint8_t*)Destination;
|
||||
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
|
||||
continue; // outside of current section, unlikely to be real code
|
||||
|
||||
if (DestinationPtr[0] == 0 && DestinationPtr[1] == 0)
|
||||
continue; // add al, [rax], unlikely to be real code
|
||||
|
||||
InitialBranchTargets.insert(Destination);
|
||||
}
|
||||
|
||||
// endbr64 marker marks an indirect branch destination
|
||||
if (pCode[0] == 0xf3 && pCode[1] == 0x0f && pCode[2] == 0x1e && pCode[3] == 0xfa) {
|
||||
InitialBranchTargets.insert((uintptr_t)pCode);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t SectionMaxAddress = Section.Base + Section.Size;
|
||||
|
||||
std::set<uint64_t> Compiled;
|
||||
std::atomic<int> counter = 0;
|
||||
|
||||
std::queue<uint64_t> BranchTargets;
|
||||
|
||||
// Setup BranchTargets, Compiled sets from InitiaBranchTargets
|
||||
|
||||
Compiled.insert(InitialBranchTargets.begin(), InitialBranchTargets.end());
|
||||
for (auto BranchTarget: InitialBranchTargets) {
|
||||
BranchTargets.push(BranchTarget);
|
||||
}
|
||||
|
||||
InitialBranchTargets.clear();
|
||||
|
||||
|
||||
std::mutex QueueMutex;
|
||||
std::vector<std::thread> ThreadPool;
|
||||
|
||||
for (int i = 0; i < get_nprocs_conf(); i++) {
|
||||
std::thread thd([&BranchTargets, CTX, &counter, &Compiled, &Section, &QueueMutex, SectionMaxAddress]() {
|
||||
|
||||
// Setup thread - Each compilation thread uses its own backing FEX thread
|
||||
FEXCore::Core::CPUState state;
|
||||
auto Thread = FEXCore::Context::CreateThread(CTX, &state, gettid());
|
||||
std::set<uint64_t> ExternalBranchesLocal;
|
||||
FEXCore::Context::ConfigureAOTGen(Thread, &ExternalBranchesLocal, SectionMaxAddress);
|
||||
|
||||
|
||||
for (;;) {
|
||||
uint64_t BranchTarget;
|
||||
|
||||
// Get a entrypoint to process from the queue
|
||||
QueueMutex.lock();
|
||||
if (BranchTargets.empty()) {
|
||||
QueueMutex.unlock();
|
||||
break; // no entrypoint to process - exit
|
||||
}
|
||||
|
||||
BranchTarget = BranchTargets.front();
|
||||
BranchTargets.pop();
|
||||
QueueMutex.unlock();
|
||||
|
||||
// Compile entrypoint
|
||||
counter++;
|
||||
FEXCore::Context::CompileRIP(Thread, BranchTarget);
|
||||
|
||||
// Are there more branches?
|
||||
if (ExternalBranchesLocal.size() > 0) {
|
||||
// Add them to the "to process" list
|
||||
QueueMutex.lock();
|
||||
for(auto Destination: ExternalBranchesLocal) {
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
|
||||
continue;
|
||||
if (Compiled.contains(Destination))
|
||||
continue;
|
||||
Compiled.insert(Destination);
|
||||
BranchTargets.push(Destination);
|
||||
}
|
||||
QueueMutex.unlock();
|
||||
ExternalBranchesLocal.clear();
|
||||
}
|
||||
}
|
||||
|
||||
// All entryproints processed, cleanup this thread
|
||||
FEXCore::Context::DestroyThread(CTX, Thread);
|
||||
});
|
||||
|
||||
// Add to the thread pool
|
||||
ThreadPool.push_back(std::move(thd));
|
||||
}
|
||||
|
||||
// Make sure all threads are finished
|
||||
for (auto & Thread: ThreadPool) {
|
||||
Thread.join();
|
||||
}
|
||||
|
||||
ThreadPool.clear();
|
||||
|
||||
LogMan::Msg::I("\nAll Done: %d", counter.load());
|
||||
}
|
||||
|
||||
int main(int argc, char **argv, char **const envp) {
|
||||
bool IsInterpreter = RanAsInterpreter(argv[0]);
|
||||
LogMan::Throw::InstallHandler(AssertHandler);
|
||||
@@ -217,13 +369,13 @@ int main(int argc, char **argv, char **const envp) {
|
||||
if (!::SilentLog) {
|
||||
auto LogFile = OutputLog();
|
||||
if (LogFile == "stderr") {
|
||||
OutputFD = stderr;
|
||||
OutputFD = STDERR_FILENO;
|
||||
}
|
||||
else if (LogFile == "stdout") {
|
||||
OutputFD = stdout;
|
||||
OutputFD = STDOUT_FILENO;
|
||||
}
|
||||
else if (!LogFile.empty()) {
|
||||
OutputFD = fopen(LogFile.c_str(), "wb");
|
||||
OutputFD = open(LogFile.c_str(), O_CREAT | O_CLOEXEC | O_WRONLY);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,6 +444,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
}
|
||||
}
|
||||
|
||||
// System allocator is now system allocator or FEX
|
||||
FEXCore::Context::InitializeStaticTables(Loader.Is64BitMode() ? FEXCore::Context::MODE_64BIT : FEXCore::Context::MODE_32BIT);
|
||||
|
||||
auto CTX = FEXCore::Context::CreateNewContext();
|
||||
@@ -327,7 +480,6 @@ int main(int argc, char **argv, char **const envp) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (AOTIRLoad() || AOTIRCapture() || AOTIRGenerate()) {
|
||||
LogMan::Msg::I("Warning: AOTIR is experimental, and might lead to crashes. Capture doesn't work with programs that fork.");
|
||||
}
|
||||
@@ -338,98 +490,26 @@ int main(int argc, char **argv, char **const envp) {
|
||||
return open(filepath.c_str(), O_RDONLY);
|
||||
});
|
||||
|
||||
for(auto Section: Loader.Sections) {
|
||||
FEXCore::Context::SetAOTIRWriter(CTX, [](const std::string& fileid) -> std::unique_ptr<std::ostream> {
|
||||
auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir");
|
||||
auto AOTWrite = std::make_unique<std::ofstream>(filepath, std::ios::out | std::ios::binary);
|
||||
if (*AOTWrite) {
|
||||
std::filesystem::resize_file(filepath, 0);
|
||||
AOTWrite->seekp(0);
|
||||
LogMan::Msg::I("AOTIR: Storing %s", fileid.c_str());
|
||||
} else {
|
||||
LogMan::Msg::I("AOTIR: Failed to store %s", fileid.c_str());
|
||||
}
|
||||
return AOTWrite;
|
||||
});
|
||||
|
||||
for(auto Section: *Loader.Sections) {
|
||||
FEXCore::Context::AddNamedRegion(CTX, Section.Base, Section.Size, Section.Offs, Section.Filename);
|
||||
}
|
||||
|
||||
if (AOTIRGenerate()) {
|
||||
for(auto Section: Loader.Sections) {
|
||||
if (Section.Executable && Section.Size > 16) {
|
||||
ELFLoader::ELFContainer container{Section.Filename, "", false};
|
||||
|
||||
std::set<uintptr_t> BranchTargets;
|
||||
|
||||
container.AddSymbols([&](ELFLoader::ELFSymbol* sym) {
|
||||
auto Destination = sym->Address + Section.ElfBase;
|
||||
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
|
||||
//printf("Sym : %lx %lx out of range\n", sym->Address, Destination);
|
||||
return; // outside of current section, unlikely to be real code
|
||||
}
|
||||
|
||||
BranchTargets.insert(Destination);
|
||||
});
|
||||
|
||||
LogMan::Msg::I("Symbol seed: %ld", BranchTargets.size());
|
||||
|
||||
container.AddUnwindEntries([&](uintptr_t Entry) {
|
||||
auto Destination = Entry + Section.ElfBase;
|
||||
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) ) {
|
||||
//printf("Sym : %lx %lx out of range\n", sym->Address, Destination);
|
||||
return; // outside of current section, unlikely to be real code
|
||||
}
|
||||
|
||||
BranchTargets.insert(Destination);
|
||||
});
|
||||
|
||||
|
||||
LogMan::Msg::I("Symbol + Unwind seed: %ld", BranchTargets.size());
|
||||
|
||||
for (size_t Offset = 0; Offset < (Section.Size - 16); Offset++) {
|
||||
uint8_t *pCode = (uint8_t *)(Section.Base + Offset);
|
||||
|
||||
if (*pCode == 0xE8) {
|
||||
uintptr_t Destination = (int)(pCode[1] | (pCode[2] << 8) | (pCode[3] << 16) | (pCode[4] << 24));
|
||||
Destination += (uintptr_t)pCode + 5;
|
||||
|
||||
auto DestinationPtr = (uint8_t*)Destination;
|
||||
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
|
||||
continue; // outside of current section, unlikely to be real code
|
||||
|
||||
if (DestinationPtr[0] == 0 && DestinationPtr[1] == 0)
|
||||
continue; // add al, [rax], unlikely to be real code
|
||||
/*
|
||||
if (DestinationPtr[0] == 0x44 && DestinationPtr[1] == 0x0f && DestinationPtr[2] == 0x6f)
|
||||
continue; // REX.W + movq leads to frontend bugs
|
||||
*/
|
||||
BranchTargets.insert(Destination);
|
||||
}
|
||||
|
||||
if (pCode[0] == 0xf3 && pCode[1] == 0x0f && pCode[2] == 0x1e && pCode[3] == 0xfa) {
|
||||
BranchTargets.insert((uintptr_t)pCode);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t SectionMaxAddress = Section.Base + Section.Size;
|
||||
std::set<uint64_t> ExternalBranches;
|
||||
|
||||
FEXCore::Context::ConfigureAOTGen(CTX, &ExternalBranches, SectionMaxAddress);
|
||||
|
||||
std::set<uint64_t> Compiled;
|
||||
int counter = 0;
|
||||
do {
|
||||
LogMan::Msg::I("Discovered %ld Branch Targets in this pass", BranchTargets.size());
|
||||
for (auto RIP: BranchTargets) {
|
||||
if ((counter++) % 1000 == 0)
|
||||
LogMan::Msg::I("Compiling %d %lX", counter, RIP - Section.ElfBase);
|
||||
FEXCore::Context::CompileRIP(CTX, RIP);
|
||||
Compiled.insert(RIP);
|
||||
}
|
||||
LogMan::Msg::I("\nPass Done");
|
||||
BranchTargets.clear();
|
||||
for (auto Destination: ExternalBranches) {
|
||||
if (! (Destination >= Section.Base && Destination <= (Section.Base + Section.Size)) )
|
||||
continue;
|
||||
if (Compiled.contains(Destination))
|
||||
continue;
|
||||
BranchTargets.insert(Destination);
|
||||
}
|
||||
ExternalBranches.clear();
|
||||
} while (BranchTargets.size() > 0);
|
||||
LogMan::Msg::I("\nAll Done: %d", counter);
|
||||
}
|
||||
for(auto &Section: *Loader.Sections) {
|
||||
AOTGenSection(CTX, Section);
|
||||
}
|
||||
} else {
|
||||
FEXCore::Context::RunUntilExit(CTX);
|
||||
@@ -448,24 +528,10 @@ int main(int argc, char **argv, char **const envp) {
|
||||
|
||||
if (AOTIRCapture() || AOTIRGenerate()) {
|
||||
|
||||
auto WroteCache = FEXCore::Context::WriteAOTIR(CTX, [](const std::string& fileid) -> std::unique_ptr<std::ostream> {
|
||||
auto filepath = std::filesystem::path(FEXCore::Config::GetDataDirectory()) / "aotir" / (fileid + ".aotir");
|
||||
auto AOTWrite = std::make_unique<std::ofstream>(filepath, std::ios::out | std::ios::binary);
|
||||
if (*AOTWrite) {
|
||||
std::filesystem::resize_file(filepath, 0);
|
||||
AOTWrite->seekp(0);
|
||||
LogMan::Msg::I("AOTIR: Storing %s", fileid.c_str());
|
||||
} else {
|
||||
LogMan::Msg::I("AOTIR: Failed to store %s", fileid.c_str());
|
||||
}
|
||||
return AOTWrite;
|
||||
});
|
||||
|
||||
if (WroteCache) {
|
||||
LogMan::Msg::I("AOTIR Cache Stored");
|
||||
} else {
|
||||
LogMan::Msg::E("AOTIR Cache Store Failed");
|
||||
}
|
||||
FEXCore::Context::FinalizeAOTIRCache(CTX);
|
||||
|
||||
LogMan::Msg::I("AOTIR Cache Stored");
|
||||
}
|
||||
|
||||
auto ProgramStatus = FEXCore::Context::GetProgramStatus(CTX);
|
||||
@@ -474,16 +540,17 @@ int main(int argc, char **argv, char **const envp) {
|
||||
SignalDelegation.reset();
|
||||
FEXCore::Context::DestroyContext(CTX);
|
||||
|
||||
FEXCore::Context::ShutdownStaticTables();
|
||||
|
||||
Loader.FreeSections();
|
||||
|
||||
FEXCore::Config::Shutdown();
|
||||
|
||||
LogMan::Throw::UnInstallHandlers();
|
||||
LogMan::Msg::UnInstallHandlers();
|
||||
|
||||
if (OutputFD != stderr &&
|
||||
OutputFD != stdout &&
|
||||
OutputFD != nullptr) {
|
||||
fclose(OutputFD);
|
||||
}
|
||||
FEXCore::Allocator::ClearHooks();
|
||||
// Allocator is now original system allocator
|
||||
|
||||
if (ShutdownReason == FEXCore::Context::ExitReason::EXIT_SHUTDOWN) {
|
||||
return ProgramStatus;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include "Common/Config.h"
|
||||
#include "Common/MathUtils.h"
|
||||
|
||||
#include <FEXCore/Core/CodeLoader.h>
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <cassert>
|
||||
@@ -10,13 +10,16 @@
|
||||
#include <fstream>
|
||||
#include <sys/mman.h>
|
||||
#include <vector>
|
||||
|
||||
#include <FEXCore/Core/CodeLoader.h>
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/BitUtils.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
#include <FEXCore/Utils/ELFContainer.h>
|
||||
#include <FEXCore/Utils/ELFSymbolDatabase.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
namespace FEX::HarnessHelper {
|
||||
inline bool CompareStates(FEXCore::Core::CPUState const& State1,
|
||||
@@ -214,7 +217,7 @@ namespace FEX::HarnessHelper {
|
||||
[[maybe_unused]] std::bitset<64> RegFlags = RegData->RegKey;
|
||||
assert(RegFlags.count() == 1 && "Must set reg data explicitly per register");
|
||||
|
||||
size_t NameIndex = __builtin_ffsl(RegData->RegKey)- 1;
|
||||
size_t NameIndex = FEXCore::FindFirstSetBit(RegData->RegKey) - 1;
|
||||
auto Offset = OffsetArray[NameIndex];
|
||||
uint64_t *State1Data = reinterpret_cast<uint64_t*>(reinterpret_cast<uint64_t>(State1) + Offset);
|
||||
uint64_t *State2Data = reinterpret_cast<uint64_t*>(reinterpret_cast<uint64_t>(State2) + Offset);
|
||||
@@ -306,24 +309,24 @@ namespace FEX::HarnessHelper {
|
||||
uint32_t OptionMemDataOffset;
|
||||
uint32_t OptionMemDataCount;
|
||||
uint8_t AdditionalData[];
|
||||
}__attribute__((packed));
|
||||
} FEX_PACKED;
|
||||
|
||||
struct MemoryRegionBase {
|
||||
uint64_t Region;
|
||||
uint64_t Size;
|
||||
} __attribute__((packed));
|
||||
} FEX_PACKED;
|
||||
|
||||
struct RegDataStructBase {
|
||||
uint32_t RegDataCount;
|
||||
uint64_t RegKey;
|
||||
uint64_t RegValues[];
|
||||
} __attribute__((packed));
|
||||
} FEX_PACKED;
|
||||
|
||||
struct MemDataStructBase {
|
||||
uint64_t address;
|
||||
uint32_t length;
|
||||
uint8_t data[];
|
||||
} __attribute__((packed));
|
||||
} FEX_PACKED;
|
||||
|
||||
std::vector<char> RawConfigFile;
|
||||
ConfigStructBase BaseConfig;
|
||||
@@ -365,7 +368,7 @@ namespace FEX::HarnessHelper {
|
||||
return RIP;
|
||||
}
|
||||
|
||||
bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override {
|
||||
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override {
|
||||
bool LimitedSize = true;
|
||||
auto DoMMap = [](uint64_t Address, size_t Size) -> void* {
|
||||
void *Result = FEXCore::Allocator::mmap(reinterpret_cast<void*>(Address), Size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
|
||||
@@ -84,7 +84,7 @@ public:
|
||||
return IR->GetEntryRIP();
|
||||
}
|
||||
|
||||
bool MapMemory(std::function<void *(void *addr, size_t length, int prot, int flags, int fd, off_t offset)> Mapper, std::function<int(void *addr, size_t length)> Unmapper) override
|
||||
bool MapMemory(const MapperFn& Mapper, const UnmapperFn& Unmapper) override
|
||||
{
|
||||
// Map the memory regions the test file asks for
|
||||
IR->MapRegions();
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace FEX::IRLoader {
|
||||
return;
|
||||
}
|
||||
|
||||
ParsedCode.reset(FEXCore::IR::Parse(&fp));
|
||||
ParsedCode = FEXCore::IR::Parse(&fp);
|
||||
|
||||
if (ParsedCode) {
|
||||
auto NewIR = ParsedCode->ViewIR();
|
||||
|
||||
@@ -41,6 +41,7 @@ add_library(LinuxEmulation STATIC
|
||||
Syscalls/FS.cpp
|
||||
Syscalls/Info.cpp
|
||||
Syscalls/IO.cpp
|
||||
Syscalls/IOUring.cpp
|
||||
Syscalls/Key.cpp
|
||||
Syscalls/Memory.cpp
|
||||
Syscalls/Msg.cpp
|
||||
@@ -58,6 +59,7 @@ add_library(LinuxEmulation STATIC
|
||||
|
||||
target_link_libraries(LinuxEmulation FEXCore pthread numa)
|
||||
target_include_directories(LinuxEmulation PRIVATE ${CMAKE_BINARY_DIR}/generated)
|
||||
target_include_directories(LinuxEmulation PRIVATE ${PROJECT_SOURCE_DIR}/External/drm-headers/include/)
|
||||
|
||||
set(HEADERS_TO_VERIFY
|
||||
x32/Types.h x86_32 # This needs to match structs to 32bit structs
|
||||
|
||||
@@ -546,38 +546,41 @@ namespace FEX::EmulatedFile {
|
||||
}
|
||||
|
||||
for (int i = 0; i < CPUCores; ++i) {
|
||||
cpu_stream << "processor : " << i << std::endl; // Logical id
|
||||
cpu_stream << "vendor_id : " << vendorid.Str << std::endl;
|
||||
cpu_stream << "cpu family : " << Family << std::endl;
|
||||
cpu_stream << "model : " << (info.Model + (info.FamilyID >= 6 ? (info.ExModelID << 4) : 0)) << std::endl;
|
||||
cpu_stream << "model name : " << modelname.Str << std::endl;
|
||||
cpu_stream << "stepping : " << info.Stepping << std::endl;
|
||||
cpu_stream << "microcode : 0x0" << std::endl;
|
||||
cpu_stream << "cpu MHz : 3000" << std::endl;
|
||||
cpu_stream << "cache size : 512 KB" << std::endl;
|
||||
cpu_stream << "physical id : 0" << std::endl; // Socket id (always 0 for a single socket system)
|
||||
cpu_stream << "siblings : " << CPUCores << std::endl; // Number of logical cores
|
||||
cpu_stream << "core id : " << i << std::endl; // Physical id
|
||||
cpu_stream << "cpu cores : " << CPUCores << std::endl; // Number of physical cores
|
||||
cpu_stream << "apicid : " << i << std::endl;
|
||||
cpu_stream << "initial apicid : " << i << std::endl;
|
||||
cpu_stream << "fpu : " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
|
||||
cpu_stream << "fpu_exception : " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
|
||||
cpu_stream << "cpuid level : " << vendorid.id << std::endl;
|
||||
cpu_stream << "wp : yes" << std::endl;
|
||||
cpu_stream << "flags : " << flags_data.str() << std::endl;
|
||||
cpu_stream << "processor\t: " << i << std::endl; // Logical id
|
||||
cpu_stream << "vendor_id\t: " << vendorid.Str << std::endl;
|
||||
cpu_stream << "cpu family\t: " << Family << std::endl;
|
||||
cpu_stream << "model\t\t: " << (info.Model + (info.FamilyID >= 6 ? (info.ExModelID << 4) : 0)) << std::endl;
|
||||
cpu_stream << "model name\t: " << modelname.Str << std::endl;
|
||||
cpu_stream << "stepping\t: " << info.Stepping << std::endl;
|
||||
cpu_stream << "microcode\t: 0x0" << std::endl;
|
||||
cpu_stream << "cpu MHz\t\t: 3000" << std::endl;
|
||||
cpu_stream << "cache size\t: 512 KB" << std::endl;
|
||||
cpu_stream << "physical id\t: 0" << std::endl; // Socket id (always 0 for a single socket system)
|
||||
cpu_stream << "siblings\t: " << CPUCores << std::endl; // Number of logical cores
|
||||
cpu_stream << "core id\t\t: " << i << std::endl; // Physical id
|
||||
cpu_stream << "cpu cores\t: " << CPUCores << std::endl; // Number of physical cores
|
||||
cpu_stream << "apicid\t\t: " << i << std::endl;
|
||||
cpu_stream << "initial apicid\t: " << i << std::endl;
|
||||
cpu_stream << "fpu\t\t: " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
|
||||
cpu_stream << "fpu_exception\t: " << (res_1.edx & (1 << 0) ? "yes" : "no") << std::endl;
|
||||
cpu_stream << "cpuid level\t: " << vendorid.id << std::endl;
|
||||
cpu_stream << "wp\t\t: yes" << std::endl;
|
||||
cpu_stream << "flags\t\t: " << flags_data.str() << std::endl;
|
||||
|
||||
// We don't have any bugs, don't question it
|
||||
cpu_stream << "bugs : " << std::endl;
|
||||
cpu_stream << "bogomips : 8000.0" << std::endl;
|
||||
cpu_stream << "bugs\t\t: " << std::endl;
|
||||
cpu_stream << "bogomips\t: 8000.0" << std::endl;
|
||||
// These next four aren't necessarily correct
|
||||
cpu_stream << "TLB size : 2560 4K pages" << std::endl;
|
||||
cpu_stream << "clflush size : 64" << std::endl;
|
||||
cpu_stream << "cache_alignment : 64" << std::endl;
|
||||
cpu_stream << "TLB size\t: 2560 4K pages" << std::endl;
|
||||
cpu_stream << "clflush size\t: 64" << std::endl;
|
||||
cpu_stream << "cache_alignment\t : 64" << std::endl;
|
||||
|
||||
// Cortex-A is 40 or 44 bits physical, and 48/52 virtual
|
||||
// Choose the lesser configuration
|
||||
cpu_stream << "address sizes : 40 bits physical, 48 bits virtual" << std::endl;
|
||||
cpu_stream << "address sizes\t: 40 bits physical, 48 bits virtual" << std::endl;
|
||||
|
||||
// No power management but required to report
|
||||
cpu_stream << "power management: " << std::endl;
|
||||
|
||||
cpu_stream << std::endl;
|
||||
}
|
||||
@@ -597,8 +600,12 @@ namespace FEX::EmulatedFile {
|
||||
|
||||
FDReadCreators["/proc/sys/kernel/osrelease"] = [&](FEXCore::Context::Context *ctx, int32_t fd, const char *pathname, int32_t flags, mode_t mode) -> int32_t {
|
||||
FILE *fp = tmpfile();
|
||||
const char kernel_version[] = "5.0.0\0";
|
||||
fwrite(kernel_version, sizeof(uint8_t), strlen(kernel_version) + 1, fp);
|
||||
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
|
||||
fprintf(fp, "%d.%d.%d\n",
|
||||
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
|
||||
fputc('\0', fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
int32_t f = fileno(fp);
|
||||
return f;
|
||||
@@ -608,8 +615,13 @@ namespace FEX::EmulatedFile {
|
||||
FILE *fp = tmpfile();
|
||||
// UTS version NEEDS to be in a format that can pass to `date -d`
|
||||
// Format of this is Linux version <Release> (<Compile By>@<Compile Host>) (<Linux Compiler>) #<version> {SMP, PREEMPT, PREEMPT_RT} <UTS version>\n"
|
||||
const char kernel_version[] = "Linux version 5.0.0 (FEX@FEX) (clang) #" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__ "\n\0";
|
||||
fwrite(kernel_version, sizeof(uint8_t), strlen(kernel_version) + 1, fp);
|
||||
const char kernel_version[] = "Linux version %d.%d.%d (FEX@FEX) (clang) #" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__ "\n";
|
||||
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
|
||||
fprintf(fp, kernel_version,
|
||||
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
|
||||
fputc('\0', fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
int32_t f = fileno(fp);
|
||||
return f;
|
||||
@@ -642,6 +654,10 @@ namespace FEX::EmulatedFile {
|
||||
// Finish off with a null terminator
|
||||
fwrite("\0", sizeof(uint8_t), 1, fp);
|
||||
}
|
||||
|
||||
// One additional null terminator to finish the list
|
||||
fwrite("\0", sizeof(uint8_t), 1, fp);
|
||||
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
int32_t f = fileno(fp);
|
||||
return f;
|
||||
|
||||
@@ -6,6 +6,7 @@ $end_info$
|
||||
*/
|
||||
|
||||
#include "Tests/LinuxSyscalls/FileManagement.h"
|
||||
#include "Tests/LinuxSyscalls/Syscalls.h"
|
||||
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <cstring>
|
||||
@@ -121,7 +122,7 @@ FileManager::FileManager(FEXCore::Context::Context *ctx)
|
||||
FileManager::~FileManager() {
|
||||
}
|
||||
|
||||
std::string FileManager::GetEmulatedPath(const char *pathname) {
|
||||
std::string FileManager::GetEmulatedPath(const char *pathname, bool FollowSymlink) {
|
||||
auto RootFSPath = LDPath();
|
||||
if (!pathname ||
|
||||
pathname[0] != '/' ||
|
||||
@@ -134,7 +135,20 @@ std::string FileManager::GetEmulatedPath(const char *pathname) {
|
||||
return thunkOverlay->second;
|
||||
}
|
||||
|
||||
return RootFSPath + pathname;
|
||||
std::string Path = RootFSPath + pathname;
|
||||
if (FollowSymlink) {
|
||||
std::error_code ec;
|
||||
while(std::filesystem::is_symlink(Path, ec)) {
|
||||
auto SymlinkTarget = std::filesystem::read_symlink(Path);
|
||||
if (SymlinkTarget.is_absolute()) {
|
||||
Path = RootFSPath + SymlinkTarget.string();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Path;
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +162,9 @@ std::optional<std::string> FileManager::GetSelf(const char *Pathname) {
|
||||
char PidSelfPath[50];
|
||||
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
|
||||
|
||||
if (strcmp(Pathname, "/proc/self/exe") == 0 || strcmp(Pathname, PidSelfPath) == 0) {
|
||||
if (strcmp(Pathname, "/proc/self/exe") == 0 ||
|
||||
strcmp(Pathname, "/proc/thread-self/exe") == 0 ||
|
||||
strcmp(Pathname, PidSelfPath) == 0) {
|
||||
return Filename();
|
||||
}
|
||||
|
||||
@@ -169,6 +185,26 @@ uint64_t FileManager::Close(int fd) {
|
||||
return ::close(fd);
|
||||
}
|
||||
|
||||
uint64_t FileManager::CloseRange(unsigned int first, unsigned int last, unsigned int flags) {
|
||||
#ifndef SYS_close_range
|
||||
#define SYS_close_range 436
|
||||
#endif
|
||||
#ifndef CLOSE_RANGE_CLOEXEC
|
||||
#define CLOSE_RANGE_CLOEXEC (1U << 2)
|
||||
#endif
|
||||
|
||||
if (!(flags & CLOSE_RANGE_CLOEXEC)) {
|
||||
// If the flag was set then it doesn't actually close the FDs
|
||||
// Just sets the flag on a range
|
||||
std::lock_guard<std::mutex> lk(FDLock);
|
||||
for (unsigned int i = first; i <= last; ++i) {
|
||||
// We remove from first to last inclusive
|
||||
FDToNameMap.erase(i);
|
||||
}
|
||||
}
|
||||
return ::syscall(SYS_close_range, first, last, flags);
|
||||
}
|
||||
|
||||
uint64_t FileManager::Stat(const char *pathname, void *buf) {
|
||||
auto NewPath = GetSelf(pathname);
|
||||
const char *SelfPath = NewPath ? NewPath->c_str() : nullptr;
|
||||
@@ -249,7 +285,9 @@ uint64_t FileManager::Readlink(const char *pathname, char *buf, size_t bufsiz) {
|
||||
char PidSelfPath[50];
|
||||
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
|
||||
|
||||
if (strcmp(pathname, "/proc/self/exe") == 0 || strcmp(pathname, PidSelfPath) == 0) {
|
||||
if (strcmp(pathname, "/proc/self/exe") == 0 ||
|
||||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
|
||||
strcmp(pathname, PidSelfPath) == 0) {
|
||||
auto App = Filename();
|
||||
strncpy(buf, App.c_str(), bufsiz);
|
||||
return std::min(bufsiz, App.size());
|
||||
@@ -301,7 +339,7 @@ uint64_t FileManager::Openat([[maybe_unused]] int dirfs, const char *pathname, i
|
||||
|
||||
fd = EmuFD.OpenAt(dirfs, SelfPath, flags, mode);
|
||||
if (fd == -1) {
|
||||
auto Path = GetEmulatedPath(SelfPath);
|
||||
auto Path = GetEmulatedPath(SelfPath, true);
|
||||
if (!Path.empty()) {
|
||||
fd = ::openat(dirfs, Path.c_str(), flags, mode);
|
||||
}
|
||||
@@ -318,6 +356,36 @@ uint64_t FileManager::Openat([[maybe_unused]] int dirfs, const char *pathname, i
|
||||
return fd;
|
||||
}
|
||||
|
||||
uint64_t FileManager::Openat2(int dirfs, const char *pathname, FEX::HLE::open_how *how, size_t usize) {
|
||||
#ifndef SYS_openat2
|
||||
#define SYS_openat2 437
|
||||
#endif
|
||||
|
||||
auto NewPath = GetSelf(pathname);
|
||||
const char *SelfPath = NewPath ? NewPath->c_str() : nullptr;
|
||||
|
||||
int32_t fd = -1;
|
||||
|
||||
fd = EmuFD.OpenAt(dirfs, SelfPath, how->flags, how->mode);
|
||||
if (fd == -1) {
|
||||
auto Path = GetEmulatedPath(SelfPath, true);
|
||||
if (!Path.empty()) {
|
||||
fd = ::syscall(SYS_openat2, dirfs, Path.c_str(), how, usize);
|
||||
}
|
||||
|
||||
if (fd == -1)
|
||||
fd = ::syscall(SYS_openat2, dirfs, SelfPath, how, usize);
|
||||
}
|
||||
|
||||
if (fd != -1) {
|
||||
std::lock_guard<std::mutex> lk(FDLock);
|
||||
FDToNameMap[fd] = SelfPath;
|
||||
}
|
||||
|
||||
return fd;
|
||||
|
||||
}
|
||||
|
||||
uint64_t FileManager::Statx(int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf) {
|
||||
auto NewPath = GetSelf(pathname);
|
||||
const char *SelfPath = NewPath ? NewPath->c_str() : nullptr;
|
||||
|
||||
@@ -23,6 +23,8 @@ struct Context;
|
||||
|
||||
namespace FEX::HLE {
|
||||
|
||||
struct open_how;
|
||||
|
||||
class FileManager final {
|
||||
public:
|
||||
FileManager() = delete;
|
||||
@@ -32,6 +34,7 @@ public:
|
||||
~FileManager();
|
||||
uint64_t Open(const char *pathname, int flags, uint32_t mode);
|
||||
uint64_t Close(int fd);
|
||||
uint64_t CloseRange(unsigned int first, unsigned int last, unsigned int flags);
|
||||
uint64_t Stat(const char *pathname, void *buf);
|
||||
uint64_t Lstat(const char *path, void *buf);
|
||||
uint64_t Access(const char *pathname, int mode);
|
||||
@@ -41,6 +44,7 @@ public:
|
||||
uint64_t Chmod(const char *pathname, mode_t mode);
|
||||
uint64_t Readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz);
|
||||
uint64_t Openat(int dirfs, const char *pathname, int flags, uint32_t mode);
|
||||
uint64_t Openat2(int dirfs, const char *pathname, FEX::HLE::open_how *how, size_t usize);
|
||||
uint64_t Statx(int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf);
|
||||
uint64_t Mknod(const char *pathname, mode_t mode, dev_t dev);
|
||||
uint64_t NewFSStatAt(int dirfd, const char *pathname, struct stat *buf, int flag);
|
||||
@@ -58,7 +62,7 @@ private:
|
||||
|
||||
std::mutex FDLock;
|
||||
std::unordered_map<int32_t, std::string> FDToNameMap;
|
||||
std::string GetEmulatedPath(const char *pathname);
|
||||
std::string GetEmulatedPath(const char *pathname, bool FollowSymlink = false);
|
||||
std::map<std::string, std::string> ThunkOverlays;
|
||||
|
||||
FEX_CONFIG_OPT(Filename, APP_FILENAME);
|
||||
|
||||
@@ -17,6 +17,7 @@ $end_info$
|
||||
|
||||
#include <linux/futex.h>
|
||||
#include <bits/types/stack_t.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
@@ -170,7 +171,7 @@ namespace FEX::HLE {
|
||||
|
||||
// Doesn't return
|
||||
FEXCore::Context::StopThread(Thread->CTX, Thread);
|
||||
std::unexpected();
|
||||
std::terminate();
|
||||
}
|
||||
}
|
||||
else if (Handler.GuestAction.sigaction_handler.handler == SIG_IGN) {
|
||||
@@ -300,7 +301,7 @@ namespace FEX::HLE {
|
||||
|
||||
// Most signals default to termination
|
||||
// These ones are slightly different
|
||||
const std::vector<std::pair<int, SignalDelegator::DefaultBehaviour>> SignalDefaultBehaviours = {
|
||||
static constexpr std::array<std::pair<int, SignalDelegator::DefaultBehaviour>, 14> SignalDefaultBehaviours = {{
|
||||
{SIGQUIT, DEFAULT_COREDUMP},
|
||||
{SIGILL, DEFAULT_COREDUMP},
|
||||
{SIGTRAP, DEFAULT_COREDUMP},
|
||||
@@ -315,10 +316,10 @@ namespace FEX::HLE {
|
||||
{SIGXFSZ, DEFAULT_COREDUMP},
|
||||
{SIGSYS, DEFAULT_COREDUMP},
|
||||
{SIGWINCH, DEFAULT_IGNORE},
|
||||
};
|
||||
}};
|
||||
|
||||
for (auto Behaviour : SignalDefaultBehaviours) {
|
||||
HostHandlers[Behaviour.first].DefaultBehaviour = Behaviour.second;
|
||||
for (const auto [Signal, Behaviour] : SignalDefaultBehaviours) {
|
||||
HostHandlers[Signal].DefaultBehaviour = Behaviour;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -342,7 +343,7 @@ namespace FEX::HLE {
|
||||
|
||||
// Set up our signal alternative stack
|
||||
// This is per thread rather than per signal
|
||||
ThreadData.AltStackPtr = FEXCore::Allocator::malloc(SIGSTKSZ);
|
||||
ThreadData.AltStackPtr = FEXCore::Allocator::mmap(nullptr, SIGSTKSZ, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
stack_t altstack{};
|
||||
altstack.ss_sp = ThreadData.AltStackPtr;
|
||||
altstack.ss_size = SIGSTKSZ;
|
||||
@@ -357,7 +358,7 @@ namespace FEX::HLE {
|
||||
}
|
||||
|
||||
void SignalDelegator::UninstallTLSState(FEXCore::Core::InternalThreadState *Thread) {
|
||||
free(ThreadData.AltStackPtr);
|
||||
FEXCore::Allocator::munmap(ThreadData.AltStackPtr, SIGSTKSZ);
|
||||
|
||||
ThreadData.Thread = nullptr;
|
||||
ThreadData.AltStackPtr = nullptr;
|
||||
@@ -420,27 +421,27 @@ namespace FEX::HLE {
|
||||
void SignalDelegator::RegisterHostSignalHandler(int Signal, FEXCore::HostSignalDelegatorFunction Func) {
|
||||
// Linux signal handlers are per-process rather than per thread
|
||||
// Multiple threads could be calling in to this
|
||||
std::lock_guard<std::mutex> lk(HostDelegatorMutex);
|
||||
HostHandlers[Signal].Handler = Func;
|
||||
std::lock_guard lk(HostDelegatorMutex);
|
||||
HostHandlers[Signal].Handler = std::move(Func);
|
||||
InstallHostThunk(Signal);
|
||||
}
|
||||
|
||||
void SignalDelegator::RegisterFrontendHostSignalHandler(int Signal, FEXCore::HostSignalDelegatorFunction Func) {
|
||||
// Linux signal handlers are per-process rather than per thread
|
||||
// Multiple threads could be calling in to this
|
||||
std::lock_guard<std::mutex> lk(HostDelegatorMutex);
|
||||
HostHandlers[Signal].FrontendHandler = Func;
|
||||
std::lock_guard lk(HostDelegatorMutex);
|
||||
HostHandlers[Signal].FrontendHandler = std::move(Func);
|
||||
InstallHostThunk(Signal);
|
||||
}
|
||||
|
||||
void SignalDelegator::RegisterHostSignalHandlerForGuest(int Signal, FEXCore::HostSignalDelegatorFunctionForGuest Func) {
|
||||
std::lock_guard<std::mutex> lk(HostDelegatorMutex);
|
||||
HostHandlers[Signal].GuestHandler = Func;
|
||||
std::lock_guard lk(HostDelegatorMutex);
|
||||
HostHandlers[Signal].GuestHandler = std::move(Func);
|
||||
InstallHostThunk(Signal);
|
||||
}
|
||||
|
||||
uint64_t SignalDelegator::RegisterGuestSignalHandler(int Signal, const FEXCore::GuestSigAction *Action, FEXCore::GuestSigAction *OldAction) {
|
||||
std::lock_guard<std::mutex> lk(GuestDelegatorMutex);
|
||||
std::lock_guard lk(GuestDelegatorMutex);
|
||||
|
||||
// Invalid signal specified
|
||||
if (Signal > MAX_SIGNALS) {
|
||||
|
||||
@@ -7,6 +7,7 @@ $end_info$
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <mutex>
|
||||
@@ -29,7 +30,7 @@ namespace FEX::HLE {
|
||||
// Returns true if the host handled the signal
|
||||
// Arguments are the same as sigaction handler
|
||||
SignalDelegator();
|
||||
virtual ~SignalDelegator();
|
||||
~SignalDelegator() override;
|
||||
|
||||
/**
|
||||
* @brief Registers an emulated thread's object to a TLS object
|
||||
@@ -108,7 +109,7 @@ namespace FEX::HLE {
|
||||
DefaultBehaviour DefaultBehaviour {DEFAULT_TERM};
|
||||
};
|
||||
|
||||
SignalHandler HostHandlers[MAX_SIGNALS + 1]{};
|
||||
std::array<SignalHandler, MAX_SIGNALS + 1> HostHandlers{};
|
||||
bool InstallHostThunk(int Signal);
|
||||
void UpdateHostThunk(int Signal);
|
||||
|
||||
|
||||
@@ -10,11 +10,13 @@ $end_info$
|
||||
#include "Common/MathUtils.h"
|
||||
|
||||
#include "Tests/LinuxSyscalls/Syscalls.h"
|
||||
#include "Tests/LinuxSyscalls/Syscalls/Thread.h"
|
||||
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
|
||||
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
|
||||
|
||||
#include <FEXCore/Core/X86Enums.h>
|
||||
#include <FEXCore/Core/CodeLoader.h>
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/Allocator.h>
|
||||
#include <FEXCore/Utils/ELFContainer.h>
|
||||
#include <fcntl.h>
|
||||
@@ -95,7 +97,7 @@ static bool IsSupportedByInterpreter(std::string const &Filename) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp) {
|
||||
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp, ExecveAtArgs *Args) {
|
||||
std::string Filename{};
|
||||
|
||||
std::error_code ec;
|
||||
@@ -123,7 +125,9 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
|
||||
char PidSelfPath[50];
|
||||
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
|
||||
|
||||
if (strcmp(pathname, "/proc/self/exe") == 0 || strcmp(pathname, PidSelfPath) == 0) {
|
||||
if (strcmp(pathname, "/proc/self/exe") == 0 ||
|
||||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
|
||||
strcmp(pathname, PidSelfPath) == 0) {
|
||||
// If pointing to self then redirect to the application
|
||||
// JRE and shapez.io does this
|
||||
Filename = FEX::HLE::_SyscallHandler->Filename();
|
||||
@@ -132,7 +136,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
|
||||
uint64_t Result{};
|
||||
if (FEX::HLE::_SyscallHandler->IsInterpreterInstalled()) {
|
||||
// If the FEX interpreter is installed then just execve the thing
|
||||
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
@@ -151,7 +160,12 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
|
||||
// We are trying to execute an ELF of a different architecture
|
||||
// We can't know if we can support this without architecture specific checks and binfmt_misc parsing
|
||||
// Just execve it and let the kernel handle the process
|
||||
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve(Filename.c_str(), const_cast<char *const *>(&argv.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
@@ -170,11 +184,136 @@ uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std
|
||||
// Append the arguments together
|
||||
ExecveArgs.insert(ExecveArgs.end(), argv.begin(), argv.end());
|
||||
|
||||
Result = execve("/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, "/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)), Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve("/proc/self/exe", const_cast<char *const *>(&ExecveArgs.at(0)), const_cast<char *const *>(&envp.at(0)));
|
||||
}
|
||||
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
static bool AnyFlagsSet(uint64_t Flags, uint64_t Mask) {
|
||||
return (Flags & Mask) != 0;
|
||||
}
|
||||
|
||||
static bool AllFlagsSet(uint64_t Flags, uint64_t Mask) {
|
||||
return (Flags & Mask) == Mask;
|
||||
}
|
||||
|
||||
uint64_t CloneHandler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) {
|
||||
uint64_t flags = args->flags;
|
||||
#define FLAGPRINT(x, y) if (args->flags & (y)) LogMan::Msg::I("\tFlag: " #x)
|
||||
FLAGPRINT(CSIGNAL, 0x000000FF);
|
||||
FLAGPRINT(CLONE_VM, 0x00000100);
|
||||
FLAGPRINT(CLONE_FS, 0x00000200);
|
||||
FLAGPRINT(CLONE_FILES, 0x00000400);
|
||||
FLAGPRINT(CLONE_SIGHAND, 0x00000800);
|
||||
FLAGPRINT(CLONE_PTRACE, 0x00002000);
|
||||
FLAGPRINT(CLONE_VFORK, 0x00004000);
|
||||
FLAGPRINT(CLONE_PARENT, 0x00008000);
|
||||
FLAGPRINT(CLONE_THREAD, 0x00010000);
|
||||
FLAGPRINT(CLONE_NEWNS, 0x00020000);
|
||||
FLAGPRINT(CLONE_SYSVSEM, 0x00040000);
|
||||
FLAGPRINT(CLONE_SETTLS, 0x00080000);
|
||||
FLAGPRINT(CLONE_PARENT_SETTID, 0x00100000);
|
||||
FLAGPRINT(CLONE_CHILD_CLEARTID, 0x00200000);
|
||||
FLAGPRINT(CLONE_DETACHED, 0x00400000);
|
||||
FLAGPRINT(CLONE_UNTRACED, 0x00800000);
|
||||
FLAGPRINT(CLONE_CHILD_SETTID, 0x01000000);
|
||||
FLAGPRINT(CLONE_NEWCGROUP, 0x02000000);
|
||||
FLAGPRINT(CLONE_NEWUTS, 0x04000000);
|
||||
FLAGPRINT(CLONE_NEWIPC, 0x08000000);
|
||||
FLAGPRINT(CLONE_NEWUSER, 0x10000000);
|
||||
FLAGPRINT(CLONE_NEWPID, 0x20000000);
|
||||
FLAGPRINT(CLONE_NEWNET, 0x40000000);
|
||||
FLAGPRINT(CLONE_IO, 0x80000000);
|
||||
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
if (AnyFlagsSet(flags, CLONE_UNTRACED | CLONE_PTRACE)) {
|
||||
LogMan::Msg::D("clone: Ptrace* not supported");
|
||||
}
|
||||
|
||||
// Clone3 flags
|
||||
#ifndef CLONE_CLEAR_SIGHAND
|
||||
#define CLONE_CLEAR_SIGHAND 0x100000000ULL
|
||||
#endif
|
||||
#ifndef CLONE_INTO_CGROUP
|
||||
#define CLONE_INTO_CGROUP 0x200000000ULL
|
||||
#endif
|
||||
|
||||
if (AnyFlagsSet(flags, CLONE_CLEAR_SIGHAND)) {
|
||||
LogMan::Msg::D("clone3: CLONE_CLEAR_SIGHAND unsupported");
|
||||
}
|
||||
|
||||
if (AnyFlagsSet(flags, CLONE_INTO_CGROUP)) {
|
||||
LogMan::Msg::D("clone3: CLONE_INTO_CGROUP unsupported");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
if (args->set_tid_size > 0) {
|
||||
LogMan::Msg::D("clone3: set_tid unsupported");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (AnyFlagsSet(flags, CLONE_NEWNS | CLONE_NEWCGROUP | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER | CLONE_NEWPID | CLONE_NEWNET)) {
|
||||
// NEWUSER doesn't need any privileges from 3.8 onward
|
||||
// We just don't support it yet
|
||||
LogMan::Msg::I("Unconditionally returning EPERM on clone namespace");
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
if (!(flags & CLONE_THREAD)) {
|
||||
|
||||
if (flags & CLONE_VFORK) {
|
||||
flags &= ~CLONE_VFORK;
|
||||
flags &= ~CLONE_VM;
|
||||
LogMan::Msg::D("clone: WARNING: CLONE_VFORK w/o CLONE_THREAD");
|
||||
}
|
||||
|
||||
if (AnyFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND | CLONE_VM)) {
|
||||
LogMan::Msg::I("clone: Unsuported flags w/o CLONE_THREAD (Shared Resources), %X", flags);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
// CLONE_PARENT is ignored (Implied by CLONE_THREAD)
|
||||
return FEX::HLE::ForkGuest(Thread, Frame, flags,
|
||||
reinterpret_cast<void*>(args->stack),
|
||||
reinterpret_cast<pid_t*>(args->parent_tid),
|
||||
reinterpret_cast<pid_t*>(args->child_tid),
|
||||
reinterpret_cast<void*>(args->tls));
|
||||
} else {
|
||||
|
||||
if (!AllFlagsSet(flags, CLONE_SYSVSEM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND)) {
|
||||
LogMan::Msg::I("clone: CLONE_THREAD: Unsuported flags w/ CLONE_THREAD (Shared Resources), %X", flags);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
auto NewThread = FEX::HLE::CreateNewThread(Thread->CTX, Frame, args);
|
||||
|
||||
// Return the new threads TID
|
||||
uint64_t Result = NewThread->ThreadManager.GetTID();
|
||||
|
||||
if (flags & CLONE_VFORK) {
|
||||
NewThread->DestroyedByParent = true;
|
||||
}
|
||||
|
||||
// Actually start the thread
|
||||
FEXCore::Context::RunThread(Thread->CTX, NewThread);
|
||||
|
||||
if (flags & CLONE_VFORK) {
|
||||
// If VFORK is set then the calling process is suspended until the thread exits with execve or exit
|
||||
NewThread->ExecutionThread->join(nullptr);
|
||||
|
||||
// Normally a thread cleans itself up on exit. But because we need to join, we are now responsible
|
||||
FEXCore::Context::DestroyThread(Thread->CTX, NewThread);
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t SyscallHandler::HandleBRK(FEXCore::Core::CpuStateFrame *Frame, void *Addr) {
|
||||
std::lock_guard<std::mutex> lk(MMapMutex);
|
||||
|
||||
@@ -251,6 +390,8 @@ SyscallHandler::SyscallHandler(FEXCore::Context::Context *ctx, FEX::HLE::SignalD
|
||||
, SignalDelegation {_SignalDelegation} {
|
||||
FEX::HLE::_SyscallHandler = this;
|
||||
HostKernelVersion = CalculateHostKernelVersion();
|
||||
GuestKernelVersion = CalculateGuestKernelVersion();
|
||||
|
||||
}
|
||||
|
||||
SyscallHandler::~SyscallHandler() {
|
||||
@@ -276,7 +417,16 @@ uint32_t SyscallHandler::CalculateHostKernelVersion() {
|
||||
return (Major << 24) | (Minor << 16) | Patch;
|
||||
}
|
||||
|
||||
uint32_t SyscallHandler::CalculateGuestKernelVersion() {
|
||||
// We currently only emulate a kernel between the ranges of Kernel 5.0.0 and 5.12.0
|
||||
return std::max(KernelVersion(5, 0), std::min(KernelVersion(5, 12), GetHostKernelVersion()));
|
||||
}
|
||||
|
||||
uint64_t SyscallHandler::HandleSyscall(FEXCore::Core::CpuStateFrame *Frame, FEXCore::HLE::SyscallArguments *Args) {
|
||||
if (Args->Argument[0] >= Definitions.size()) {
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
auto &Def = Definitions[Args->Argument[0]];
|
||||
uint64_t Result{};
|
||||
switch (Def.NumArgs) {
|
||||
|
||||
@@ -13,6 +13,7 @@ $end_info$
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXCore/HLE/SyscallHandler.h>
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
@@ -20,6 +21,7 @@ $end_info$
|
||||
#include <unordered_map>
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// #define DEBUG_STRACE
|
||||
|
||||
@@ -34,6 +36,7 @@ class SyscallHandler;
|
||||
void RegisterFS();
|
||||
void RegisterInfo();
|
||||
void RegisterIO();
|
||||
void RegisterIOUring(FEX::HLE::SyscallHandler *const Handler);
|
||||
void RegisterKey();
|
||||
void RegisterMemory();
|
||||
void RegisterMsg();
|
||||
@@ -41,7 +44,7 @@ class SyscallHandler;
|
||||
void RegisterSched();
|
||||
void RegisterSemaphore();
|
||||
void RegisterSHM();
|
||||
void RegisterSignals();
|
||||
void RegisterSignals(FEX::HLE::SyscallHandler *const Handler);
|
||||
void RegisterSocket();
|
||||
void RegisterThread();
|
||||
void RegisterTime();
|
||||
@@ -52,7 +55,12 @@ class SyscallHandler;
|
||||
uint64_t UnimplementedSyscall(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber);
|
||||
uint64_t UnimplementedSyscallSafe(FEXCore::Core::CpuStateFrame *Frame, uint64_t SyscallNumber);
|
||||
|
||||
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp);
|
||||
struct ExecveAtArgs {
|
||||
int dirfd;
|
||||
int flags;
|
||||
};
|
||||
|
||||
uint64_t ExecveHandler(const char *pathname, std::vector<const char*> &argv, std::vector<const char*> &envp, ExecveAtArgs *Args);
|
||||
|
||||
class SyscallHandler : public FEXCore::HLE::SyscallHandler {
|
||||
public:
|
||||
@@ -113,12 +121,19 @@ public:
|
||||
FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE);
|
||||
|
||||
uint32_t GetHostKernelVersion() const { return HostKernelVersion; }
|
||||
uint32_t GetGuestKernelVersion() const { return GuestKernelVersion; }
|
||||
|
||||
static uint32_t CalculateHostKernelVersion();
|
||||
uint32_t CalculateGuestKernelVersion();
|
||||
|
||||
static uint32_t KernelVersion(uint32_t Major, uint32_t Minor = 0, uint32_t Patch = 0) {
|
||||
return (Major << 24) | (Minor << 16) | Patch;
|
||||
}
|
||||
|
||||
static uint32_t KernelMajor(uint32_t Version) { return Version >> 24; }
|
||||
static uint32_t KernelMinor(uint32_t Version) { return (Version >> 16) & 0xFF; }
|
||||
static uint32_t KernelPatch(uint32_t Version) { return Version & 0xFFFF; }
|
||||
|
||||
protected:
|
||||
std::vector<SyscallFunctionDefinition> Definitions{};
|
||||
std::mutex MMapMutex;
|
||||
@@ -131,6 +146,7 @@ protected:
|
||||
|
||||
// (Major << 24) | (Minor << 16) | Patch
|
||||
uint32_t HostKernelVersion{};
|
||||
uint32_t GuestKernelVersion{};
|
||||
|
||||
private:
|
||||
|
||||
@@ -201,12 +217,6 @@ std::string CollectArgsFmtString() {
|
||||
#define ARG_TO_STR(tpy, str)
|
||||
#endif
|
||||
|
||||
/////
|
||||
// REGISTER_SYSCALL_FORWARD_ERRNO implementation
|
||||
// Given a syscall wrapper, it generate a syscall implementation using the wrapper's signature, forward the arguments
|
||||
// and register to syscalls via RegisterSyscall
|
||||
/////
|
||||
|
||||
// Helper that allows us to create a variadic template lambda from a given signature
|
||||
// by creating a function that expects a fuction pointer with the given signature as a parameter
|
||||
template <typename T>
|
||||
@@ -231,7 +241,7 @@ struct FunctionToLambda<R(*)(Args...) noexcept> {
|
||||
}
|
||||
};
|
||||
|
||||
struct __attribute__((packed)) epoll_event_x86 {
|
||||
struct FEX_PACKED epoll_event_x86 {
|
||||
uint32_t events;
|
||||
epoll_data_t data;
|
||||
|
||||
@@ -251,23 +261,82 @@ struct __attribute__((packed)) epoll_event_x86 {
|
||||
};
|
||||
static_assert(std::is_trivial<epoll_event_x86>::value, "Needs to be trivial");
|
||||
static_assert(sizeof(epoll_event_x86) == 12, "Incorrect size");
|
||||
|
||||
struct open_how {
|
||||
uint64_t flags;
|
||||
uint64_t mode;
|
||||
uint64_t resolve;
|
||||
};
|
||||
|
||||
struct clone3_args {
|
||||
uint64_t flags;
|
||||
uint64_t pidfd;
|
||||
uint64_t child_tid;
|
||||
uint64_t parent_tid;
|
||||
uint64_t exit_signal;
|
||||
uint64_t stack;
|
||||
uint64_t stack_size;
|
||||
uint64_t tls;
|
||||
uint64_t set_tid;
|
||||
uint64_t set_tid_size;
|
||||
uint64_t cgroup;
|
||||
};
|
||||
uint64_t CloneHandler(FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args);
|
||||
|
||||
inline static int RemapFromX86Flags(int flags) {
|
||||
#ifdef _M_X86_64
|
||||
// Nothing to change here
|
||||
#elif _M_ARM_64
|
||||
constexpr int X86_64_FLAG_O_DIRECT = 040000;
|
||||
constexpr int X86_64_FLAG_O_LARGEFILE = 0100000;
|
||||
constexpr int X86_64_FLAG_O_DIRECTORY = 0200000;
|
||||
constexpr int X86_64_FLAG_O_NOFOLLOW = 0400000;
|
||||
|
||||
constexpr int AARCH64_FLAG_O_DIRECTORY = 040000;
|
||||
constexpr int AARCH64_FLAG_O_NOFOLLOW = 0100000;
|
||||
constexpr int AARCH64_FLAG_O_DIRECT = 0200000;
|
||||
constexpr int AARCH64_FLAG_O_LARGEFILE = 0400000;
|
||||
|
||||
int new_flags{};
|
||||
if (flags & X86_64_FLAG_O_DIRECT) { flags = (flags & ~X86_64_FLAG_O_DIRECT); new_flags |= AARCH64_FLAG_O_DIRECT; }
|
||||
if (flags & X86_64_FLAG_O_LARGEFILE) { flags = (flags & ~X86_64_FLAG_O_LARGEFILE); new_flags |= AARCH64_FLAG_O_LARGEFILE; }
|
||||
if (flags & X86_64_FLAG_O_DIRECTORY) { flags = (flags & ~X86_64_FLAG_O_DIRECTORY); new_flags |= AARCH64_FLAG_O_DIRECTORY; }
|
||||
if (flags & X86_64_FLAG_O_NOFOLLOW) { flags = (flags & ~X86_64_FLAG_O_NOFOLLOW); new_flags |= AARCH64_FLAG_O_NOFOLLOW; }
|
||||
flags |= new_flags;
|
||||
#else
|
||||
#error Unknown flag remappings for this host platform
|
||||
#endif
|
||||
return flags;
|
||||
}
|
||||
|
||||
inline static int RemapToX86Flags(int flags) {
|
||||
#ifdef _M_X86_64
|
||||
// Nothing to change here
|
||||
#elif _M_ARM_64
|
||||
constexpr int X86_64_FLAG_O_DIRECT = 040000;
|
||||
constexpr int X86_64_FLAG_O_LARGEFILE = 0100000;
|
||||
constexpr int X86_64_FLAG_O_DIRECTORY = 0200000;
|
||||
constexpr int X86_64_FLAG_O_NOFOLLOW = 0400000;
|
||||
|
||||
constexpr int AARCH64_FLAG_O_DIRECTORY = 040000;
|
||||
constexpr int AARCH64_FLAG_O_NOFOLLOW = 0100000;
|
||||
constexpr int AARCH64_FLAG_O_DIRECT = 0200000;
|
||||
constexpr int AARCH64_FLAG_O_LARGEFILE = 0400000;
|
||||
|
||||
int new_flags{};
|
||||
if (flags & AARCH64_FLAG_O_DIRECT) { flags = (flags & ~AARCH64_FLAG_O_DIRECT); new_flags |= X86_64_FLAG_O_DIRECT; }
|
||||
if (flags & AARCH64_FLAG_O_LARGEFILE) { flags = (flags & ~AARCH64_FLAG_O_LARGEFILE); new_flags |= X86_64_FLAG_O_LARGEFILE; }
|
||||
if (flags & AARCH64_FLAG_O_DIRECTORY) { flags = (flags & ~AARCH64_FLAG_O_DIRECTORY); new_flags |= X86_64_FLAG_O_DIRECTORY; }
|
||||
if (flags & AARCH64_FLAG_O_NOFOLLOW) { flags = (flags & ~AARCH64_FLAG_O_NOFOLLOW); new_flags |= X86_64_FLAG_O_NOFOLLOW; }
|
||||
flags |= new_flags;
|
||||
#else
|
||||
#error Unknown flag remappings for this host platform
|
||||
#endif
|
||||
return flags;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Creates a variadic template lambda from a global function (via FunctionToLambda), then forwards the arguments to the specified function
|
||||
// also handles errno
|
||||
#define SYSCALL_FORWARD_ERRNO(function) \
|
||||
FEX::HLE::FunctionToLambda<decltype(&::function)>::ReturnFunctionPointer([](FEXCore::Core::CpuStateFrame *Frame, auto... Args) { \
|
||||
FEX::HLE::FunctionToLambda<decltype(&::function)>::RType Result = ::function(Args...); \
|
||||
do { if (Result == -1) return (FEX::HLE::FunctionToLambda<decltype(&::function)>::RType)-errno; return Result; } while(0); \
|
||||
})
|
||||
|
||||
// Helpers to register a syscall implementation
|
||||
// Creates a syscall forward from a glibc wrapper, and registers it
|
||||
#define REGISTER_SYSCALL_FORWARD_ERRNO(function) do { \
|
||||
FEX::HLE::x64::RegisterSyscall(FEX::HLE::x64::SYSCALL_x64_##function, #function, SYSCALL_FORWARD_ERRNO(function)); \
|
||||
FEX::HLE::x32::RegisterSyscall(FEX::HLE::x32::SYSCALL_x86_##function, #function, SYSCALL_FORWARD_ERRNO(function)); \
|
||||
} while(0)
|
||||
|
||||
// Registers syscall for both 32bit and 64bit
|
||||
#define REGISTER_SYSCALL_IMPL(name, lambda) \
|
||||
struct impl_##name { \
|
||||
|
||||
@@ -24,24 +24,6 @@ $end_info$
|
||||
#include <sys/syscall.h>
|
||||
|
||||
namespace FEX::HLE {
|
||||
static int RemapFlags(int flags) {
|
||||
#ifdef _M_X86_64
|
||||
// Nothing to change here
|
||||
#elif _M_ARM_64
|
||||
constexpr int X86_64_FLAG_O_DIRECT = 040000;
|
||||
constexpr int X86_64_FLAG_O_LARGEFILE = 0100000;
|
||||
constexpr int X86_64_FLAG_O_DIRECTORY = 0200000;
|
||||
constexpr int X86_64_FLAG_O_NOFOLLOW = 0400000;
|
||||
if (flags & X86_64_FLAG_O_DIRECT) flags = (flags & ~X86_64_FLAG_O_DIRECT) | O_DIRECT;
|
||||
if (flags & X86_64_FLAG_O_LARGEFILE) flags = (flags & ~X86_64_FLAG_O_LARGEFILE) | O_LARGEFILE;
|
||||
if (flags & X86_64_FLAG_O_DIRECTORY) flags = (flags & ~X86_64_FLAG_O_DIRECTORY) | O_DIRECTORY;
|
||||
if (flags & X86_64_FLAG_O_NOFOLLOW) flags = (flags & ~X86_64_FLAG_O_NOFOLLOW) | O_NOFOLLOW;
|
||||
#else
|
||||
#error Unknown flag remappings for this host platform
|
||||
#endif
|
||||
return flags;
|
||||
}
|
||||
|
||||
void RegisterFD(FEX::HLE::SyscallHandler *const Handler) {
|
||||
REGISTER_SYSCALL_IMPL(read, [](FEXCore::Core::CpuStateFrame *Frame, int fd, void *buf, size_t count) -> uint64_t {
|
||||
uint64_t Result = ::read(fd, buf, count);
|
||||
@@ -54,7 +36,7 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(open, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, int flags, uint32_t mode) -> uint64_t {
|
||||
flags = RemapFlags(flags);
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Open(pathname, flags, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
@@ -64,23 +46,20 @@ namespace FEX::HLE {
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(chown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
|
||||
SYSCALL_STUB(chown);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(chown);
|
||||
uint64_t Result = ::chown(pathname, owner, group);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(fchown, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uid_t owner, gid_t group) -> uint64_t {
|
||||
SYSCALL_STUB(fchown);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(fchown);
|
||||
uint64_t Result = ::fchown(fd, owner, group);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(lchown, [](FEXCore::Core::CpuStateFrame *Frame, const char *pathname, uid_t owner, gid_t group) -> uint64_t {
|
||||
SYSCALL_STUB(lchown);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(lchown);
|
||||
uint64_t Result = ::lchown(pathname, owner, group);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(lseek, [](FEXCore::Core::CpuStateFrame *Frame, int fd, uint64_t offset, int whence) -> uint64_t {
|
||||
uint64_t Result = ::lseek(fd, offset, whence);
|
||||
@@ -108,7 +87,7 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(dup3, [](FEXCore::Core::CpuStateFrame* Frame, int oldfd, int newfd, int flags) -> uint64_t {
|
||||
flags = RemapFlags(flags);
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = ::dup3(oldfd, newfd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
@@ -149,7 +128,7 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(fadvise64, [](FEXCore::Core::CpuStateFrame *Frame, int fd, off_t offset, off_t len, int advice) -> uint64_t {
|
||||
uint64_t Result = ::posix_fadvise64(fd, offset, len, advice);
|
||||
uint64_t Result = ::syscall(SYS_fadvise64, fd, offset, len, advice);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
@@ -169,7 +148,7 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(openat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfs, const char *pathname, int flags, uint32_t mode) -> uint64_t {
|
||||
flags = RemapFlags(flags);
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat(dirfs, pathname, flags, mode);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
@@ -185,11 +164,13 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(fchownat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, uid_t owner, gid_t group, int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::fchownat(dirfd, pathname, owner, group, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(unlinkat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::unlinkat(dirfd, pathname, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
@@ -200,6 +181,7 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(linkat, [](FEXCore::Core::CpuStateFrame *Frame, int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::linkat(olddirfd, oldpath, newdirfd, newpath, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
@@ -230,32 +212,57 @@ namespace FEX::HLE {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.FAccessat2(dirfd, pathname, mode, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(pidfd_getfd, [](FEXCore::Core::CpuStateFrame *Frame, int pidfd, int fd, unsigned int flags) -> uint64_t {
|
||||
#ifndef SYS_pidfd_getfd
|
||||
#define SYS_pidfd_getfd 438
|
||||
#endif
|
||||
uint64_t Result = ::syscall(SYS_pidfd_getfd, pidfd, fd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(openat2, [](FEXCore::Core::CpuStateFrame *Frame, int dirfs, const char *pathname, struct open_how *how, size_t usize) -> uint64_t {
|
||||
open_how HostHow{};
|
||||
size_t HostSize = std::min(sizeof(open_how), usize);
|
||||
memcpy(&HostHow, how, HostSize);
|
||||
|
||||
HostHow.flags = FEX::HLE::RemapFromX86Flags(HostHow.flags);
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Openat2(dirfs, pathname, &HostHow, HostSize);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
else {
|
||||
REGISTER_SYSCALL_IMPL(faccessat2, UnimplementedSyscallSafe);
|
||||
REGISTER_SYSCALL_IMPL(pidfd_getfd, UnimplementedSyscallSafe);
|
||||
REGISTER_SYSCALL_IMPL(openat2, UnimplementedSyscallSafe);
|
||||
}
|
||||
|
||||
REGISTER_SYSCALL_IMPL(splice, [](FEXCore::Core::CpuStateFrame *Frame, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::splice(fd_in, off_in, fd_out, off_out, len, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(tee, [](FEXCore::Core::CpuStateFrame *Frame, int fd_in, int fd_out, size_t len, unsigned int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::tee(fd_in, fd_out, len, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(sync_file_range, [](FEXCore::Core::CpuStateFrame *Frame, int fd, off64_t offset, off64_t nbytes, unsigned int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::sync_file_range(fd, offset, nbytes, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(timerfd_create, [](FEXCore::Core::CpuStateFrame *Frame, int32_t clockid, int32_t flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::timerfd_create(clockid, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(timerfd_settime, [](FEXCore::Core::CpuStateFrame *Frame, int fd, int flags, const struct itimerspec *new_value, struct itimerspec *old_value) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::timerfd_settime(fd, flags, new_value, old_value);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
@@ -271,52 +278,77 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(pipe2, [](FEXCore::Core::CpuStateFrame *Frame, int pipefd[2], int flags) -> uint64_t {
|
||||
flags = RemapFlags(flags);
|
||||
flags = FEX::HLE::RemapFromX86Flags(flags);
|
||||
uint64_t Result = ::pipe2(pipefd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(inotify_init1, [](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
|
||||
flags = RemapFlags(flags);
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::inotify_init1(flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(renameat2, [](FEXCore::Core::CpuStateFrame *Frame, int olddirfd, const char *oldpath, int newdirfd, const char *newpath, unsigned int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::renameat2(olddirfd, oldpath, newdirfd, newpath, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(memfd_create, [](FEXCore::Core::CpuStateFrame *Frame, const char *name, uint32_t flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::memfd_create(name, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(statx, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, int flags, uint32_t mask, struct statx *statxbuf) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.Statx(dirfd, pathname, flags, mask, statxbuf);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(name_to_handle_at, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, struct file_handle *handle, int *mount_id, int flags) -> uint64_t {
|
||||
flags = RemapFlags(flags);
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::name_to_handle_at(dirfd, pathname, handle, mount_id, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(open_by_handle_at, [](FEXCore::Core::CpuStateFrame *Frame, int mount_fd, struct file_handle *handle, int flags) -> uint64_t {
|
||||
flags = RemapFlags(flags);
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::open_by_handle_at(mount_fd, handle, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(eventfd2, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int count, int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::syscall(SYS_eventfd2, count, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(copy_file_range, [](FEXCore::Core::CpuStateFrame *Frame, int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, size_t len, unsigned int flags) -> uint64_t {
|
||||
// Flags don't need remapped
|
||||
uint64_t Result = ::copy_file_range(fd_in, off_in, fd_out, off_out, len, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 3, 0)) {
|
||||
REGISTER_SYSCALL_IMPL(pidfd_open, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, unsigned int flags) -> uint64_t {
|
||||
uint64_t Result = ::syscall(SYS_pidfd_open, pid, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
else {
|
||||
REGISTER_SYSCALL_IMPL(pidfd_open, UnimplementedSyscallSafe);
|
||||
}
|
||||
|
||||
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 9, 0)) {
|
||||
REGISTER_SYSCALL_IMPL(close_range, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int first, unsigned int last, unsigned int flags) -> uint64_t {
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->FM.CloseRange(first, last, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
else {
|
||||
REGISTER_SYSCALL_IMPL(close_range, UnimplementedSyscallSafe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,83 +166,70 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(syncfs, [](FEXCore::Core::CpuStateFrame *Frame, int fd) -> uint64_t {
|
||||
SYSCALL_STUB(syncfs);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(syncfs);
|
||||
uint64_t Result = ::syncfs(fd);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(setxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, const void *value, size_t size, int flags) -> uint64_t {
|
||||
SYSCALL_STUB(setxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(setxattr);
|
||||
uint64_t Result = ::setxattr(path, name, value, size, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(lsetxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, const void *value, size_t size, int flags) -> uint64_t {
|
||||
SYSCALL_STUB(lsetxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(lsetxattr);
|
||||
uint64_t Result = ::lsetxattr(path, name, value, size, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(fsetxattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name, const void *value, size_t size, int flags) -> uint64_t {
|
||||
SYSCALL_STUB(fsetxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(fsetxattr);
|
||||
uint64_t Result = ::fsetxattr(fd, name, value, size, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(getxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, void *value, size_t size) -> uint64_t {
|
||||
SYSCALL_STUB(getxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(getxattr);
|
||||
uint64_t Result = ::getxattr(path, name, value, size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(lgetxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name, void *value, size_t size) -> uint64_t {
|
||||
SYSCALL_STUB(lgetxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(lgetxattr);
|
||||
uint64_t Result = ::lgetxattr(path, name, value, size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(fgetxattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name, void *value, size_t size) -> uint64_t {
|
||||
SYSCALL_STUB(fgetxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(fgetxattr);
|
||||
uint64_t Result = ::fgetxattr(fd, name, value, size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(listxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, char *list, size_t size) -> uint64_t {
|
||||
SYSCALL_STUB(listxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(listxattr);
|
||||
uint64_t Result = ::listxattr(path, list, size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(llistxattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, char *list, size_t size) -> uint64_t {
|
||||
SYSCALL_STUB(llistxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(llistxattr);
|
||||
uint64_t Result = ::llistxattr(path, list, size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(flistxattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, char *list, size_t size) -> uint64_t {
|
||||
SYSCALL_STUB(flistxattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(flistxattr);
|
||||
uint64_t Result = ::flistxattr(fd, list, size);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(removexattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name) -> uint64_t {
|
||||
SYSCALL_STUB(removexattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(removexattr);
|
||||
uint64_t Result = ::removexattr(path, name);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(lremovexattr, [](FEXCore::Core::CpuStateFrame *Frame, const char *path, const char *name) -> uint64_t {
|
||||
SYSCALL_STUB(lremovexattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(lremovexattr);
|
||||
uint64_t Result = ::lremovexattr(path, name);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(fremovexattr, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const char *name) -> uint64_t {
|
||||
SYSCALL_STUB(fremovexattr);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(fremovexattr);
|
||||
uint64_t Result = ::fremovexattr(fd, name);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(fanotify_init, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int flags, unsigned int event_f_flags) -> uint64_t {
|
||||
uint64_t Result = ::fanotify_init(flags, event_f_flags);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
$info$
|
||||
tags: LinuxSyscalls|syscalls-shared
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "Tests/LinuxSyscalls/Syscalls.h"
|
||||
#include "Tests/LinuxSyscalls/Syscalls/Thread.h"
|
||||
#include "Tests/LinuxSyscalls/x64/Syscalls.h"
|
||||
#include "Tests/LinuxSyscalls/x32/Syscalls.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace SignalDelegator {
|
||||
struct GuestSigAction;
|
||||
}
|
||||
|
||||
|
||||
namespace FEX::HLE {
|
||||
void RegisterIOUring(FEX::HLE::SyscallHandler *const Handler) {
|
||||
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 1, 0)) {
|
||||
REGISTER_SYSCALL_IMPL(io_uring_setup, [](FEXCore::Core::CpuStateFrame *Frame, uint32_t entries, void* params) -> uint64_t {
|
||||
uint64_t Result = ::syscall(SYS_io_uring_setup, entries, params);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(io_uring_enter, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, uint32_t to_submit, uint32_t min_complete, uint32_t flags, void *argp, size_t argsz) -> uint64_t {
|
||||
uint64_t Result = ::syscall(SYS_io_uring_enter, fd, to_submit, min_complete, flags, argp, argsz);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(io_uring_register, [](FEXCore::Core::CpuStateFrame *Frame, unsigned int fd, unsigned int opcode, void *arg, uint32_t nr_args) -> uint64_t {
|
||||
uint64_t Result = ::syscall(SYS_io_uring_register, fd, opcode, arg, nr_args);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
else {
|
||||
REGISTER_SYSCALL_IMPL(io_uring_setup, UnimplementedSyscallSafe);
|
||||
REGISTER_SYSCALL_IMPL(io_uring_enter, UnimplementedSyscallSafe);
|
||||
REGISTER_SYSCALL_IMPL(io_uring_register, UnimplementedSyscallSafe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,13 +34,20 @@ namespace FEX::HLE {
|
||||
if (::uname(&Local) == 0) {
|
||||
memcpy(buf->nodename, Local.nodename, sizeof(Local.nodename));
|
||||
static_assert(sizeof(Local.nodename) <= sizeof(buf->nodename));
|
||||
memcpy(buf->domainname, Local.domainname, sizeof(Local.domainname));
|
||||
static_assert(sizeof(Local.domainname) <= sizeof(buf->domainname));
|
||||
}
|
||||
else {
|
||||
strcpy(buf->nodename, "FEXCore");
|
||||
LogMan::Msg::E("Couldn't determine host nodename. Defaulting to '%s'", buf->nodename);
|
||||
}
|
||||
strcpy(buf->sysname, "Linux");
|
||||
strcpy(buf->release, "5.0.0");
|
||||
uint32_t GuestVersion = FEX::HLE::_SyscallHandler->GetGuestKernelVersion();
|
||||
snprintf(buf->release, sizeof(buf->release), "%d.%d.%d",
|
||||
FEX::HLE::SyscallHandler::KernelMajor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelMinor(GuestVersion),
|
||||
FEX::HLE::SyscallHandler::KernelPatch(GuestVersion));
|
||||
|
||||
const char version[] = "#" GIT_DESCRIBE_STRING " SMP " __DATE__ " " __TIME__;
|
||||
strcpy(buf->version, version);
|
||||
static_assert(sizeof(version) <= sizeof(buf->version), "uname version define became too large!");
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace SignalDelegator {
|
||||
}
|
||||
|
||||
namespace FEX::HLE {
|
||||
void RegisterSignals() {
|
||||
void RegisterSignals(FEX::HLE::SyscallHandler *const Handler) {
|
||||
REGISTER_SYSCALL_IMPL(rt_sigprocmask, [](FEXCore::Core::CpuStateFrame *Frame, int how, const uint64_t *set, uint64_t *oldset) -> uint64_t {
|
||||
return FEX::HLE::_SyscallHandler->GetSignalDelegator()->GuestSigProcMask(how, set, oldset);
|
||||
});
|
||||
@@ -35,8 +35,21 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(userfaultfd, [](FEXCore::Core::CpuStateFrame *Frame, int flags) -> uint64_t {
|
||||
// Disable userfaultfd until we can properly emulate it
|
||||
// This is okay because the kernel configuration allows you to disable it at compile time
|
||||
return -ENOSYS;
|
||||
uint64_t Result = ::syscall(SYS_userfaultfd, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 1, 0)) {
|
||||
REGISTER_SYSCALL_IMPL(pidfd_send_signal, [](FEXCore::Core::CpuStateFrame *Frame, int pidfd, int sig, siginfo_t *info, unsigned int flags) -> uint64_t {
|
||||
uint64_t Result = ::syscall(SYS_pidfd_send_signal);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
else {
|
||||
REGISTER_SYSCALL_IMPL(pidfd_send_signal, UnimplementedSyscallSafe);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,11 +64,6 @@ namespace FEX::HLE {
|
||||
SYSCALL_STUB(rt_tgsigqueueinfo);
|
||||
});
|
||||
|
||||
// execute program relative to a directory file descriptor
|
||||
REGISTER_SYSCALL_IMPL(execveat, [](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, char *const argv[], char *const envp[], int flags) -> uint64_t {
|
||||
SYSCALL_STUB(execveat);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(rseq, [](FEXCore::Core::CpuStateFrame *Frame, struct rseq *rseq, uint32_t rseq_len, int flags, uint32_t sig) -> uint64_t {
|
||||
SYSCALL_STUB(rseq);
|
||||
});
|
||||
|
||||
@@ -33,7 +33,8 @@ $end_info$
|
||||
ARG_TO_STR(idtype_t, "%u")
|
||||
|
||||
namespace FEX::HLE {
|
||||
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context:: Context *CTX, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls) {
|
||||
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context:: Context *CTX, FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args) {
|
||||
uint64_t flags = args->flags;
|
||||
FEXCore::Core::CPUState NewThreadState{};
|
||||
// Clone copies the parent thread's state
|
||||
memcpy(&NewThreadState, Frame, sizeof(FEXCore::Core::CPUState));
|
||||
@@ -41,21 +42,21 @@ namespace FEX::HLE {
|
||||
NewThreadState.gregs[FEXCore::X86State::REG_RAX] = 0;
|
||||
NewThreadState.gregs[FEXCore::X86State::REG_RBX] = 0;
|
||||
NewThreadState.gregs[FEXCore::X86State::REG_RBP] = 0;
|
||||
NewThreadState.gregs[FEXCore::X86State::REG_RSP] = reinterpret_cast<uint64_t>(stack);
|
||||
NewThreadState.gregs[FEXCore::X86State::REG_RSP] = args->stack;
|
||||
|
||||
auto NewThread = FEXCore::Context::CreateThread(CTX, &NewThreadState, reinterpret_cast<uint64_t>(parent_tid));
|
||||
auto NewThread = FEXCore::Context::CreateThread(CTX, &NewThreadState, args->parent_tid);
|
||||
FEXCore::Context::InitializeThread(CTX, NewThread);
|
||||
|
||||
if (FEX::HLE::_SyscallHandler->Is64BitMode()) {
|
||||
if (flags & CLONE_SETTLS) {
|
||||
x64::SetThreadArea(NewThread->CurrentFrame, tls);
|
||||
x64::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(args->tls));
|
||||
}
|
||||
// Set us to start just after the syscall instruction
|
||||
x64::AdjustRipForNewThread(NewThread->CurrentFrame);
|
||||
}
|
||||
else {
|
||||
if (flags & CLONE_SETTLS) {
|
||||
x32::SetThreadArea(NewThread->CurrentFrame, tls);
|
||||
x32::SetThreadArea(NewThread->CurrentFrame, reinterpret_cast<void*>(args->tls));
|
||||
}
|
||||
x32::AdjustRipForNewThread(NewThread->CurrentFrame);
|
||||
}
|
||||
@@ -65,20 +66,32 @@ namespace FEX::HLE {
|
||||
|
||||
// Sets the child TID to pointer in ParentTID
|
||||
if (flags & CLONE_PARENT_SETTID) {
|
||||
*parent_tid = Result;
|
||||
*reinterpret_cast<pid_t*>(args->parent_tid) = Result;
|
||||
}
|
||||
|
||||
// Sets the child TID to the pointer in ChildTID
|
||||
if (flags & CLONE_CHILD_SETTID) {
|
||||
NewThread->ThreadManager.set_child_tid = child_tid;
|
||||
*child_tid = Result;
|
||||
NewThread->ThreadManager.set_child_tid = reinterpret_cast<int32_t*>(args->child_tid);
|
||||
*reinterpret_cast<pid_t*>(args->child_tid) = Result;
|
||||
}
|
||||
|
||||
// When the thread exits, clear the child thread ID at ChildTID
|
||||
// Additionally wakeup a futex at that address
|
||||
// Address /may/ be changed with SET_TID_ADDRESS syscall
|
||||
if (flags & CLONE_CHILD_CLEARTID) {
|
||||
NewThread->ThreadManager.clear_child_tid = child_tid;
|
||||
NewThread->ThreadManager.clear_child_tid = reinterpret_cast<int32_t*>(args->child_tid);
|
||||
}
|
||||
|
||||
// clone3 flag
|
||||
if (flags & CLONE_PIDFD) {
|
||||
// Use pidfd_open to emulate this flag
|
||||
int pidfd = ::syscall(SYS_pidfd_open, Result, 0);
|
||||
if (Result == ~0ULL) {
|
||||
LogMan::Msg::E("Couldn't get pidfd of TID %d\n", Result);
|
||||
}
|
||||
else {
|
||||
*reinterpret_cast<int*>(args->pidfd) = pidfd;
|
||||
}
|
||||
}
|
||||
|
||||
return NewThread;
|
||||
@@ -162,6 +175,12 @@ namespace FEX::HLE {
|
||||
return ForkGuest(Frame->Thread, Frame, 0, 0, 0, 0, 0);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(clone3, ([](FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *cl_args, size_t size) -> uint64_t {
|
||||
FEX::HLE::clone3_args args{};
|
||||
memcpy(&args, cl_args, std::min(sizeof(FEX::HLE::clone3_args), size));
|
||||
return CloneHandler(Frame, &args);
|
||||
}));
|
||||
|
||||
REGISTER_SYSCALL_IMPL(exit, [](FEXCore::Core::CpuStateFrame *Frame, int status) -> uint64_t {
|
||||
auto Thread = Frame->Thread;
|
||||
if (Thread->ThreadManager.clear_child_tid) {
|
||||
@@ -303,13 +322,22 @@ namespace FEX::HLE {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(arch_prctl, [](FEXCore::Core::CpuStateFrame *Frame, int code, unsigned long addr) -> uint64_t {
|
||||
constexpr uint64_t TASK_MAX = (1ULL << 48); // 48-bits until we can query the host side VA sanely. AArch64 doesn't expose this in cpuinfo
|
||||
uint64_t Result{};
|
||||
switch (code) {
|
||||
case 0x1001: // ARCH_SET_GS
|
||||
if (addr >= TASK_MAX) {
|
||||
// Ignore a non-canonical address
|
||||
return -EPERM;
|
||||
}
|
||||
Frame->State.gs = addr;
|
||||
Result = 0;
|
||||
break;
|
||||
case 0x1002: // ARCH_SET_FS
|
||||
if (addr >= TASK_MAX) {
|
||||
// Ignore a non-canonical address
|
||||
return -EPERM;
|
||||
}
|
||||
Frame->State.fs = addr;
|
||||
Result = 0;
|
||||
break;
|
||||
@@ -348,7 +376,7 @@ namespace FEX::HLE {
|
||||
Thread->StatusCode = status;
|
||||
FEXCore::Context::Stop(Thread->CTX);
|
||||
// This will never be reached
|
||||
std::unexpected();
|
||||
std::terminate();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(prlimit64, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int resource, const struct rlimit *new_limit, struct rlimit *old_limit) -> uint64_t {
|
||||
@@ -356,31 +384,30 @@ namespace FEX::HLE {
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*
|
||||
REGISTER_SYSCALL_IMPL(setpgid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, pid_t pgid) -> uint64_t {
|
||||
SYSCALL_STUB(setpgid);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(setpgid);
|
||||
uint64_t Result = ::setpgid(pid, pgid);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*REGISTER_SYSCALL_IMPL(getpgid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
|
||||
SYSCALL_STUB(getpgid);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(getpgid);
|
||||
REGISTER_SYSCALL_IMPL(getpgid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
|
||||
uint64_t Result = ::getpgid(pid);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*REGISTER_SYSCALL_IMPL(setfsuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsuid) -> uint64_t {
|
||||
SYSCALL_STUB(setfsuid);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(setfsuid);
|
||||
REGISTER_SYSCALL_IMPL(setfsuid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsuid) -> uint64_t {
|
||||
uint64_t Result = ::setfsuid(fsuid);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*REGISTER_SYSCALL_IMPL(setfsgid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsgid) -> uint64_t {
|
||||
SYSCALL_STUB(setfsgid);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(setfsgid);
|
||||
REGISTER_SYSCALL_IMPL(setfsgid, [](FEXCore::Core::CpuStateFrame *Frame, uid_t fsgid) -> uint64_t {
|
||||
uint64_t Result = ::setfsgid(fsgid);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
/*REGISTER_SYSCALL_IMPL(getsid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
|
||||
SYSCALL_STUB(getsid);
|
||||
});*/
|
||||
REGISTER_SYSCALL_FORWARD_ERRNO(getsid);
|
||||
REGISTER_SYSCALL_IMPL(getsid, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid) -> uint64_t {
|
||||
uint64_t Result = ::getsid(pid);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL(waitid, [](FEXCore::Core::CpuStateFrame *Frame, idtype_t idtype, id_t id, siginfo_t *infop, int options) -> uint64_t {
|
||||
uint64_t Result = ::waitid(idtype, id, infop, options);
|
||||
|
||||
@@ -14,6 +14,6 @@ struct CPUState;
|
||||
}
|
||||
|
||||
namespace FEX::HLE {
|
||||
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context::Context *CTX, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls);
|
||||
FEXCore::Core::InternalThreadState *CreateNewThread(FEXCore::Context::Context *CTX, FEXCore::Core::CpuStateFrame *Frame, FEX::HLE::clone3_args *args);
|
||||
uint64_t ForkGuest(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::CpuStateFrame *Frame, uint32_t flags, void *stack, pid_t *parent_tid, pid_t *child_tid, void *tls);
|
||||
}
|
||||
@@ -12,18 +12,20 @@ $end_info$
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
ARG_TO_STR(FEX::HLE::x32::compat_ptr<FEX::HLE::epoll_event_x86>, "%lx")
|
||||
|
||||
namespace FEX::HLE::x32 {
|
||||
void RegisterEpoll() {
|
||||
void RegisterEpoll(FEX::HLE::SyscallHandler *const Handler) {
|
||||
REGISTER_SYSCALL_IMPL_X32(epoll_wait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevents, int timeout) -> uint64_t {
|
||||
std::vector<struct epoll_event> Events;
|
||||
Events.resize(maxevents);
|
||||
uint64_t Result = epoll_wait(epfd, &Events.at(0), maxevents, timeout);
|
||||
std::vector<struct epoll_event> Events(std::max(0, maxevents));
|
||||
uint64_t Result = ::syscall(SYS_epoll_pwait, epfd, Events.data(), maxevents, timeout, nullptr);
|
||||
|
||||
if (Result != -1) {
|
||||
for (size_t i = 0; i < Result; ++i) {
|
||||
@@ -35,23 +37,23 @@ namespace FEX::HLE::x32 {
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(epoll_ctl, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, int op, int fd, epoll_event_x86 *event) -> uint64_t {
|
||||
struct epoll_event Event = *event;
|
||||
uint64_t Result = epoll_ctl(epfd, op, fd, &Event);
|
||||
uint64_t Result = ::syscall(SYS_epoll_ctl, epfd, op, fd, &Event);
|
||||
if (Result != -1) {
|
||||
*event = Event;
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(epoll_pwait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevent, int timeout, const void* sigmask) -> uint64_t {
|
||||
std::vector<struct epoll_event> Events;
|
||||
Events.resize(maxevent);
|
||||
REGISTER_SYSCALL_IMPL_X32(epoll_pwait, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevent, int timeout, const uint64_t* sigmask, size_t sigsetsize) -> uint64_t {
|
||||
std::vector<struct epoll_event> Events(std::max(0, maxevent));
|
||||
|
||||
uint64_t Result = epoll_pwait(
|
||||
uint64_t Result = ::syscall(SYS_epoll_pwait,
|
||||
epfd,
|
||||
&Events.at(0),
|
||||
Events.data(),
|
||||
maxevent,
|
||||
timeout,
|
||||
reinterpret_cast<const sigset_t*>(sigmask));
|
||||
sigmask,
|
||||
sigsetsize);
|
||||
|
||||
if (Result != -1) {
|
||||
for (size_t i = 0; i < Result; ++i) {
|
||||
@@ -61,5 +63,41 @@ namespace FEX::HLE::x32 {
|
||||
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
if (Handler->GetHostKernelVersion() >= FEX::HLE::SyscallHandler::KernelVersion(5, 11, 0)) {
|
||||
#ifndef SYS_epoll_pwait2
|
||||
#define SYS_epoll_pwait2 354
|
||||
#endif
|
||||
REGISTER_SYSCALL_IMPL_X32(epoll_pwait2, [](FEXCore::Core::CpuStateFrame *Frame, int epfd, compat_ptr<epoll_event_x86> events, int maxevent, compat_ptr<timespec32> timeout, const uint64_t* sigmask, size_t sigsetsize) -> uint64_t {
|
||||
std::vector<struct epoll_event> Events(std::max(0, maxevent));
|
||||
|
||||
struct timespec tp64{};
|
||||
struct timespec *timed_ptr{};
|
||||
if (timeout) {
|
||||
tp64 = *timeout;
|
||||
timed_ptr = &tp64;
|
||||
}
|
||||
|
||||
uint64_t Result = ::syscall(SYS_epoll_pwait2,
|
||||
epfd,
|
||||
Events.data(),
|
||||
maxevent,
|
||||
timed_ptr,
|
||||
sigmask,
|
||||
sigsetsize);
|
||||
|
||||
if (Result != -1) {
|
||||
for (size_t i = 0; i < Result; ++i) {
|
||||
events[i] = Events[i];
|
||||
}
|
||||
}
|
||||
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
}
|
||||
else {
|
||||
REGISTER_SYSCALL_IMPL_X32(epoll_pwait2, UnimplementedSyscallSafe);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ $end_info$
|
||||
#include <FEXCore/Debug/InternalThreadState.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
@@ -32,7 +33,27 @@ $end_info$
|
||||
ARG_TO_STR(FEX::HLE::x32::compat_ptr<FEX::HLE::x32::sigset_argpack32>, "%lx")
|
||||
|
||||
namespace FEX::HLE::x32 {
|
||||
// Used to ensure no bogus values are passed into readv/writev family syscalls.
|
||||
// This is mainly to sanitize vector sizing. It's fine for the bogus value
|
||||
// itself to pass into the syscall, since the kernel will handle it.
|
||||
static constexpr int SanitizeIOCount(int count) {
|
||||
return std::max(0, count);
|
||||
}
|
||||
|
||||
using fd_set32 = uint32_t;
|
||||
#ifdef _M_X86_64
|
||||
uint32_t ioctl_32(FEXCore::Core::CpuStateFrame*, int fd, uint32_t cmd, uint32_t args) {
|
||||
uint32_t Result{};
|
||||
__asm volatile("int $0x80;"
|
||||
: "=a" (Result)
|
||||
: "a" (SYSCALL_x86_ioctl)
|
||||
, "b" (fd)
|
||||
, "c" (cmd)
|
||||
, "d" (args)
|
||||
: "memory");
|
||||
return Result;
|
||||
}
|
||||
#endif
|
||||
|
||||
void RegisterFD() {
|
||||
REGISTER_SYSCALL_IMPL_X32(poll, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, int timeout) -> uint64_t {
|
||||
@@ -49,23 +70,12 @@ namespace FEX::HLE::x32 {
|
||||
timed_ptr = &tp64;
|
||||
}
|
||||
|
||||
sigset_t HostSet{};
|
||||
|
||||
if (sigmask) {
|
||||
sigemptyset(&HostSet);
|
||||
|
||||
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
|
||||
if (*sigmask & (1ULL << i)) {
|
||||
sigaddset(&HostSet, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Result = ppoll(
|
||||
uint64_t Result = ::syscall(SYS_ppoll,
|
||||
fds,
|
||||
nfds,
|
||||
timed_ptr,
|
||||
sigmask ? &HostSet : nullptr);
|
||||
sigmask,
|
||||
sigsetsize);
|
||||
|
||||
if (timeout_ts) {
|
||||
*timeout_ts = tp64;
|
||||
@@ -75,24 +85,13 @@ namespace FEX::HLE::x32 {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(ppoll_time64, [](FEXCore::Core::CpuStateFrame *Frame, struct pollfd *fds, nfds_t nfds, struct timespec *timeout_ts, const uint64_t *sigmask, size_t sigsetsize) -> uint64_t {
|
||||
// sigsetsize is unused here since it is currently a constant and not exposed through glibc
|
||||
sigset_t HostSet{};
|
||||
|
||||
if (sigmask) {
|
||||
sigemptyset(&HostSet);
|
||||
|
||||
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
|
||||
if (*sigmask & (1ULL << i)) {
|
||||
sigaddset(&HostSet, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t Result = ppoll(
|
||||
uint64_t Result = ::syscall(SYS_ppoll,
|
||||
fds,
|
||||
nfds,
|
||||
timeout_ts,
|
||||
sigmask ? &HostSet : nullptr);
|
||||
sigmask,
|
||||
sigsetsize);
|
||||
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
@@ -109,21 +108,14 @@ namespace FEX::HLE::x32 {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(readv, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt) -> uint64_t {
|
||||
std::vector<iovec> Host_iovec(iovcnt);
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
Host_iovec[i] = iov[i];
|
||||
}
|
||||
|
||||
uint64_t Result = ::readv(fd, &Host_iovec.at(0), iovcnt);
|
||||
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
|
||||
uint64_t Result = ::readv(fd, Host_iovec.data(), iovcnt);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(writev, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt) -> uint64_t {
|
||||
std::vector<iovec> Host_iovec(iovcnt);
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
Host_iovec[i] = iov[i];
|
||||
}
|
||||
uint64_t Result = ::writev(fd, &Host_iovec.at(0), iovcnt);
|
||||
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
|
||||
uint64_t Result = ::writev(fd, Host_iovec.data(), iovcnt);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
@@ -250,13 +242,15 @@ namespace FEX::HLE::x32 {
|
||||
break;
|
||||
}
|
||||
|
||||
case F_SETFL:
|
||||
lock_arg = (void*)FEX::HLE::RemapFromX86Flags(arg);
|
||||
break;
|
||||
// Maps directly
|
||||
case F_DUPFD:
|
||||
case F_DUPFD_CLOEXEC:
|
||||
case F_GETFD:
|
||||
case F_SETFD:
|
||||
case F_GETFL:
|
||||
case F_SETFL:
|
||||
break;
|
||||
|
||||
default: LOGMAN_MSG_A("Unhandled fcntl64: 0x%x", cmd); break;
|
||||
@@ -281,8 +275,12 @@ namespace FEX::HLE::x32 {
|
||||
break;
|
||||
case F_DUPFD:
|
||||
case F_DUPFD_CLOEXEC:
|
||||
FEX::HLE::x32::CheckAndAddFDDuplication(fd, Result);
|
||||
break;
|
||||
FEX::HLE::x32::CheckAndAddFDDuplication(fd, Result);
|
||||
break;
|
||||
case F_GETFL: {
|
||||
Result = FEX::HLE::RemapToX86Flags(Result);
|
||||
break;
|
||||
}
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
@@ -306,74 +304,46 @@ namespace FEX::HLE::x32 {
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(preadv, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset) -> uint64_t {
|
||||
std::vector<iovec> Host_iovec(iovcnt);
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
Host_iovec[i] = iov[i];
|
||||
}
|
||||
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
|
||||
|
||||
uint64_t Result = ::preadv(fd, &Host_iovec.at(0), iovcnt, offset);
|
||||
uint64_t Result = ::preadv(fd, Host_iovec.data(), iovcnt, offset);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(pwritev, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset) -> uint64_t {
|
||||
std::vector<iovec> Host_iovec(iovcnt);
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
Host_iovec[i] = iov[i];
|
||||
}
|
||||
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
|
||||
|
||||
uint64_t Result = ::pwritev(fd, &Host_iovec.at(0), iovcnt, offset);
|
||||
uint64_t Result = ::pwritev(fd, Host_iovec.data(), iovcnt, offset);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(process_vm_readv, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec32 *local_iov, unsigned long liovcnt, const struct iovec32 *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
|
||||
std::vector<iovec> Host_local_iovec(liovcnt);
|
||||
std::vector<iovec> Host_remote_iovec(riovcnt);
|
||||
std::vector<iovec> Host_local_iovec(local_iov, local_iov + SanitizeIOCount(liovcnt));
|
||||
std::vector<iovec> Host_remote_iovec(remote_iov, remote_iov + SanitizeIOCount(riovcnt));
|
||||
|
||||
for (int i = 0; i < liovcnt; ++i) {
|
||||
Host_local_iovec[i] = local_iov[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < riovcnt; ++i) {
|
||||
Host_remote_iovec[i] = remote_iov[i];
|
||||
}
|
||||
|
||||
uint64_t Result = ::process_vm_readv(pid, &Host_local_iovec.at(0), liovcnt, &Host_remote_iovec.at(0), riovcnt, flags);
|
||||
uint64_t Result = ::process_vm_readv(pid, Host_local_iovec.data(), liovcnt, Host_remote_iovec.data(), riovcnt, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(process_vm_writev, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, const struct iovec32 *local_iov, unsigned long liovcnt, const struct iovec32 *remote_iov, unsigned long riovcnt, unsigned long flags) -> uint64_t {
|
||||
std::vector<iovec> Host_local_iovec(liovcnt);
|
||||
std::vector<iovec> Host_remote_iovec(riovcnt);
|
||||
std::vector<iovec> Host_local_iovec(local_iov, local_iov + SanitizeIOCount(liovcnt));
|
||||
std::vector<iovec> Host_remote_iovec(remote_iov, remote_iov + SanitizeIOCount(riovcnt));
|
||||
|
||||
for (int i = 0; i < liovcnt; ++i) {
|
||||
Host_local_iovec[i] = local_iov[i];
|
||||
}
|
||||
|
||||
for (int i = 0; i < riovcnt; ++i) {
|
||||
Host_remote_iovec[i] = remote_iov[i];
|
||||
}
|
||||
|
||||
uint64_t Result = ::process_vm_writev(pid, &Host_local_iovec.at(0), liovcnt, &Host_remote_iovec.at(0), riovcnt, flags);
|
||||
uint64_t Result = ::process_vm_writev(pid, Host_local_iovec.data(), liovcnt, Host_remote_iovec.data(), riovcnt, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(preadv2, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset, int flags) -> uint64_t {
|
||||
std::vector<iovec> Host_iovec(iovcnt);
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
Host_iovec[i] = iov[i];
|
||||
}
|
||||
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
|
||||
|
||||
uint64_t Result = ::preadv2(fd, &Host_iovec.at(0), iovcnt, offset, flags);
|
||||
uint64_t Result = ::preadv2(fd, Host_iovec.data(), iovcnt, offset, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(pwritev2, [](FEXCore::Core::CpuStateFrame *Frame, int fd, const struct iovec32 *iov, int iovcnt, off_t offset, int flags) -> uint64_t {
|
||||
std::vector<iovec> Host_iovec(iovcnt);
|
||||
for (int i = 0; i < iovcnt; ++i) {
|
||||
Host_iovec[i] = iov[i];
|
||||
}
|
||||
std::vector<iovec> Host_iovec(iov, iov + SanitizeIOCount(iovcnt));
|
||||
|
||||
uint64_t Result = ::pwritev2(fd, &Host_iovec.at(0), iovcnt, offset, flags);
|
||||
uint64_t Result = ::pwritev2(fd, Host_iovec.data(), iovcnt, offset, flags);
|
||||
SYSCALL_ERRNO();
|
||||
});
|
||||
|
||||
@@ -625,7 +595,7 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
}
|
||||
|
||||
if (sigmaskpack) {
|
||||
if (sigmaskpack && sigmaskpack->sigset) {
|
||||
uint64_t *sigmask = sigmaskpack->sigset;
|
||||
size_t sigsetsize = sigmaskpack->size;
|
||||
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
|
||||
@@ -749,7 +719,7 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
}
|
||||
|
||||
if (sigmaskpack) {
|
||||
if (sigmaskpack && sigmaskpack->sigset) {
|
||||
uint64_t *sigmask = sigmaskpack->sigset;
|
||||
size_t sigsetsize = sigmaskpack->size;
|
||||
for (int32_t i = 0; i < (sigsetsize * 8); ++i) {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/CompilerDefs.h>
|
||||
|
||||
#include "Tests/LinuxSyscalls/x32/Types.h"
|
||||
#include "Tests/LinuxSyscalls/x32/Ioctl/HelperDefines.h"
|
||||
|
||||
@@ -20,8 +22,8 @@ namespace FEX::HLE::x32 {
|
||||
|
||||
namespace DRM {
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_version")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_version")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_version {
|
||||
int version_major; /**< Major version */
|
||||
int version_minor; /**< Minor version */
|
||||
@@ -66,8 +68,8 @@ fex_drm_version {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_unique")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_unique")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_unique {
|
||||
compat_size_t unique_len;
|
||||
compat_ptr<char> unique;
|
||||
@@ -88,8 +90,8 @@ fex_drm_unique {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_map")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_map")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_map {
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
@@ -122,8 +124,8 @@ fex_drm_map {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_client")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_client")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_client {
|
||||
int32_t idx;
|
||||
int32_t auth;
|
||||
@@ -156,8 +158,8 @@ fex_drm_client {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_stats")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_stats")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_stats {
|
||||
uint32_t count;
|
||||
struct {
|
||||
@@ -187,8 +189,8 @@ fex_drm_stats {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_buf_desc")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_buf_desc")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_buf_desc {
|
||||
int32_t count;
|
||||
int32_t size;
|
||||
@@ -225,8 +227,8 @@ fex_drm_buf_desc {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_buf_info")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_buf_info")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_buf_info {
|
||||
int32_t count;
|
||||
compat_ptr<struct drm_buf_desc> list;
|
||||
@@ -247,8 +249,8 @@ fex_drm_buf_info {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_buf_pub")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_buf_pub")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_buf_pub {
|
||||
int32_t idx;
|
||||
int32_t total;
|
||||
@@ -275,8 +277,8 @@ fex_drm_buf_pub {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_buf_map")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_buf_map")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_buf_map {
|
||||
int32_t count;
|
||||
#ifdef __cplusplus
|
||||
@@ -313,8 +315,8 @@ fex_drm_buf_map {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_buf_free")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_buf_free")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_buf_free {
|
||||
int32_t count;
|
||||
compat_ptr<int> list;
|
||||
@@ -335,8 +337,8 @@ fex_drm_buf_free {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_ctx_priv_map")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_ctx_priv_map")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_ctx_priv_map {
|
||||
uint32_t ctx_id;
|
||||
compat_ptr<void> handle;
|
||||
@@ -357,8 +359,8 @@ fex_drm_ctx_priv_map {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_ctx_res")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_ctx_res")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_ctx_res {
|
||||
int32_t count;
|
||||
compat_ptr<struct drm_ctx> contexts;
|
||||
@@ -378,8 +380,8 @@ fex_drm_ctx_res {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_dma")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_dma")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_dma {
|
||||
int32_t context;
|
||||
int32_t send_count;
|
||||
@@ -424,8 +426,8 @@ fex_drm_dma {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_scatter_gather")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_scatter_gather")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_scatter_gather {
|
||||
uint32_t size;
|
||||
uint32_t handle;
|
||||
@@ -446,8 +448,8 @@ fex_drm_scatter_gather {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_wait_vblank_request")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_wait_vblank_request")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_wait_vblank_request {
|
||||
enum drm_vblank_seq_type type;
|
||||
uint32_t sequence;
|
||||
@@ -471,8 +473,8 @@ fex_drm_wait_vblank_request {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_wait_vblank_reply")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_wait_vblank_reply")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_wait_vblank_reply {
|
||||
enum drm_vblank_seq_type type;
|
||||
uint32_t sequence;
|
||||
@@ -499,8 +501,8 @@ fex_drm_wait_vblank_reply {
|
||||
};
|
||||
|
||||
union
|
||||
__attribute__((annotate("alias-x86_32-drm_wait_vblank")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_wait_vblank")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_wait_vblank {
|
||||
fex_drm_wait_vblank_request request;
|
||||
fex_drm_wait_vblank_reply reply;
|
||||
@@ -509,9 +511,9 @@ fex_drm_wait_vblank {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_update_draw")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
__attribute__((packed))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_update_draw")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
FEX_PACKED
|
||||
fex_drm_update_draw {
|
||||
drm_drawable_t handle;
|
||||
uint32_t type;
|
||||
@@ -538,9 +540,9 @@ fex_drm_update_draw {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_mode_get_plane_res")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
__attribute__((packed))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_mode_get_plane_res")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
FEX_PACKED
|
||||
fex_drm_mode_get_plane_res {
|
||||
compat_uint64_t plane_id_ptr;
|
||||
uint32_t count_planes;
|
||||
@@ -560,9 +562,9 @@ fex_drm_mode_get_plane_res {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_mode_fb_cmd2")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
__attribute__((packed))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_mode_fb_cmd2")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
FEX_PACKED
|
||||
fex_drm_mode_fb_cmd2 {
|
||||
uint32_t fb_id;
|
||||
uint32_t width;
|
||||
@@ -608,9 +610,9 @@ fex_drm_mode_fb_cmd2 {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_mode_obj_get_properties")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
__attribute__((packed))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_mode_obj_get_properties")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
FEX_PACKED
|
||||
fex_drm_mode_obj_get_properties {
|
||||
compat_uint64_t props_ptr;
|
||||
compat_uint64_t prop_values_ptr;
|
||||
@@ -639,9 +641,9 @@ fex_drm_mode_obj_get_properties {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_mode_obj_set_property")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
__attribute__((packed))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_mode_obj_set_property")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
FEX_PACKED
|
||||
fex_drm_mode_obj_set_property {
|
||||
compat_uint64_t value;
|
||||
uint32_t prop_id;
|
||||
@@ -671,8 +673,8 @@ fex_drm_mode_obj_set_property {
|
||||
|
||||
namespace AMDGPU {
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_amdgpu_gem_metadata")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_amdgpu_gem_metadata")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_amdgpu_gem_metadata {
|
||||
__u32 handle;
|
||||
__u32 op;
|
||||
@@ -708,8 +710,8 @@ fex_drm_amdgpu_gem_metadata {
|
||||
|
||||
namespace MSM {
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_msm_timespec")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_msm_timespec")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_msm_timespec {
|
||||
compat_int64_t tv_sec;
|
||||
compat_int64_t tv_nsec;
|
||||
@@ -729,9 +731,9 @@ fex_drm_msm_timespec {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_msm_wait_fence")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
__attribute__((packed))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_msm_wait_fence")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
FEX_PACKED
|
||||
fex_drm_msm_wait_fence {
|
||||
uint32_t fence;
|
||||
uint32_t pad;
|
||||
@@ -762,8 +764,8 @@ fex_drm_msm_wait_fence {
|
||||
namespace I915 {
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_i915_batchbuffer")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_i915_batchbuffer")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_i915_batchbuffer_t {
|
||||
int32_t start;
|
||||
int32_t used;
|
||||
@@ -796,8 +798,8 @@ fex_drm_i915_batchbuffer_t {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_i915_irq_emit")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_i915_irq_emit")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_i915_irq_emit_t {
|
||||
compat_ptr<int> irq_seq;
|
||||
|
||||
@@ -815,8 +817,8 @@ fex_drm_i915_irq_emit_t {
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_i915_getparam")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_i915_getparam")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_i915_getparam_t
|
||||
{
|
||||
int32_t param;
|
||||
@@ -837,8 +839,8 @@ fex_drm_i915_getparam_t
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-drm_i915_mem_alloc")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-drm_i915_mem_alloc")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_i915_mem_alloc_t
|
||||
{
|
||||
int32_t region;
|
||||
@@ -865,8 +867,8 @@ fex_drm_i915_mem_alloc_t
|
||||
};
|
||||
|
||||
struct
|
||||
__attribute__((annotate("alias-x86_32-_drm_i915_cmdbuffer")))
|
||||
__attribute__((annotate("fex-match")))
|
||||
FEX_ANNOTATE("alias-x86_32-_drm_i915_cmdbuffer")
|
||||
FEX_ANNOTATE("fex-match")
|
||||
fex_drm_i915_cmdbuffer_t
|
||||
{
|
||||
compat_ptr<char> buf;
|
||||
|
||||
@@ -27,20 +27,6 @@ extern "C" {
|
||||
#include <unistd.h>
|
||||
|
||||
namespace FEX::HLE::x32 {
|
||||
#ifdef _M_X86_64
|
||||
uint32_t ioctl_32(int fd, uint32_t cmd, uint32_t args) {
|
||||
uint32_t Result{};
|
||||
__asm volatile("int $0x80;"
|
||||
: "=a" (Result)
|
||||
: "a" (SYSCALL_x86_ioctl)
|
||||
, "b" (fd)
|
||||
, "c" (cmd)
|
||||
, "d" (args)
|
||||
: "memory");
|
||||
return Result;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void UnhandledIoctl(const char *Type, int fd, uint32_t cmd, uint32_t args) {
|
||||
LogMan::Msg::E("@@@@@@@@@@@@@@@@@@@@@@@@@");
|
||||
LogMan::Msg::E("Unhandled %s ioctl(%d, 0x%08x, 0x%08x)", Type, fd, cmd, args);
|
||||
@@ -59,14 +45,108 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
|
||||
namespace DRM {
|
||||
std::map<uint32_t, std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)>> FDToHandler;
|
||||
uint32_t AddAndRunHandler(int fd, uint32_t cmd, uint32_t args);
|
||||
void AssignDeviceTypeToFD(int fd, drm_version const &Version);
|
||||
|
||||
template <size_t LRUSize>
|
||||
class LRUCacheFDCache {
|
||||
public:
|
||||
LRUCacheFDCache() {
|
||||
// Set the last element to our handler
|
||||
// This element will always be the last one
|
||||
LRUCache[LRUSize] = std::make_pair(0, AddAndRunHandler);
|
||||
}
|
||||
|
||||
using HandlerType = uint32_t(*)(int fd, uint32_t cmd, uint32_t args);
|
||||
void SetFDHandler(uint32_t FD, HandlerType Handler) {
|
||||
FDToHandler[FD] = Handler;
|
||||
}
|
||||
|
||||
void DuplicateFD(int fd, int NewFD) {
|
||||
auto it = FDToHandler.find(fd);
|
||||
if (it != FDToHandler.end()) {
|
||||
FDToHandler[NewFD] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
HandlerType FindHandler(uint32_t FD) {
|
||||
HandlerType Handler{};
|
||||
for (size_t i = 0; i < LRUSize; ++i) {
|
||||
auto &it = LRUCache[i];
|
||||
if (it.first == FD) {
|
||||
if (i == 0) {
|
||||
// If we are the first in the queue then just return it
|
||||
return it.second;
|
||||
}
|
||||
Handler = it.second;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Handler) {
|
||||
AddToFront(FD, Handler);
|
||||
return Handler;
|
||||
}
|
||||
return LRUCache[LRUSize].second;
|
||||
}
|
||||
|
||||
uint32_t AddAndRunMapHandler(int fd, uint32_t cmd, uint32_t args) {
|
||||
// Couldn't find in cache, check map
|
||||
{
|
||||
auto it = FDToHandler.find(fd);
|
||||
if (it != FDToHandler.end()) {
|
||||
// Found, add to the cache
|
||||
AddToFront(fd, it->second);
|
||||
return it->second(fd, cmd, args);
|
||||
}
|
||||
}
|
||||
|
||||
// Wasn't found in map, query it
|
||||
drm_version Host_Version{};
|
||||
Host_Version.name = reinterpret_cast<char*>(alloca(128));
|
||||
Host_Version.name_len = 128;
|
||||
uint64_t Result = ioctl(fd, DRM_IOCTL_VERSION, &Host_Version);
|
||||
|
||||
// Add it to the map and double check that it was added
|
||||
// Next time around when the ioctl is used then it will be added to cache
|
||||
if (Result != -1) {
|
||||
AssignDeviceTypeToFD(fd, Host_Version);
|
||||
}
|
||||
|
||||
auto it = FDToHandler.find(fd);
|
||||
|
||||
if (it == FDToHandler.end()) {
|
||||
// We don't understand this DRM ioctl
|
||||
return -EPERM;
|
||||
}
|
||||
Result = it->second(fd, cmd, args);
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
private:
|
||||
void AddToFront(uint32_t FD, HandlerType Handler) {
|
||||
// Push the element to the front if we found one
|
||||
// First copy all the other elements back one
|
||||
// Ensuring the final element isn't written over
|
||||
memmove(&LRUCache[1], &LRUCache[0], (LRUSize - 1) * sizeof(LRUCache[0]));
|
||||
// Now set the first element to the one we just found
|
||||
LRUCache[0] = std::make_pair(FD, Handler);
|
||||
}
|
||||
// With four elements total (3 + 1) then this is a single cacheline in size
|
||||
std::pair<uint32_t, HandlerType> LRUCache[LRUSize + 1];
|
||||
std::map<uint32_t, HandlerType> FDToHandler;
|
||||
};
|
||||
|
||||
static LRUCacheFDCache<3> FDToHandler;
|
||||
|
||||
uint32_t AddAndRunHandler(int fd, uint32_t cmd, uint32_t args) {
|
||||
return FDToHandler.AddAndRunMapHandler(fd, cmd, args);
|
||||
}
|
||||
|
||||
void CheckAndAddFDDuplication(int fd, int NewFD) {
|
||||
auto it = FDToHandler.find(fd);
|
||||
if (it != FDToHandler.end()) {
|
||||
FDToHandler[NewFD] = it->second;
|
||||
}
|
||||
FDToHandler.DuplicateFD(fd, NewFD);
|
||||
}
|
||||
|
||||
uint32_t AMDGPU_Handler(int fd, uint32_t cmd, uint32_t args) {
|
||||
switch (_IOC_NR(cmd)) {
|
||||
case _IOC_NR(FEX_DRM_IOCTL_AMDGPU_GEM_METADATA): {
|
||||
@@ -260,22 +340,22 @@ namespace FEX::HLE::x32 {
|
||||
void AssignDeviceTypeToFD(int fd, drm_version const &Version) {
|
||||
if (Version.name) {
|
||||
if (strcmp(Version.name, "amdgpu") == 0) {
|
||||
FDToHandler[fd] = AMDGPU_Handler;
|
||||
FDToHandler.SetFDHandler(fd, AMDGPU_Handler);
|
||||
}
|
||||
else if (strcmp(Version.name, "msm") == 0) {
|
||||
FDToHandler[fd] = MSM_Handler;
|
||||
FDToHandler.SetFDHandler(fd, MSM_Handler);
|
||||
}
|
||||
else if (strcmp(Version.name, "nouveau") == 0) {
|
||||
FDToHandler[fd] = Nouveau_Handler;
|
||||
FDToHandler.SetFDHandler(fd, Nouveau_Handler);
|
||||
}
|
||||
else if (strcmp(Version.name, "i915") == 0) {
|
||||
FDToHandler[fd] = I915_Handler;
|
||||
FDToHandler.SetFDHandler(fd, I915_Handler);
|
||||
}
|
||||
else if (strcmp(Version.name, "panfrost") == 0) {
|
||||
FDToHandler[fd] = Panfrost_Handler;
|
||||
FDToHandler.SetFDHandler(fd, Panfrost_Handler);
|
||||
}
|
||||
else if (strcmp(Version.name, "lima") == 0) {
|
||||
FDToHandler[fd] = Lima_Handler;
|
||||
FDToHandler.SetFDHandler(fd, Lima_Handler);
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::E("Unknown DRM device: '%s'", Version.name);
|
||||
@@ -360,24 +440,8 @@ namespace FEX::HLE::x32 {
|
||||
|
||||
case DRM_COMMAND_BASE ... (DRM_COMMAND_END - 1): {
|
||||
// This is the space of the DRM device commands
|
||||
auto it = FDToHandler.find(fd);
|
||||
if (it == FDToHandler.end()) {
|
||||
drm_version Host_Version{};
|
||||
Host_Version.name = reinterpret_cast<char*>(alloca(128));
|
||||
Host_Version.name_len = 128;
|
||||
uint64_t Result = ioctl(fd, DRM_IOCTL_VERSION, &Host_Version);
|
||||
|
||||
if (Result != -1) {
|
||||
AssignDeviceTypeToFD(fd, Host_Version);
|
||||
}
|
||||
|
||||
it = FDToHandler.find(fd);
|
||||
|
||||
if (it == FDToHandler.end()) {
|
||||
return -EPERM;
|
||||
}
|
||||
}
|
||||
return it->second(fd, cmd, args);
|
||||
auto it = FDToHandler.FindHandler(fd);
|
||||
return it(fd, cmd, args);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -400,7 +464,7 @@ namespace FEX::HLE::x32 {
|
||||
std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)> Handler;
|
||||
};
|
||||
|
||||
static std::unordered_map<uint32_t, std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)>> Handlers;
|
||||
static std::vector<std::function<uint32_t(int fd, uint32_t cmd, uint32_t args)>> Handlers;
|
||||
|
||||
void InitializeStaticIoctlHandlers() {
|
||||
using namespace DRM;
|
||||
@@ -452,20 +516,15 @@ namespace FEX::HLE::x32 {
|
||||
#undef _CUSTOM_META_OFFSET
|
||||
}};
|
||||
|
||||
Handlers.assign(1U << _IOC_TYPEBITS, FEX::HLE::x32::BasicHandler::BasicHandler);
|
||||
|
||||
for (auto &Arg : LocalHandlers) {
|
||||
Handlers[Arg.Command] = Arg.Handler;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t ioctl32(FEXCore::Core::CpuStateFrame *Frame, int fd, uint32_t request, uint32_t args) {
|
||||
//return ioctl_32(fd, request, args);
|
||||
auto It = Handlers.find(_IOC_TYPE(request));
|
||||
if (It == Handlers.end()) {
|
||||
UnhandledIoctl("Base", fd, request, args);
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
return It->second(fd, request, args);
|
||||
return Handlers[_IOC_TYPE(request)](fd, request, args);
|
||||
}
|
||||
|
||||
void CheckAndAddFDDuplication(int fd, int NewFD) {
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace FEX::HLE::x32 {
|
||||
};
|
||||
|
||||
struct msgbuf_32 {
|
||||
uint32_t mtype;
|
||||
compat_long_t mtype;
|
||||
char mtext[1];
|
||||
};
|
||||
|
||||
@@ -640,13 +640,29 @@ namespace FEX::HLE::x32 {
|
||||
std::vector<uint8_t> Tmp(second + sizeof(size_t));
|
||||
struct msgbuf *TmpMsg = reinterpret_cast<struct msgbuf *>(&Tmp.at(0));
|
||||
|
||||
Result = ::msgrcv(first, TmpMsg, second, *reinterpret_cast<uint32_t*>(fifth), third);
|
||||
if (call >> 16) {
|
||||
Result = ::msgrcv(first, TmpMsg, second, fifth, third);
|
||||
if (Result != -1) {
|
||||
msgbuf_32 *src = reinterpret_cast<msgbuf_32*>(ptr);
|
||||
src->mtype = TmpMsg->mtype;
|
||||
memcpy(src->mtext, TmpMsg->mtext, Result);
|
||||
}
|
||||
|
||||
if (Result != -1) {
|
||||
msgbuf_32 *src = reinterpret_cast<msgbuf_32*>(*reinterpret_cast<uint32_t*>(ptr));
|
||||
src->mtype = TmpMsg->mtype;
|
||||
memcpy(src->mtext, TmpMsg->mtext, Result);
|
||||
}
|
||||
else {
|
||||
struct compat_ipc_kludge {
|
||||
compat_uptr_t msgp;
|
||||
compat_long_t msgtyp;
|
||||
};
|
||||
compat_ipc_kludge *ipck = reinterpret_cast<compat_ipc_kludge*>(ptr);
|
||||
Result = ::msgrcv(first, TmpMsg, second, ipck->msgtyp, third);
|
||||
if (Result != -1) {
|
||||
msgbuf_32 *src = reinterpret_cast<msgbuf_32*>(ipck->msgp);
|
||||
ipck->msgtyp = TmpMsg->mtype;
|
||||
memcpy(src->mtext, TmpMsg->mtext, Result);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case OP_MSGGET: {
|
||||
@@ -655,9 +671,10 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
case OP_MSGCTL: {
|
||||
uint32_t msqid = first;
|
||||
int32_t cmd = third & 0xFF;
|
||||
msgun_32 *msgun = reinterpret_cast<msgun_32*>(ptr);
|
||||
bool IPC64 = third & 0x100;
|
||||
int32_t cmd = second & 0xFF;
|
||||
msgun_32 msgun{};
|
||||
msgun.val = ptr;
|
||||
bool IPC64 = second & 0x100;
|
||||
#define UNHANDLED(x) case x: LOGMAN_MSG_A("Unhandled msgctl cmd: " #x); break
|
||||
switch (cmd) {
|
||||
UNHANDLED(IPC_SET);
|
||||
@@ -668,10 +685,10 @@ namespace FEX::HLE::x32 {
|
||||
Result = ::msgctl(msqid, cmd, &buf);
|
||||
if (Result != -1) {
|
||||
if (IPC64) {
|
||||
*msgun->buf64 = buf;
|
||||
*msgun.buf64 = buf;
|
||||
}
|
||||
else {
|
||||
*msgun->buf32 = buf;
|
||||
*msgun.buf32 = buf;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -681,7 +698,7 @@ namespace FEX::HLE::x32 {
|
||||
struct msginfo mi{};
|
||||
Result = ::msgctl(msqid, cmd, reinterpret_cast<struct msqid_ds*>(&mi));
|
||||
if (Result != -1) {
|
||||
memcpy(msgun->__buf, &mi, sizeof(mi));
|
||||
memcpy(msgun.__buf, &mi, sizeof(mi));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -29,7 +29,11 @@ namespace FEX::HLE::x32 {
|
||||
return static_cast<uint32_t>(reinterpret_cast<uint64_t>(oldact.sigaction_handler.handler));
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(rt_sigaction, [](FEXCore::Core::CpuStateFrame *Frame, int signum, const GuestSigAction_32 *act, GuestSigAction_32 *oldact) -> uint64_t {
|
||||
REGISTER_SYSCALL_IMPL_X32(rt_sigaction, [](FEXCore::Core::CpuStateFrame *Frame, int signum, const GuestSigAction_32 *act, GuestSigAction_32 *oldact, size_t sigsetsize) -> uint64_t {
|
||||
if (sigsetsize != 8) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
FEXCore::GuestSigAction *act64_p{};
|
||||
FEXCore::GuestSigAction *old64_p{};
|
||||
|
||||
@@ -45,11 +49,11 @@ namespace FEX::HLE::x32 {
|
||||
}
|
||||
|
||||
uint64_t Result = FEX::HLE::_SyscallHandler->GetSignalDelegator()->RegisterGuestSignalHandler(signum, act64_p, old64_p);
|
||||
if (Result != -1 && oldact) {
|
||||
if (Result == 0 && oldact) {
|
||||
*oldact = old64;
|
||||
}
|
||||
|
||||
SYSCALL_ERRNO();
|
||||
return Result;
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(rt_sigtimedwait, [](FEXCore::Core::CpuStateFrame *Frame, uint64_t *set, siginfo_t *info, const struct timespec32* timeout, size_t sigsetsize) -> uint64_t {
|
||||
|
||||
Loaded 100 of 185 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user