mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 15:00:17 +02:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
63ce78c41d | ||
|
|
fc38df2ff0 | ||
|
|
2fca207e14 | ||
|
|
308fa76aa3 | ||
|
|
b6ac26e0e9 | ||
|
|
ecd144de6a | ||
|
|
7e66508016 | ||
|
|
d6f50bf7b0 | ||
|
|
e7069f9f95 | ||
|
|
ea96ccb63d | ||
|
|
4d4eac0987 | ||
|
|
f0ee8a49b2 | ||
|
|
e0d8fc7c2b | ||
|
|
adf1de5562 | ||
|
|
e310e29898 | ||
|
|
37ec68421c | ||
|
|
41731e2680 | ||
|
|
5e6a3c6280 | ||
|
|
e71e3ec930 | ||
|
|
4cac100660 | ||
|
|
378e0692b9 | ||
|
|
678415c4c9 | ||
|
|
e869b2fe67 | ||
|
|
2194a1027c | ||
|
|
52b4378e49 | ||
|
|
0f45318040 | ||
|
|
21fbcef0bd | ||
|
|
ef02083767 | ||
|
|
24904f48c4 | ||
|
|
9461ab5094 | ||
|
|
66c8b14470 | ||
|
|
6ab78ca93b | ||
|
|
2fe808f5cd | ||
|
|
81a94b9ffe | ||
|
|
0d87ed46da | ||
|
|
545a216da6 | ||
|
|
11f65df554 | ||
|
|
29ff642499 | ||
|
|
d36517a9d3 | ||
|
|
83419b410d | ||
|
|
77fad28b69 | ||
|
|
70aefc9db2 | ||
|
|
d2e0adf540 | ||
|
|
84060cd947 | ||
|
|
aaf17b6d41 | ||
|
|
8ded25ada7 | ||
|
|
5b9fe8f26b | ||
|
|
e65b429c83 | ||
|
|
dd290f129f | ||
|
|
1832cc80d6 | ||
|
|
fe1faf9ebe | ||
|
|
12d0a7fa98 | ||
|
|
c1b08079f3 | ||
|
|
d688026fe4 | ||
|
|
1c388b455a | ||
|
|
9426abc98d | ||
|
|
5ee2db34a7 | ||
|
|
ad37c19043 | ||
|
|
0e6c5911b8 | ||
|
|
f2aa0026b5 | ||
|
|
553efbeb29 | ||
|
|
b39a882a2d | ||
|
|
85f7f8e6c0 | ||
|
|
4d25de31de | ||
|
|
9e01730c6c | ||
|
|
fea3ee1298 | ||
|
|
e1c42315ed | ||
|
|
165db37c8d | ||
|
|
9b23ae9133 | ||
|
|
f951a406e6 | ||
|
|
497b5c0561 | ||
|
|
95393b07fb | ||
|
|
372da1b820 | ||
|
|
61a59d0314 | ||
|
|
1045e05870 | ||
|
|
052872725c | ||
|
|
4d655218ab | ||
|
|
78ba195b66 | ||
|
|
ae2b28716d | ||
|
|
326e5e8d57 | ||
|
|
0a8fc2cbef | ||
|
|
552293b226 | ||
|
|
751a4c8019 | ||
|
|
68b2072eab | ||
|
|
e3cac40b1b | ||
|
|
9b123353b3 | ||
|
|
f2c0c55b9c | ||
|
|
a4c694ffc7 | ||
|
|
1eb722dea7 | ||
|
|
a6746988d7 | ||
|
|
0a1707f1bd | ||
|
|
23b9d8e108 | ||
|
|
3fa44604ba | ||
|
|
d3bc0c084d | ||
|
|
e206414919 | ||
|
|
e635cc5404 | ||
|
|
822d67467b | ||
|
|
e043d2c0f5 | ||
|
|
d58c4405f7 | ||
|
|
66d879f387 | ||
|
|
55d3edb8e6 | ||
|
|
98f0f22f41 | ||
|
|
5f574fb935 | ||
|
|
618f5bb869 | ||
|
|
b5ca5f173e | ||
|
|
5cf6a680bb | ||
|
|
645f40bb96 | ||
|
|
268deddd09 | ||
|
|
e4488b0cfc | ||
|
|
65b9dcd20b | ||
|
|
b2c333c383 | ||
|
|
1ea53c65ab | ||
|
|
8beae0fce4 | ||
|
|
add775c5cd | ||
|
|
f3e6f62356 | ||
|
|
59ab10f155 | ||
|
|
2e1bd4b32b | ||
|
|
f71f2445db | ||
|
|
f6e2fe1515 | ||
|
|
11c8db5a14 | ||
|
|
65b2da20d6 | ||
|
|
273f5e1f26 | ||
|
|
35af4bd42a | ||
|
|
7f1464b135 | ||
|
|
e594b2c4c7 | ||
|
|
2aead5aec2 | ||
|
|
c3f1f602fe | ||
|
|
9c256bfe96 | ||
|
|
b5bc8cd294 | ||
|
|
e78b573610 | ||
|
|
81a89ab747 | ||
|
|
fd17a3de50 | ||
|
|
2f260ae6ad | ||
|
|
bf7118fc85 | ||
|
|
a90f5363dd | ||
|
|
ab03e59500 | ||
|
|
a59d700bbe | ||
|
|
7cf27a7c26 | ||
|
|
14e1d16710 | ||
|
|
203f29a91f | ||
|
|
25f0a03ceb | ||
|
|
3ced41414e | ||
|
|
1a64b26d03 | ||
|
|
efafe0e6e9 | ||
|
|
35746c7669 | ||
|
|
4a69b87cb9 | ||
|
|
fb2de47e73 | ||
|
|
bcee3e9374 | ||
|
|
6d87154ac8 | ||
|
|
c5d799df8c | ||
|
|
449645669a | ||
|
|
3ac7b2cddf | ||
|
|
504d409cf6 | ||
|
|
29a6d584a9 | ||
|
|
310fcf969c | ||
|
|
b329442c09 | ||
|
|
f4d799abdd | ||
|
|
4bb7f49c2a | ||
|
|
f7f2dc2210 | ||
|
|
d40812929f | ||
|
|
c381185a7d | ||
|
|
ac5d09885e | ||
|
|
d9a505e22e | ||
|
|
a96ad0fc9d | ||
|
|
9268a356f6 | ||
|
|
92141d3edc | ||
|
|
8c8b680640 | ||
|
|
504be62a92 | ||
|
|
62e2f1b45d | ||
|
|
880cc72842 | ||
|
|
feacd897fc | ||
|
|
fdd950e1d1 | ||
|
|
b02af95629 | ||
|
|
2bd64ad24e | ||
|
|
fa95a823c9 | ||
|
|
399ed61380 | ||
|
|
71550e29eb | ||
|
|
1b8d8f8280 | ||
|
|
ff6c70f5e1 | ||
|
|
13ee2b5ec4 | ||
|
|
3c1ba846f7 | ||
|
|
1b4488e7a3 | ||
|
|
6b4df4c998 | ||
|
|
2a1ef0ba56 | ||
|
|
dd2e70e4aa | ||
|
|
915a8b23ae | ||
|
|
6eeafd0724 | ||
|
|
e6fc159d88 | ||
|
|
5fd68b6f07 | ||
|
|
5f80702cf1 | ||
|
|
b45b980b3a | ||
|
|
f341755e3b | ||
|
|
c53e7d759b | ||
|
|
143ef57141 | ||
|
|
8689038533 | ||
|
|
ade34eeda6 | ||
|
|
0218c966bd | ||
|
|
ec5bc9cf3e | ||
|
|
63bf0d5826 | ||
|
|
2526fa8b6f | ||
|
|
ef6f5d2003 | ||
|
|
be02cafb05 | ||
|
|
e8fd8ef3b7 | ||
|
|
d7c6ed842d | ||
|
|
86a6118b62 | ||
|
|
7a75e43125 | ||
|
|
4ef3066b69 | ||
|
|
88fee019a1 | ||
|
|
5d3141dffc | ||
|
|
94e91565b1 | ||
|
|
acbfee55b4 | ||
|
|
a2481d6892 | ||
|
|
e255f1cdef | ||
|
|
cb3cfed9c2 | ||
|
|
2b12a46d2d | ||
|
|
8f50109501 | ||
|
|
1d451b8df1 | ||
|
|
49772c6826 | ||
|
|
535a2ab2ba | ||
|
|
b8b212719f | ||
|
|
be593d43ce | ||
|
|
a89b7c5dbb | ||
|
|
c682f51811 | ||
|
|
2c7562c54c | ||
|
|
8f5ec20cb7 | ||
|
|
582108a68a | ||
|
|
79abe2aa64 | ||
|
|
9f3857b3b0 | ||
|
|
9521638910 | ||
|
|
60b76f53cf | ||
|
|
5da90aac46 | ||
|
|
ba5ad72ca2 | ||
|
|
c7c47a827a | ||
|
|
c4b66b41cd | ||
|
|
6047ca9fe2 | ||
|
|
a5762b6faa | ||
|
|
3bc722ca69 | ||
|
|
d7d8a4e28a | ||
|
|
c54c568fef | ||
|
|
37421d36e6 | ||
|
|
bd86deb9ba | ||
|
|
2e701fc9e6 | ||
|
|
5b97e7f1a0 | ||
|
|
844e27e9ad | ||
|
|
cf147e8ab2 | ||
|
|
e61132b481 | ||
|
|
c58e7a732e | ||
|
|
0538574dd0 | ||
|
|
1ed546d48f | ||
|
|
5ba0053edc | ||
|
|
abb8de0966 | ||
|
|
abc596c634 | ||
|
|
d107bc9a24 | ||
|
|
a45047bc1e | ||
|
|
1089987a29 | ||
|
|
6522d3d6e4 | ||
|
|
d65fcf7bb8 | ||
|
|
16e0f628cd | ||
|
|
d81097482d | ||
|
|
75bc997ab7 | ||
|
|
600e8749d7 | ||
|
|
fc3863f444 | ||
|
|
347abf09ef | ||
|
|
7442ef3a83 | ||
|
|
3783ad8dd1 | ||
|
|
65ad916984 | ||
|
|
d491ce7125 | ||
|
|
c0bc5d9748 | ||
|
|
3f6edf7b5a | ||
|
|
5563a51b84 | ||
|
|
dac075b871 | ||
|
|
db8317caf8 | ||
|
|
3508f7a667 | ||
|
|
02861f41eb | ||
|
|
51c9f70904 | ||
|
|
4f9530cec3 | ||
|
|
ecd711e691 | ||
|
|
870115dd5d | ||
|
|
848e5561ce | ||
|
|
db8a9bb5cf | ||
|
|
f8a1c43c06 | ||
|
|
3a98190119 | ||
|
|
19ad19193e |
No files matched your search
@@ -72,6 +72,11 @@ jobs:
|
||||
# Execute the build. You can specify a specific target with "--target <NAME>"
|
||||
run: cmake --build . --config $BUILD_TYPE
|
||||
|
||||
- name: Install
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target install
|
||||
|
||||
- name: ASM Tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
@@ -199,12 +204,6 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ThunkgenTests.log || true
|
||||
|
||||
- name: Install
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target install
|
||||
|
||||
- name: Test GL No-Thunks
|
||||
if: matrix.arch[1] == 'x64'
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"Config": {
|
||||
"HideHypervisorBit": "1"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"Config": {
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": "1"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -30,7 +30,7 @@
|
||||
#include <tiny-json.h>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace FEXCore::Config {
|
||||
|
||||
+14
-5
@@ -240,6 +240,17 @@
|
||||
"Also needs x86_64-linux-gnu-objdump in PATH.",
|
||||
"Can be very slow."
|
||||
]
|
||||
},
|
||||
"InjectLibSegFault": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"Sets the environment variable LD_PRELOAD=libSegFault.so",
|
||||
"This allows the user to very easily enable libSegFault without dealing with environment variables",
|
||||
"Very useful for applications that have launch scripts that set the variable to nothing at launch",
|
||||
"Set this in an application configuration for injecting in to only specific applications.",
|
||||
"\tNote: If x86/x86_64 libSegFault.so isn't installed then this option won't work."
|
||||
]
|
||||
}
|
||||
},
|
||||
"Logging": {
|
||||
@@ -332,14 +343,12 @@
|
||||
"Useful for a process that keeps restarting and doesn't work"
|
||||
]
|
||||
},
|
||||
"x86dec_SynchronizeRIPOnAllBlocks": {
|
||||
"HideHypervisorBit": {
|
||||
"Type": "bool",
|
||||
"Default": "false",
|
||||
"Desc": [
|
||||
"An application that uses try-catch or longjump extensively needs the ability to do context aware state flushing",
|
||||
"In the case of FEX's block-linking, it won't always ensure that RIP is synchronized.",
|
||||
"If an exception occurs and RIP isn't synchronized, then FEX's exception stack restore may not long jump as expected",
|
||||
"Can be useful for Wine applications that rely on stack unwinding"
|
||||
"Hides the hypervisor CPUID bit when set.",
|
||||
"Should only be used for applications that have issues with this set."
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
+57
-152
@@ -28,203 +28,108 @@ namespace FEXCore::Context {
|
||||
FEXCore::Paths::ShutdownPaths();
|
||||
}
|
||||
|
||||
FEXCore::Context::Context *CreateNewContext() {
|
||||
return new FEXCore::Context::Context{};
|
||||
FEXCore::Context::Context *FEXCore::Context::Context::CreateNewContext() {
|
||||
return new FEXCore::Context::ContextImpl{};
|
||||
}
|
||||
|
||||
bool InitializeContext(FEXCore::Context::Context *CTX) {
|
||||
return FEXCore::CPU::CreateCPUCore(CTX);
|
||||
}
|
||||
|
||||
void DestroyContext(FEXCore::Context::Context *CTX) {
|
||||
if (CTX->ParentThread) {
|
||||
CTX->DestroyThread(CTX->ParentThread);
|
||||
}
|
||||
void FEXCore::Context::Context::DestroyContext(FEXCore::Context::Context *CTX) {
|
||||
CTX->DestroyContext();
|
||||
delete CTX;
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState* InitCore(FEXCore::Context::Context *CTX, uint64_t InitialRIP, uint64_t StackPointer) {
|
||||
return CTX->InitCore(InitialRIP, StackPointer);
|
||||
bool FEXCore::Context::ContextImpl::InitializeContext() {
|
||||
return FEXCore::CPU::CreateCPUCore(this);
|
||||
}
|
||||
|
||||
void SetExitHandler(FEXCore::Context::Context *CTX, ExitHandler handler) {
|
||||
CTX->CustomExitHandler = std::move(handler);
|
||||
void FEXCore::Context::ContextImpl::DestroyContext() {
|
||||
if (ParentThread) {
|
||||
DestroyThread(ParentThread);
|
||||
}
|
||||
}
|
||||
|
||||
ExitHandler GetExitHandler(const FEXCore::Context::Context *CTX) {
|
||||
return CTX->CustomExitHandler;
|
||||
void FEXCore::Context::ContextImpl::SetExitHandler(ExitHandler handler) {
|
||||
CustomExitHandler = std::move(handler);
|
||||
}
|
||||
|
||||
void Run(FEXCore::Context::Context *CTX) {
|
||||
CTX->Run();
|
||||
ExitHandler FEXCore::Context::ContextImpl::GetExitHandler() const {
|
||||
return CustomExitHandler;
|
||||
}
|
||||
|
||||
void Step(FEXCore::Context::Context *CTX) {
|
||||
CTX->Step();
|
||||
void FEXCore::Context::ContextImpl::Stop() {
|
||||
Stop(false);
|
||||
}
|
||||
|
||||
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
Thread->CTX->CompileBlock(Thread->CurrentFrame, GuestRIP);
|
||||
void FEXCore::Context::ContextImpl::CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
CompileBlock(Thread->CurrentFrame, GuestRIP);
|
||||
}
|
||||
|
||||
FEXCore::Context::ExitReason RunUntilExit(FEXCore::Context::Context *CTX) {
|
||||
return CTX->RunUntilExit();
|
||||
FEXCore::Context::ExitReason FEXCore::Context::ContextImpl::GetExitReason() {
|
||||
return ParentThread->ExitReason;
|
||||
}
|
||||
|
||||
int GetProgramStatus(const FEXCore::Context::Context *CTX) {
|
||||
return CTX->GetProgramStatus();
|
||||
bool FEXCore::Context::ContextImpl::IsDone() const {
|
||||
return IsPaused();
|
||||
}
|
||||
|
||||
FEXCore::Context::ExitReason GetExitReason(const FEXCore::Context::Context *CTX) {
|
||||
return CTX->ParentThread->ExitReason;
|
||||
void FEXCore::Context::ContextImpl::GetCPUState(FEXCore::Core::CPUState *State) const {
|
||||
memcpy(State, ParentThread->CurrentFrame, sizeof(FEXCore::Core::CPUState));
|
||||
}
|
||||
|
||||
bool IsDone(const FEXCore::Context::Context *CTX) {
|
||||
return CTX->IsPaused();
|
||||
void FEXCore::Context::ContextImpl::SetCPUState(const FEXCore::Core::CPUState *State) {
|
||||
memcpy(ParentThread->CurrentFrame, State, sizeof(FEXCore::Core::CPUState));
|
||||
}
|
||||
|
||||
void GetCPUState(const FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *State) {
|
||||
memcpy(State, CTX->ParentThread->CurrentFrame, sizeof(FEXCore::Core::CPUState));
|
||||
void FEXCore::Context::ContextImpl::SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) {
|
||||
CustomCPUFactory = std::move(Factory);
|
||||
}
|
||||
|
||||
void SetCPUState(FEXCore::Context::Context *CTX, const FEXCore::Core::CPUState *State) {
|
||||
memcpy(CTX->ParentThread->CurrentFrame, State, sizeof(FEXCore::Core::CPUState));
|
||||
}
|
||||
|
||||
void Pause(FEXCore::Context::Context *CTX) {
|
||||
CTX->Pause();
|
||||
}
|
||||
|
||||
void Stop(FEXCore::Context::Context *CTX) {
|
||||
CTX->Stop(false);
|
||||
}
|
||||
|
||||
void SetCustomCPUBackendFactory(FEXCore::Context::Context *CTX, CustomCPUFactoryType Factory) {
|
||||
CTX->CustomCPUFactory = std::move(Factory);
|
||||
}
|
||||
|
||||
bool AddVirtualMemoryMapping([[maybe_unused]] FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t VirtualAddress, [[maybe_unused]] uint64_t PhysicalAddress, [[maybe_unused]] uint64_t Size) {
|
||||
bool FEXCore::Context::ContextImpl::AddVirtualMemoryMapping([[maybe_unused]] uint64_t VirtualAddress, [[maybe_unused]] uint64_t PhysicalAddress, [[maybe_unused]] uint64_t Size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, [[maybe_unused]] uint64_t Syscall, [[maybe_unused]] FEXCore::HLE::SyscallVisitor *Visitor) {
|
||||
HostFeatures FEXCore::Context::ContextImpl::GetHostFeatures() const {
|
||||
return HostFeatures;
|
||||
}
|
||||
|
||||
HostFeatures GetHostFeatures(const FEXCore::Context::Context *CTX) {
|
||||
return CTX->HostFeatures;
|
||||
void FEXCore::Context::ContextImpl::SetSignalDelegator(FEXCore::SignalDelegator *_SignalDelegation) {
|
||||
SignalDelegation = _SignalDelegation;
|
||||
}
|
||||
|
||||
void HandleCallback(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) {
|
||||
CTX->HandleCallback(Thread, RIP);
|
||||
void FEXCore::Context::ContextImpl::SetSyscallHandler(FEXCore::HLE::SyscallHandler *Handler) {
|
||||
SyscallHandler = Handler;
|
||||
SourcecodeResolver = Handler->GetSourcecodeResolver();
|
||||
}
|
||||
|
||||
void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required) {
|
||||
CTX->RegisterHostSignalHandler(Signal, std::move(Func), Required);
|
||||
FEXCore::CPUID::FunctionResults FEXCore::Context::ContextImpl::RunCPUIDFunction(uint32_t Function, uint32_t Leaf) {
|
||||
return CPUID.RunFunction(Function, Leaf);
|
||||
}
|
||||
|
||||
void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required) {
|
||||
CTX->RegisterFrontendHostSignalHandler(Signal, std::move(Func), Required);
|
||||
FEXCore::CPUID::FunctionResults FEXCore::Context::ContextImpl::RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) {
|
||||
return CPUID.RunFunctionName(Function, Leaf, CPU);
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
|
||||
return CTX->CreateThread(NewThreadState, ParentTID);
|
||||
}
|
||||
|
||||
void ExecutionThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
return CTX->ExecutionThread(Thread);
|
||||
}
|
||||
|
||||
void InitializeThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
return CTX->InitializeThread(Thread);
|
||||
}
|
||||
|
||||
void RunThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
CTX->RunThread(Thread);
|
||||
}
|
||||
|
||||
void StopThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
CTX->StopThread(Thread);
|
||||
}
|
||||
|
||||
void DestroyThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
CTX->DestroyThread(Thread);
|
||||
}
|
||||
|
||||
void CleanupAfterFork(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread) {
|
||||
CTX->CleanupAfterFork(Thread);
|
||||
}
|
||||
|
||||
void SetSignalDelegator(FEXCore::Context::Context *CTX, FEXCore::SignalDelegator *SignalDelegation) {
|
||||
CTX->SignalDelegation = SignalDelegation;
|
||||
}
|
||||
|
||||
void SetSyscallHandler(FEXCore::Context::Context *CTX, FEXCore::HLE::SyscallHandler *Handler) {
|
||||
CTX->SyscallHandler = Handler;
|
||||
CTX->SourcecodeResolver = Handler->GetSourcecodeResolver();
|
||||
}
|
||||
|
||||
FEXCore::CPUID::FunctionResults RunCPUIDFunction(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf) {
|
||||
return CTX->CPUID.RunFunction(Function, Leaf);
|
||||
}
|
||||
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf, uint32_t CPU) {
|
||||
return CTX->CPUID.RunFunctionName(Function, Leaf, CPU);
|
||||
}
|
||||
|
||||
void SetAOTIRLoader(FEXCore::Context::Context *CTX, std::function<int(const std::string&)> CacheReader) {
|
||||
CTX->SetAOTIRLoader(CacheReader);
|
||||
}
|
||||
|
||||
void SetAOTIRWriter(FEXCore::Context::Context *CTX, std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) {
|
||||
CTX->SetAOTIRWriter(CacheWriter);
|
||||
}
|
||||
|
||||
void SetAOTIRRenamer(FEXCore::Context::Context *CTX, std::function<void(const std::string&)> CacheRenamer) {
|
||||
CTX->SetAOTIRRenamer(CacheRenamer);
|
||||
}
|
||||
|
||||
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) {
|
||||
CTX->WriteFilesWithCode(Writer);
|
||||
}
|
||||
|
||||
IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(FEXCore::Context::Context *CTX, const std::string &Name) {
|
||||
return CTX->LoadAOTIRCacheEntry(Name);
|
||||
}
|
||||
void UnloadAOTIRCacheEntry(FEXCore::Context::Context *CTX, IR::AOTIRCacheEntry *Entry) {
|
||||
return CTX->UnloadAOTIRCacheEntry(Entry);
|
||||
}
|
||||
|
||||
CustomIRResult AddCustomIREntrypoint(FEXCore::Context::Context *CTX, uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data) {
|
||||
return CTX->AddCustomIREntrypoint(Entrypoint, Handler, Creator, Data);
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
|
||||
CTX->AppendThunkDefinitions(Definitions);
|
||||
void SetVDSOSigReturn(FEXCore::Context::Context *CTX, const VDSOSigReturn &Pointers) {
|
||||
CTX->SetVDSOSigReturn(Pointers);
|
||||
}
|
||||
|
||||
namespace Debug {
|
||||
void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
|
||||
CTX->CompileRIP(CTX->ParentThread, RIP);
|
||||
}
|
||||
uint64_t GetThreadCount(FEXCore::Context::Context *CTX) {
|
||||
return CTX->GetThreadCount();
|
||||
}
|
||||
//void CompileRIP(FEXCore::Context::Context *CTX, uint64_t RIP) {
|
||||
// CTX->CompileRIP(CTX->ParentThread, RIP);
|
||||
//}
|
||||
//uint64_t GetThreadCount(FEXCore::Context::Context *CTX) {
|
||||
// return CTX->GetThreadCount();
|
||||
//}
|
||||
|
||||
FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(FEXCore::Context::Context *CTX, uint64_t Thread) {
|
||||
return CTX->GetRuntimeStatsForThread(Thread);
|
||||
}
|
||||
//FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(FEXCore::Context::Context *CTX, uint64_t Thread) {
|
||||
// return CTX->GetRuntimeStatsForThread(Thread);
|
||||
//}
|
||||
|
||||
bool GetDebugDataForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::Core::DebugData *Data) {
|
||||
return CTX->GetDebugDataForRIP(RIP, Data);
|
||||
}
|
||||
//bool GetDebugDataForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::Core::DebugData *Data) {
|
||||
// return CTX->GetDebugDataForRIP(RIP, Data);
|
||||
//}
|
||||
|
||||
bool FindHostCodeForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, uint8_t **Code) {
|
||||
return CTX->FindHostCodeForRIP(RIP, Code);
|
||||
}
|
||||
//bool FindHostCodeForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, uint8_t **Code) {
|
||||
// return CTX->FindHostCodeForRIP(RIP, Code);
|
||||
//}
|
||||
|
||||
// XXX:
|
||||
// bool FindIRForRIP(FEXCore::Context::Context *CTX, uint64_t RIP, FEXCore::IR::IntrusiveIRList **ir) {
|
||||
|
||||
+138
-95
@@ -70,7 +70,130 @@ namespace FEXCore::Context {
|
||||
MODE_SINGLESTEP = 1,
|
||||
};
|
||||
|
||||
struct Context {
|
||||
class ContextImpl final : public FEXCore::Context::Context {
|
||||
public:
|
||||
// Context base class implementation.
|
||||
bool InitializeContext() override;
|
||||
|
||||
void DestroyContext() override;
|
||||
|
||||
FEXCore::Core::InternalThreadState* InitCore(uint64_t InitialRIP, uint64_t StackPointer) override;
|
||||
|
||||
void SetExitHandler(ExitHandler handler) override;
|
||||
ExitHandler GetExitHandler() const override;
|
||||
|
||||
void Pause() override;
|
||||
void Run() override;
|
||||
void Stop() override;
|
||||
void Step() override;
|
||||
|
||||
ExitReason RunUntilExit() override;
|
||||
|
||||
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) override;
|
||||
|
||||
int GetProgramStatus() const override;
|
||||
|
||||
ExitReason GetExitReason() override;
|
||||
|
||||
bool IsDone() const override;
|
||||
|
||||
void GetCPUState(FEXCore::Core::CPUState *State) const override;
|
||||
void SetCPUState(const FEXCore::Core::CPUState *State) override;
|
||||
|
||||
void SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) override;
|
||||
|
||||
bool AddVirtualMemoryMapping(uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size) override;
|
||||
|
||||
HostFeatures GetHostFeatures() const override;
|
||||
|
||||
void HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) override;
|
||||
|
||||
void RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) override;
|
||||
[[noreturn]] void HandleSignalHandlerReturn(bool RT) override ;
|
||||
void RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) override;
|
||||
|
||||
/**
|
||||
* @brief Used to create FEX thread objects in preparation for creating a true OS thread. Does set a TID or PID.
|
||||
*
|
||||
* @param NewThreadState The initial thread state to setup for our state
|
||||
* @param ParentTID The PID that was the parent thread that created this
|
||||
*
|
||||
* @return The InternalThreadState object that tracks all of the emulated thread's state
|
||||
*
|
||||
* Usecases:
|
||||
* OS thread Creation:
|
||||
* - Thread = CreateThread(NewState, PPID);
|
||||
* - InitializeThread(Thread);
|
||||
* OS fork (New thread created with a clone of thread state):
|
||||
* - clone{2, 3}
|
||||
* - Thread = CreateThread(CopyOfThreadState, PPID);
|
||||
* - ExecutionThread(Thread); // Starts executing without creating another host thread
|
||||
* Thunk callback executing guest code from native host thread
|
||||
* - Thread = CreateThread(NewState, PPID);
|
||||
* - InitializeThreadTLSData(Thread);
|
||||
* - HandleCallback(Thread, RIP);
|
||||
*/
|
||||
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) override;
|
||||
|
||||
// Public for threading
|
||||
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
/**
|
||||
* @brief Initializes the OS thread object and prepares to start executing on that new OS thread
|
||||
*
|
||||
* @param Thread The internal FEX thread state object
|
||||
*
|
||||
* The OS thread will wait until RunThread is executed
|
||||
*/
|
||||
void InitializeThread(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
/**
|
||||
* @brief Starts the OS thread object to start executing guest code
|
||||
*
|
||||
* @param Thread The internal FEX thread state object
|
||||
*/
|
||||
void RunThread(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
void StopThread(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
/**
|
||||
* @brief Destroys this FEX thread object and stops tracking it internally
|
||||
*
|
||||
* @param Thread The internal FEX thread state object
|
||||
*/
|
||||
void DestroyThread(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
void CleanupAfterFork(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
void SetSignalDelegator(FEXCore::SignalDelegator *SignalDelegation) override;
|
||||
void SetSyscallHandler(FEXCore::HLE::SyscallHandler *Handler) override;
|
||||
FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) override;
|
||||
FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) override;
|
||||
|
||||
FEXCore::IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(const std::string& Name) override;
|
||||
void UnloadAOTIRCacheEntry(FEXCore::IR::AOTIRCacheEntry *Entry) override;
|
||||
|
||||
void SetAOTIRLoader(std::function<int(const std::string&)> CacheReader) override {
|
||||
IRCaptureCache.SetAOTIRLoader(CacheReader);
|
||||
}
|
||||
void SetAOTIRWriter(std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) override {
|
||||
IRCaptureCache.SetAOTIRWriter(CacheWriter);
|
||||
}
|
||||
void SetAOTIRRenamer(std::function<void(const std::string&)> CacheRenamer) override {
|
||||
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
|
||||
}
|
||||
|
||||
void FinalizeAOTIRCache() override {
|
||||
IRCaptureCache.FinalizeAOTIRCache();
|
||||
}
|
||||
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) override {
|
||||
IRCaptureCache.WriteFilesWithCode(Writer);
|
||||
}
|
||||
void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length) override;
|
||||
void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> callback) override;
|
||||
void MarkMemoryShared() override;
|
||||
|
||||
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) override;
|
||||
// returns false if a handler was already registered
|
||||
CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr) override;
|
||||
|
||||
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) override;
|
||||
|
||||
public:
|
||||
friend class FEXCore::HLE::SyscallHandler;
|
||||
#ifdef JIT_ARM64
|
||||
friend class FEXCore::CPU::Arm64JITCore;
|
||||
@@ -116,7 +239,6 @@ namespace FEXCore::Context {
|
||||
FEX_CONFIG_OPT(ParanoidTSO, PARANOIDTSO);
|
||||
FEX_CONFIG_OPT(CacheObjectCodeCompilation, CACHEOBJECTCODECOMPILATION);
|
||||
FEX_CONFIG_OPT(x87ReducedPrecision, X87REDUCEDPRECISION);
|
||||
FEX_CONFIG_OPT(x86dec_SynchronizeRIPOnAllBlocks, X86DEC_SYNCHRONIZERIPONALLBLOCKS);
|
||||
FEX_CONFIG_OPT(EnableAVX, ENABLEAVX);
|
||||
} Config;
|
||||
|
||||
@@ -152,36 +274,27 @@ namespace FEXCore::Context {
|
||||
|
||||
SignalDelegator *SignalDelegation{};
|
||||
X86GeneratedCode X86CodeGen;
|
||||
VDSOSigReturn VDSOPointers{};
|
||||
|
||||
Context();
|
||||
~Context();
|
||||
ContextImpl();
|
||||
~ContextImpl();
|
||||
|
||||
FEXCore::Core::InternalThreadState* InitCore(uint64_t InitialRIP, uint64_t StackPointer);
|
||||
FEXCore::Context::ExitReason RunUntilExit();
|
||||
int GetProgramStatus() const;
|
||||
bool IsPaused() const { return !Running; }
|
||||
void Pause();
|
||||
void Run();
|
||||
void WaitForThreadsToRun();
|
||||
void Step();
|
||||
void Stop(bool IgnoreCurrentThread);
|
||||
void WaitForIdle();
|
||||
void StopThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
void SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event);
|
||||
|
||||
bool GetGdbServerStatus() const { return DebugServer != nullptr; }
|
||||
void StartGdbServer();
|
||||
void StopGdbServer();
|
||||
void HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
|
||||
void RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required);
|
||||
void RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required);
|
||||
|
||||
static void ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
static void ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker);
|
||||
|
||||
template<auto Fn>
|
||||
static uint64_t ThreadExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame, uint64_t *record) {
|
||||
FHU::ScopedSignalMaskWithSharedLock lk(Frame->Thread->CTX->CodeInvalidationMutex);
|
||||
FHU::ScopedSignalMaskWithSharedLock lk(static_cast<ContextImpl*>(Frame->Thread->CTX)->CodeInvalidationMutex);
|
||||
|
||||
return Fn(Frame, record);
|
||||
}
|
||||
@@ -190,21 +303,17 @@ namespace FEXCore::Context {
|
||||
// Must be called from owning thread
|
||||
static void ThreadRemoveCodeEntryFromJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
|
||||
LogMan::Throw::AFmt(Thread->ThreadManager.GetTID() == FHU::Syscalls::gettid(), "Must be called from owning thread {}, not {}", Thread->ThreadManager.GetTID(), FHU::Syscalls::gettid());
|
||||
|
||||
FHU::ScopedSignalMaskWithUniqueLock lk(Thread->CTX->CodeInvalidationMutex);
|
||||
FHU::ScopedSignalMaskWithUniqueLock lk(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex);
|
||||
|
||||
ThreadRemoveCodeEntry(Thread, GuestRIP);
|
||||
}
|
||||
|
||||
// returns false if a handler was already registered
|
||||
CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data);
|
||||
|
||||
void RemoveCustomIREntrypoint(uintptr_t Entrypoint);
|
||||
|
||||
// Debugger interface
|
||||
void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
|
||||
uint64_t GetThreadCount() const;
|
||||
FEXCore::Core::RuntimeStats *GetRuntimeStatsForThread(uint64_t Thread);
|
||||
bool GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data);
|
||||
@@ -236,29 +345,6 @@ namespace FEXCore::Context {
|
||||
void CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP);
|
||||
|
||||
// Used for thread creation from syscalls
|
||||
/**
|
||||
* @brief Used to create FEX thread objects in preparation for creating a true OS thread. Does set a TID or PID.
|
||||
*
|
||||
* @param NewThreadState The initial thread state to setup for our state
|
||||
* @param ParentTID The PID that was the parent thread that created this
|
||||
*
|
||||
* @return The InternalThreadState object that tracks all of the emulated thread's state
|
||||
*
|
||||
* Usecases:
|
||||
* OS thread Creation:
|
||||
* - Thread = CreateThread(NewState, PPID);
|
||||
* - InitializeThread(Thread);
|
||||
* OS fork (New thread created with a clone of thread state):
|
||||
* - clone{2, 3}
|
||||
* - Thread = CreateThread(CopyOfThreadState, PPID);
|
||||
* - ExecutionThread(Thread); // Starts executing without creating another host thread
|
||||
* Thunk callback executing guest code from native host thread
|
||||
* - Thread = CreateThread(NewState, PPID);
|
||||
* - InitializeThreadTLSData(Thread);
|
||||
* - HandleCallback(Thread, RIP);
|
||||
*/
|
||||
FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
|
||||
|
||||
/**
|
||||
* @brief Initializes TID, PID and TLS data for a thread
|
||||
*
|
||||
@@ -266,71 +352,28 @@ namespace FEXCore::Context {
|
||||
*/
|
||||
void InitializeThreadTLSData(FEXCore::Core::InternalThreadState *Thread);
|
||||
|
||||
/**
|
||||
* @brief Initializes the OS thread object and prepares to start executing on that new OS thread
|
||||
*
|
||||
* @param Thread The internal FEX thread state object
|
||||
*
|
||||
* The OS thread will wait until RunThread is executed
|
||||
*/
|
||||
void InitializeThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
|
||||
/**
|
||||
* @brief Starts the OS thread object to start executing guest code
|
||||
*
|
||||
* @param Thread The internal FEX thread state object
|
||||
*/
|
||||
void RunThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
|
||||
/**
|
||||
* @brief Destroys this FEX thread object and stops tracking it internally
|
||||
*
|
||||
* @param Thread The internal FEX thread state object
|
||||
*/
|
||||
void DestroyThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
void CopyMemoryMapping(FEXCore::Core::InternalThreadState *ParentThread, FEXCore::Core::InternalThreadState *ChildThread);
|
||||
|
||||
void CleanupAfterFork(FEXCore::Core::InternalThreadState *ExceptForThread);
|
||||
|
||||
std::vector<FEXCore::Core::InternalThreadState*>* GetThreads() { return &Threads; }
|
||||
|
||||
uint8_t GetGPRSize() const { return Config.Is64BitMode ? 8 : 4; }
|
||||
|
||||
IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(const std::string &filename);
|
||||
void UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry *Entry);
|
||||
|
||||
FEXCore::JITSymbols Symbols;
|
||||
|
||||
// Public for threading
|
||||
void ExecutionThread(FEXCore::Core::InternalThreadState *Thread);
|
||||
void SetVDSOSigReturn(const VDSOSigReturn &Pointers) override {
|
||||
VDSOPointers = Pointers;
|
||||
if (VDSOPointers.VDSO_kernel_sigreturn == nullptr) {
|
||||
VDSOPointers.VDSO_kernel_sigreturn = reinterpret_cast<void*>(X86CodeGen.sigreturn_32);
|
||||
}
|
||||
|
||||
void FinalizeAOTIRCache() {
|
||||
IRCaptureCache.FinalizeAOTIRCache();
|
||||
if (VDSOPointers.VDSO_kernel_rt_sigreturn == nullptr) {
|
||||
VDSOPointers.VDSO_kernel_rt_sigreturn = reinterpret_cast<void*>(X86CodeGen.rt_sigreturn_32);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) {
|
||||
IRCaptureCache.WriteFilesWithCode(Writer);
|
||||
}
|
||||
|
||||
void SetAOTIRLoader(std::function<int(const std::string&)> CacheReader) {
|
||||
IRCaptureCache.SetAOTIRLoader(CacheReader);
|
||||
}
|
||||
|
||||
void SetAOTIRWriter(std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) {
|
||||
IRCaptureCache.SetAOTIRWriter(CacheWriter);
|
||||
}
|
||||
|
||||
void SetAOTIRRenamer(std::function<void(const std::string&)> CacheRenamer) {
|
||||
IRCaptureCache.SetAOTIRRenamer(CacheRenamer);
|
||||
}
|
||||
|
||||
void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
|
||||
|
||||
FEXCore::Utils::PooledAllocatorMMap OpDispatcherAllocator;
|
||||
FEXCore::Utils::PooledAllocatorMMap FrontendAllocator;
|
||||
|
||||
void MarkMemoryShared();
|
||||
|
||||
bool IsTSOEnabled() { return (IsMemoryShared || !Config.TSOAutoMigration) && Config.TSOEnabled; }
|
||||
|
||||
protected:
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "Interface/HLE/Thunks/Thunks.h"
|
||||
|
||||
#include <FEXCore/Core/CoreState.h>
|
||||
#include <FEXCore/Utils/BitUtils.h>
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXCore/Utils/MathUtils.h>
|
||||
|
||||
@@ -20,7 +21,7 @@
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
// We want vixl to not allocate a default buffer. Jit and dispatcher will manually create one.
|
||||
Arm64Emitter::Arm64Emitter(FEXCore::Context::Context *ctx, size_t size)
|
||||
Arm64Emitter::Arm64Emitter(FEXCore::Context::ContextImpl *ctx, size_t size)
|
||||
: Emitter(size ? (uint8_t*)FEXCore::Allocator::mmap(nullptr, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0) : nullptr, size)
|
||||
, EmitterCTX {ctx} {
|
||||
CPU.SetUp();
|
||||
@@ -216,13 +217,13 @@ void Arm64Emitter::SpillStaticRegs(bool FPRs, uint32_t GPRSpillMask, uint32_t FP
|
||||
|
||||
if (((1U << Reg.Idx()) & FPRSpillMask) != 0) {
|
||||
mov(ARMEmitter::Size::i64Bit, TMP4.R(), offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
|
||||
st1b<ARMEmitter::SubRegSize::i8Bit>(Reg, PRED_TMP_32B, STATE.R(), TMP4.R());
|
||||
st1b<ARMEmitter::SubRegSize::i8Bit>(Reg.Z(), PRED_TMP_32B, STATE.R(), TMP4.R());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (GPRSpillMask && FPRSpillMask == ~0U) {
|
||||
// Optimize the common case where we can spill four registers per instruction
|
||||
auto TmpReg = SRA64[__builtin_ffs(GPRSpillMask)];
|
||||
auto TmpReg = SRA64[FindFirstSetBit(GPRSpillMask)];
|
||||
|
||||
// Load the sse offset in to the temporary register
|
||||
add(ARMEmitter::Size::i64Bit, TmpReg, STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[0][0]));
|
||||
@@ -273,14 +274,14 @@ void Arm64Emitter::FillStaticRegs(bool FPRs, uint32_t GPRFillMask, uint32_t FPRF
|
||||
const auto Reg = SRAFPR[i];
|
||||
if (((1U << Reg.Idx()) & FPRFillMask) != 0) {
|
||||
mov(ARMEmitter::Size::i64Bit, TMP4.R(), offsetof(Core::CpuStateFrame, State.xmm.avx.data[i][0]));
|
||||
ld1b<ARMEmitter::SubRegSize::i8Bit>(Reg, PRED_TMP_32B, STATE.R(), TMP4.R());
|
||||
ld1b<ARMEmitter::SubRegSize::i8Bit>(Reg.Z(), PRED_TMP_32B.Zeroing(), STATE.R(), TMP4.R());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (GPRFillMask && FPRFillMask == ~0U) {
|
||||
// Optimize the common case where we can fill four registers per instruction.
|
||||
// Use one of the filling static registers before we fill it.
|
||||
auto TmpReg = SRA64[__builtin_ffs(GPRFillMask)];
|
||||
auto TmpReg = SRA64[FindFirstSetBit(GPRFillMask)];
|
||||
|
||||
// Load the sse offset in to the temporary register
|
||||
add(ARMEmitter::Size::i64Bit, TmpReg, STATE.R(), offsetof(FEXCore::Core::CpuStateFrame, State.xmm.sse.data[0][0]));
|
||||
@@ -347,7 +348,7 @@ void Arm64Emitter::PushDynamicRegsAndLR(FEXCore::ARMEmitter::Register TmpReg) {
|
||||
const auto Reg2 = RAFPR[i + 1];
|
||||
const auto Reg3 = RAFPR[i + 2];
|
||||
const auto Reg4 = RAFPR[i + 3];
|
||||
st4b(Reg1, Reg2, Reg3, Reg4, PRED_TMP_32B, TmpReg, 0);
|
||||
st4b(Reg1.Z(), Reg2.Z(), Reg3.Z(), Reg4.Z(), PRED_TMP_32B, TmpReg, 0);
|
||||
add(ARMEmitter::Size::i64Bit, TmpReg, TmpReg, 32 * 4);
|
||||
}
|
||||
} else {
|
||||
@@ -373,7 +374,7 @@ void Arm64Emitter::PopDynamicRegsAndLR() {
|
||||
const auto Reg2 = RAFPR[i + 1];
|
||||
const auto Reg3 = RAFPR[i + 2];
|
||||
const auto Reg4 = RAFPR[i + 3];
|
||||
ld4b(Reg1, Reg2, Reg3, Reg4, PRED_TMP_32B, ARMEmitter::Reg::rsp);
|
||||
ld4b(Reg1.Z(), Reg2.Z(), Reg3.Z(), Reg4.Z(), PRED_TMP_32B.Zeroing(), ARMEmitter::Reg::rsp);
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, 32 * 4);
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -85,10 +85,10 @@ constexpr FEXCore::ARMEmitter::PRegister PRED_TMP_32B = FEXCore::ARMEmitter::PRe
|
||||
// be used by both Arm64 JIT and ARM64 Dispatcher
|
||||
class Arm64Emitter : public FEXCore::ARMEmitter::Emitter {
|
||||
protected:
|
||||
Arm64Emitter(FEXCore::Context::Context *ctx, size_t size);
|
||||
Arm64Emitter(FEXCore::Context::ContextImpl *ctx, size_t size);
|
||||
~Arm64Emitter();
|
||||
|
||||
FEXCore::Context::Context *EmitterCTX;
|
||||
FEXCore::Context::ContextImpl *EmitterCTX;
|
||||
vixl::aarch64::CPU CPU;
|
||||
void LoadConstant(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register Reg, uint64_t Constant, bool NOPPad = false);
|
||||
|
||||
|
||||
+12
-13
@@ -257,8 +257,8 @@ public:
|
||||
void sxth(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn) {
|
||||
sbfm(s, rd, rn, 0, 15);
|
||||
}
|
||||
void sxtw(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::XRegister rn) {
|
||||
sbfm(ARMEmitter::Size::i64Bit, rd, rn, 0, 31);
|
||||
void sxtw(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn) {
|
||||
sbfm(ARMEmitter::Size::i64Bit, rd, rn.X(), 0, 31);
|
||||
}
|
||||
void sbfx(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t lsb, uint32_t width) {
|
||||
LOGMAN_THROW_A_FMT(width > 0, "sbfx needs width > 0");
|
||||
@@ -287,12 +287,12 @@ public:
|
||||
|
||||
void lsl(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t shift) {
|
||||
const auto RegSize = RegSizeInBits(s);
|
||||
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to asr a region larger than the register");
|
||||
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to lsl a region larger than the register");
|
||||
ubfm(s, rd, rn, (RegSize - shift) % RegSize, RegSize - shift - 1);
|
||||
}
|
||||
void lsr(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t shift) {
|
||||
const auto RegSize = RegSizeInBits(s);
|
||||
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to asr a region larger than the register");
|
||||
LOGMAN_THROW_A_FMT(shift < RegSize, "Tried to lsr a region larger than the register");
|
||||
ubfm(s, rd, rn, shift, RegSize - 1);
|
||||
}
|
||||
void ubfx(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t lsb, uint32_t width) {
|
||||
@@ -303,8 +303,8 @@ public:
|
||||
|
||||
void bfi(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t lsb, uint32_t width) {
|
||||
const auto RegSize = RegSizeInBits(s);
|
||||
LOGMAN_THROW_A_FMT(width > 0, "sbfx needs width > 0");
|
||||
LOGMAN_THROW_A_FMT((lsb + width) <= RegSize, "Tried to sbfx a region larger than the register");
|
||||
LOGMAN_THROW_A_FMT(width > 0, "bfi needs width > 0");
|
||||
LOGMAN_THROW_A_FMT((lsb + width) <= RegSize, "Tried to bfi a region larger than the register");
|
||||
bfm(s, rd, rn, (RegSize - lsb) & (RegSize - 1), width - 1);
|
||||
}
|
||||
|
||||
@@ -316,7 +316,6 @@ public:
|
||||
}
|
||||
|
||||
void ror(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t Imm) {
|
||||
LOGMAN_THROW_A_FMT(Imm < RegSizeInBits(s), "Tried to extr a region larger than the register");
|
||||
extr(s, rd, rn, rn, Imm);
|
||||
}
|
||||
|
||||
@@ -711,28 +710,28 @@ public:
|
||||
DataProcessing_3Source(Op, 0, s, rd, rn, rm, ra);
|
||||
}
|
||||
void mul(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm) {
|
||||
madd(s, rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
|
||||
madd(s, rd, rn, rm, XReg::zr);
|
||||
}
|
||||
void msub(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm, FEXCore::ARMEmitter::Register ra) {
|
||||
constexpr uint32_t Op = 0b001'1011'000U << 21;
|
||||
DataProcessing_3Source(Op, 1, s, rd, rn, rm, ra);
|
||||
}
|
||||
void mneg(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm) {
|
||||
msub(s, rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
|
||||
msub(s, rd, rn, rm, XReg::zr);
|
||||
}
|
||||
void smaddl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm, FEXCore::ARMEmitter::XRegister ra) {
|
||||
constexpr uint32_t Op = 0b001'1011'001U << 21;
|
||||
DataProcessing_3Source(Op, 0, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
|
||||
}
|
||||
void smull(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
|
||||
smaddl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
|
||||
smaddl(rd, rn, rm, XReg::zr);
|
||||
}
|
||||
void smsubl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm, FEXCore::ARMEmitter::XRegister ra) {
|
||||
constexpr uint32_t Op = 0b001'1011'001U << 21;
|
||||
DataProcessing_3Source(Op, 1, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
|
||||
}
|
||||
void smnegl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
|
||||
smsubl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
|
||||
smsubl(rd, rn, rm, XReg::zr);
|
||||
}
|
||||
void smulh(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::XRegister rn, FEXCore::ARMEmitter::XRegister rm) {
|
||||
constexpr uint32_t Op = 0b001'1011'010U << 21;
|
||||
@@ -743,14 +742,14 @@ public:
|
||||
DataProcessing_3Source(Op, 0, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
|
||||
}
|
||||
void umull(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
|
||||
umaddl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
|
||||
umaddl(rd, rn, rm, XReg::zr);
|
||||
}
|
||||
void umsubl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm, FEXCore::ARMEmitter::XRegister ra) {
|
||||
constexpr uint32_t Op = 0b001'1011'101U << 21;
|
||||
DataProcessing_3Source(Op, 1, FEXCore::ARMEmitter::Size::i64Bit, rd, rn, rm, ra);
|
||||
}
|
||||
void umnegl(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::WRegister rn, FEXCore::ARMEmitter::WRegister rm) {
|
||||
umsubl(rd, rn, rm, FEXCore::ARMEmitter::Reg::zr);
|
||||
umsubl(rd, rn, rm, XReg::zr);
|
||||
}
|
||||
void umulh(FEXCore::ARMEmitter::XRegister rd, FEXCore::ARMEmitter::XRegister rn, FEXCore::ARMEmitter::XRegister rm) {
|
||||
constexpr uint32_t Op = 0b001'1011'110U << 21;
|
||||
|
||||
+323
-540
File diff suppressed because it is too large.
Load diff
@@ -544,6 +544,36 @@ namespace FEXCore::ARMEmitter {
|
||||
ROTATE_270 = 0b11,
|
||||
};
|
||||
|
||||
// Concept for contraining some instructions to accept only an XRegister or WRegister.
|
||||
// Particularly for operations that differ encodings depending on which one is used.
|
||||
template <typename T>
|
||||
concept IsXOrWRegister = std::is_same_v<T, XRegister> || std::is_same_v<T, WRegister>;
|
||||
|
||||
// Whether or not a given set of vector registers are sequential
|
||||
// in increasing order as far as the register file is concerned (modulo its size)
|
||||
//
|
||||
// For example, a set of registers like:
|
||||
//
|
||||
// v1, v2, v3 and
|
||||
// v31, v0, v1
|
||||
//
|
||||
// would both be considered sequential sequences, and some instructions in particular
|
||||
// limit register lists to these kind of sequences.
|
||||
//
|
||||
template <typename T, typename... Args>
|
||||
constexpr bool AreVectorsSequential(T first, const Args&... args) {
|
||||
// Ensure we always have a pair of registers to compare against.
|
||||
static_assert(sizeof...(args) >= 1, "Number of arguments must be greater than 1");
|
||||
|
||||
const auto fn = [](auto& lhs, const auto& rhs) {
|
||||
const auto result = ((lhs.Idx() + 1) % 32) == rhs.Idx();
|
||||
lhs = rhs;
|
||||
return result;
|
||||
};
|
||||
|
||||
return (fn(first, args) && ...);
|
||||
}
|
||||
|
||||
// This is an emitter that is designed around the smallest code bloat as possible.
|
||||
// Eschewing most developer convenience in order to keep code as small as possible.
|
||||
|
||||
|
||||
+439
-565
File diff suppressed because it is too large.
Load diff
+76
-249
@@ -1,5 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <FEXCore/Utils/EnumUtils.h>
|
||||
|
||||
#include <compare>
|
||||
#include <cstdint>
|
||||
|
||||
namespace FEXCore::ARMEmitter {
|
||||
@@ -15,13 +18,12 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit Register(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const Register&, const Register&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator WRegister() const;
|
||||
operator XRegister() const;
|
||||
|
||||
WRegister W() const;
|
||||
XRegister X() const;
|
||||
|
||||
@@ -41,9 +43,7 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit WRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
bool operator==(const WRegister &rhs) {
|
||||
return Idx() == rhs.Idx();
|
||||
}
|
||||
friend constexpr auto operator<=>(const WRegister&, const WRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
@@ -53,10 +53,7 @@ namespace FEXCore::ARMEmitter {
|
||||
return Register(Index);
|
||||
}
|
||||
|
||||
operator XRegister() const;
|
||||
|
||||
XRegister X() const;
|
||||
|
||||
Register R() const;
|
||||
|
||||
private:
|
||||
@@ -75,9 +72,7 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit XRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
bool operator==(const XRegister &rhs) {
|
||||
return Idx() == rhs.Idx();
|
||||
}
|
||||
friend constexpr auto operator<=>(const XRegister&, const XRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
@@ -87,10 +82,7 @@ namespace FEXCore::ARMEmitter {
|
||||
return Register(Index);
|
||||
}
|
||||
|
||||
operator WRegister() const;
|
||||
|
||||
WRegister W() const;
|
||||
|
||||
Register R() const;
|
||||
|
||||
private:
|
||||
@@ -101,45 +93,29 @@ namespace FEXCore::ARMEmitter {
|
||||
static_assert(std::is_standard_layout_v<Register>, "Needs to be standard");
|
||||
|
||||
inline WRegister Register::W() const {
|
||||
return *this;
|
||||
return WRegister{Index};
|
||||
}
|
||||
|
||||
inline XRegister Register::X() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline Register::operator WRegister () const {
|
||||
return WRegister(Index);
|
||||
}
|
||||
|
||||
inline Register::operator XRegister () const {
|
||||
return XRegister(Index);
|
||||
return XRegister{Index};
|
||||
}
|
||||
|
||||
inline XRegister WRegister::X() const {
|
||||
return *this;
|
||||
return XRegister{Index};
|
||||
}
|
||||
|
||||
inline Register WRegister::R() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline WRegister::operator XRegister () const {
|
||||
return XRegister(Index);
|
||||
}
|
||||
|
||||
inline WRegister XRegister::W() const {
|
||||
return *this;
|
||||
return WRegister{Index};
|
||||
}
|
||||
|
||||
inline Register XRegister::R() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline XRegister::operator WRegister () const {
|
||||
return WRegister(Index);
|
||||
}
|
||||
|
||||
// Namespace containing all unsized GPR register objects.
|
||||
namespace Reg {
|
||||
constexpr static Register r0(0);
|
||||
@@ -291,20 +267,15 @@ namespace FEXCore::ARMEmitter {
|
||||
class VRegister {
|
||||
public:
|
||||
VRegister() = delete;
|
||||
constexpr VRegister(uint32_t Idx)
|
||||
constexpr explicit VRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const VRegister&, const VRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator BRegister() const;
|
||||
operator HRegister() const;
|
||||
operator SRegister() const;
|
||||
operator DRegister() const;
|
||||
operator QRegister() const;
|
||||
operator ZRegister() const;
|
||||
|
||||
BRegister B() const;
|
||||
HRegister H() const;
|
||||
SRegister S() const;
|
||||
@@ -328,16 +299,15 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit BRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const BRegister&, const BRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator VRegister() const;
|
||||
operator HRegister() const;
|
||||
operator SRegister() const;
|
||||
operator DRegister() const;
|
||||
operator QRegister() const;
|
||||
operator ZRegister() const;
|
||||
operator VRegister () const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
|
||||
BRegister V() const;
|
||||
HRegister H() const;
|
||||
@@ -362,16 +332,15 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit HRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const HRegister&, const HRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator VRegister() const;
|
||||
operator BRegister() const;
|
||||
operator SRegister() const;
|
||||
operator DRegister() const;
|
||||
operator QRegister() const;
|
||||
operator ZRegister() const;
|
||||
operator VRegister() const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
|
||||
HRegister V() const;
|
||||
BRegister B() const;
|
||||
@@ -396,16 +365,15 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit SRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const SRegister&, const SRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator VRegister() const;
|
||||
operator BRegister() const;
|
||||
operator HRegister() const;
|
||||
operator DRegister() const;
|
||||
operator QRegister() const;
|
||||
operator ZRegister() const;
|
||||
operator VRegister() const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
|
||||
SRegister V() const;
|
||||
BRegister B() const;
|
||||
@@ -431,16 +399,15 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit DRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const DRegister&, const DRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator VRegister() const;
|
||||
operator BRegister() const;
|
||||
operator HRegister() const;
|
||||
operator SRegister() const;
|
||||
operator QRegister() const;
|
||||
operator ZRegister() const;
|
||||
operator VRegister() const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
|
||||
DRegister V() const;
|
||||
BRegister B() const;
|
||||
@@ -466,16 +433,15 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit QRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const QRegister&, const QRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator VRegister() const;
|
||||
operator BRegister() const;
|
||||
operator HRegister() const;
|
||||
operator SRegister() const;
|
||||
operator DRegister() const;
|
||||
operator ZRegister() const;
|
||||
operator VRegister () const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
|
||||
QRegister V() const;
|
||||
BRegister B() const;
|
||||
@@ -500,6 +466,8 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr explicit ZRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
friend constexpr auto operator<=>(const ZRegister&, const ZRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
@@ -520,41 +488,22 @@ namespace FEXCore::ARMEmitter {
|
||||
|
||||
// VRegister
|
||||
inline BRegister VRegister::B() const {
|
||||
return *this;
|
||||
return BRegister{Index};
|
||||
}
|
||||
inline HRegister VRegister::H() const {
|
||||
return *this;
|
||||
return HRegister{Index};
|
||||
}
|
||||
inline SRegister VRegister::S() const {
|
||||
return *this;
|
||||
return SRegister{Index};
|
||||
}
|
||||
inline DRegister VRegister::D() const {
|
||||
return *this;
|
||||
return DRegister{Index};
|
||||
}
|
||||
inline QRegister VRegister::Q() const {
|
||||
return *this;
|
||||
return QRegister{Index};
|
||||
}
|
||||
inline ZRegister VRegister::Z() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline VRegister::operator BRegister () const {
|
||||
return BRegister(Index);
|
||||
}
|
||||
inline VRegister::operator HRegister () const {
|
||||
return HRegister(Index);
|
||||
}
|
||||
inline VRegister::operator SRegister () const {
|
||||
return SRegister(Index);
|
||||
}
|
||||
inline VRegister::operator DRegister () const {
|
||||
return DRegister(Index);
|
||||
}
|
||||
inline VRegister::operator QRegister () const {
|
||||
return QRegister(Index);
|
||||
}
|
||||
inline VRegister::operator ZRegister () const {
|
||||
return ZRegister(Index);
|
||||
return ZRegister{Index};
|
||||
}
|
||||
|
||||
// BRegister
|
||||
@@ -562,38 +511,19 @@ namespace FEXCore::ARMEmitter {
|
||||
return *this;
|
||||
}
|
||||
inline HRegister BRegister::H() const {
|
||||
return *this;
|
||||
return HRegister{Index};
|
||||
}
|
||||
inline SRegister BRegister::S() const {
|
||||
return *this;
|
||||
return SRegister{Index};
|
||||
}
|
||||
inline DRegister BRegister::D() const {
|
||||
return *this;
|
||||
return DRegister{Index};
|
||||
}
|
||||
inline QRegister BRegister::Q() const {
|
||||
return *this;
|
||||
return QRegister{Index};
|
||||
}
|
||||
inline ZRegister BRegister::Z() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline BRegister::operator VRegister () const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
inline BRegister::operator HRegister () const {
|
||||
return HRegister(Index);
|
||||
}
|
||||
inline BRegister::operator SRegister () const {
|
||||
return SRegister(Index);
|
||||
}
|
||||
inline BRegister::operator DRegister () const {
|
||||
return DRegister(Index);
|
||||
}
|
||||
inline BRegister::operator QRegister () const {
|
||||
return QRegister(Index);
|
||||
}
|
||||
inline BRegister::operator ZRegister () const {
|
||||
return ZRegister(Index);
|
||||
return ZRegister{Index};
|
||||
}
|
||||
|
||||
// HRegister
|
||||
@@ -601,38 +531,19 @@ namespace FEXCore::ARMEmitter {
|
||||
return *this;
|
||||
}
|
||||
inline BRegister HRegister::B() const {
|
||||
return *this;
|
||||
return BRegister{Index};
|
||||
}
|
||||
inline SRegister HRegister::S() const {
|
||||
return *this;
|
||||
return SRegister{Index};
|
||||
}
|
||||
inline DRegister HRegister::D() const {
|
||||
return *this;
|
||||
return DRegister{Index};
|
||||
}
|
||||
inline QRegister HRegister::Q() const {
|
||||
return *this;
|
||||
return QRegister{Index};
|
||||
}
|
||||
inline ZRegister HRegister::Z() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline HRegister::operator VRegister () const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
inline HRegister::operator BRegister () const {
|
||||
return BRegister(Index);
|
||||
}
|
||||
inline HRegister::operator SRegister () const {
|
||||
return SRegister(Index);
|
||||
}
|
||||
inline HRegister::operator DRegister () const {
|
||||
return DRegister(Index);
|
||||
}
|
||||
inline HRegister::operator QRegister () const {
|
||||
return QRegister(Index);
|
||||
}
|
||||
inline HRegister::operator ZRegister () const {
|
||||
return ZRegister(Index);
|
||||
return ZRegister{Index};
|
||||
}
|
||||
|
||||
// SRegister
|
||||
@@ -640,77 +551,39 @@ namespace FEXCore::ARMEmitter {
|
||||
return *this;
|
||||
}
|
||||
inline BRegister SRegister::B() const {
|
||||
return *this;
|
||||
return BRegister{Index};
|
||||
}
|
||||
inline HRegister SRegister::H() const {
|
||||
return *this;
|
||||
return HRegister{Index};
|
||||
}
|
||||
inline DRegister SRegister::D() const {
|
||||
return *this;
|
||||
return DRegister{Index};
|
||||
}
|
||||
inline QRegister SRegister::Q() const {
|
||||
return *this;
|
||||
return QRegister{Index};
|
||||
}
|
||||
inline ZRegister SRegister::Z() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline SRegister::operator VRegister () const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
inline SRegister::operator BRegister () const {
|
||||
return BRegister(Index);
|
||||
}
|
||||
inline SRegister::operator HRegister () const {
|
||||
return HRegister(Index);
|
||||
}
|
||||
inline SRegister::operator DRegister () const {
|
||||
return DRegister(Index);
|
||||
}
|
||||
inline SRegister::operator QRegister () const {
|
||||
return QRegister(Index);
|
||||
}
|
||||
inline SRegister::operator ZRegister () const {
|
||||
return ZRegister(Index);
|
||||
return ZRegister{Index};
|
||||
}
|
||||
|
||||
// DRegister
|
||||
inline DRegister DRegister::V() const {
|
||||
return *this;
|
||||
return DRegister{Index};
|
||||
}
|
||||
inline BRegister DRegister::B() const {
|
||||
return *this;
|
||||
return BRegister{Index};
|
||||
}
|
||||
inline HRegister DRegister::H() const {
|
||||
return *this;
|
||||
return HRegister{Index};
|
||||
}
|
||||
inline SRegister DRegister::S() const {
|
||||
return *this;
|
||||
return SRegister{Index};
|
||||
}
|
||||
inline QRegister DRegister::Q() const {
|
||||
return *this;
|
||||
return QRegister{Index};
|
||||
}
|
||||
inline ZRegister DRegister::Z() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline DRegister::operator VRegister () const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
inline DRegister::operator BRegister () const {
|
||||
return BRegister(Index);
|
||||
}
|
||||
inline DRegister::operator HRegister () const {
|
||||
return HRegister(Index);
|
||||
}
|
||||
inline DRegister::operator SRegister () const {
|
||||
return SRegister(Index);
|
||||
}
|
||||
inline DRegister::operator QRegister () const {
|
||||
return QRegister(Index);
|
||||
}
|
||||
inline DRegister::operator ZRegister () const {
|
||||
return ZRegister(Index);
|
||||
return ZRegister{Index};
|
||||
}
|
||||
|
||||
// QRegister
|
||||
@@ -718,38 +591,19 @@ namespace FEXCore::ARMEmitter {
|
||||
return *this;
|
||||
}
|
||||
inline BRegister QRegister::B() const {
|
||||
return *this;
|
||||
return BRegister{Index};
|
||||
}
|
||||
inline HRegister QRegister::H() const {
|
||||
return *this;
|
||||
return HRegister{Index};
|
||||
}
|
||||
inline SRegister QRegister::S() const {
|
||||
return *this;
|
||||
return SRegister{Index};
|
||||
}
|
||||
inline DRegister QRegister::D() const {
|
||||
return *this;
|
||||
return DRegister{Index};
|
||||
}
|
||||
inline ZRegister QRegister::Z() const {
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline QRegister::operator VRegister () const {
|
||||
return VRegister(Index);
|
||||
}
|
||||
inline QRegister::operator BRegister () const {
|
||||
return BRegister(Index);
|
||||
}
|
||||
inline QRegister::operator HRegister () const {
|
||||
return HRegister(Index);
|
||||
}
|
||||
inline QRegister::operator SRegister () const {
|
||||
return SRegister(Index);
|
||||
}
|
||||
inline QRegister::operator DRegister () const {
|
||||
return DRegister(Index);
|
||||
}
|
||||
inline QRegister::operator ZRegister () const {
|
||||
return ZRegister(Index);
|
||||
return ZRegister{Index};
|
||||
}
|
||||
|
||||
// ZRegister
|
||||
@@ -1069,17 +923,12 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr PRegister(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
operator uint32_t() const {
|
||||
return Index;
|
||||
}
|
||||
friend constexpr auto operator<=>(const PRegister&, const PRegister&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator PRegisterZero() const;
|
||||
operator PRegisterMerge() const;
|
||||
|
||||
PRegisterZero Zeroing() const;
|
||||
PRegisterMerge Merging() const;
|
||||
|
||||
@@ -1097,16 +946,13 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr PRegisterZero(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
operator uint32_t() const {
|
||||
return Index;
|
||||
}
|
||||
friend constexpr auto operator<=>(const PRegisterZero&, const PRegisterZero&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator PRegister() const;
|
||||
operator PRegisterMerge() const;
|
||||
|
||||
PRegister P() const;
|
||||
PRegisterMerge Merging() const;
|
||||
@@ -1125,16 +971,13 @@ namespace FEXCore::ARMEmitter {
|
||||
constexpr PRegisterMerge(uint32_t Idx)
|
||||
: Index {Idx} {}
|
||||
|
||||
operator uint32_t() const {
|
||||
return Index;
|
||||
}
|
||||
friend constexpr auto operator<=>(const PRegisterMerge&, const PRegisterMerge&) = default;
|
||||
|
||||
uint32_t Idx() const {
|
||||
return Index;
|
||||
}
|
||||
|
||||
operator PRegister() const;
|
||||
operator PRegisterZero() const;
|
||||
|
||||
PRegister P() const;
|
||||
PRegisterZero Zeroing() const;
|
||||
@@ -1148,14 +991,6 @@ namespace FEXCore::ARMEmitter {
|
||||
|
||||
|
||||
// PRegister
|
||||
inline PRegister::operator PRegisterZero() const {
|
||||
return PRegisterZero(Index);
|
||||
}
|
||||
|
||||
inline PRegister::operator PRegisterMerge() const {
|
||||
return PRegisterMerge(Index);
|
||||
}
|
||||
|
||||
inline PRegisterZero PRegister::Zeroing() const {
|
||||
return PRegisterZero(Idx());
|
||||
}
|
||||
@@ -1169,10 +1004,6 @@ namespace FEXCore::ARMEmitter {
|
||||
return PRegister(Index);
|
||||
}
|
||||
|
||||
inline PRegisterZero::operator PRegisterMerge() const {
|
||||
return PRegisterMerge(Index);
|
||||
}
|
||||
|
||||
inline PRegister PRegisterZero::P() const {
|
||||
return PRegister(Idx());
|
||||
}
|
||||
@@ -1186,10 +1017,6 @@ namespace FEXCore::ARMEmitter {
|
||||
return PRegisterZero(Index);
|
||||
}
|
||||
|
||||
inline PRegisterMerge::operator PRegisterZero() const {
|
||||
return PRegisterZero(Index);
|
||||
}
|
||||
|
||||
inline PRegister PRegisterMerge::P() const {
|
||||
return PRegister(Idx());
|
||||
}
|
||||
|
||||
+1623
-1658
File diff suppressed because it is too large.
Load diff
+10
-18
@@ -17,25 +17,17 @@
|
||||
*/
|
||||
public:
|
||||
// Advanced SIMD scalar copy
|
||||
void dup(FEXCore::ARMEmitter::ScalarRegSize size, FEXCore::ARMEmitter::VRegister rd, FEXCore::ARMEmitter::VRegister rn, uint32_t Index) {
|
||||
void dup(ScalarRegSize size, VRegister rd, VRegister rn, uint32_t Index) {
|
||||
constexpr uint32_t Op = 0b0101'1110'0000'0000'0000'01 << 10;
|
||||
uint32_t imm5 = 0b00000;
|
||||
if (size == ScalarRegSize::i8Bit) {
|
||||
LOGMAN_THROW_AA_FMT(Index < 16, "Index too large");
|
||||
imm5 = (Index << 1) | 1;
|
||||
}
|
||||
else if (size == ScalarRegSize::i16Bit) {
|
||||
LOGMAN_THROW_AA_FMT(Index < 8, "Index too large");
|
||||
imm5 = (Index << 2) | 0b10;
|
||||
}
|
||||
else if (size == ScalarRegSize::i32Bit) {
|
||||
LOGMAN_THROW_AA_FMT(Index < 4, "Index too large");
|
||||
imm5 = (Index << 3) | 0b100;
|
||||
}
|
||||
else if (size == ScalarRegSize::i64Bit) {
|
||||
LOGMAN_THROW_AA_FMT(Index < 2, "Index too large");
|
||||
imm5 = (Index << 4) | 0b1000;
|
||||
}
|
||||
|
||||
const uint32_t SizeImm = FEXCore::ToUnderlying(size);
|
||||
const uint32_t IndexShift = SizeImm + 1;
|
||||
const uint32_t ElementSize = 1U << SizeImm;
|
||||
const uint32_t MaxIndex = 128U / (ElementSize * 8);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(Index < MaxIndex, "Index too large. Index={}, Max Index: {}", Index, MaxIndex);
|
||||
|
||||
const uint32_t imm5 = (Index << IndexShift) | ElementSize;
|
||||
|
||||
ASIMDScalarCopy(Op, 1, imm5, 0b0000, rd, rn);
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ namespace FEXCore::ArchHelpers::Context {
|
||||
|
||||
enum ContextFlags : uint32_t {
|
||||
CONTEXT_FLAG_INJIT = (1U << 0),
|
||||
CONTEXT_FLAG_32BIT = (1U << 1),
|
||||
};
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
@@ -238,8 +237,10 @@ static inline void BackupContext(void* ucontext, T *Backup) {
|
||||
template <typename T>
|
||||
static inline void RestoreContext(void* ucontext, T *Backup) {
|
||||
if constexpr (std::is_same<T, ArmContextBackup>::value) {
|
||||
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
|
||||
|
||||
auto _ucontext = GetUContext(ucontext);
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
|
||||
HostFPRState *HostState = reinterpret_cast<HostFPRState*>(&_mcontext->__reserved[0]);
|
||||
LOGMAN_THROW_AA_FMT(HostState->Head.Magic == FPR_MAGIC, "Wrong FPR Magic: 0x{:08x}", HostState->Head.Magic);
|
||||
@@ -255,8 +256,6 @@ static inline void RestoreContext(void* ucontext, T *Backup) {
|
||||
|
||||
// Restore the signal mask now
|
||||
memcpy(&_ucontext->uc_sigmask, &Backup->sa_mask, sizeof(uint64_t));
|
||||
|
||||
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
|
||||
} else {
|
||||
// This must be a runtime error
|
||||
ERROR_AND_DIE_FMT("Wrong context type");
|
||||
@@ -335,6 +334,8 @@ static inline void BackupContext(void* ucontext, T *Backup) {
|
||||
template <typename T>
|
||||
static inline void RestoreContext(void* ucontext, T *Backup) {
|
||||
if constexpr (std::is_same<T, X86ContextBackup>::value) {
|
||||
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
|
||||
|
||||
auto _ucontext = GetUContext(ucontext);
|
||||
auto _mcontext = GetMContext(ucontext);
|
||||
|
||||
@@ -345,8 +346,6 @@ static inline void RestoreContext(void* ucontext, T *Backup) {
|
||||
|
||||
// Restore the signal mask now
|
||||
memcpy(&_ucontext->uc_sigmask, &Backup->sa_mask, sizeof(uint64_t));
|
||||
|
||||
LOGMAN_THROW_A_FMT(Backup->StackCookie == STACK_COOKIE_MAGIC, "Stack cookie didn't match! 0x{:x}", Backup->StackCookie);
|
||||
} else {
|
||||
// This must be a runtime error
|
||||
ERROR_AND_DIE_FMT("Wrong context type");
|
||||
|
||||
+2
-2
@@ -60,8 +60,8 @@ auto CPUBackend::AllocateNewCodeBuffer(size_t Size) -> CodeBuffer {
|
||||
FEXCore::Allocator::mmap(nullptr, Buffer.Size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0));
|
||||
LOGMAN_THROW_AA_FMT(!!Buffer.Ptr, "Couldn't allocate code buffer");
|
||||
|
||||
if (ThreadState->CTX->Config.GlobalJITNaming()) {
|
||||
ThreadState->CTX->Symbols.RegisterJITSpace(Buffer.Ptr, Buffer.Size);
|
||||
if (static_cast<Context::ContextImpl*>(ThreadState->CTX)->Config.GlobalJITNaming()) {
|
||||
static_cast<Context::ContextImpl*>(ThreadState->CTX)->Symbols.RegisterJITSpace(Buffer.Ptr, Buffer.Size);
|
||||
}
|
||||
return Buffer;
|
||||
}
|
||||
|
||||
+5
-2
@@ -410,6 +410,9 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) {
|
||||
// XXX: Enable once the rest of the SSE4.2 instructions are emulated
|
||||
uint32_t SupportsSSE42 = CTX->HostFeatures.SupportsCRC && false ? 1 : 0;
|
||||
|
||||
// Hypervisor bit is normally set but some applications have issues with it.
|
||||
uint32_t Hypervisor = HideHypervisorBit() ? 0 : 1;
|
||||
|
||||
Res.eax = FAMILY_IDENTIFIER;
|
||||
|
||||
Res.ebx = 0 | // Brand index
|
||||
@@ -449,7 +452,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) {
|
||||
(SUPPORTS_AVX << 28) | // AVX
|
||||
(0 << 29) | // F16C
|
||||
(CTX->HostFeatures.SupportsRAND << 30) | // RDRAND
|
||||
(1 << 31); // Hypervisor always returns one
|
||||
(Hypervisor << 31);
|
||||
|
||||
Res.edx =
|
||||
(1 << 0) | // FPU
|
||||
@@ -1210,7 +1213,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_Reserved(uint32_t Leaf) {
|
||||
return Res;
|
||||
}
|
||||
|
||||
void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
|
||||
void CPUIDEmu::Init(FEXCore::Context::ContextImpl *ctx) {
|
||||
CTX = ctx;
|
||||
|
||||
// Setup some state tracking
|
||||
|
||||
+4
-3
@@ -10,7 +10,7 @@
|
||||
|
||||
namespace FEXCore {
|
||||
namespace Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
// Debugging define to switch what family of CPU we execute as.
|
||||
@@ -31,7 +31,7 @@ public:
|
||||
// if we report anything differently then applications are likely to break
|
||||
constexpr static uint64_t CACHELINE_SIZE = 64;
|
||||
|
||||
void Init(FEXCore::Context::Context *ctx);
|
||||
void Init(FEXCore::Context::ContextImpl *ctx);
|
||||
|
||||
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, uint32_t Leaf) {
|
||||
if (Function < Primary.size()) {
|
||||
@@ -64,9 +64,10 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
bool Hybrid{};
|
||||
FEX_CONFIG_OPT(Cores, THREADS);
|
||||
FEX_CONFIG_OPT(HideHypervisorBit, HIDEHYPERVISORBIT);
|
||||
|
||||
using FunctionHandler = FEXCore::CPUID::FunctionResults (CPUIDEmu::*)(uint32_t Leaf);
|
||||
struct CPUData {
|
||||
|
||||
+97
-89
@@ -79,7 +79,7 @@ $end_info$
|
||||
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
bool CreateCPUCore(FEXCore::Context::Context *CTX) {
|
||||
bool CreateCPUCore(Context::ContextImpl *CTX) {
|
||||
// This should be used for generating things that are shared between threads
|
||||
CTX->CPUID.Init(CTX);
|
||||
return true;
|
||||
@@ -147,7 +147,7 @@ std::string_view const& GetGRegName(unsigned Reg) {
|
||||
} // namespace FEXCore::Core
|
||||
|
||||
namespace FEXCore::Context {
|
||||
Context::Context()
|
||||
ContextImpl::ContextImpl()
|
||||
: IRCaptureCache {this} {
|
||||
#ifdef BLOCKSTATS
|
||||
BlockData = std::make_unique<FEXCore::BlockSamplingData>();
|
||||
@@ -172,7 +172,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
Context::~Context() {
|
||||
ContextImpl::~ContextImpl() {
|
||||
{
|
||||
if (CodeObjectCacheService) {
|
||||
CodeObjectCacheService->Shutdown();
|
||||
@@ -214,7 +214,7 @@ namespace FEXCore::Context {
|
||||
return NewThreadState;
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState* Context::InitCore(uint64_t InitialRIP, uint64_t StackPointer) {
|
||||
FEXCore::Core::InternalThreadState* ContextImpl::InitCore(uint64_t InitialRIP, uint64_t StackPointer) {
|
||||
// Initialize the CPU core signal handlers & DispatcherConfig
|
||||
switch (Config.Core) {
|
||||
#ifdef INTERPRETER_ENABLED
|
||||
@@ -255,13 +255,13 @@ namespace FEXCore::Context {
|
||||
// Initialize common signal handlers
|
||||
|
||||
auto PauseHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
return Thread->CTX->Dispatcher->HandleSignalPause(Thread, Signal, info, ucontext);
|
||||
return static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->HandleSignalPause(Thread, Signal, info, ucontext);
|
||||
};
|
||||
|
||||
SignalDelegation->RegisterHostSignalHandler(SignalDelegator::SIGNAL_FOR_PAUSE, PauseHandler, true);
|
||||
|
||||
auto GuestSignalHandler = [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext, GuestSigAction *GuestAction, stack_t *GuestStack) -> bool {
|
||||
return Thread->CTX->Dispatcher->HandleGuestSignal(Thread, Signal, info, ucontext, GuestAction, GuestStack);
|
||||
return static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->HandleGuestSignal(Thread, Signal, info, ucontext, GuestAction, GuestStack);
|
||||
};
|
||||
|
||||
for (uint32_t Signal = 0; Signal <= SignalDelegator::MAX_SIGNALS; ++Signal) {
|
||||
@@ -295,30 +295,45 @@ namespace FEXCore::Context {
|
||||
return Thread;
|
||||
}
|
||||
|
||||
void Context::StartGdbServer() {
|
||||
void ContextImpl::StartGdbServer() {
|
||||
if (!DebugServer) {
|
||||
DebugServer = std::make_unique<GdbServer>(this);
|
||||
StartPaused = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Context::StopGdbServer() {
|
||||
void ContextImpl::StopGdbServer() {
|
||||
DebugServer.reset();
|
||||
}
|
||||
|
||||
void Context::HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) {
|
||||
Thread->CTX->Dispatcher->ExecuteJITCallback(Thread->CurrentFrame, RIP);
|
||||
void ContextImpl::HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) {
|
||||
static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->ExecuteJITCallback(Thread->CurrentFrame, RIP);
|
||||
}
|
||||
|
||||
void Context::RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
|
||||
void ContextImpl::HandleSignalHandlerReturn(bool RT) {
|
||||
using SignalHandlerReturnFunc = void(*)();
|
||||
|
||||
SignalHandlerReturnFunc SignalHandlerReturn{};
|
||||
if (RT) {
|
||||
SignalHandlerReturn = reinterpret_cast<SignalHandlerReturnFunc>(Dispatcher->SignalHandlerReturnAddressRT);
|
||||
}
|
||||
else {
|
||||
SignalHandlerReturn = reinterpret_cast<SignalHandlerReturnFunc>(Dispatcher->SignalHandlerReturnAddress);
|
||||
}
|
||||
|
||||
SignalHandlerReturn();
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
void ContextImpl::RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
|
||||
SignalDelegation->RegisterHostSignalHandler(Signal, Func, Required);
|
||||
}
|
||||
|
||||
void Context::RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
|
||||
void ContextImpl::RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) {
|
||||
SignalDelegation->RegisterFrontendHostSignalHandler(Signal, Func, Required);
|
||||
}
|
||||
|
||||
void Context::WaitForIdle() {
|
||||
void ContextImpl::WaitForIdle() {
|
||||
std::unique_lock<std::mutex> lk(IdleWaitMutex);
|
||||
IdleWaitCV.wait(lk, [this] {
|
||||
return IdleWaitRefCount.load() == 0;
|
||||
@@ -327,7 +342,7 @@ namespace FEXCore::Context {
|
||||
Running = false;
|
||||
}
|
||||
|
||||
void Context::WaitForIdleWithTimeout() {
|
||||
void ContextImpl::WaitForIdleWithTimeout() {
|
||||
std::unique_lock<std::mutex> lk(IdleWaitMutex);
|
||||
bool WaitResult = IdleWaitCV.wait_for(lk, std::chrono::milliseconds(1500),
|
||||
[this] {
|
||||
@@ -345,7 +360,7 @@ namespace FEXCore::Context {
|
||||
WaitForIdle();
|
||||
}
|
||||
|
||||
void Context::NotifyPause() {
|
||||
void ContextImpl::NotifyPause() {
|
||||
|
||||
// Tell all the threads that they should pause
|
||||
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
|
||||
@@ -358,7 +373,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::Pause() {
|
||||
void ContextImpl::Pause() {
|
||||
// If we aren't running, WaitForIdle will never compete.
|
||||
if (Running) {
|
||||
NotifyPause();
|
||||
@@ -367,7 +382,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::Run() {
|
||||
void ContextImpl::Run() {
|
||||
// Spin up all the threads
|
||||
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
|
||||
for (auto &Thread : Threads) {
|
||||
@@ -379,7 +394,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::WaitForThreadsToRun() {
|
||||
void ContextImpl::WaitForThreadsToRun() {
|
||||
size_t NumThreads{};
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
|
||||
@@ -395,7 +410,7 @@ namespace FEXCore::Context {
|
||||
Running = true;
|
||||
}
|
||||
|
||||
void Context::Step() {
|
||||
void ContextImpl::Step() {
|
||||
{
|
||||
std::lock_guard<std::mutex> lk(ThreadCreationMutex);
|
||||
// Walk the threads and tell them to clear their caches
|
||||
@@ -415,7 +430,7 @@ namespace FEXCore::Context {
|
||||
this->Config.MaxInstPerBlock = PreviousMaxIntPerBlock;
|
||||
}
|
||||
|
||||
void Context::Stop(bool IgnoreCurrentThread) {
|
||||
void ContextImpl::Stop(bool IgnoreCurrentThread) {
|
||||
pid_t tid = FHU::Syscalls::gettid();
|
||||
FEXCore::Core::InternalThreadState* CurrentThread{};
|
||||
|
||||
@@ -453,21 +468,21 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::StopThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::StopThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
if (Thread->RunningEvents.Running.exchange(false)) {
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Stop);
|
||||
FHU::Syscalls::tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
void Context::SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event) {
|
||||
void ContextImpl::SignalThread(FEXCore::Core::InternalThreadState *Thread, FEXCore::Core::SignalEvent Event) {
|
||||
if (Thread->RunningEvents.Running.load()) {
|
||||
Thread->SignalReason.store(Event);
|
||||
FHU::Syscalls::tgkill(Thread->ThreadManager.PID, Thread->ThreadManager.TID, SignalDelegator::SIGNAL_FOR_PAUSE);
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::Context::ExitReason Context::RunUntilExit() {
|
||||
FEXCore::Context::ExitReason ContextImpl::RunUntilExit() {
|
||||
if(!StartPaused) {
|
||||
// We will only have one thread at this point, but just in case run notify everything
|
||||
std::lock_guard lk(ThreadCreationMutex);
|
||||
@@ -488,16 +503,16 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
int Context::GetProgramStatus() const {
|
||||
int ContextImpl::GetProgramStatus() const {
|
||||
return ParentThread->StatusCode;
|
||||
}
|
||||
|
||||
void Context::InitializeThreadData(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::InitializeThreadData(FEXCore::Core::InternalThreadState *Thread) {
|
||||
Thread->CPUBackend->Initialize();
|
||||
}
|
||||
|
||||
struct ExecutionThreadHandler {
|
||||
FEXCore::Context::Context *This;
|
||||
ContextImpl *This;
|
||||
FEXCore::Core::InternalThreadState *Thread;
|
||||
};
|
||||
|
||||
@@ -508,7 +523,7 @@ namespace FEXCore::Context {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Context::InitializeThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::InitializeThread(FEXCore::Core::InternalThreadState *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;
|
||||
@@ -530,7 +545,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::InitializeThreadTLSData(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::InitializeThreadTLSData(FEXCore::Core::InternalThreadState *Thread) {
|
||||
// Let's do some initial bookkeeping here
|
||||
Thread->ThreadManager.TID = FHU::Syscalls::gettid();
|
||||
Thread->ThreadManager.PID = ::getpid();
|
||||
@@ -538,12 +553,12 @@ namespace FEXCore::Context {
|
||||
ThunkHandler->RegisterTLSState(Thread);
|
||||
}
|
||||
|
||||
void Context::RunThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::RunThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
// Tell the thread to start executing
|
||||
Thread->StartRunning.NotifyAll();
|
||||
}
|
||||
|
||||
void Context::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread) {
|
||||
void ContextImpl::InitializeCompiler(FEXCore::Core::InternalThreadState* Thread) {
|
||||
Thread->OpDispatcher = std::make_unique<FEXCore::IR::OpDispatchBuilder>(this);
|
||||
Thread->OpDispatcher->SetMultiblock(Config.Multiblock);
|
||||
Thread->LookupCache = std::make_unique<FEXCore::LookupCache>(this);
|
||||
@@ -595,7 +610,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::Core::InternalThreadState* Context::CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
|
||||
FEXCore::Core::InternalThreadState* ContextImpl::CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) {
|
||||
FEXCore::Core::InternalThreadState *Thread = new FEXCore::Core::InternalThreadState{};
|
||||
|
||||
// Copy over the new thread state to the new object
|
||||
@@ -617,7 +632,7 @@ namespace FEXCore::Context {
|
||||
return Thread;
|
||||
}
|
||||
|
||||
void Context::DestroyThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::DestroyThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
// remove new thread object
|
||||
{
|
||||
std::lock_guard lk(ThreadCreationMutex);
|
||||
@@ -636,7 +651,7 @@ namespace FEXCore::Context {
|
||||
delete Thread;
|
||||
}
|
||||
|
||||
void Context::CleanupAfterFork(FEXCore::Core::InternalThreadState *LiveThread) {
|
||||
void ContextImpl::CleanupAfterFork(FEXCore::Core::InternalThreadState *LiveThread) {
|
||||
// This function is called after fork
|
||||
// We need to cleanup some of the thread data that is dead
|
||||
for (auto &DeadThread : Threads) {
|
||||
@@ -675,11 +690,11 @@ namespace FEXCore::Context {
|
||||
FEXCore::Threads::Thread::CleanupAfterFork();
|
||||
}
|
||||
|
||||
void Context::AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr) {
|
||||
void ContextImpl::AddBlockMapping(FEXCore::Core::InternalThreadState *Thread, uint64_t Address, void *Ptr) {
|
||||
Thread->LookupCache->AddBlockMapping(Address, Ptr);
|
||||
}
|
||||
|
||||
void Context::ClearCodeCache(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::ClearCodeCache(FEXCore::Core::InternalThreadState *Thread) {
|
||||
FEXCORE_PROFILE_INSTANT("ClearCodeCache");
|
||||
|
||||
{
|
||||
@@ -697,7 +712,7 @@ namespace FEXCore::Context {
|
||||
static void IRDumper(FEXCore::Core::InternalThreadState *Thread, IR::IREmitter *IREmitter, uint64_t GuestRIP, IR::RegisterAllocationData* RA) {
|
||||
FILE* f = nullptr;
|
||||
bool CloseAfter = false;
|
||||
const auto DumpIRStr = Thread->CTX->Config.DumpIR();
|
||||
const auto DumpIRStr = static_cast<ContextImpl*>(Thread->CTX)->Config.DumpIR();
|
||||
|
||||
// DumpIRStr might be no if not dumping but ShouldDump is set in OpDisp
|
||||
if (DumpIRStr =="stderr" || DumpIRStr =="no") {
|
||||
@@ -724,7 +739,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
};
|
||||
|
||||
static void ValidateIR(FEXCore::Context::Context *ctx, IR::IREmitter *IREmitter) {
|
||||
static void ValidateIR(ContextImpl *ctx, IR::IREmitter *IREmitter) {
|
||||
// Convert to text, Parse, Convert to text again and make sure the texts match
|
||||
std::stringstream out;
|
||||
static auto compaction = IR::CreateIRCompaction(ctx->OpDispatcherAllocator);
|
||||
@@ -748,7 +763,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
Context::GenerateIRResult Context::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
|
||||
ContextImpl::GenerateIRResult ContextImpl::GenerateIR(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP, bool ExtendedDebugInfo) {
|
||||
FEXCORE_PROFILE_SCOPED("GenerateIR");
|
||||
|
||||
Thread->OpDispatcher->ReownOrClaimBuffer();
|
||||
@@ -775,7 +790,7 @@ namespace FEXCore::Context {
|
||||
|
||||
Thread->FrontendDecoder->DecodeInstructionsAtEntry(GuestCode, GuestRIP, [Thread](uint64_t BlockEntry, uint64_t Start, uint64_t Length) {
|
||||
if (Thread->LookupCache->AddBlockExecutableRange(BlockEntry, Start, Length)) {
|
||||
Thread->CTX->SyscallHandler->MarkGuestExecutableRange(Start, Length);
|
||||
static_cast<ContextImpl*>(Thread->CTX)->SyscallHandler->MarkGuestExecutableRange(Start, Length);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -795,13 +810,6 @@ namespace FEXCore::Context {
|
||||
// Reset any block-specific state
|
||||
Thread->OpDispatcher->StartNewBlock();
|
||||
|
||||
if (Config.x86dec_SynchronizeRIPOnAllBlocks) {
|
||||
// Ensure the RIP is synchronized to the context on block entry.
|
||||
// In the case of block linking, the RIP may not have synchronized.
|
||||
auto NewRIP = Thread->OpDispatcher->_EntrypointOffset(Block.Entry - GuestRIP, GPRSize);
|
||||
Thread->OpDispatcher->_StoreContext(GPRSize, IR::GPRClass, NewRIP, offsetof(FEXCore::Core::CPUState, rip));
|
||||
}
|
||||
|
||||
uint64_t InstsInBlock = Block.NumInstructions;
|
||||
|
||||
for (size_t i = 0; i < InstsInBlock; ++i) {
|
||||
@@ -893,14 +901,14 @@ namespace FEXCore::Context {
|
||||
|
||||
IR::IREmitter *IREmitter = Thread->OpDispatcher.get();
|
||||
|
||||
auto ShouldDump = Thread->CTX->Config.DumpIR() != "no" || Thread->OpDispatcher->ShouldDump;
|
||||
auto ShouldDump = static_cast<ContextImpl*>(Thread->CTX)->Config.DumpIR() != "no" || Thread->OpDispatcher->ShouldDump;
|
||||
// Debug
|
||||
{
|
||||
if (ShouldDump) {
|
||||
IRDumper(Thread, IREmitter, GuestRIP, nullptr);
|
||||
}
|
||||
|
||||
if (Thread->CTX->Config.ValidateIRarser) {
|
||||
if (static_cast<ContextImpl*>(Thread->CTX)->Config.ValidateIRarser) {
|
||||
ValidateIR(this, IREmitter);
|
||||
}
|
||||
}
|
||||
@@ -930,7 +938,7 @@ namespace FEXCore::Context {
|
||||
};
|
||||
}
|
||||
|
||||
Context::CompileCodeResult Context::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
ContextImpl::CompileCodeResult ContextImpl::CompileCode(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
FEXCore::IR::IRListView *IRList {};
|
||||
FEXCore::Core::DebugData *DebugData {};
|
||||
FEXCore::IR::RegisterAllocationData::UniquePtr RAData {};
|
||||
@@ -1002,7 +1010,10 @@ namespace FEXCore::Context {
|
||||
}
|
||||
// Attempt to get the CPU backend to compile this code
|
||||
return {
|
||||
.CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, IRList, DebugData, RAData.get(), GetGdbServerStatus()),
|
||||
// FEX currently throws away the CPUBackend::CompiledCode object other than the entrypoint
|
||||
// In the future with code caching getting wired up, we will pass the rest of the data forward.
|
||||
// TODO: Pass the data forward when code caching is wired up to this.
|
||||
.CompiledCode = Thread->CPUBackend->CompileCode(GuestRIP, IRList, DebugData, RAData.get(), GetGdbServerStatus()).BlockEntry,
|
||||
.IRData = IRList,
|
||||
.DebugData = DebugData,
|
||||
.RAData = std::move(RAData),
|
||||
@@ -1012,7 +1023,7 @@ namespace FEXCore::Context {
|
||||
};
|
||||
}
|
||||
|
||||
void Context::CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
void ContextImpl::CompileBlockJit(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
auto NewBlock = CompileBlock(Frame, GuestRIP);
|
||||
|
||||
if (NewBlock == 0) {
|
||||
@@ -1023,7 +1034,7 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
uintptr_t Context::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
uintptr_t ContextImpl::CompileBlock(FEXCore::Core::CpuStateFrame *Frame, uint64_t GuestRIP) {
|
||||
FEXCORE_PROFILE_SCOPED("CompileBlock");
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
@@ -1123,7 +1134,7 @@ namespace FEXCore::Context {
|
||||
return (uintptr_t)CodePtr;
|
||||
}
|
||||
|
||||
void Context::ExecutionThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
void ContextImpl::ExecutionThread(FEXCore::Core::InternalThreadState *Thread) {
|
||||
Core::ThreadData.Thread = Thread;
|
||||
Thread->ExitReason = FEXCore::Context::ExitReason::EXIT_WAITING;
|
||||
|
||||
@@ -1134,7 +1145,7 @@ namespace FEXCore::Context {
|
||||
// Now notify the thread that we are initialized
|
||||
Thread->ThreadWaiting.NotifyAll();
|
||||
|
||||
if (Thread != Thread->CTX->ParentThread || StartPaused || Thread->StartPaused) {
|
||||
if (Thread != static_cast<ContextImpl*>(Thread->CTX)->ParentThread || StartPaused || Thread->StartPaused) {
|
||||
// Parent thread doesn't need to wait to run
|
||||
Thread->StartRunning.Wait();
|
||||
}
|
||||
@@ -1146,7 +1157,7 @@ namespace FEXCore::Context {
|
||||
|
||||
Thread->RunningEvents.Running = true;
|
||||
|
||||
Thread->CTX->Dispatcher->ExecuteDispatch(Thread->CurrentFrame);
|
||||
static_cast<ContextImpl*>(Thread->CTX)->Dispatcher->ExecuteDispatch(Thread->CurrentFrame);
|
||||
|
||||
Thread->RunningEvents.Running = false;
|
||||
}
|
||||
@@ -1175,7 +1186,7 @@ namespace FEXCore::Context {
|
||||
SignalDelegation->UninstallTLSState(Thread);
|
||||
|
||||
// If the parent thread is waiting to join, then we can't destroy our thread object
|
||||
if (!Thread->DestroyedByParent && Thread != Thread->CTX->ParentThread) {
|
||||
if (!Thread->DestroyedByParent && Thread != static_cast<ContextImpl*>(Thread->CTX)->ParentThread) {
|
||||
Thread->CTX->DestroyThread(Thread);
|
||||
}
|
||||
}
|
||||
@@ -1188,34 +1199,34 @@ namespace FEXCore::Context {
|
||||
|
||||
for (auto it = lower; it != upper; it++) {
|
||||
for (auto Address: it->second) {
|
||||
Context::ThreadRemoveCodeEntry(Thread, Address);
|
||||
ContextImpl::ThreadRemoveCodeEntry(Thread, Address);
|
||||
}
|
||||
it->second.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static void InvalidateGuestCodeRangeInternal(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
|
||||
std::lock_guard lk(CTX->ThreadCreationMutex);
|
||||
static void InvalidateGuestCodeRangeInternal(ContextImpl *CTX, uint64_t Start, uint64_t Length) {
|
||||
std::lock_guard lk(static_cast<ContextImpl*>(CTX)->ThreadCreationMutex);
|
||||
|
||||
for (auto &Thread : CTX->Threads) {
|
||||
for (auto &Thread : static_cast<ContextImpl*>(CTX)->Threads) {
|
||||
InvalidateGuestThreadCodeRange(Thread, Start, Length);
|
||||
}
|
||||
}
|
||||
|
||||
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
|
||||
void ContextImpl::InvalidateGuestCodeRange(uint64_t Start, uint64_t Length) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CodeInvalidationMutex);
|
||||
|
||||
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
|
||||
InvalidateGuestCodeRangeInternal(this, Start, Length);
|
||||
}
|
||||
|
||||
void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> CallAfter) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CTX->CodeInvalidationMutex);
|
||||
void ContextImpl::InvalidateGuestCodeRange(uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> CallAfter) {
|
||||
FHU::ScopedSignalMaskWithUniqueLock CodeInvalidationLock(CodeInvalidationMutex);
|
||||
|
||||
InvalidateGuestCodeRangeInternal(CTX, Start, Length);
|
||||
InvalidateGuestCodeRangeInternal(this, Start, Length);
|
||||
CallAfter(Start, Length);
|
||||
}
|
||||
|
||||
void Context::MarkMemoryShared() {
|
||||
void ContextImpl::MarkMemoryShared() {
|
||||
if (!IsMemoryShared) {
|
||||
IsMemoryShared = true;
|
||||
|
||||
@@ -1235,18 +1246,14 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
void MarkMemoryShared(FEXCore::Context::Context *CTX) {
|
||||
CTX->MarkMemoryShared();
|
||||
}
|
||||
|
||||
void Context::ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker) {
|
||||
std::shared_lock lk(Thread->CTX->CodeInvalidationMutex);
|
||||
void ContextImpl::ThreadAddBlockLink(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestDestination, uintptr_t HostLink, const std::function<void()> &delinker) {
|
||||
std::shared_lock lk(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex);
|
||||
|
||||
Thread->LookupCache->AddBlockLink(GuestDestination, HostLink, delinker);
|
||||
}
|
||||
|
||||
void Context::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
LogMan::Throw::AFmt(Thread->CTX->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
|
||||
void ContextImpl::ThreadRemoveCodeEntry(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) {
|
||||
LogMan::Throw::AFmt(static_cast<ContextImpl*>(Thread->CTX)->CodeInvalidationMutex.try_lock() == false, "CodeInvalidationMutex needs to be unique_locked here");
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lk(Thread->LookupCache->WriteLock);
|
||||
|
||||
@@ -1254,7 +1261,7 @@ namespace FEXCore::Context {
|
||||
Thread->LookupCache->Erase(GuestRIP);
|
||||
}
|
||||
|
||||
CustomIRResult Context::AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data) {
|
||||
CustomIRResult ContextImpl::AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator, void *Data) {
|
||||
LOGMAN_THROW_A_FMT(Config.Is64BitMode || !(Entrypoint >> 32), "64-bit Entrypoint in 32-bit mode {:x}", Entrypoint);
|
||||
|
||||
std::unique_lock lk(CustomIRMutex);
|
||||
@@ -1270,12 +1277,12 @@ namespace FEXCore::Context {
|
||||
}
|
||||
}
|
||||
|
||||
void Context::RemoveCustomIREntrypoint(uintptr_t Entrypoint) {
|
||||
void ContextImpl::RemoveCustomIREntrypoint(uintptr_t Entrypoint) {
|
||||
LOGMAN_THROW_A_FMT(Config.Is64BitMode || !(Entrypoint >> 32), "64-bit Entrypoint in 32-bit mode {:x}", Entrypoint);
|
||||
|
||||
std::scoped_lock lk(CustomIRMutex);
|
||||
|
||||
InvalidateGuestCodeRange(this, Entrypoint, 1, [this](uint64_t Entrypoint, uint64_t) {
|
||||
InvalidateGuestCodeRange(Entrypoint, 1, [this](uint64_t Entrypoint, uint64_t) {
|
||||
CustomIRHandlers.erase(Entrypoint);
|
||||
});
|
||||
}
|
||||
@@ -1285,24 +1292,26 @@ namespace FEXCore::Context {
|
||||
uint64_t RIPBackup = Thread->CurrentFrame->State.rip;
|
||||
Thread->CurrentFrame->State.rip = RIP;
|
||||
|
||||
auto CTX = static_cast<ContextImpl*>(Thread->CTX);
|
||||
|
||||
// Erase the RIP from all the storage backings if it exists
|
||||
ThreadRemoveCodeEntry(Thread, RIP);
|
||||
CTX->ThreadRemoveCodeEntry(Thread, RIP);
|
||||
|
||||
// We don't care if compilation passes or not
|
||||
CompileBlock(Thread->CurrentFrame, RIP);
|
||||
CTX->CompileBlock(Thread->CurrentFrame, RIP);
|
||||
|
||||
Thread->CurrentFrame->State.rip = RIPBackup;
|
||||
}
|
||||
|
||||
uint64_t Context::GetThreadCount() const {
|
||||
uint64_t ContextImpl::GetThreadCount() const {
|
||||
return Threads.size();
|
||||
}
|
||||
|
||||
FEXCore::Core::RuntimeStats *Context::GetRuntimeStatsForThread(uint64_t Thread) {
|
||||
FEXCore::Core::RuntimeStats *ContextImpl::GetRuntimeStatsForThread(uint64_t Thread) {
|
||||
return &Threads[Thread]->Stats;
|
||||
}
|
||||
|
||||
bool Context::GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data) {
|
||||
bool ContextImpl::GetDebugDataForRIP(uint64_t RIP, FEXCore::Core::DebugData *Data) {
|
||||
std::lock_guard<std::recursive_mutex> lk(ParentThread->LookupCache->WriteLock);
|
||||
auto it = ParentThread->DebugStore.find(RIP);
|
||||
if (it == ParentThread->DebugStore.end()) {
|
||||
@@ -1313,7 +1322,7 @@ namespace FEXCore::Context {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Context::FindHostCodeForRIP(uint64_t RIP, uint8_t **Code) {
|
||||
bool ContextImpl::FindHostCodeForRIP(uint64_t RIP, uint8_t **Code) {
|
||||
uintptr_t HostCode = ParentThread->LookupCache->FindBlock(RIP);
|
||||
if (!HostCode) {
|
||||
return false;
|
||||
@@ -1329,7 +1338,7 @@ namespace FEXCore::Context {
|
||||
return Result;
|
||||
}
|
||||
|
||||
IR::AOTIRCacheEntry *Context::LoadAOTIRCacheEntry(const std::string &filename) {
|
||||
IR::AOTIRCacheEntry *ContextImpl::LoadAOTIRCacheEntry(const std::string &filename) {
|
||||
auto rv = IRCaptureCache.LoadAOTIRCacheEntry(filename);
|
||||
if (DebugServer) {
|
||||
DebugServer->AlertLibrariesChanged();
|
||||
@@ -1337,19 +1346,18 @@ namespace FEXCore::Context {
|
||||
return rv;
|
||||
}
|
||||
|
||||
void Context::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry *Entry) {
|
||||
void ContextImpl::UnloadAOTIRCacheEntry(IR::AOTIRCacheEntry *Entry) {
|
||||
IRCaptureCache.UnloadAOTIRCacheEntry(Entry);
|
||||
if (DebugServer) {
|
||||
DebugServer->AlertLibrariesChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Context::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
|
||||
void ContextImpl::AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) {
|
||||
ThunkHandler->AppendThunkDefinitions(Definitions);
|
||||
}
|
||||
|
||||
void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
void ContextImpl::ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) {
|
||||
Thread->FrontendDecoder->SetExternalBranches(ExternalBranches);
|
||||
Thread->FrontendDecoder->SetSectionMaxAddress(SectionMaxAddress);
|
||||
}
|
||||
|
||||
+3
-3
@@ -5,7 +5,7 @@ namespace FEXCore {
|
||||
}
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
@@ -17,7 +17,7 @@ namespace FEXCore::CPU {
|
||||
*
|
||||
* @return true if core was able to be create
|
||||
*/
|
||||
bool CreateCPUCore(FEXCore::Context::Context *CTX);
|
||||
bool CreateCPUCore(FEXCore::Context::ContextImpl *CTX);
|
||||
|
||||
bool LoadCode(FEXCore::Context::Context *CTX, FEXCore::CodeLoader *Loader);
|
||||
bool LoadCode(FEXCore::Context::ContextImpl *CTX, FEXCore::CodeLoader *Loader);
|
||||
}
|
||||
@@ -35,7 +35,7 @@ namespace FEXCore::CPU {
|
||||
|
||||
constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
|
||||
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
|
||||
Arm64Dispatcher::Arm64Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config)
|
||||
: FEXCore::CPU::Dispatcher(ctx, config), Arm64Emitter(ctx, MAX_DISPATCHER_CODE_SIZE)
|
||||
#ifdef VIXL_SIMULATOR
|
||||
, Simulator {&Decoder}
|
||||
@@ -578,10 +578,10 @@ size_t Arm64Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t Gues
|
||||
// If we have a gdb server running then run in a less efficient mode that checks if we need to exit
|
||||
// This happens when single stepping
|
||||
|
||||
static_assert(sizeof(FEXCore::Context::Context::Config.RunningMode) == 4, "This is expected to be size of 4");
|
||||
static_assert(sizeof(FEXCore::Context::ContextImpl::Config.RunningMode) == 4, "This is expected to be size of 4");
|
||||
emit.ldr(ARMEmitter::XReg::x0, STATE_PTR(CpuStateFrame, Thread));
|
||||
emit.ldr(ARMEmitter::XReg::x0, ARMEmitter::Reg::r0, offsetof(FEXCore::Core::InternalThreadState, CTX)); // Get Context
|
||||
emit.ldr(ARMEmitter::WReg::w0, ARMEmitter::Reg::r0, offsetof(FEXCore::Context::Context, Config.RunningMode));
|
||||
emit.ldr(ARMEmitter::WReg::w0, ARMEmitter::Reg::r0, offsetof(FEXCore::Context::ContextImpl, Config.RunningMode));
|
||||
|
||||
// If the value == 0 then we don't need to stop
|
||||
emit.cbz(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, &RunBlock);
|
||||
@@ -672,7 +672,7 @@ void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thr
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Dispatcher> Dispatcher::CreateArm64(FEXCore::Context::Context *CTX, const DispatcherConfig &Config) {
|
||||
std::unique_ptr<Dispatcher> Dispatcher::CreateArm64(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config) {
|
||||
return std::make_unique<Arm64Dispatcher>(CTX, Config);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,10 +7,6 @@
|
||||
#include <aarch64/simulator-aarch64.h>
|
||||
#endif
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct InternalThreadState;
|
||||
}
|
||||
@@ -22,7 +18,7 @@ namespace FEXCore::CPU {
|
||||
|
||||
class Arm64Dispatcher final : public Dispatcher, public Arm64Emitter {
|
||||
public:
|
||||
Arm64Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config);
|
||||
Arm64Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config);
|
||||
void InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) override;
|
||||
size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) override;
|
||||
|
||||
+171
-52
@@ -22,7 +22,7 @@
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
void Dispatcher::SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame) {
|
||||
void Dispatcher::SleepThread(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::CpuStateFrame *Frame) {
|
||||
auto Thread = Frame->Thread;
|
||||
|
||||
--ctx->IdleWaitRefCount;
|
||||
@@ -40,6 +40,27 @@ void Dispatcher::SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuS
|
||||
ctx->IdleWaitCV.notify_all();
|
||||
}
|
||||
|
||||
uint64_t Dispatcher::ReconstructRIPFromContext(FEXCore::Core::CpuStateFrame *Frame, void *ucontext) const {
|
||||
const uint64_t HostPC = ArchHelpers::Context::GetPc(ucontext);
|
||||
const uint64_t BlockBegin = Frame->State.InlineJITBlockHeader;
|
||||
const CPUBackend::JITCodeHeader *InlineHeader = reinterpret_cast<const CPUBackend::JITCodeHeader *>(BlockBegin);
|
||||
|
||||
if (InlineHeader) {
|
||||
const CPUBackend::JITCodeTail *InlineTail = reinterpret_cast<const CPUBackend::JITCodeTail *>(Frame->State.InlineJITBlockHeader + InlineHeader->OffsetToBlockTail);
|
||||
|
||||
// Check if the host PC is currently within a code block.
|
||||
// If it is then RIP can be reconstructed from the beginning of the code block.
|
||||
// This is currently as close as FEX can get RIP reconstructions.
|
||||
if (HostPC >= reinterpret_cast<uint64_t>(BlockBegin) &&
|
||||
HostPC < reinterpret_cast<uint64_t>(BlockBegin + InlineTail->Size)) {
|
||||
return InlineTail->RIP;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to what is stored in the RIP currently.
|
||||
return Frame->State.rip;
|
||||
}
|
||||
|
||||
ArchHelpers::Context::ContextBackup* Dispatcher::StoreThreadState(FEXCore::Core::InternalThreadState *Thread, int Signal, void *ucontext) {
|
||||
// We can end up getting a signal at any point in our host state
|
||||
// Jump to a handler that saves all state so we can safely return
|
||||
@@ -70,12 +91,6 @@ ArchHelpers::Context::ContextBackup* Dispatcher::StoreThreadState(FEXCore::Core:
|
||||
// Set the new SP
|
||||
ArchHelpers::Context::SetSp(ucontext, NewSP);
|
||||
|
||||
// Signal frames are only used on the interpreter
|
||||
// The JITS require the stack to be setup correctly on rt_sigreturn
|
||||
if (CTX->Config.Core() == FEXCore::Config::CONFIG_INTERPRETER) {
|
||||
SignalFrames.push(NewSP);
|
||||
}
|
||||
|
||||
Context->Flags = 0;
|
||||
Context->FPStateLocation = 0;
|
||||
Context->UContextLocation = 0;
|
||||
@@ -255,25 +270,93 @@ void Dispatcher::RestoreRTFrame_ia32(ArchHelpers::Context::ContextBackup* Contex
|
||||
}
|
||||
|
||||
void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread, void *ucontext, RestoreType Type) {
|
||||
// Pulling from context here
|
||||
const bool Is64BitMode = CTX->Config.Is64BitMode;
|
||||
const bool IsAVXEnabled = CTX->Config.EnableAVX;
|
||||
|
||||
uint64_t OldSP{};
|
||||
if (CTX->Config.Core() == FEXCore::Config::CONFIG_IRJIT) {
|
||||
if (Type == RestoreType::TYPE_PAUSE) [[unlikely]] {
|
||||
OldSP = ArchHelpers::Context::GetSp(ucontext);
|
||||
}
|
||||
else {
|
||||
LOGMAN_THROW_A_FMT(!SignalFrames.empty(), "Trying to restore a signal frame when we don't have any");
|
||||
OldSP = SignalFrames.top();
|
||||
SignalFrames.pop();
|
||||
// Some fun introspection here.
|
||||
// We store a pointer to our host-stack on the guest stack.
|
||||
// We need to inspect the guest state coming in, so we can get our host stack back.
|
||||
uint64_t GuestSP = Thread->CurrentFrame->State.gregs[X86State::REG_RSP];
|
||||
|
||||
if (Is64BitMode) {
|
||||
// Signal frame layout on stack needs to be as follows
|
||||
// void* ReturnPointer
|
||||
// ucontext_t
|
||||
// siginfo_t
|
||||
// FP state
|
||||
// Host stack location
|
||||
|
||||
GuestSP += sizeof(FEXCore::x86_64::ucontext_t);
|
||||
GuestSP = AlignUp(GuestSP, alignof(FEXCore::x86_64::ucontext_t));
|
||||
|
||||
GuestSP += sizeof(siginfo_t);
|
||||
GuestSP = AlignUp(GuestSP, alignof(siginfo_t));
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
GuestSP += sizeof(x86_64::xstate);
|
||||
GuestSP = AlignUp(GuestSP, alignof(x86_64::xstate));
|
||||
} else {
|
||||
GuestSP += sizeof(x86_64::_libc_fpstate);
|
||||
GuestSP = AlignUp(GuestSP, alignof(x86_64::_libc_fpstate));
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (Type == RestoreType::TYPE_NONREALTIME) {
|
||||
// Signal frame layout on stack needs to be as follows
|
||||
// SigFrame_i32
|
||||
// FPState
|
||||
// Host stack location
|
||||
|
||||
// Remove the 4-byte pretcode /AND/ a legacy argument that is ignored.
|
||||
GuestSP += sizeof(SigFrame_i32) - 8;
|
||||
GuestSP = AlignUp(GuestSP, alignof(SigFrame_i32));
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
GuestSP += sizeof(x86::xstate);
|
||||
GuestSP = AlignUp(GuestSP, alignof(x86::xstate));
|
||||
} else {
|
||||
GuestSP += sizeof(x86::_libc_fpstate);
|
||||
GuestSP = AlignUp(GuestSP, alignof(x86::_libc_fpstate));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Signal frame layout on stack needs to be as follows
|
||||
// RTSigFrame_i32
|
||||
// FPState
|
||||
// Host stack location
|
||||
|
||||
// Remove the 4-byte pretcode.
|
||||
GuestSP += sizeof(RTSigFrame_i32) - 4;
|
||||
GuestSP = AlignUp(GuestSP, alignof(RTSigFrame_i32));
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
GuestSP += sizeof(x86::xstate);
|
||||
GuestSP = AlignUp(GuestSP, alignof(x86::xstate));
|
||||
} else {
|
||||
GuestSP += sizeof(x86::_libc_fpstate);
|
||||
GuestSP = AlignUp(GuestSP, alignof(x86::_libc_fpstate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OldSP = *reinterpret_cast<uint64_t*>(GuestSP);
|
||||
}
|
||||
|
||||
uintptr_t NewSP = OldSP;
|
||||
auto Context = reinterpret_cast<ArchHelpers::Context::ContextBackup*>(NewSP);
|
||||
|
||||
// First thing, reset the guest state
|
||||
memcpy(Thread->CurrentFrame, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
// Now restore host state
|
||||
// Restore host state
|
||||
ArchHelpers::Context::RestoreContext(ucontext, Context);
|
||||
|
||||
// Reset the guest state
|
||||
memcpy(Thread->CurrentFrame, &Context->GuestState, sizeof(FEXCore::Core::CPUState));
|
||||
|
||||
if (Context->UContextLocation) {
|
||||
auto Frame = Thread->CurrentFrame;
|
||||
|
||||
@@ -283,7 +366,7 @@ void Dispatcher::RestoreThreadState(FEXCore::Core::InternalThreadState *Thread,
|
||||
// We can't currently support this since it might result in tearing without real state reconstruction
|
||||
}
|
||||
|
||||
if (!(Context->Flags & ArchHelpers::Context::ContextFlags::CONTEXT_FLAG_32BIT)) {
|
||||
if (Is64BitMode) {
|
||||
RestoreFrame_x64(Context, Frame, ucontext);
|
||||
}
|
||||
else {
|
||||
@@ -354,9 +437,12 @@ uint64_t Dispatcher::SetupFrame_ia32(
|
||||
uint64_t NewGuestSP, const uint32_t eflags) {
|
||||
|
||||
const bool IsAVXEnabled = CTX->Config.EnableAVX;
|
||||
const uint64_t SignalReturn = CTX->X86CodeGen.SignalReturn;
|
||||
const uint64_t SignalReturn = reinterpret_cast<uint64_t>(CTX->VDSOPointers.VDSO_kernel_sigreturn);
|
||||
|
||||
ContextBackup->Flags |= ArchHelpers::Context::ContextFlags::CONTEXT_FLAG_32BIT;
|
||||
NewGuestSP -= sizeof(uint64_t);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(uint64_t));
|
||||
|
||||
uint64_t HostStackLocation = NewGuestSP;
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
NewGuestSP -= sizeof(x86::xstate);
|
||||
@@ -377,6 +463,8 @@ uint64_t Dispatcher::SetupFrame_ia32(
|
||||
ContextBackup->SigInfoLocation = 0;
|
||||
|
||||
SigFrame_i32 *guest_uctx = reinterpret_cast<SigFrame_i32*>(SigFrameLocation);
|
||||
// Store where the host context lives in the guest stack.
|
||||
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
|
||||
|
||||
// Pointer to where the fpreg memory is
|
||||
guest_uctx->sc.fpstate = static_cast<uint32_t>(FPStateLocation);
|
||||
@@ -400,7 +488,7 @@ uint64_t Dispatcher::SetupFrame_ia32(
|
||||
guest_uctx->sc.err = ConvertSignalToError(ucontext, Signal, HostSigInfo);
|
||||
}
|
||||
|
||||
guest_uctx->sc.ip = Frame->State.rip;
|
||||
guest_uctx->sc.ip = ContextBackup->OriginalRIP;
|
||||
guest_uctx->sc.flags = eflags;
|
||||
guest_uctx->sc.sp_at_signal = 0;
|
||||
|
||||
@@ -453,9 +541,21 @@ uint64_t Dispatcher::SetupFrame_ia32(
|
||||
// Copy over the signal information.
|
||||
guest_uctx->Signal = Signal;
|
||||
|
||||
// Retcode needs to be bit-exact for debuggers
|
||||
constexpr static uint8_t retcode[] = {
|
||||
0x58, // pop eax
|
||||
0xb8, // mov
|
||||
0x77, 0x00, 0x00, 0x00, // 32-bit sigreturn
|
||||
0xcd, 0x80, // int 0x80
|
||||
};
|
||||
|
||||
memcpy(guest_uctx->retcode, &retcode, sizeof(retcode));
|
||||
|
||||
// 32-bit Guest can provide its own restorer or we need to provide our own.
|
||||
// On a real host this restorer will live in VDSO.
|
||||
if (GuestAction->restorer && incomplete_guest_restorer_support) {
|
||||
constexpr uint32_t SA_RESTORER = 0x04000000;
|
||||
const bool HasRestorer = (GuestAction->sa_flags & SA_RESTORER) == SA_RESTORER;
|
||||
if (HasRestorer) {
|
||||
// TODO: Support guest restorer
|
||||
guest_uctx->pretcode = (uint32_t)(uint64_t)GuestAction->restorer;
|
||||
}
|
||||
@@ -479,9 +579,12 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
|
||||
uint64_t NewGuestSP, const uint32_t eflags) {
|
||||
|
||||
const bool IsAVXEnabled = CTX->Config.EnableAVX;
|
||||
const uint64_t SignalReturn = CTX->X86CodeGen.SignalReturnRT;
|
||||
const uint64_t SignalReturn = reinterpret_cast<uint64_t>(CTX->VDSOPointers.VDSO_kernel_rt_sigreturn);
|
||||
|
||||
ContextBackup->Flags |= ArchHelpers::Context::ContextFlags::CONTEXT_FLAG_32BIT;
|
||||
NewGuestSP -= sizeof(uint64_t);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(uint64_t));
|
||||
|
||||
uint64_t HostStackLocation = NewGuestSP;
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
NewGuestSP -= sizeof(x86::xstate);
|
||||
@@ -495,8 +598,11 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
|
||||
|
||||
NewGuestSP -= sizeof(RTSigFrame_i32);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(RTSigFrame_i32));
|
||||
|
||||
uint64_t SigFrameLocation = NewGuestSP;
|
||||
RTSigFrame_i32 *guest_uctx = reinterpret_cast<RTSigFrame_i32*>(SigFrameLocation);
|
||||
// Store where the host context lives in the guest stack.
|
||||
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
|
||||
|
||||
ContextBackup->FPStateLocation = FPStateLocation;
|
||||
ContextBackup->UContextLocation = SigFrameLocation;
|
||||
@@ -504,6 +610,7 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
|
||||
|
||||
// We have extended float information
|
||||
guest_uctx->uc.uc_flags = FEXCore::x86::UC_FP_XSTATE;
|
||||
guest_uctx->uc.uc_link = 0;
|
||||
|
||||
// Pointer to where the fpreg memory is
|
||||
guest_uctx->uc.uc_mcontext.fpregs = static_cast<uint32_t>(FPStateLocation);
|
||||
@@ -528,9 +635,10 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
|
||||
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_ERR] = ConvertSignalToError(ucontext, Signal, HostSigInfo);
|
||||
}
|
||||
|
||||
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP] = Frame->State.rip;
|
||||
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EIP] = ContextBackup->OriginalRIP;
|
||||
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_EFL] = eflags;
|
||||
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_UESP] = 0;
|
||||
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_UESP] = Frame->State.gregs[X86State::REG_RSP];
|
||||
guest_uctx->uc.uc_mcontext.cr2 = 0;
|
||||
|
||||
#define COPY_REG(x) \
|
||||
guest_uctx->uc.uc_mcontext.gregs[FEXCore::x86::FEX_REG_##x] = Frame->State.gregs[X86State::REG_##x];
|
||||
@@ -576,7 +684,6 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
|
||||
(Frame->State.flags[FEXCore::X86State::X87FLAG_C2_LOC] << 10) |
|
||||
(Frame->State.flags[FEXCore::X86State::X87FLAG_C3_LOC] << 14);
|
||||
|
||||
|
||||
// Copy over signal stack information
|
||||
guest_uctx->uc.uc_stack.ss_flags = GuestStack->ss_flags;
|
||||
guest_uctx->uc.uc_stack.ss_sp = static_cast<uint32_t>(reinterpret_cast<uint64_t>(GuestStack->ss_sp));
|
||||
@@ -605,7 +712,7 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
|
||||
case SIGILL:
|
||||
// Macro expansion to get the si_addr
|
||||
// Can't really give a real result here. Pull from the context for now
|
||||
guest_uctx->info._sifields._sigfault.addr = Frame->State.rip;
|
||||
guest_uctx->info._sifields._sigfault.addr = ContextBackup->OriginalRIP;
|
||||
break;
|
||||
case SIGCHLD:
|
||||
guest_uctx->info._sifields._sigchld.pid = HostSigInfo->si_pid;
|
||||
@@ -630,9 +737,21 @@ uint64_t Dispatcher::SetupRTFrame_ia32(
|
||||
guest_uctx->pinfo = (uint32_t)(uint64_t)&guest_uctx->info;
|
||||
guest_uctx->puc = (uint32_t)(uint64_t)&guest_uctx->uc;
|
||||
|
||||
// Retcode needs to be bit-exact for debuggers
|
||||
constexpr static uint8_t rt_retcode[] = {
|
||||
0xb8, // mov
|
||||
0xad, 0x00, 0x00, 0x00, // 32-bit rt_sigreturn
|
||||
0xcd, 0x80, // int 0x80
|
||||
0x0, // Pad
|
||||
};
|
||||
|
||||
memcpy(guest_uctx->retcode, &rt_retcode, sizeof(rt_retcode));
|
||||
|
||||
// 32-bit Guest can provide its own restorer or we need to provide our own.
|
||||
// On a real host this restorer will live in VDSO.
|
||||
if (GuestAction->restorer && incomplete_guest_restorer_support) {
|
||||
constexpr uint32_t SA_RESTORER = 0x04000000;
|
||||
const bool HasRestorer = (GuestAction->sa_flags & SA_RESTORER) == SA_RESTORER;
|
||||
if (HasRestorer) {
|
||||
// TODO: Support guest restorer
|
||||
guest_uctx->pretcode = (uint32_t)(uint64_t)GuestAction->restorer;
|
||||
}
|
||||
@@ -734,7 +853,6 @@ uint64_t Dispatcher::SetupFrame_x64(
|
||||
NewGuestSP -= 128;
|
||||
|
||||
const bool IsAVXEnabled = CTX->Config.EnableAVX;
|
||||
const uint64_t SignalReturn = CTX->X86CodeGen.SignalReturn;
|
||||
|
||||
// On 64-bit the kernel sets up the siginfo_t and ucontext_t regardless of SA_SIGINFO set.
|
||||
// This allows the application to /always/ get the siginfo and ucontext even if it didn't set this flag.
|
||||
@@ -744,6 +862,12 @@ uint64_t Dispatcher::SetupFrame_x64(
|
||||
// ucontext_t
|
||||
// siginfo_t
|
||||
// FP state
|
||||
// Host stack location
|
||||
NewGuestSP -= sizeof(uint64_t);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(uint64_t));
|
||||
|
||||
uint64_t HostStackLocation = NewGuestSP;
|
||||
|
||||
if (IsAVXEnabled) {
|
||||
NewGuestSP -= sizeof(x86_64::xstate);
|
||||
NewGuestSP = AlignDown(NewGuestSP, alignof(x86_64::xstate));
|
||||
@@ -768,6 +892,8 @@ uint64_t Dispatcher::SetupFrame_x64(
|
||||
|
||||
FEXCore::x86_64::ucontext_t *guest_uctx = reinterpret_cast<FEXCore::x86_64::ucontext_t*>(UContextLocation);
|
||||
siginfo_t *guest_siginfo = reinterpret_cast<siginfo_t*>(SigInfoLocation);
|
||||
// Store where the host context lives in the guest stack.
|
||||
*(uint64_t*)HostStackLocation = (uint64_t)ContextBackup;
|
||||
|
||||
// We have extended float information
|
||||
guest_uctx->uc_flags = FEXCore::x86_64::UC_FP_XSTATE |
|
||||
@@ -779,7 +905,7 @@ uint64_t Dispatcher::SetupFrame_x64(
|
||||
auto *xstate = reinterpret_cast<x86_64::xstate*>(FPStateLocation);
|
||||
SetXStateInfo(xstate, IsAVXEnabled);
|
||||
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP] = Frame->State.rip;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_RIP] = ContextBackup->OriginalRIP;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_EFL] = eflags;
|
||||
guest_uctx->uc_mcontext.gregs[FEXCore::x86_64::FEX_REG_CSGSFS] = 0;
|
||||
|
||||
@@ -866,14 +992,14 @@ uint64_t Dispatcher::SetupFrame_x64(
|
||||
// The host is required to provide us a restorer.
|
||||
// If the guest didn't provide a restorer then the application should fail with a SIGSEGV.
|
||||
// TODO: Emulate SIGSEGV when the guest doesn't provide a restorer.
|
||||
// TODO: Support the guest's restorer. Required for libunwind.
|
||||
NewGuestSP -= 8;
|
||||
if (incomplete_guest_restorer_support) {
|
||||
if (GuestAction->restorer) {
|
||||
// TODO: Once we support the guest's restorer.
|
||||
*(uint64_t*)NewGuestSP = (uint64_t)GuestAction->restorer;
|
||||
}
|
||||
else {
|
||||
*(uint64_t*)NewGuestSP = SignalReturn;
|
||||
// XXX: Emulate SIGSEGV here
|
||||
// *(uint64_t*)NewGuestSP = SignalReturn;
|
||||
}
|
||||
|
||||
return NewGuestSP;
|
||||
@@ -889,14 +1015,6 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
++Thread->CurrentFrame->SignalHandlerRefCounter;
|
||||
|
||||
uint64_t OldPC = ArchHelpers::Context::GetPc(ucontext);
|
||||
// Set the new PC
|
||||
ArchHelpers::Context::SetPc(ucontext, AbsoluteLoopTopAddressFillSRA);
|
||||
// Set our state register to point to our guest thread data
|
||||
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
|
||||
|
||||
uint64_t OldGuestSP = Frame->State.gregs[X86State::REG_RSP];
|
||||
uint64_t NewGuestSP = OldGuestSP;
|
||||
|
||||
// Pulling from context here
|
||||
const bool Is64BitMode = CTX->Config.Is64BitMode;
|
||||
|
||||
@@ -912,14 +1030,7 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
// We need to spill SRA but only some of it, since some values have already been spilled
|
||||
// Lower 16 bits tells us which registers are already spilled to the context
|
||||
// So we ignore spilling those ones
|
||||
uint16_t NumRegisters = std::popcount(Frame->InSyscallInfo & 0xFFFF);
|
||||
if (NumRegisters >= 4) {
|
||||
// Unhandled case
|
||||
IgnoreMask = 0;
|
||||
}
|
||||
else {
|
||||
IgnoreMask = Frame->InSyscallInfo & 0xFFFF;
|
||||
}
|
||||
IgnoreMask = Frame->InSyscallInfo & 0xFFFF;
|
||||
}
|
||||
else {
|
||||
// We must spill everything
|
||||
@@ -945,6 +1056,9 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t OldGuestSP = Frame->State.gregs[X86State::REG_RSP];
|
||||
uint64_t NewGuestSP = OldGuestSP;
|
||||
|
||||
// altstack is only used if the signal handler was setup with SA_ONSTACK
|
||||
if (GuestAction->sa_flags & SA_ONSTACK) {
|
||||
// Additionally the altstack is only used if the enabled (SS_DISABLE flag is not set)
|
||||
@@ -967,7 +1081,7 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
siginfo_t *HostSigInfo = reinterpret_cast<siginfo_t*>(info);
|
||||
|
||||
// Backup where we think the RIP currently is
|
||||
ContextBackup->OriginalRIP = Frame->State.rip;
|
||||
ContextBackup->OriginalRIP = ReconstructRIPFromContext(Frame, ucontext);
|
||||
// Calculate eflags upfront.
|
||||
uint32_t eflags = 0;
|
||||
for (size_t i = 0; i < Core::CPUState::NUM_EFLAG_BITS; ++i) {
|
||||
@@ -998,6 +1112,11 @@ bool Dispatcher::HandleGuestSignal(FEXCore::Core::InternalThreadState *Thread, i
|
||||
Frame->State.FCW = 0x37F;
|
||||
Frame->State.FTW = 0xFFFF;
|
||||
|
||||
// Set the new PC
|
||||
ArchHelpers::Context::SetPc(ucontext, AbsoluteLoopTopAddressFillSRA);
|
||||
// Set our state register to point to our guest thread data
|
||||
ArchHelpers::Context::SetState(ucontext, reinterpret_cast<uint64_t>(Frame));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1085,7 +1204,7 @@ bool Dispatcher::HandleSignalPause(FEXCore::Core::InternalThreadState *Thread, i
|
||||
if (Thread->RunningEvents.ThreadSleeping) {
|
||||
// If the thread was sleeping then its idle counter was decremented
|
||||
// Reincrement it here to not break logic
|
||||
++Thread->CTX->IdleWaitRefCount;
|
||||
++static_cast<Context::ContextImpl*>(Thread->CTX)->IdleWaitRefCount;
|
||||
}
|
||||
|
||||
Thread->SignalReason.store(FEXCore::Core::SignalEvent::Nothing);
|
||||
@@ -1107,14 +1226,14 @@ bool Dispatcher::HandleSignalPause(FEXCore::Core::InternalThreadState *Thread, i
|
||||
}
|
||||
|
||||
uint64_t Dispatcher::GetCompileBlockPtr() {
|
||||
using ClassPtrType = void (FEXCore::Context::Context::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
|
||||
using ClassPtrType = void (FEXCore::Context::ContextImpl::*)(FEXCore::Core::CpuStateFrame *, uint64_t);
|
||||
union PtrCast {
|
||||
ClassPtrType ClassPtr;
|
||||
uintptr_t Data;
|
||||
};
|
||||
|
||||
PtrCast CompileBlockPtr;
|
||||
CompileBlockPtr.ClassPtr = &FEXCore::Context::Context::CompileBlockJit;
|
||||
CompileBlockPtr.ClassPtr = &FEXCore::Context::ContextImpl::CompileBlockJit;
|
||||
return CompileBlockPtr.Data;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ struct InternalThreadState;
|
||||
}
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
@@ -74,8 +74,8 @@ public:
|
||||
virtual size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) = 0;
|
||||
virtual size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) = 0;
|
||||
|
||||
static std::unique_ptr<Dispatcher> CreateX86(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
|
||||
static std::unique_ptr<Dispatcher> CreateArm64(FEXCore::Context::Context *CTX, const DispatcherConfig &Config);
|
||||
static std::unique_ptr<Dispatcher> CreateX86(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config);
|
||||
static std::unique_ptr<Dispatcher> CreateArm64(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config);
|
||||
|
||||
virtual void ExecuteDispatch(FEXCore::Core::CpuStateFrame *Frame) {
|
||||
DispatchPtr(Frame);
|
||||
@@ -86,17 +86,16 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &Config)
|
||||
Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &Config)
|
||||
: CTX {ctx}
|
||||
, config {Config}
|
||||
{}
|
||||
|
||||
uint64_t ReconstructRIPFromContext(FEXCore::Core::CpuStateFrame *Frame, void *ucontext) const;
|
||||
void RestoreFrame_x64(ArchHelpers::Context::ContextBackup* Context, FEXCore::Core::CpuStateFrame *Frame, void *ucontext);
|
||||
void RestoreFrame_ia32(ArchHelpers::Context::ContextBackup* Context, FEXCore::Core::CpuStateFrame *Frame, void *ucontext);
|
||||
void RestoreRTFrame_ia32(ArchHelpers::Context::ContextBackup* Context, FEXCore::Core::CpuStateFrame *Frame, void *ucontext);
|
||||
|
||||
const bool incomplete_guest_restorer_support = false;
|
||||
|
||||
///< Setup the signal frame for x64.
|
||||
uint64_t SetupFrame_x64(FEXCore::Core::InternalThreadState *Thread, ArchHelpers::Context::ContextBackup* ContextBackup, FEXCore::Core::CpuStateFrame *Frame,
|
||||
int Signal, siginfo_t *HostSigInfo, void *ucontext,
|
||||
@@ -160,10 +159,10 @@ protected:
|
||||
|
||||
virtual void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) {}
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
DispatcherConfig config;
|
||||
|
||||
static void SleepThread(FEXCore::Context::Context *ctx, FEXCore::Core::CpuStateFrame *Frame);
|
||||
static void SleepThread(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::CpuStateFrame *Frame);
|
||||
|
||||
static uint64_t GetCompileBlockPtr();
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace FEXCore::CPU {
|
||||
static constexpr size_t MAX_DISPATCHER_CODE_SIZE = 4096;
|
||||
#define STATE r14
|
||||
|
||||
X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config)
|
||||
X86Dispatcher::X86Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config)
|
||||
: Dispatcher(ctx, config)
|
||||
, Xbyak::CodeGenerator(MAX_DISPATCHER_CODE_SIZE,
|
||||
FEXCore::Allocator::mmap(nullptr, MAX_DISPATCHER_CODE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0),
|
||||
@@ -433,7 +433,7 @@ size_t X86Dispatcher::GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestR
|
||||
emit.mov(rax, reinterpret_cast<uint64_t>(CTX));
|
||||
|
||||
// If the value == 0 then we don't need to stop
|
||||
emit.cmp(dword [rax + (offsetof(FEXCore::Context::Context, Config.RunningMode))], 0);
|
||||
emit.cmp(dword [rax + (offsetof(FEXCore::Context::ContextImpl, Config.RunningMode))], 0);
|
||||
emit.je(RunBlock);
|
||||
{
|
||||
// Make sure RIP is syncronized to the context
|
||||
@@ -499,7 +499,7 @@ void X86Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Threa
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Dispatcher> Dispatcher::CreateX86(FEXCore::Context::Context *CTX, const DispatcherConfig &Config) {
|
||||
std::unique_ptr<Dispatcher> Dispatcher::CreateX86(FEXCore::Context::ContextImpl *CTX, const DispatcherConfig &Config) {
|
||||
return std::make_unique<X86Dispatcher>(CTX, Config);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,10 +5,6 @@
|
||||
#define XBYAK64
|
||||
#include <xbyak/xbyak.h>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core {
|
||||
struct InternalThreadState;
|
||||
}
|
||||
@@ -17,7 +13,7 @@ namespace FEXCore::CPU {
|
||||
|
||||
class X86Dispatcher final : public Dispatcher, public Xbyak::CodeGenerator {
|
||||
public:
|
||||
X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherConfig &config);
|
||||
X86Dispatcher(FEXCore::Context::ContextImpl *ctx, const DispatcherConfig &config);
|
||||
void InitThreadPointers(FEXCore::Core::InternalThreadState *Thread) override;
|
||||
size_t GenerateGDBPauseCheck(uint8_t *CodeBuffer, uint64_t GuestRIP) override;
|
||||
size_t GenerateInterpreterTrampoline(uint8_t *CodeBuffer) override;
|
||||
|
||||
+4
-5
@@ -79,7 +79,7 @@ static uint32_t MapVEXToReg(uint8_t vvvv, bool HasXMM) {
|
||||
}
|
||||
}
|
||||
|
||||
Decoder::Decoder(FEXCore::Context::Context *ctx)
|
||||
Decoder::Decoder(FEXCore::Context::ContextImpl *ctx)
|
||||
: CTX {ctx}
|
||||
, OSABI { ctx->SyscallHandler ? ctx->SyscallHandler->GetOSABI() : FEXCore::HLE::SyscallOSABI::OS_UNKNOWN }
|
||||
, PoolObject {ctx->FrontendAllocator, sizeof(FEXCore::X86Tables::DecodedInst) * DefaultDecodedBufferSize} {
|
||||
@@ -321,7 +321,6 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
const bool HasMODRM = !!(Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_MODRM);
|
||||
|
||||
const bool HasREX = !!(DecodeInst->Flags & DecodeFlags::FLAG_REX_PREFIX);
|
||||
const bool HasHighXMM = HAS_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_HIGH_XMM_REG);
|
||||
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
|
||||
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
|
||||
|
||||
@@ -444,7 +443,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
// ADDITIONALLY:
|
||||
// If there is a REX prefix then that allows extended GPR usage
|
||||
CurrentDest->Type = DecodedOperand::OpType::GPR;
|
||||
DecodeInst->Dest.Data.GPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100) || HasHighXMM;
|
||||
DecodeInst->Dest.Data.GPR.HighBits = (Is8BitDest && !HasREX && (Op & 0b111) >= 0b100);
|
||||
CurrentDest->Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_B ? 1 : 0, Op & 0b111, Is8BitDest, HasREX, false, false);
|
||||
|
||||
if (CurrentDest->Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
@@ -472,7 +471,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
|
||||
// Decode the GPR source first
|
||||
GPR.Type = DecodedOperand::OpType::GPR;
|
||||
GPR.Data.GPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX) || HasHighXMM;
|
||||
GPR.Data.GPR.HighBits = (GPR8Bit && ModRM.reg >= 0b100 && !HasREX);
|
||||
GPR.Data.GPR.GPR = MapModRMToReg(DecodeInst->Flags & DecodeFlags::FLAG_REX_XGPR_R ? 1 : 0, ModRM.reg, GPR8Bit, HasREX, HasXMMGPR, HasMMGPR);
|
||||
|
||||
if (GPR.Data.GPR.GPR == FEXCore::X86State::REG_INVALID)
|
||||
@@ -482,7 +481,7 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
// ModRM.Mod != 0b11 == Register-direct addressing
|
||||
if (ModRM.mod == 0b11) {
|
||||
NonGPR.Type = DecodedOperand::OpType::GPR;
|
||||
NonGPR.Data.GPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX) || HasHighXMM;
|
||||
NonGPR.Data.GPR.HighBits = (NonGPR8Bit && ModRM.rm >= 0b100 && !HasREX);
|
||||
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;
|
||||
|
||||
+3
-3
@@ -11,7 +11,7 @@
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::Frontend {
|
||||
@@ -25,7 +25,7 @@ public:
|
||||
bool HasInvalidInstruction{};
|
||||
};
|
||||
|
||||
Decoder(FEXCore::Context::Context *ctx);
|
||||
Decoder(FEXCore::Context::ContextImpl *ctx);
|
||||
~Decoder();
|
||||
void DecodeInstructionsAtEntry(uint8_t const* InstStream, uint64_t PC, std::function<void(uint64_t BlockEntry, uint64_t Start, uint64_t Length)> AddContainedCodePage);
|
||||
|
||||
@@ -52,7 +52,7 @@ private:
|
||||
bool L; // VEX.L bit (if set then 256 bit operation, if unset then scalar or 128-bit operation)
|
||||
};
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
const FEXCore::HLE::SyscallOSABI OSABI{};
|
||||
|
||||
bool DecodeInstruction(uint64_t PC);
|
||||
|
||||
+2
-2
@@ -68,11 +68,11 @@ void GdbServer::WaitForThreadWakeup() {
|
||||
ThreadBreakEvent.Wait();
|
||||
}
|
||||
|
||||
GdbServer::GdbServer(FEXCore::Context::Context *ctx) : CTX(ctx) {
|
||||
GdbServer::GdbServer(FEXCore::Context::ContextImpl *ctx) : CTX(ctx) {
|
||||
// Pass all signals by default
|
||||
std::fill(PassSignals.begin(), PassSignals.end(), true);
|
||||
|
||||
Context::SetExitHandler(ctx, [this](uint64_t ThreadId, FEXCore::Context::ExitReason ExitReason) {
|
||||
ctx->SetExitHandler([this](uint64_t ThreadId, FEXCore::Context::ExitReason ExitReason) {
|
||||
if (ExitReason == FEXCore::Context::ExitReason::EXIT_DEBUG) {
|
||||
this->Break(SIGTRAP);
|
||||
}
|
||||
|
||||
+3
-3
@@ -19,12 +19,12 @@ $end_info$
|
||||
namespace FEXCore {
|
||||
|
||||
namespace Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
class GdbServer {
|
||||
public:
|
||||
GdbServer(FEXCore::Context::Context *ctx);
|
||||
GdbServer(FEXCore::Context::ContextImpl *ctx);
|
||||
|
||||
// Public for threading
|
||||
void GdbServerLoop();
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
std::string readRegs();
|
||||
HandledPacketType readReg(const std::string& packet);
|
||||
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
std::unique_ptr<FEXCore::Threads::Thread> gdbServerThread;
|
||||
std::unique_ptr<std::iostream> CommsStream;
|
||||
std::mutex sendMutex;
|
||||
|
||||
@@ -17,22 +17,8 @@ $end_info$
|
||||
#include <unistd.h>
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
[[noreturn]]
|
||||
static void SignalReturn(FEXCore::Core::InternalThreadState *Thread, bool RT) {
|
||||
Thread->CTX->SignalThread(Thread, RT ? FEXCore::Core::SignalEvent::ReturnRT : FEXCore::Core::SignalEvent::Return);
|
||||
|
||||
LOGMAN_MSG_A_FMT("unreachable");
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
|
||||
|
||||
DEF_OP(SignalReturn) {
|
||||
auto Op = IROp->C<IR::IROp_SignalReturn>();
|
||||
|
||||
SignalReturn(Data->State, Op->IsRT);
|
||||
}
|
||||
|
||||
DEF_OP(CallbackReturn) {
|
||||
Data->State->CurrentFrame->Pointers.Interpreter.CallbackReturn(Data->State, Data->StackEntry);
|
||||
}
|
||||
@@ -93,7 +79,7 @@ DEF_OP(Syscall) {
|
||||
Args.Argument[j] = *GetSrc<uint64_t*>(Data->SSAData, Op->Header.Args[j]);
|
||||
}
|
||||
|
||||
uint64_t Res = FEXCore::Context::HandleSyscall(Data->State->CTX->SyscallHandler, Data->State->CurrentFrame, &Args);
|
||||
uint64_t Res = FEXCore::Context::HandleSyscall(static_cast<Context::ContextImpl*>(Data->State->CTX)->SyscallHandler, Data->State->CurrentFrame, &Args);
|
||||
GD = Res;
|
||||
}
|
||||
|
||||
@@ -128,7 +114,7 @@ DEF_OP(InlineSyscall) {
|
||||
DEF_OP(Thunk) {
|
||||
auto Op = IROp->C<IR::IROp_Thunk>();
|
||||
|
||||
auto thunkFn = Data->State->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
auto thunkFn = static_cast<Context::ContextImpl*>(Data->State->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
thunkFn(*GetSrc<void**>(Data->SSAData, Op->ArgPtr));
|
||||
}
|
||||
|
||||
@@ -144,7 +130,7 @@ DEF_OP(ValidateCode) {
|
||||
}
|
||||
|
||||
DEF_OP(ThreadRemoveCodeEntry) {
|
||||
Data->State->CTX->ThreadRemoveCodeEntryFromJit(Data->State->CurrentFrame, Data->CurrentEntry);
|
||||
static_cast<Context::ContextImpl*>(Data->State->CTX)->ThreadRemoveCodeEntryFromJit(Data->State->CurrentFrame, Data->CurrentEntry);
|
||||
}
|
||||
|
||||
DEF_OP(CPUID) {
|
||||
@@ -153,7 +139,7 @@ DEF_OP(CPUID) {
|
||||
const uint64_t Arg = *GetSrc<uint64_t*>(Data->SSAData, Op->Function);
|
||||
const uint64_t Leaf = *GetSrc<uint64_t*>(Data->SSAData, Op->Leaf);
|
||||
|
||||
auto Results = Data->State->CTX->CPUID.RunFunction(Arg, Leaf);
|
||||
auto Results = Data->State->CTX->RunCPUIDFunction(Arg, Leaf);
|
||||
memcpy(DstPtr, &Results, sizeof(uint32_t) * 4);
|
||||
}
|
||||
|
||||
|
||||
@@ -62,6 +62,23 @@ DEF_OP(VCastFromGPR) {
|
||||
memcpy(GDP, GetSrc<void*>(Data->SSAData, Op->Src), Op->Header.ElementSize);
|
||||
}
|
||||
|
||||
DEF_OP(VDupFromGPR) {
|
||||
const auto Op = IROp->C<IR::IROp_VDupFromGPR>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = IROp->ElementSize;
|
||||
const auto NumElements = OpSize / IROp->ElementSize;
|
||||
|
||||
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
|
||||
|
||||
const auto *Src = GetSrc<void*>(Data->SSAData, Op->Src);
|
||||
for (size_t i = 0; i < NumElements; i++) {
|
||||
memcpy(Tmp + (i * ElementSize), Src, ElementSize);
|
||||
}
|
||||
|
||||
memcpy(GDP, Tmp, sizeof(Tmp));
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
|
||||
[[nodiscard]] std::string GetName() override { return "Interpreter"; }
|
||||
|
||||
[[nodiscard]] void *CompileCode(uint64_t Entry,
|
||||
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
|
||||
FEXCore::IR::IRListView const *IR,
|
||||
FEXCore::Core::DebugData *DebugData,
|
||||
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
|
||||
@@ -35,7 +35,7 @@ public:
|
||||
|
||||
[[nodiscard]] bool NeedsOpDispatch() override { return true; }
|
||||
|
||||
static void InitializeSignalHandlers(FEXCore::Context::Context *CTX);
|
||||
static void InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX);
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
|
||||
@@ -49,7 +49,10 @@ InterpreterCore::InterpreterCore(Dispatcher *Dispatcher, FEXCore::Core::Internal
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
void InterpreterCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
void InterpreterCore::InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
return reinterpret_cast<Context::ContextImpl*>(Thread->CTX)->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
|
||||
}, true);
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGBUS, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
@@ -58,18 +61,21 @@ void InterpreterCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
|
||||
CPUBackend::CompiledCode InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
|
||||
|
||||
const auto IRSize = AlignUp(IR->GetInlineSize(), 16);
|
||||
const auto MaxSize = IRSize + Dispatcher::MaxInterpreterTrampolineSize + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
|
||||
|
||||
if ((BufferUsed + MaxSize) > CurrentCodeBuffer->Size) {
|
||||
ThreadState->CTX->ClearCodeCache(ThreadState);
|
||||
static_cast<Context::ContextImpl*>(ThreadState->CTX)->ClearCodeCache(ThreadState);
|
||||
}
|
||||
|
||||
const auto BufferStart = CurrentCodeBuffer->Ptr + BufferUsed;
|
||||
CPUBackend::CompiledCode CodeData{};
|
||||
|
||||
auto DestBuffer = BufferStart;
|
||||
const auto BufferStartOffset = BufferUsed;
|
||||
CodeData.BlockBegin = CodeData.BlockEntry = CurrentCodeBuffer->Ptr + BufferStartOffset;
|
||||
|
||||
auto DestBuffer = CodeData.BlockBegin;
|
||||
|
||||
if (GDBEnabled) {
|
||||
const auto GDBSize = Dispatch->GenerateGDBPauseCheck(DestBuffer, Entry);
|
||||
@@ -86,7 +92,9 @@ void *InterpreterCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR:
|
||||
DestBuffer += IRSize;
|
||||
BufferUsed += IRSize;
|
||||
|
||||
return BufferStart;
|
||||
CodeData.Size = BufferUsed - BufferStartOffset;
|
||||
|
||||
return CodeData;
|
||||
}
|
||||
|
||||
void InterpreterCore::ClearCache() {
|
||||
@@ -95,11 +103,11 @@ void InterpreterCore::ClearCache() {
|
||||
BufferUsed = 0;
|
||||
}
|
||||
|
||||
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
return std::make_unique<InterpreterCore>(ctx->Dispatcher.get(), Thread);
|
||||
}
|
||||
|
||||
void InitializeInterpreterSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
void InitializeInterpreterSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
|
||||
InterpreterCore::InitializeSignalHandlers(CTX);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <memory>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core {
|
||||
@@ -14,9 +14,9 @@ namespace FEXCore::CPU {
|
||||
class CPUBackend;
|
||||
struct DispatcherConfig;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::Context *ctx,
|
||||
[[nodiscard]] std::unique_ptr<CPUBackend> CreateInterpreterCore(FEXCore::Context::ContextImpl *ctx,
|
||||
FEXCore::Core::InternalThreadState *Thread);
|
||||
void InitializeInterpreterSignalHandlers(FEXCore::Context::Context *CTX);
|
||||
void InitializeInterpreterSignalHandlers(FEXCore::Context::ContextImpl *CTX);
|
||||
CPUBackendFeatures GetInterpreterBackendFeatures();
|
||||
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -113,7 +113,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
|
||||
|
||||
// Branch ops
|
||||
REGISTER_OP(SIGNALRETURN, SignalReturn);
|
||||
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
|
||||
REGISTER_OP(EXITFUNCTION, ExitFunction);
|
||||
REGISTER_OP(JUMP, Jump);
|
||||
@@ -128,6 +127,7 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
// Conversion ops
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(VDUPFROMGPR, VDupFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
|
||||
REGISTER_OP(FLOAT_FTOF, Float_FToF);
|
||||
REGISTER_OP(VECTOR_STOF, Vector_SToF);
|
||||
@@ -154,6 +154,7 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(STOREMEM, StoreMem);
|
||||
REGISTER_OP(LOADMEMTSO, LoadMem);
|
||||
REGISTER_OP(STOREMEMTSO, StoreMem);
|
||||
REGISTER_OP(MEMSET, MemSet);
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
|
||||
REGISTER_OP(CACHELINEZERO, CacheLineZero);
|
||||
@@ -222,6 +223,8 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(VZIP2, VZip);
|
||||
REGISTER_OP(VUNZIP, VUnZip);
|
||||
REGISTER_OP(VUNZIP2, VUnZip);
|
||||
REGISTER_OP(VTRN, VTrn);
|
||||
REGISTER_OP(VTRN2, VTrn);
|
||||
REGISTER_OP(VBSL, VBSL);
|
||||
REGISTER_OP(VCMPEQ, VCMPEQ);
|
||||
REGISTER_OP(VCMPEQZ, VCMPEQZ);
|
||||
|
||||
@@ -142,7 +142,6 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(AtomicFetchNeg);
|
||||
|
||||
///< Branch ops
|
||||
DEF_OP(SignalReturn);
|
||||
DEF_OP(CallbackReturn);
|
||||
DEF_OP(ExitFunction);
|
||||
DEF_OP(Jump);
|
||||
@@ -157,6 +156,7 @@ namespace FEXCore::CPU {
|
||||
///< Conversion ops
|
||||
DEF_OP(VInsGPR);
|
||||
DEF_OP(VCastFromGPR);
|
||||
DEF_OP(VDupFromGPR);
|
||||
DEF_OP(Float_FromGPR_S);
|
||||
DEF_OP(Float_FToF);
|
||||
DEF_OP(Vector_SToF);
|
||||
@@ -181,6 +181,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(StoreFlag);
|
||||
DEF_OP(LoadMem);
|
||||
DEF_OP(StoreMem);
|
||||
DEF_OP(MemSet);
|
||||
DEF_OP(CacheLineClear);
|
||||
DEF_OP(CacheLineClean);
|
||||
DEF_OP(CacheLineZero);
|
||||
@@ -242,6 +243,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(VSMax);
|
||||
DEF_OP(VZip);
|
||||
DEF_OP(VUnZip);
|
||||
DEF_OP(VTrn);
|
||||
DEF_OP(VBSL);
|
||||
DEF_OP(VCMPEQ);
|
||||
DEF_OP(VCMPEQZ);
|
||||
|
||||
@@ -288,6 +288,111 @@ DEF_OP(StoreMem) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(MemSet) {
|
||||
const auto Op = IROp->C<IR::IROp_MemSet>();
|
||||
const int32_t Size = Op->Size;
|
||||
|
||||
char *MemData = *GetSrc<char **>(Data->SSAData, Op->Addr);
|
||||
const auto Value = *GetSrc<uint64_t*>(Data->SSAData, Op->Value);
|
||||
const auto Length = *GetSrc<uint64_t*>(Data->SSAData, Op->Length);
|
||||
const auto Direction = *GetSrc<uint8_t*>(Data->SSAData, Op->Direction);
|
||||
|
||||
auto MemSetElements = [](auto* Memory, uint64_t Value, size_t Length) {
|
||||
for (size_t i = 0; i < Length; ++i) {
|
||||
Memory[i] = Value;
|
||||
}
|
||||
};
|
||||
|
||||
auto MemSetElementsInverse = [](auto* Memory, uint64_t Value, size_t Length) {
|
||||
for (size_t i = 0; i < Length; ++i) {
|
||||
Memory[-i] = Value;
|
||||
}
|
||||
};
|
||||
|
||||
if (Direction == 0) { // Forward
|
||||
if (Op->IsAtomic) {
|
||||
switch (Size) {
|
||||
case 1:
|
||||
MemSetElements(reinterpret_cast<std::atomic<uint8_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
case 2:
|
||||
MemSetElements(reinterpret_cast<std::atomic<uint16_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
case 4:
|
||||
MemSetElements(reinterpret_cast<std::atomic<uint32_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
case 8:
|
||||
MemSetElements(reinterpret_cast<std::atomic<uint64_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (Size) {
|
||||
case 1:
|
||||
MemSetElements(reinterpret_cast<uint8_t*>(MemData), Value, Length);
|
||||
break;
|
||||
case 2:
|
||||
MemSetElements(reinterpret_cast<uint16_t*>(MemData), Value, Length);
|
||||
break;
|
||||
case 4:
|
||||
MemSetElements(reinterpret_cast<uint32_t*>(MemData), Value, Length);
|
||||
break;
|
||||
case 8:
|
||||
MemSetElements(reinterpret_cast<uint64_t*>(MemData), Value, Length);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
GD = reinterpret_cast<uint64_t>(MemData + (Length * Size));
|
||||
}
|
||||
else { // Backward
|
||||
if (Op->IsAtomic) {
|
||||
switch (Size) {
|
||||
case 1:
|
||||
MemSetElementsInverse(reinterpret_cast<std::atomic<uint8_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
case 2:
|
||||
MemSetElementsInverse(reinterpret_cast<std::atomic<uint16_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
case 4:
|
||||
MemSetElementsInverse(reinterpret_cast<std::atomic<uint32_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
case 8:
|
||||
MemSetElementsInverse(reinterpret_cast<std::atomic<uint64_t>*>(MemData), Value, Length);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (Size) {
|
||||
case 1:
|
||||
MemSetElementsInverse(reinterpret_cast<uint8_t*>(MemData), Value, Length);
|
||||
break;
|
||||
case 2:
|
||||
MemSetElementsInverse(reinterpret_cast<uint16_t*>(MemData), Value, Length);
|
||||
break;
|
||||
case 4:
|
||||
MemSetElementsInverse(reinterpret_cast<uint32_t*>(MemData), Value, Length);
|
||||
break;
|
||||
case 8:
|
||||
MemSetElementsInverse(reinterpret_cast<uint64_t*>(MemData), Value, Length);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
}
|
||||
GD = reinterpret_cast<uint64_t>(MemData - (Length * Size));
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClear) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineClear>();
|
||||
|
||||
|
||||
@@ -902,6 +902,67 @@ DEF_OP(VZip) {
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
}
|
||||
|
||||
DEF_OP(VTrn) {
|
||||
const auto Op = IROp->C<IR::IROp_VTrn>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
void *Src1 = GetSrc<void*>(Data->SSAData, Op->VectorLower);
|
||||
void *Src2 = GetSrc<void*>(Data->SSAData, Op->VectorUpper);
|
||||
uint8_t Tmp[Core::CPUState::XMM_AVX_REG_SIZE]{};
|
||||
const uint8_t ElementSize = Op->Header.ElementSize;
|
||||
uint8_t Elements = OpSize / ElementSize;
|
||||
const uint8_t BaseOffset = IROp->Op == IR::OP_VTRN2 ? 1 : 0;
|
||||
Elements >>= 1;
|
||||
|
||||
switch (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*2] = Src1_d[i*2 + BaseOffset];
|
||||
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
|
||||
}
|
||||
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*2] = Src1_d[i*2 + BaseOffset];
|
||||
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
|
||||
}
|
||||
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*2] = Src1_d[i*2 + BaseOffset];
|
||||
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
|
||||
}
|
||||
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*2] = Src1_d[i*2 + BaseOffset];
|
||||
Dst_d[i*2+1] = Src2_d[i*2 + BaseOffset];
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
|
||||
break;
|
||||
}
|
||||
|
||||
memcpy(GDP, Tmp, OpSize);
|
||||
}
|
||||
|
||||
DEF_OP(VUnZip) {
|
||||
const auto Op = IROp->C<IR::IROp_VUnZip>();
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
@@ -964,7 +1025,9 @@ DEF_OP(VUnZip) {
|
||||
}
|
||||
|
||||
DEF_OP(VBSL) {
|
||||
auto Op = IROp->C<IR::IROp_VBSL>();
|
||||
const auto Op = IROp->C<IR::IROp_VBSL>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Src1 = *GetSrc<InterpVector256*>(Data->SSAData, Op->VectorMask);
|
||||
const auto Src2 = *GetSrc<InterpVector256*>(Data->SSAData, Op->VectorTrue);
|
||||
const auto Src3 = *GetSrc<InterpVector256*>(Data->SSAData, Op->VectorFalse);
|
||||
@@ -974,7 +1037,8 @@ DEF_OP(VBSL) {
|
||||
.Upper = (Src2.Upper & Src1.Upper) | (Src3.Upper & ~Src1.Upper),
|
||||
};
|
||||
|
||||
memcpy(GDP, &Tmp, sizeof(Tmp));
|
||||
memset(GDP, 0, sizeof(InterpVector256));
|
||||
memcpy(GDP, &Tmp, OpSize);
|
||||
}
|
||||
|
||||
DEF_OP(VCMPEQ) {
|
||||
|
||||
@@ -262,13 +262,13 @@ DEF_OP(MulH) {
|
||||
const auto Src2 = GetReg(Op->Src2.ID());
|
||||
|
||||
if (OpSize == 4) {
|
||||
sxtw(TMP1, Src1);
|
||||
sxtw(TMP2, Src2);
|
||||
sxtw(TMP1, Src1.W());
|
||||
sxtw(TMP2, Src2.W());
|
||||
mul(ARMEmitter::Size::i32Bit, Dst, TMP1, TMP2);
|
||||
ubfx(ARMEmitter::Size::i32Bit, Dst, Dst, 32, 32);
|
||||
}
|
||||
else {
|
||||
smulh(Dst, Src1, Src2);
|
||||
smulh(Dst.X(), Src1.X(), Src2.X());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ DEF_OP(UMulH) {
|
||||
ubfx(ARMEmitter::Size::i64Bit, Dst, Dst, 32, 32);
|
||||
}
|
||||
else {
|
||||
umulh(Dst, Src1, Src2);
|
||||
umulh(Dst.X(), Src1.X(), Src2.X());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -610,7 +610,7 @@ DEF_OP(LDiv) {
|
||||
case 4: {
|
||||
mov(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 32, 32);
|
||||
sxtw(TMP2, Divisor);
|
||||
sxtw(TMP2, Divisor.W());
|
||||
sdiv(EmitSize, Dst, TMP1, TMP2);
|
||||
break;
|
||||
}
|
||||
@@ -744,7 +744,7 @@ DEF_OP(LRem) {
|
||||
case 4: {
|
||||
mov(EmitSize, TMP1, Lower);
|
||||
bfi(EmitSize, TMP1, Upper, 32, 32);
|
||||
sxtw(TMP3, Divisor);
|
||||
sxtw(TMP3, Divisor.W());
|
||||
sdiv(EmitSize, TMP2, TMP1, TMP3);
|
||||
msub(EmitSize, Dst, TMP2, TMP3, TMP1);
|
||||
break;
|
||||
@@ -1173,8 +1173,8 @@ DEF_OP(VExtractToGPR) {
|
||||
// Inverting our dedicated predicate for 128-bit operations selects
|
||||
// all of the top lanes. We can then compact those into a temporary.
|
||||
const auto CompactPred = ARMEmitter::PReg::p0;
|
||||
not_(CompactPred, PRED_TMP_32B, PRED_TMP_16B);
|
||||
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1, CompactPred, Vector);
|
||||
not_(CompactPred, PRED_TMP_32B.Zeroing(), PRED_TMP_16B);
|
||||
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), CompactPred, Vector.Z());
|
||||
|
||||
// Sanitize the zero-based index to work on the now-moved
|
||||
// upper half of the vector.
|
||||
|
||||
@@ -27,7 +27,7 @@ void Arm64JITCore::InsertNamedThunkRelocation(ARMEmitter::Register Reg, const IR
|
||||
MoveABI.NamedThunkMove.Header.Type = FEXCore::CPU::RelocationTypes::RELOC_NAMED_THUNK_MOVE;
|
||||
// Offset is the offset from the entrypoint of the block
|
||||
auto CurrentCursor = GetCursorAddress<uint8_t *>();
|
||||
MoveABI.NamedThunkMove.Offset = CurrentCursor - GuestEntry;
|
||||
MoveABI.NamedThunkMove.Offset = CurrentCursor - CodeData.BlockBegin;
|
||||
MoveABI.NamedThunkMove.Symbol = Sum;
|
||||
MoveABI.NamedThunkMove.RegisterIndex = Reg.Idx();
|
||||
|
||||
@@ -58,7 +58,7 @@ Arm64JITCore::NamedSymbolLiteralPair Arm64JITCore::InsertNamedSymbolLiteral(FEXC
|
||||
void Arm64JITCore::PlaceNamedSymbolLiteral(NamedSymbolLiteralPair &Lit) {
|
||||
// Offset is the offset from the entrypoint of the block
|
||||
auto CurrentCursor = GetCursorAddress<uint8_t *>();
|
||||
Lit.MoveABI.NamedSymbolLiteral.Offset = CurrentCursor - GuestEntry;
|
||||
Lit.MoveABI.NamedSymbolLiteral.Offset = CurrentCursor - CodeData.BlockBegin;
|
||||
|
||||
Bind(&Lit.Loc);
|
||||
dc64(Lit.Lit);
|
||||
@@ -70,7 +70,7 @@ void Arm64JITCore::InsertGuestRIPMove(ARMEmitter::Register Reg, uint64_t Constan
|
||||
MoveABI.GuestRIPMove.Header.Type = FEXCore::CPU::RelocationTypes::RELOC_GUEST_RIP_MOVE;
|
||||
// Offset is the offset from the entrypoint of the block
|
||||
auto CurrentCursor = GetCursorAddress<uint8_t *>();
|
||||
MoveABI.GuestRIPMove.Offset = CurrentCursor - GuestEntry;
|
||||
MoveABI.GuestRIPMove.Offset = CurrentCursor - CodeData.BlockBegin;
|
||||
MoveABI.GuestRIPMove.GuestRIP = Constant;
|
||||
MoveABI.GuestRIPMove.RegisterIndex = Reg.Idx();
|
||||
|
||||
|
||||
+28
-38
@@ -20,23 +20,6 @@ $end_info$
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
|
||||
|
||||
DEF_OP(SignalReturn) {
|
||||
auto Op = IROp->C<IR::IROp_SignalReturn>();
|
||||
|
||||
// First we must reset the stack
|
||||
ResetStack();
|
||||
|
||||
// Now branch to our signal return helper
|
||||
// This can't be a direct branch since the code needs to live at a constant location
|
||||
if (Op->IsRT) {
|
||||
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandlerRT));
|
||||
}
|
||||
else {
|
||||
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler));
|
||||
}
|
||||
br(ARMEmitter::Reg::r0);
|
||||
}
|
||||
|
||||
DEF_OP(CallbackReturn) {
|
||||
// spill back to CTX
|
||||
SpillStaticRegs();
|
||||
@@ -184,14 +167,23 @@ DEF_OP(Syscall) {
|
||||
FEXCore::IR::SyscallFlags Flags = Op->Flags;
|
||||
PushDynamicRegsAndLR(TMP1);
|
||||
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) != FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) {
|
||||
SpillStaticRegs();
|
||||
}
|
||||
else {
|
||||
uint32_t GPRSpillMask = ~0U;
|
||||
uint32_t FPRSpillMask = ~0U;
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) == FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) {
|
||||
// Need to spill all caller saved registers still
|
||||
SpillStaticRegs(true, CALLER_GPR_MASK, CALLER_FPR_MASK);
|
||||
GPRSpillMask = CALLER_GPR_MASK;
|
||||
FPRSpillMask = CALLER_FPR_MASK;
|
||||
}
|
||||
|
||||
SpillStaticRegs(true, GPRSpillMask, FPRSpillMask);
|
||||
|
||||
// Now that we are spilled, store in the state that we are in a syscall
|
||||
// Still without overwriting registers that matter
|
||||
// 16bit LoadConstant to be a single instruction
|
||||
// This gives the signal handler a value to check to see if we are in a syscall at all
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, GPRSpillMask & 0xFFFF);
|
||||
str(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
|
||||
|
||||
uint64_t SPOffset = AlignUp(FEXCore::HLE::SyscallArguments::MAX_ARGS * 8, 16);
|
||||
sub(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset);
|
||||
for (uint32_t i = 0; i < FEXCore::HLE::SyscallArguments::MAX_ARGS; ++i) {
|
||||
@@ -213,19 +205,17 @@ DEF_OP(Syscall) {
|
||||
|
||||
add(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::rsp, ARMEmitter::Reg::rsp, SPOffset);
|
||||
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY) != FEXCore::IR::SyscallFlags::NOSYNCSTATEONENTRY &&
|
||||
(Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
|
||||
FillStaticRegs();
|
||||
}
|
||||
else {
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
|
||||
// Result is now in x0
|
||||
// Fix the stack and any values that were stepped on
|
||||
FillStaticRegs(true, CALLER_GPR_MASK, CALLER_FPR_MASK);
|
||||
}
|
||||
FillStaticRegs(true, GPRSpillMask, FPRSpillMask);
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
// Now the registers we've spilled are back in their original host registers
|
||||
// We can safely claim we are no longer in a syscall
|
||||
str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
|
||||
|
||||
PopDynamicRegsAndLR();
|
||||
|
||||
if ((Flags & FEXCore::IR::SyscallFlags::NORETURN) != FEXCore::IR::SyscallFlags::NORETURN) {
|
||||
// Move result to its destination register
|
||||
mov(ARMEmitter::Size::i64Bit, GetReg(Node), ARMEmitter::Reg::r0);
|
||||
}
|
||||
@@ -255,9 +245,9 @@ DEF_OP(InlineSyscall) {
|
||||
if (Op->Header.Args[i].IsInvalid()) break;
|
||||
|
||||
auto Reg = GetReg(Op->Header.Args[i].ID());
|
||||
if (Reg.Idx() == ARMEmitter::Reg::r8.Idx() ||
|
||||
Reg.Idx() == ARMEmitter::Reg::r4.Idx() ||
|
||||
Reg.Idx() == ARMEmitter::Reg::r5.Idx()) {
|
||||
if (Reg == ARMEmitter::Reg::r8 ||
|
||||
Reg == ARMEmitter::Reg::r4 ||
|
||||
Reg == ARMEmitter::Reg::r5) {
|
||||
|
||||
SpillMask |= (1U << Reg.Idx());
|
||||
Intersects = true;
|
||||
@@ -288,13 +278,13 @@ DEF_OP(InlineSyscall) {
|
||||
// In the case of intersection with x4, x5, or x8 then these are currently SRA
|
||||
// for registers RAX, RBX, and RSI. Which have just been spilled
|
||||
// Just load back from the context. Could be slightly smarter but this is fairly uncommon
|
||||
if (Reg.Idx() == FEXCore::ARMEmitter::Reg::r8.Idx()) {
|
||||
if (Reg == ARMEmitter::Reg::r8) {
|
||||
ldr(EmitSubSize, RegArgs[i].R(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RSI]));
|
||||
}
|
||||
else if (Reg.Idx() == FEXCore::ARMEmitter::Reg::r4.Idx()) {
|
||||
else if (Reg == ARMEmitter::Reg::r4) {
|
||||
ldr(EmitSubSize, RegArgs[i].R(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RAX]));
|
||||
}
|
||||
else if (Reg.Idx() == FEXCore::ARMEmitter::Reg::r5.Idx()) {
|
||||
else if (Reg == ARMEmitter::Reg::r5) {
|
||||
ldr(EmitSubSize, RegArgs[i].R(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.gregs[X86State::REG_RBX]));
|
||||
}
|
||||
else {
|
||||
@@ -341,7 +331,7 @@ DEF_OP(Thunk) {
|
||||
|
||||
mov(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, GetReg(Op->ArgPtr.ID()));
|
||||
|
||||
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
auto thunkFn = static_cast<Context::ContextImpl*>(ThreadState->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r2, (uintptr_t)thunkFn);
|
||||
#ifdef VIXL_SIMULATOR
|
||||
GenerateIndirectRuntimeCall<void, void*, void*>(ARMEmitter::Reg::r2);
|
||||
|
||||
@@ -55,7 +55,7 @@ DEF_OP(VInsGPR) {
|
||||
// Move the upper lane down for the insertion.
|
||||
const auto CompactPred = ARMEmitter::PReg::p0;
|
||||
not_(CompactPred, PRED_TMP_32B.Zeroing(), PRED_TMP_16B);
|
||||
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), CompactPred, DestVector);
|
||||
compact(ARMEmitter::SubRegSize::i64Bit, VTMP1.Z(), CompactPred, DestVector.Z());
|
||||
}
|
||||
|
||||
// Put data in place for destructive SPLICE below.
|
||||
@@ -108,6 +108,32 @@ DEF_OP(VCastFromGPR) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VDupFromGPR) {
|
||||
const auto Op = IROp->C<IR::IROp_VDupFromGPR>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Src = GetReg(Op->Src.ID());
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = IROp->ElementSize;
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 8 || ElementSize == 4 || ElementSize == 2 || ElementSize == 1,
|
||||
"Unexpected {} element size: {}", __func__, ElementSize);
|
||||
|
||||
const auto SubEmitSize =
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 1 ? ARMEmitter::SubRegSize::i8Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
dup(SubEmitSize, Dst.Z(), Src);
|
||||
} else {
|
||||
dup(SubEmitSize, Dst.Q(), Src);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
const auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
|
||||
|
||||
@@ -199,7 +225,7 @@ DEF_OP(Vector_FToZS) {
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B;
|
||||
fcvtzs(Dst, SubEmitSize, Mask.Merging(), Vector, SubEmitSize);
|
||||
fcvtzs(Dst.Z(), SubEmitSize, Mask.Merging(), Vector.Z(), SubEmitSize);
|
||||
} else {
|
||||
fcvtzs(SubEmitSize, Dst.Q(), Vector.Q());
|
||||
}
|
||||
@@ -222,8 +248,8 @@ DEF_OP(Vector_FToS) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B;
|
||||
frinti(SubEmitSize, Dst, Mask.Merging(), Vector);
|
||||
fcvtzs(Dst, SubEmitSize, Mask.Merging(), Dst, SubEmitSize);
|
||||
frinti(SubEmitSize, Dst.Z(), Mask.Merging(), Vector.Z());
|
||||
fcvtzs(Dst.Z(), SubEmitSize, Mask.Merging(), Dst.Z(), SubEmitSize);
|
||||
} else {
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
@@ -276,12 +302,12 @@ DEF_OP(Vector_FToF) {
|
||||
break;
|
||||
}
|
||||
case 0x0204: { // Half <- Float
|
||||
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i16Bit, Dst, Mask, Vector);
|
||||
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i16Bit, Dst.Z(), Mask, Vector.Z());
|
||||
uzp2(FEXCore::ARMEmitter::SubRegSize::i16Bit, Dst.Z(), Dst.Z(), Dst.Z());
|
||||
break;
|
||||
}
|
||||
case 0x0408: { // Float <- Double
|
||||
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i32Bit, Dst, Mask, Vector);
|
||||
fcvtnt(FEXCore::ARMEmitter::SubRegSize::i32Bit, Dst.Z(), Mask, Vector.Z());
|
||||
uzp2(FEXCore::ARMEmitter::SubRegSize::i32Bit, Dst.Z(), Dst.Z(), Dst.Z());
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,37 +17,73 @@ DEF_OP(AESImc) {
|
||||
}
|
||||
|
||||
DEF_OP(AESEnc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEnc>();
|
||||
const auto Op = IROp->C<IR::IROp_VAESEnc>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Key = GetVReg(Op->Key.ID());
|
||||
const auto State = GetVReg(Op->State.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
|
||||
"Currently only supports 128-bit operations.");
|
||||
|
||||
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
|
||||
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
|
||||
mov(VTMP1.Q(), State.Q());
|
||||
aese(VTMP1, VTMP2);
|
||||
aesmc(VTMP1, VTMP1);
|
||||
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
|
||||
eor(Dst.Q(), VTMP1.Q(), Key.Q());
|
||||
}
|
||||
|
||||
DEF_OP(AESEncLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEncLast>();
|
||||
const auto Op = IROp->C<IR::IROp_VAESEncLast>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Key = GetVReg(Op->Key.ID());
|
||||
const auto State = GetVReg(Op->State.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
|
||||
"Currently only supports 128-bit operations.");
|
||||
|
||||
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
|
||||
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
|
||||
mov(VTMP1.Q(), State.Q());
|
||||
aese(VTMP1, VTMP2);
|
||||
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
|
||||
eor(Dst.Q(), VTMP1.Q(), Key.Q());
|
||||
}
|
||||
|
||||
DEF_OP(AESDec) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDec>();
|
||||
const auto Op = IROp->C<IR::IROp_VAESDec>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Key = GetVReg(Op->Key.ID());
|
||||
const auto State = GetVReg(Op->State.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
|
||||
"Currently only supports 128-bit operations.");
|
||||
|
||||
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
|
||||
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
|
||||
mov(VTMP1.Q(), State.Q());
|
||||
aesd(VTMP1, VTMP2);
|
||||
aesimc(VTMP1, VTMP1);
|
||||
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
|
||||
eor(Dst.Q(), VTMP1.Q(), Key.Q());
|
||||
}
|
||||
|
||||
DEF_OP(AESDecLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDecLast>();
|
||||
const auto Op = IROp->C<IR::IROp_VAESDecLast>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Key = GetVReg(Op->Key.ID());
|
||||
const auto State = GetVReg(Op->State.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
|
||||
"Currently only supports 128-bit operations.");
|
||||
|
||||
eor(VTMP2.Q(), VTMP2.Q(), VTMP2.Q());
|
||||
mov(VTMP1.Q(), GetVReg(Op->State.ID()).Q());
|
||||
mov(VTMP1.Q(), State.Q());
|
||||
aesd(VTMP1, VTMP2);
|
||||
eor(GetVReg(Node).Q(), VTMP1.Q(), GetVReg(Op->Key.ID()).Q());
|
||||
eor(Dst.Q(), VTMP1.Q(), Key.Q());
|
||||
}
|
||||
|
||||
DEF_OP(AESKeyGenAssist) {
|
||||
@@ -101,18 +137,22 @@ DEF_OP(CRC32) {
|
||||
crc32cw(Dst.W(), Src1.W(), Src2.W());
|
||||
break;
|
||||
case 8:
|
||||
crc32cx(Dst, Src1, Src2);
|
||||
crc32cx(Dst.X(), Src1.X(), Src2.X());
|
||||
break;
|
||||
default: LOGMAN_MSG_A_FMT("Unknown CRC32 size: {}", Op->SrcSize);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(PCLMUL) {
|
||||
auto Op = IROp->C<IR::IROp_PCLMUL>();
|
||||
const auto Op = IROp->C<IR::IROp_PCLMUL>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
auto Dst = GetVReg(Node);
|
||||
auto Src1 = GetVReg(Op->Src1.ID());
|
||||
auto Src2 = GetVReg(Op->Src2.ID());
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Src1 = GetVReg(Op->Src1.ID());
|
||||
const auto Src2 = GetVReg(Op->Src2.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(OpSize == Core::CPUState::XMM_SSE_REG_SIZE,
|
||||
"Currently only supports 128-bit operations.");
|
||||
|
||||
switch (Op->Selector) {
|
||||
case 0b00000000:
|
||||
|
||||
+59
-27
@@ -484,7 +484,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
|
||||
FEXCore::ARMEmitter::Emitter::ClearICache((void*)branch, 24);
|
||||
|
||||
// Add de-linking handler
|
||||
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [branch, LinkerAddress]{
|
||||
Context::ContextImpl::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [branch, LinkerAddress]{
|
||||
FEXCore::ARMEmitter::Emitter emit((uint8_t*)(branch), 24);
|
||||
FEXCore::ARMEmitter::ForwardLabel l_BranchHost;
|
||||
emit.ldr(FEXCore::ARMEmitter::XReg::x0, &l_BranchHost);
|
||||
@@ -498,7 +498,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
|
||||
record[0] = HostCode;
|
||||
|
||||
// Add de-linking handler
|
||||
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
Context::ContextImpl::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
record[0] = LinkerAddress;
|
||||
});
|
||||
}
|
||||
@@ -509,7 +509,7 @@ static uint64_t Arm64JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Fram
|
||||
void Arm64JITCore::Op_NoOp(IR::IROp_Header const *IROp, IR::NodeID Node) {
|
||||
}
|
||||
|
||||
Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
|
||||
, Arm64Emitter(ctx, 0)
|
||||
, HostSupportsSVE{ctx->HostFeatures.SupportsAVX}
|
||||
@@ -543,7 +543,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
|
||||
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
|
||||
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
|
||||
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
|
||||
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadRemoveCodeEntryFromJit);
|
||||
Common.CPUIDObj = reinterpret_cast<uint64_t>(&CTX->CPUID);
|
||||
|
||||
{
|
||||
@@ -553,7 +553,7 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
|
||||
Common.SyscallHandlerObj = reinterpret_cast<uint64_t>(CTX->SyscallHandler);
|
||||
Common.SyscallHandlerFunc = reinterpret_cast<uint64_t>(FEXCore::Context::HandleSyscall);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::Context::ThreadExitFunctionLink<Arm64JITCore_ExitFunctionLink>);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadExitFunctionLink<Arm64JITCore_ExitFunctionLink>);
|
||||
|
||||
|
||||
// Fill in the fallback handlers
|
||||
@@ -572,9 +572,9 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
return Thread->CTX->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
|
||||
return reinterpret_cast<Context::ContextImpl*>(Thread->CTX)->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
|
||||
}, true);
|
||||
|
||||
#ifdef _M_ARM_64
|
||||
@@ -584,7 +584,7 @@ void Arm64JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return FEXCore::ArchHelpers::Arm64::HandleSIGBUS(Thread->CTX->Config.ParanoidTSO(), Signal, info, ucontext);
|
||||
return FEXCore::ArchHelpers::Arm64::HandleSIGBUS(static_cast<Context::ContextImpl*>(Thread->CTX)->Config.ParanoidTSO(), Signal, info, ucontext);
|
||||
}, true);
|
||||
#endif
|
||||
}
|
||||
@@ -656,7 +656,7 @@ bool Arm64JITCore::IsGPR(IR::NodeID Node) const {
|
||||
return Class == IR::GPRClass || Class == IR::GPRFixedClass;
|
||||
}
|
||||
|
||||
void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
CPUBackend::CompiledCode Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
FEXCore::IR::IRListView const *IR,
|
||||
FEXCore::Core::DebugData *DebugData,
|
||||
FEXCore::IR::RegisterAllocationData *RAData,
|
||||
@@ -669,6 +669,21 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
this->Entry = Entry;
|
||||
this->RAData = RAData;
|
||||
this->DebugData = DebugData;
|
||||
this->IR = IR;
|
||||
|
||||
// Fairly excessive buffer range to make sure we don't overflow
|
||||
uint32_t BufferRange = SSACount * 16 + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
|
||||
if ((GetCursorOffset() + BufferRange) > CurrentCodeBuffer->Size) {
|
||||
CTX->ClearCodeCache(ThreadState);
|
||||
}
|
||||
|
||||
CodeData.BlockBegin = GetCursorAddress<uint8_t*>();
|
||||
|
||||
// Put the code header at the start of the data block.
|
||||
ARMEmitter::BackwardLabel JITCodeHeaderLabel{};
|
||||
Bind(&JITCodeHeaderLabel);
|
||||
JITCodeHeader *CodeHeader = GetCursorAddress<JITCodeHeader *>();
|
||||
CursorIncrement(sizeof(JITCodeHeader));
|
||||
|
||||
#ifdef VIXL_DISASSEMBLER
|
||||
const auto DisasmBegin = GetCursorAddress<const vixl::aarch64::Instruction*>();
|
||||
@@ -678,14 +693,6 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r0, Entry);
|
||||
#endif
|
||||
|
||||
this->IR = IR;
|
||||
|
||||
// Fairly excessive buffer range to make sure we don't overflow
|
||||
uint32_t BufferRange = SSACount * 16 + GDBEnabled * Dispatcher::MaxGDBPauseCheckSize;
|
||||
if ((GetCursorOffset() + BufferRange) > CurrentCodeBuffer->Size) {
|
||||
CTX->ClearCodeCache(ThreadState);
|
||||
}
|
||||
|
||||
// AAPCS64
|
||||
// r30 = LR
|
||||
// r29 = FP
|
||||
@@ -706,10 +713,15 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
// X1-X3 = Temp
|
||||
// X4-r18 = RA
|
||||
|
||||
GuestEntry = GetCursorAddress<uint8_t *>();
|
||||
CodeData.BlockEntry = GetCursorAddress<uint8_t*>();
|
||||
|
||||
// Get the address of the JITCodeHeader and store in to the core state.
|
||||
// Two instruction cost, each 1 cycle.
|
||||
adr(TMP1, &JITCodeHeaderLabel);
|
||||
str(TMP1, STATE, offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader));
|
||||
|
||||
if (GDBEnabled) {
|
||||
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(GuestEntry, Entry);
|
||||
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(CodeData.BlockEntry, Entry);
|
||||
CursorIncrement(GDBSize);
|
||||
}
|
||||
|
||||
@@ -822,7 +834,6 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
|
||||
|
||||
// Branch ops
|
||||
REGISTER_OP(SIGNALRETURN, SignalReturn);
|
||||
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
|
||||
REGISTER_OP(EXITFUNCTION, ExitFunction);
|
||||
REGISTER_OP(JUMP, Jump);
|
||||
@@ -837,6 +848,7 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
// Conversion ops
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(VDUPFROMGPR, VDupFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
|
||||
REGISTER_OP(FLOAT_FTOF, Float_FToF);
|
||||
REGISTER_OP(VECTOR_STOF, Vector_SToF);
|
||||
@@ -887,6 +899,8 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
Op_StoreMemTSO(IROp, ID);
|
||||
}
|
||||
break;
|
||||
|
||||
REGISTER_OP(MEMSET, MemSet);
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
|
||||
REGISTER_OP(CACHELINEZERO, CacheLineZero);
|
||||
@@ -955,6 +969,8 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
REGISTER_OP(VZIP2, VZip2);
|
||||
REGISTER_OP(VUNZIP, VUnZip);
|
||||
REGISTER_OP(VUNZIP2, VUnZip2);
|
||||
REGISTER_OP(VTRN, VTrn);
|
||||
REGISTER_OP(VTRN2, VTrn2);
|
||||
REGISTER_OP(VBSL, VBSL);
|
||||
REGISTER_OP(VCMPEQ, VCMPEQ);
|
||||
REGISTER_OP(VCMPEQZ, VCMPEQZ);
|
||||
@@ -1009,7 +1025,7 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
|
||||
if (DebugData) {
|
||||
DebugData->Subblocks.push_back({
|
||||
static_cast<uint32_t>(BlockStartHostCode - GuestEntry),
|
||||
static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockEntry),
|
||||
static_cast<uint32_t>(GetCursorAddress<uint8_t *>() - BlockStartHostCode)
|
||||
});
|
||||
}
|
||||
@@ -1022,8 +1038,24 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
}
|
||||
PendingTargetLabel = nullptr;
|
||||
|
||||
auto CodeEnd = GetCursorAddress<uint8_t *>();
|
||||
ClearICache(GuestEntry, CodeEnd - GuestEntry);
|
||||
// Add the JitCodeTail
|
||||
auto JITBlockTailLocation = GetCursorAddress<uint8_t *>();
|
||||
auto JITBlockTail = GetCursorAddress<JITCodeTail*>();
|
||||
CursorIncrement(sizeof(JITCodeTail));
|
||||
|
||||
// Put the block's RIP entry in the tail.
|
||||
// This will be used for RIP reconstruction in the future.
|
||||
// TODO: This needs to be a data RIP relocation once code caching works.
|
||||
// Current relocation code doesn't support this feature yet.
|
||||
JITBlockTail->RIP = Entry;
|
||||
|
||||
CodeHeader->OffsetToBlockTail = JITBlockTailLocation - CodeData.BlockBegin;
|
||||
|
||||
CodeData.Size = GetCursorAddress<uint8_t *>() - CodeData.BlockBegin;
|
||||
|
||||
JITBlockTail->Size = CodeData.Size;
|
||||
|
||||
ClearICache(CodeData.BlockBegin, CodeData.Size);
|
||||
|
||||
#ifdef VIXL_DISASSEMBLER
|
||||
const auto DisasmEnd = GetCursorAddress<const vixl::aarch64::Instruction*>();
|
||||
@@ -1031,13 +1063,13 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
#endif
|
||||
|
||||
if (DebugData) {
|
||||
DebugData->HostCodeSize = CodeEnd - GuestEntry;
|
||||
DebugData->HostCodeSize = CodeData.Size;
|
||||
DebugData->Relocations = &Relocations;
|
||||
}
|
||||
|
||||
this->IR = nullptr;
|
||||
|
||||
return GuestEntry;
|
||||
return CodeData;
|
||||
}
|
||||
|
||||
void Arm64JITCore::ResetStack() {
|
||||
@@ -1056,11 +1088,11 @@ void Arm64JITCore::ResetStack() {
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
return std::make_unique<Arm64JITCore>(ctx, Thread);
|
||||
}
|
||||
|
||||
void InitializeArm64JITSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
void InitializeArm64JITSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
|
||||
Arm64JITCore::InitializeSignalHandlers(CTX);
|
||||
}
|
||||
|
||||
|
||||
+9
-10
@@ -31,13 +31,13 @@ namespace FEXCore::Core {
|
||||
namespace FEXCore::CPU {
|
||||
class Arm64JITCore final : public CPUBackend, public Arm64Emitter {
|
||||
public:
|
||||
explicit Arm64JITCore(FEXCore::Context::Context *ctx,
|
||||
explicit Arm64JITCore(FEXCore::Context::ContextImpl *ctx,
|
||||
FEXCore::Core::InternalThreadState *Thread);
|
||||
~Arm64JITCore() override;
|
||||
|
||||
[[nodiscard]] std::string GetName() override { return "JIT"; }
|
||||
|
||||
[[nodiscard]] void *CompileCode(uint64_t Entry,
|
||||
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
|
||||
FEXCore::IR::IRListView const *IR,
|
||||
FEXCore::Core::DebugData *DebugData,
|
||||
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
static void InitializeSignalHandlers(FEXCore::Context::Context *CTX);
|
||||
static void InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX);
|
||||
|
||||
void ClearRelocations() override { Relocations.clear(); }
|
||||
|
||||
@@ -57,9 +57,10 @@ private:
|
||||
const bool HostSupportsSVE{};
|
||||
|
||||
ARMEmitter::BiDirectionalLabel *PendingTargetLabel;
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
FEXCore::IR::IRListView const *IR;
|
||||
uint64_t Entry;
|
||||
CPUBackend::CompiledCode CodeData{};
|
||||
|
||||
std::map<IR::NodeID, ARMEmitter::BiDirectionalLabel> JumpTargets;
|
||||
|
||||
@@ -230,11 +231,6 @@ private:
|
||||
/** @} */
|
||||
|
||||
uint32_t SpillSlots{};
|
||||
/**
|
||||
* @brief Current guest RIP entrypoint
|
||||
*/
|
||||
uint8_t *GuestEntry{};
|
||||
|
||||
#define DEF_OP(x) void Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
|
||||
|
||||
///< Unhandled handler
|
||||
@@ -312,7 +308,6 @@ private:
|
||||
DEF_OP(AtomicFetchNeg);
|
||||
|
||||
///< Branch ops
|
||||
DEF_OP(SignalReturn);
|
||||
DEF_OP(CallbackReturn);
|
||||
DEF_OP(ExitFunction);
|
||||
DEF_OP(Jump);
|
||||
@@ -327,6 +322,7 @@ private:
|
||||
///< Conversion ops
|
||||
DEF_OP(VInsGPR);
|
||||
DEF_OP(VCastFromGPR);
|
||||
DEF_OP(VDupFromGPR);
|
||||
DEF_OP(Float_FromGPR_S);
|
||||
DEF_OP(Float_FToF);
|
||||
DEF_OP(Vector_SToF);
|
||||
@@ -353,6 +349,7 @@ private:
|
||||
DEF_OP(StoreMem);
|
||||
DEF_OP(LoadMemTSO);
|
||||
DEF_OP(StoreMemTSO);
|
||||
DEF_OP(MemSet);
|
||||
DEF_OP(ParanoidLoadMemTSO);
|
||||
DEF_OP(ParanoidStoreMemTSO);
|
||||
DEF_OP(CacheLineClear);
|
||||
@@ -417,6 +414,8 @@ private:
|
||||
DEF_OP(VZip2);
|
||||
DEF_OP(VUnZip);
|
||||
DEF_OP(VUnZip2);
|
||||
DEF_OP(VTrn);
|
||||
DEF_OP(VTrn2);
|
||||
DEF_OP(VBSL);
|
||||
DEF_OP(VCMPEQ);
|
||||
DEF_OP(VCMPEQZ);
|
||||
|
||||
+185
-22
@@ -703,7 +703,7 @@ DEF_OP(SpillRegister) {
|
||||
const auto Src = GetReg(Op->Value.ID());
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
if (SlotOffset > 4095) {
|
||||
if (SlotOffset > LSByteMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
strb(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -713,7 +713,7 @@ DEF_OP(SpillRegister) {
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (SlotOffset > 8190) {
|
||||
if (SlotOffset > LSHalfMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
strh(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -723,7 +723,7 @@ DEF_OP(SpillRegister) {
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
if (SlotOffset > LSWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -733,7 +733,7 @@ DEF_OP(SpillRegister) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
if (SlotOffset > LSDWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -751,7 +751,7 @@ DEF_OP(SpillRegister) {
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
if (SlotOffset > LSWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -761,7 +761,7 @@ DEF_OP(SpillRegister) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
if (SlotOffset > LSDWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -771,7 +771,7 @@ DEF_OP(SpillRegister) {
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
if (SlotOffset > 65520) {
|
||||
if (SlotOffset > LSQWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -803,7 +803,7 @@ DEF_OP(FillRegister) {
|
||||
const auto Dst = GetReg(Node);
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
if (SlotOffset > 4095) {
|
||||
if (SlotOffset > LSByteMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldrb(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -813,7 +813,7 @@ DEF_OP(FillRegister) {
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (SlotOffset > 8190) {
|
||||
if (SlotOffset > LSHalfMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldrh(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -823,7 +823,7 @@ DEF_OP(FillRegister) {
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
if (SlotOffset > LSWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -833,7 +833,7 @@ DEF_OP(FillRegister) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
if (SlotOffset > LSDWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -851,7 +851,7 @@ DEF_OP(FillRegister) {
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
if (SlotOffset > LSWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -861,7 +861,7 @@ DEF_OP(FillRegister) {
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
if (SlotOffset > LSDWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -871,7 +871,7 @@ DEF_OP(FillRegister) {
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
if (SlotOffset > 65520) {
|
||||
if (SlotOffset > LSQWordMaxUnsignedOffset) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
@@ -911,20 +911,20 @@ FEXCore::ARMEmitter::ExtendedMemOperand Arm64JITCore::GenerateMemOperand(uint8_t
|
||||
IR::MemOffsetType OffsetType,
|
||||
uint8_t OffsetScale) {
|
||||
if (Offset.IsInvalid()) {
|
||||
return FEXCore::ARMEmitter::ExtendedMemOperand(Base, ARMEmitter::IndexType::OFFSET, 0);
|
||||
return ARMEmitter::ExtendedMemOperand(Base.X(), ARMEmitter::IndexType::OFFSET, 0);
|
||||
} else {
|
||||
if (OffsetScale != 1 && OffsetScale != AccessSize) {
|
||||
LOGMAN_MSG_A_FMT("Unhandled GenerateMemOperand OffsetScale: {}", OffsetScale);
|
||||
}
|
||||
uint64_t Const;
|
||||
if (IsInlineConstant(Offset, &Const)) {
|
||||
return FEXCore::ARMEmitter::ExtendedMemOperand(Base, ARMEmitter::IndexType::OFFSET, Const);
|
||||
return ARMEmitter::ExtendedMemOperand(Base.X(), ARMEmitter::IndexType::OFFSET, Const);
|
||||
} else {
|
||||
auto RegOffset = GetReg(Offset.ID());
|
||||
switch(OffsetType.Val) {
|
||||
case IR::MEM_OFFSET_SXTX.Val: return FEXCore::ARMEmitter::ExtendedMemOperand(Base, RegOffset, FEXCore::ARMEmitter::ExtendedType::SXTX, (int)std::log2(OffsetScale) );
|
||||
case IR::MEM_OFFSET_UXTW.Val: return FEXCore::ARMEmitter::ExtendedMemOperand(Base, RegOffset, FEXCore::ARMEmitter::ExtendedType::UXTW, (int)std::log2(OffsetScale) );
|
||||
case IR::MEM_OFFSET_SXTW.Val: return FEXCore::ARMEmitter::ExtendedMemOperand(Base, RegOffset, FEXCore::ARMEmitter::ExtendedType::SXTW, (int)std::log2(OffsetScale) );
|
||||
case IR::MEM_OFFSET_SXTX.Val: return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::SXTX, (int)std::log2(OffsetScale) );
|
||||
case IR::MEM_OFFSET_UXTW.Val: return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::UXTW, (int)std::log2(OffsetScale) );
|
||||
case IR::MEM_OFFSET_SXTW.Val: return ARMEmitter::ExtendedMemOperand(Base.X(), RegOffset.X(), ARMEmitter::ExtendedType::SXTW, (int)std::log2(OffsetScale) );
|
||||
default: LOGMAN_MSG_A_FMT("Unhandled GenerateMemOperand OffsetType: {}", OffsetType.Val); break;
|
||||
}
|
||||
}
|
||||
@@ -1101,14 +1101,14 @@ DEF_OP(LoadMemTSO) {
|
||||
const auto Dst = GetReg(Node);
|
||||
if (OpSize == 1) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
ldaprb(Dst, MemReg);
|
||||
ldaprb(Dst.W(), MemReg);
|
||||
}
|
||||
else {
|
||||
// Aligned
|
||||
nop();
|
||||
switch (OpSize) {
|
||||
case 2:
|
||||
ldaprh(Dst, MemReg);
|
||||
ldaprh(Dst.W(), MemReg);
|
||||
break;
|
||||
case 4:
|
||||
ldapr(Dst.W(), MemReg);
|
||||
@@ -1340,6 +1340,169 @@ DEF_OP(StoreMemTSO) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(MemSet) {
|
||||
// TODO: A future looking task would be to support this with ARM's MOPS instructions.
|
||||
// The 8-bit non-atomic forward path directly matches ARM's SETP/SETM/SETE instruction,
|
||||
// while the backward version needs some fixup to convert it to a forward direction.
|
||||
//
|
||||
// Assuming non-atomicity and non-faulting behaviour, this can accelerate this implementation.
|
||||
// Additionally: This is commonly used as a memset to zero. If we know up-front with an inline constant
|
||||
// that the value is zero, we can optimize any operation larger than 8-bit down to 8-bit to use the MOPS implementation.
|
||||
const auto Op = IROp->C<IR::IROp_MemSet>();
|
||||
|
||||
const int32_t Size = Op->Size;
|
||||
const auto MemReg = GetReg(Op->Addr.ID());
|
||||
const auto Value = GetReg(Op->Value.ID());
|
||||
const auto Length = GetReg(Op->Length.ID());
|
||||
const auto Direction = GetReg(Op->Direction.ID());
|
||||
const auto Dst = GetReg(Node);
|
||||
|
||||
// If Direction == 0 then:
|
||||
// MemReg is incremented (by size)
|
||||
// else:
|
||||
// MemReg is decremented (by size)
|
||||
//
|
||||
// Counter is decremented regardless.
|
||||
|
||||
ARMEmitter::ForwardLabel BackwardImpl{};
|
||||
ARMEmitter::ForwardLabel Done{};
|
||||
|
||||
mov(TMP1, Length.X());
|
||||
if (Op->Prefix.IsInvalid()) {
|
||||
mov(TMP2, MemReg.X());
|
||||
}
|
||||
else {
|
||||
const auto Prefix = GetReg(Op->Prefix.ID());
|
||||
add(TMP2, Prefix.X(), MemReg.X());
|
||||
}
|
||||
|
||||
// Backward or forwards implementation depends on flag
|
||||
cbnz(ARMEmitter::Size::i64Bit, Direction, &BackwardImpl);
|
||||
|
||||
auto MemStore = [this](auto Value, uint32_t OpSize, int32_t Size) {
|
||||
switch (OpSize) {
|
||||
case 1:
|
||||
strb<ARMEmitter::IndexType::POST>(Value.W(), TMP2, Size);
|
||||
break;
|
||||
case 2:
|
||||
strh<ARMEmitter::IndexType::POST>(Value.W(), TMP2, Size);
|
||||
break;
|
||||
case 4:
|
||||
str<ARMEmitter::IndexType::POST>(Value.W(), TMP2, Size);
|
||||
break;
|
||||
case 8:
|
||||
str<ARMEmitter::IndexType::POST>(Value.X(), TMP2, Size);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
auto MemStoreTSO = [this](auto Value, uint32_t OpSize, int32_t Size) {
|
||||
if (OpSize == 1) {
|
||||
// 8bit load is always aligned to natural alignment
|
||||
stlrb(Value.W(), TMP2);
|
||||
}
|
||||
else {
|
||||
nop();
|
||||
switch (OpSize) {
|
||||
case 2:
|
||||
stlrh(Value.W(), TMP2);
|
||||
break;
|
||||
case 4:
|
||||
stlr(Value.W(), TMP2);
|
||||
break;
|
||||
case 8:
|
||||
stlr(Value.X(), TMP2);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
nop();
|
||||
}
|
||||
|
||||
if (Size >= 0) {
|
||||
add(ARMEmitter::Size::i64Bit, TMP2, TMP2, OpSize);
|
||||
}
|
||||
else {
|
||||
sub(ARMEmitter::Size::i64Bit, TMP2, TMP2, OpSize);
|
||||
}
|
||||
};
|
||||
|
||||
// Emit forward direction memset then backward direction memset.
|
||||
for (int32_t Direction : { 1, -1 }) {
|
||||
const int32_t OpSize = Size;
|
||||
const int32_t SizeDirection = Size * Direction;
|
||||
|
||||
ARMEmitter::BackwardLabel AgainInternal{};
|
||||
ARMEmitter::ForwardLabel DoneInternal{};
|
||||
|
||||
// Early exit if zero count.
|
||||
cbz(ARMEmitter::Size::i64Bit, TMP1, &DoneInternal);
|
||||
|
||||
Bind(&AgainInternal);
|
||||
if (Op->IsAtomic) {
|
||||
MemStoreTSO(Value, OpSize, SizeDirection);
|
||||
}
|
||||
else {
|
||||
MemStore(Value, OpSize, SizeDirection);
|
||||
}
|
||||
sub(ARMEmitter::Size::i64Bit, TMP1, TMP1, 1);
|
||||
cbnz(ARMEmitter::Size::i64Bit, TMP1, &AgainInternal);
|
||||
|
||||
Bind(&DoneInternal);
|
||||
|
||||
if (SizeDirection >= 0) {
|
||||
switch (OpSize) {
|
||||
case 1:
|
||||
add(Dst.X(), MemReg.X(), Length.X());
|
||||
break;
|
||||
case 2:
|
||||
add(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 1);
|
||||
break;
|
||||
case 4:
|
||||
add(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 2);
|
||||
break;
|
||||
case 8:
|
||||
add(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 3);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, OpSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (OpSize) {
|
||||
case 1:
|
||||
sub(Dst.X(), MemReg.X(), Length.X());
|
||||
break;
|
||||
case 2:
|
||||
sub(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 1);
|
||||
break;
|
||||
case 4:
|
||||
sub(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 2);
|
||||
break;
|
||||
case 8:
|
||||
sub(Dst.X(), MemReg.X(), Length.X(), ARMEmitter::ShiftType::LSL, 3);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, OpSize);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Direction == 1) {
|
||||
b(&Done);
|
||||
Bind(&BackwardImpl);
|
||||
}
|
||||
}
|
||||
|
||||
Bind(&Done);
|
||||
// Destination already set to the final pointer.
|
||||
}
|
||||
|
||||
DEF_OP(ParanoidLoadMemTSO) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadMemTSO>();
|
||||
const auto OpSize = IROp->Size;
|
||||
@@ -1473,7 +1636,7 @@ DEF_OP(ParanoidStoreMemTSO) {
|
||||
}
|
||||
case 32: {
|
||||
dmb(FEXCore::ARMEmitter::BarrierScope::ISH);
|
||||
st1b<ARMEmitter::SubRegSize::i8Bit>(Src, PRED_TMP_32B, Addr, 0);
|
||||
st1b<ARMEmitter::SubRegSize::i8Bit>(Src.Z(), PRED_TMP_32B, Addr, 0);
|
||||
dmb(FEXCore::ARMEmitter::BarrierScope::ISH);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(GuestOpcode) {
|
||||
auto Op = IROp->C<IR::IROp_GuestOpcode>();
|
||||
// metadata
|
||||
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, GetCursorAddress<uint8_t*>() - GuestEntry});
|
||||
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, GetCursorAddress<uint8_t*>() - CodeData.BlockBegin});
|
||||
}
|
||||
|
||||
DEF_OP(Fence) {
|
||||
|
||||
+204
-137
@@ -11,12 +11,14 @@ $end_info$
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void Arm64JITCore::Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
|
||||
DEF_OP(VectorZero) {
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
if (HostSupportsSVE) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
eor(Dst.Z(), Dst.Z(), Dst.Z());
|
||||
} else {
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
switch (OpSize) {
|
||||
case 8: {
|
||||
eor(Dst.D(), Dst.D(), Dst.D());
|
||||
@@ -34,8 +36,11 @@ DEF_OP(VectorZero) {
|
||||
}
|
||||
|
||||
DEF_OP(VectorImm) {
|
||||
auto Op = IROp->C<IR::IROp_VectorImm>();
|
||||
const uint8_t ElementSize = Op->Header.ElementSize;
|
||||
const auto Op = IROp->C<IR::IROp_VectorImm>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize =
|
||||
@@ -46,7 +51,7 @@ DEF_OP(VectorImm) {
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
if (ElementSize > 1 && (Op->Immediate & 0x80)) {
|
||||
// SVE dup uses sign extension where VectorImm wants zext
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, Op->Immediate);
|
||||
@@ -120,13 +125,15 @@ DEF_OP(VMov) {
|
||||
}
|
||||
|
||||
DEF_OP(VAnd) {
|
||||
auto Op = IROp->C<IR::IROp_VAnd>();
|
||||
const auto Op = IROp->C<IR::IROp_VAnd>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
and_(Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
and_(Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
@@ -134,13 +141,15 @@ DEF_OP(VAnd) {
|
||||
}
|
||||
|
||||
DEF_OP(VBic) {
|
||||
auto Op = IROp->C<IR::IROp_VBic>();
|
||||
const auto Op = IROp->C<IR::IROp_VBic>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
bic(Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
bic(Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
@@ -148,13 +157,15 @@ DEF_OP(VBic) {
|
||||
}
|
||||
|
||||
DEF_OP(VOr) {
|
||||
auto Op = IROp->C<IR::IROp_VOr>();
|
||||
const auto Op = IROp->C<IR::IROp_VOr>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
orr(Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
orr(Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
@@ -162,13 +173,15 @@ DEF_OP(VOr) {
|
||||
}
|
||||
|
||||
DEF_OP(VXor) {
|
||||
auto Op = IROp->C<IR::IROp_VXor>();
|
||||
const auto Op = IROp->C<IR::IROp_VXor>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Vector1 = GetVReg(Op->Vector1.ID());
|
||||
const auto Vector2 = GetVReg(Op->Vector2.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
eor(Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
eor(Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
@@ -176,8 +189,10 @@ DEF_OP(VXor) {
|
||||
}
|
||||
|
||||
DEF_OP(VAdd) {
|
||||
auto Op = IROp->C<IR::IROp_VAdd>();
|
||||
const auto Op = IROp->C<IR::IROp_VAdd>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -190,17 +205,19 @@ DEF_OP(VAdd) {
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
if (HostSupportsSVE) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
add(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
add(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VSub) {
|
||||
auto Op = IROp->C<IR::IROp_VSub>();
|
||||
const auto Op = IROp->C<IR::IROp_VSub>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -213,17 +230,19 @@ DEF_OP(VSub) {
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
if (HostSupportsSVE) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
sub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sub(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUQAdd) {
|
||||
auto Op = IROp->C<IR::IROp_VUQAdd>();
|
||||
const auto Op = IROp->C<IR::IROp_VUQAdd>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -236,17 +255,19 @@ DEF_OP(VUQAdd) {
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
if (HostSupportsSVE) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
uqadd(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
uqadd(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VUQSub) {
|
||||
auto Op = IROp->C<IR::IROp_VUQSub>();
|
||||
const auto Op = IROp->C<IR::IROp_VUQSub>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -259,17 +280,19 @@ DEF_OP(VUQSub) {
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
if (HostSupportsSVE) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
uqsub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
uqsub(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VSQAdd) {
|
||||
auto Op = IROp->C<IR::IROp_VSQAdd>();
|
||||
const auto Op = IROp->C<IR::IROp_VSQAdd>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -282,17 +305,19 @@ DEF_OP(VSQAdd) {
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
if (HostSupportsSVE) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
sqadd(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sqadd(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VSQSub) {
|
||||
auto Op = IROp->C<IR::IROp_VSQSub>();
|
||||
const auto Op = IROp->C<IR::IROp_VSQSub>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -305,10 +330,10 @@ DEF_OP(VSQSub) {
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
if (HostSupportsSVE) {
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
sqsub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
sqsub(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
}
|
||||
}
|
||||
@@ -332,7 +357,7 @@ DEF_OP(VAddP) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
// SVE ADDP is a destructive operation, so we need a temporary
|
||||
@@ -401,9 +426,10 @@ DEF_OP(VAddV) {
|
||||
}
|
||||
|
||||
DEF_OP(VUMinV) {
|
||||
auto Op = IROp->C<IR::IROp_VUMinV>();
|
||||
|
||||
const auto Op = IROp->C<IR::IROp_VUMinV>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -416,14 +442,9 @@ DEF_OP(VUMinV) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
LOGMAN_THROW_AA_FMT(OpSize == 16 || OpSize == 32,
|
||||
"Unsupported vector length: {}", OpSize);
|
||||
|
||||
const auto Pred = OpSize == 16 ? PRED_TMP_16B
|
||||
: PRED_TMP_32B;
|
||||
|
||||
uminv(SubRegSize, Dst.Z(), Pred, Vector.Z());
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B;
|
||||
uminv(SubRegSize, Dst, Pred, Vector.Z());
|
||||
} else {
|
||||
// Vector
|
||||
uminv(SubRegSize, Dst.Q(), Vector.Q());
|
||||
@@ -465,7 +486,8 @@ DEF_OP(VAbs) {
|
||||
const auto Op = IROp->C<IR::IROp_VAbs>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const uint8_t ElementSize = Op->Header.ElementSize;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto Src = GetVReg(Op->Vector.ID());
|
||||
@@ -477,7 +499,7 @@ DEF_OP(VAbs) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && OpSize == 32) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
abs(SubRegSize, Dst.Z(), PRED_TMP_32B.Merging(), Src.Z());
|
||||
} else {
|
||||
if (ElementSize == OpSize) {
|
||||
@@ -493,7 +515,9 @@ DEF_OP(VAbs) {
|
||||
DEF_OP(VPopcount) {
|
||||
const auto Op = IROp->C<IR::IROp_VPopcount>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const bool IsScalar = OpSize == 8;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto IsScalar = OpSize == 8;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
@@ -507,17 +531,13 @@ DEF_OP(VPopcount) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
const auto Pred = OpSize == 16 ? PRED_TMP_16B.Merging()
|
||||
: PRED_TMP_32B.Merging();
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
cnt(SubRegSize, Dst.Z(), Pred, Src.Z());
|
||||
} else {
|
||||
if (IsScalar) {
|
||||
// Scalar
|
||||
cnt(SubRegSize, Dst.D(), Src.D());
|
||||
} else {
|
||||
// Scalar
|
||||
cnt(SubRegSize, Dst.Q(), Src.Q());
|
||||
}
|
||||
}
|
||||
@@ -528,6 +548,7 @@ DEF_OP(VFAdd) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto IsScalar = ElementSize == OpSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -540,7 +561,7 @@ DEF_OP(VFAdd) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
fadd(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
if (IsScalar) {
|
||||
@@ -571,21 +592,19 @@ DEF_OP(VFAddP) {
|
||||
const auto Op = IROp->C<IR::IROp_VFAddP>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto VectorLower = GetVReg(Op->VectorLower.ID());
|
||||
const auto VectorUpper = GetVReg(Op->VectorUpper.ID());
|
||||
|
||||
const bool Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize =
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
@@ -613,6 +632,7 @@ DEF_OP(VFSub) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto IsScalar = ElementSize == OpSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -625,7 +645,7 @@ DEF_OP(VFSub) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
fsub(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
if (IsScalar) {
|
||||
@@ -657,6 +677,7 @@ DEF_OP(VFMul) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto IsScalar = ElementSize == OpSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -669,7 +690,7 @@ DEF_OP(VFMul) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
fmul(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
if (IsScalar) {
|
||||
@@ -714,7 +735,7 @@ DEF_OP(VFDiv) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
|
||||
// SVE VDIV is a destructive operation, so we need a temporary.
|
||||
@@ -772,9 +793,8 @@ DEF_OP(VFMin) {
|
||||
//
|
||||
// * - Not exactly (differs slightly with SNaNs), but close enough for the explanation
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
const auto Mask = Is256Bit ? PRED_TMP_32B
|
||||
: PRED_TMP_16B;
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B;
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
// General idea:
|
||||
@@ -843,10 +863,10 @@ DEF_OP(VFMax) {
|
||||
// NOTE: See VFMin implementation for reasons why we
|
||||
// don't just use FMAX/FMIN for these implementations.
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
const auto Mask = Is256Bit ? PRED_TMP_32B
|
||||
: PRED_TMP_16B;
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B;
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
fcmgt(SubRegSize, ComparePred, Mask.Zeroing(),
|
||||
Vector2.Z(), Vector1.Z());
|
||||
mov(VTMP1.Z(), Vector1.Z());
|
||||
@@ -900,9 +920,8 @@ DEF_OP(VFRecp) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
|
||||
: PRED_TMP_16B.Merging();
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
fmov(SubRegSize.Vector, VTMP1.Z(), 1.0);
|
||||
fdiv(SubRegSize.Vector, VTMP1.Z(), Pred, VTMP1.Z(), Vector.Z());
|
||||
@@ -951,9 +970,8 @@ DEF_OP(VFSqrt) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && !IsScalar) {
|
||||
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
|
||||
: PRED_TMP_16B.Merging();
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
fsqrt(SubRegSize, Dst.Z(), Pred, Vector.Z());
|
||||
} else {
|
||||
@@ -998,7 +1016,7 @@ DEF_OP(VFRSqrt) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
fmov(SubRegSize.Vector, VTMP1.Z(), 1.0);
|
||||
fsqrt(SubRegSize.Vector, VTMP2.Z(), Pred, Vector.Z());
|
||||
@@ -1052,10 +1070,8 @@ DEF_OP(VNeg) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
|
||||
: PRED_TMP_16B.Merging();
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
neg(SubRegSize, Dst.Z(), Pred, Vector.Z());
|
||||
} else {
|
||||
neg(SubRegSize, Dst.Q(), Vector.Q());
|
||||
@@ -1078,9 +1094,8 @@ DEF_OP(VFNeg) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
const auto Pred = Is256Bit ? PRED_TMP_32B.Merging()
|
||||
: PRED_TMP_16B.Merging();
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
fneg(SubRegSize, Dst.Z(), Pred, Vector.Z());
|
||||
} else {
|
||||
@@ -1097,7 +1112,7 @@ DEF_OP(VNot) {
|
||||
const auto Vector = GetVReg(Op->Vector.ID());
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
not_(ARMEmitter::SubRegSize::i8Bit, Dst.Z(), PRED_TMP_32B, Vector.Z());
|
||||
not_(ARMEmitter::SubRegSize::i8Bit, Dst.Z(), PRED_TMP_32B.Merging(), Vector.Z());
|
||||
} else {
|
||||
mvn(ARMEmitter::SubRegSize::i8Bit, Dst.Q(), Vector.Q());
|
||||
}
|
||||
@@ -1108,7 +1123,6 @@ DEF_OP(VUMin) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto IsScalar = ElementSize == OpSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -1122,7 +1136,7 @@ DEF_OP(VUMin) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
// SVE UMIN is a destructive operation so we need a temporary.
|
||||
@@ -1156,7 +1170,6 @@ DEF_OP(VSMin) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto IsScalar = ElementSize == OpSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -1170,7 +1183,7 @@ DEF_OP(VSMin) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
// SVE SMIN is a destructive operation, so we need a temporary.
|
||||
@@ -1204,7 +1217,6 @@ DEF_OP(VUMax) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto IsScalar = ElementSize == OpSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -1218,7 +1230,7 @@ DEF_OP(VUMax) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
// SVE UMAX is a destructive operation, so we need a temporary.
|
||||
@@ -1252,7 +1264,6 @@ DEF_OP(VSMax) {
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto IsScalar = ElementSize == OpSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -1266,7 +1277,7 @@ DEF_OP(VSMax) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Pred = PRED_TMP_32B.Merging();
|
||||
|
||||
// SVE SMAX is a destructive operation, so we need a temporary.
|
||||
@@ -1411,20 +1422,79 @@ DEF_OP(VUnZip2) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VTrn) {
|
||||
const auto Op = IROp->C<IR::IROp_VTrn>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto VectorLower = GetVReg(Op->VectorLower.ID());
|
||||
const auto VectorUpper = GetVReg(Op->VectorUpper.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize =
|
||||
ElementSize == 1 ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
trn1(SubRegSize, Dst.Z(), VectorLower.Z(), VectorUpper.Z());
|
||||
} else {
|
||||
if (OpSize == 8) {
|
||||
trn1(SubRegSize, Dst.D(), VectorLower.D(), VectorUpper.D());
|
||||
} else {
|
||||
trn1(SubRegSize, Dst.Q(), VectorLower.Q(), VectorUpper.Q());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VTrn2) {
|
||||
const auto Op = IROp->C<IR::IROp_VTrn2>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto VectorLower = GetVReg(Op->VectorLower.ID());
|
||||
const auto VectorUpper = GetVReg(Op->VectorUpper.ID());
|
||||
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 1 || ElementSize == 2 || ElementSize == 4 || ElementSize == 8, "Invalid size");
|
||||
const auto SubRegSize =
|
||||
ElementSize == 1 ? ARMEmitter::SubRegSize::i8Bit :
|
||||
ElementSize == 2 ? ARMEmitter::SubRegSize::i16Bit :
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
trn2(SubRegSize, Dst.Z(), VectorLower.Z(), VectorUpper.Z());
|
||||
} else {
|
||||
if (OpSize == 8) {
|
||||
trn2(SubRegSize, Dst.D(), VectorLower.D(), VectorUpper.D());
|
||||
} else {
|
||||
trn2(SubRegSize, Dst.Q(), VectorLower.Q(), VectorUpper.Q());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VBSL) {
|
||||
const auto Op = IROp->C<IR::IROp_VBSL>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto VectorFalse = GetVReg(Op->VectorFalse.ID());
|
||||
const auto VectorTrue = GetVReg(Op->VectorTrue.ID());
|
||||
const auto VectorMask = GetVReg(Op->VectorMask.ID());
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
// NOTE: Slight parameter difference from ASIMD
|
||||
// ASIMD -> BSL Mask, True, False
|
||||
// SVE -> BSL True, True, False, Mask
|
||||
mov(VTMP1.Z(), VectorTrue.Z());
|
||||
movprfx(VTMP1.Z(), VectorTrue.Z());
|
||||
bsl(VTMP1.Z(), VTMP1.Z(), VectorFalse.Z(), VectorMask.Z());
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
} else {
|
||||
@@ -1459,7 +1529,7 @@ DEF_OP(VCMPEQ) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1501,7 +1571,7 @@ DEF_OP(VCMPEQZ) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1543,7 +1613,7 @@ DEF_OP(VCMPGT) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1585,7 +1655,7 @@ DEF_OP(VCMPGTZ) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1623,7 +1693,7 @@ DEF_OP(VCMPLTZ) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i128Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1661,7 +1731,7 @@ DEF_OP(VFCMPEQ) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1710,7 +1780,7 @@ DEF_OP(VFCMPNEQ) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1761,7 +1831,7 @@ DEF_OP(VFCMPLT) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1810,7 +1880,7 @@ DEF_OP(VFCMPGT) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1859,7 +1929,7 @@ DEF_OP(VFCMPLE) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1909,7 +1979,7 @@ DEF_OP(VFCMPORD) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -1970,7 +2040,7 @@ DEF_OP(VFCMPUNO) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit);
|
||||
|
||||
if (HostSupportsSVE && Is256Bit && !IsScalar) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
const auto Mask = PRED_TMP_32B.Zeroing();
|
||||
const auto ComparePred = ARMEmitter::PReg::p0;
|
||||
|
||||
@@ -2218,10 +2288,10 @@ DEF_OP(VInsElement) {
|
||||
dup(SubRegSize, VTMP2.Z(), SrcVector.Z(), SrcIdx);
|
||||
mov(Dst.Z(), Reg.Z());
|
||||
if (ElementSize == 16) {
|
||||
mov(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Predicate, VTMP2.Z());
|
||||
mov(ARMEmitter::SubRegSize::i64Bit, Dst.Z(), Predicate.Merging(), VTMP2.Z());
|
||||
}
|
||||
else {
|
||||
mov(SubRegSize, Dst.Z(), Predicate, VTMP2.Z());
|
||||
mov(SubRegSize, Dst.Z(), Predicate.Merging(), VTMP2.Z());
|
||||
}
|
||||
|
||||
// Set up a label to jump over the data we inserted, so we don't try and execute it.
|
||||
@@ -2456,14 +2526,13 @@ DEF_OP(VUShrNI2) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
mov(VTMP1.Z(), VectorLower.Z());
|
||||
|
||||
const auto Mask = PRED_TMP_16B;
|
||||
|
||||
shrnb(SubRegSize, VTMP2.Z(), VectorUpper.Z(), BitShift);
|
||||
uzp1(SubRegSize, VTMP2.Z(), VTMP2.Z(), VTMP2.Z());
|
||||
splice<ARMEmitter::OpType::Destructive>(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
|
||||
movprfx(Dst.Z(), VectorLower.Z());
|
||||
splice<ARMEmitter::OpType::Destructive>(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
|
||||
} else {
|
||||
mov(VTMP1.Q(), VectorLower.Q());
|
||||
shrn2(SubRegSize, VTMP1.Q(), VectorUpper.Q(), BitShift);
|
||||
@@ -2560,7 +2629,7 @@ DEF_OP(VUXTL2) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
uunpkhi(SubRegSize, Dst.Z(), Vector.Z());
|
||||
} else {
|
||||
uxtl2(SubRegSize, Dst.D(), Vector.D());
|
||||
uxtl2(SubRegSize, Dst.Q(), Vector.Q());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2639,12 +2708,6 @@ DEF_OP(VSQXTN2) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
// Need to use the destructive variant of SPLICE, since
|
||||
// the constructive variant requires a register list, and
|
||||
// we can't guarantee VectorLower and VectorUpper will always
|
||||
// have consecutive indexes with one another.
|
||||
mov(VTMP1.Z(), VectorLower.Z());
|
||||
|
||||
// We use the 16 byte mask due to how SPLICE works. We only
|
||||
// want to get at the first 16 bytes in the lower vector, so
|
||||
// that SPLICE will then begin copying the first 16 bytes
|
||||
@@ -2654,20 +2717,23 @@ DEF_OP(VSQXTN2) {
|
||||
|
||||
sqxtnb(SubRegSize, VTMP2.Z(), VectorUpper.Z());
|
||||
uzp1(SubRegSize, VTMP2.Z(), VTMP2.Z(), VTMP2.Z());
|
||||
splice<ARMEmitter::OpType::Destructive>(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
|
||||
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
// Need to use the destructive variant of SPLICE, since
|
||||
// the constructive variant requires a register list, and
|
||||
// we can't guarantee VectorLower and VectorUpper will always
|
||||
// have consecutive indexes with one another.
|
||||
movprfx(Dst.Z(), VectorLower.Z());
|
||||
splice<ARMEmitter::OpType::Destructive>(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
|
||||
} else {
|
||||
mov(VTMP1.Q(), VectorLower.Q());
|
||||
|
||||
if (OpSize == 8) {
|
||||
sqxtn(SubRegSize, VTMP2, VectorUpper);
|
||||
ins(ARMEmitter::SubRegSize::i32Bit, VTMP1, 1, VTMP2, 0);
|
||||
mov(Dst.Q(), VectorLower.Q());
|
||||
ins(ARMEmitter::SubRegSize::i32Bit, Dst, 1, VTMP2, 0);
|
||||
} else {
|
||||
mov(VTMP1.Q(), VectorLower.Q());
|
||||
sqxtn2(SubRegSize, VTMP1, VectorUpper);
|
||||
mov(Dst.Q(), VTMP1.Q());
|
||||
}
|
||||
|
||||
mov(Dst.Q(), VTMP1.Q());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2716,30 +2782,31 @@ DEF_OP(VSQXTUN2) {
|
||||
// NOTE: See VSQXTN2 implementation for an in-depth explanation
|
||||
// of everything going on here.
|
||||
|
||||
mov(VTMP1.Z(), VectorLower.Z());
|
||||
|
||||
const auto Mask = PRED_TMP_16B;
|
||||
|
||||
sqxtunb(SubRegSize, VTMP2.Z(), VectorUpper.Z());
|
||||
uzp1(SubRegSize, VTMP2.Z(), VTMP2.Z(), VTMP2.Z());
|
||||
splice<ARMEmitter::OpType::Destructive>(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
|
||||
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
movprfx(Dst.Z(), VectorLower.Z());
|
||||
splice<ARMEmitter::OpType::Destructive>(SubRegSize, Dst.Z(), Mask, Dst.Z(), VTMP2.Z());
|
||||
} else {
|
||||
mov(VTMP1.Q(), VectorLower.Q());
|
||||
if (OpSize == 8) {
|
||||
sqxtun(SubRegSize, VTMP2, VectorUpper);
|
||||
ins(ARMEmitter::SubRegSize::i32Bit, VTMP1, 1, VTMP2, 0);
|
||||
mov(Dst.Q(), VectorLower.Q());
|
||||
ins(ARMEmitter::SubRegSize::i32Bit, Dst, 1, VTMP2, 0);
|
||||
} else {
|
||||
mov(VTMP1.Q(), VectorLower.Q());
|
||||
sqxtun2(SubRegSize, VTMP1, VectorUpper);
|
||||
mov(Dst.Q(), VTMP1.Q());
|
||||
}
|
||||
mov(Dst.Q(), VTMP1.Q());
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VMul) {
|
||||
const auto Op = IROp->C<IR::IROp_VUMul>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
@@ -2753,7 +2820,7 @@ DEF_OP(VMul) {
|
||||
ElementSize == 4 ? ARMEmitter::SubRegSize::i32Bit :
|
||||
ElementSize == 8 ? ARMEmitter::SubRegSize::i64Bit : ARMEmitter::SubRegSize::i8Bit;
|
||||
|
||||
if (HostSupportsSVE) {
|
||||
if (HostSupportsSVE && Is256Bit) {
|
||||
mul(SubRegSize, Dst.Z(), Vector1.Z(), Vector2.Z());
|
||||
} else {
|
||||
mul(SubRegSize, Dst.Q(), Vector1.Q(), Vector2.Q());
|
||||
@@ -2905,7 +2972,7 @@ DEF_OP(VTBL1) {
|
||||
|
||||
switch (OpSize) {
|
||||
case 8: {
|
||||
tbl(Dst.D(), VectorTable.D(), VectorIndices.D());
|
||||
tbl(Dst.D(), VectorTable.Q(), VectorIndices.D());
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
|
||||
+5
-5
@@ -3,7 +3,7 @@
|
||||
#include <memory>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core {
|
||||
@@ -13,14 +13,14 @@ struct InternalThreadState;
|
||||
namespace FEXCore::CPU {
|
||||
class CPUBackend;
|
||||
|
||||
[[nodiscard]] std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx,
|
||||
[[nodiscard]] std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::ContextImpl *ctx,
|
||||
FEXCore::Core::InternalThreadState *Thread);
|
||||
void InitializeX86JITSignalHandlers(FEXCore::Context::Context *CTX);
|
||||
void InitializeX86JITSignalHandlers(FEXCore::Context::ContextImpl *CTX);
|
||||
CPUBackendFeatures GetX86JITBackendFeatures();
|
||||
|
||||
[[nodiscard]] std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::Context *ctx,
|
||||
[[nodiscard]] std::unique_ptr<CPUBackend> CreateArm64JITCore(FEXCore::Context::ContextImpl *ctx,
|
||||
FEXCore::Core::InternalThreadState *Thread);
|
||||
void InitializeArm64JITSignalHandlers(FEXCore::Context::Context *CTX);
|
||||
void InitializeArm64JITSignalHandlers(FEXCore::Context::ContextImpl *CTX);
|
||||
CPUBackendFeatures GetArm64JITBackendFeatures();
|
||||
|
||||
} // namespace FEXCore::CPU
|
||||
@@ -30,22 +30,6 @@ $end_info$
|
||||
namespace FEXCore::CPU {
|
||||
#define DEF_OP(x) void X86JITCore::Op_##x(IR::IROp_Header *IROp, IR::NodeID Node)
|
||||
|
||||
DEF_OP(SignalReturn) {
|
||||
auto Op = IROp->C<IR::IROp_SignalReturn>();
|
||||
|
||||
// Adjust the stack first for a regular return
|
||||
if (SpillSlots) {
|
||||
add(rsp, SpillSlots * MaxSpillSlotSize); // + 8 to consume return address
|
||||
}
|
||||
|
||||
if (Op->IsRT) {
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandlerRT)]);
|
||||
}
|
||||
else {
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler)]);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(CallbackReturn) {
|
||||
// Adjust the stack first for a regular return
|
||||
if (SpillSlots) {
|
||||
@@ -218,7 +202,7 @@ DEF_OP(Thunk) {
|
||||
|
||||
mov(rdi, GetSrc<RA_64>(Op->ArgPtr.ID()));
|
||||
|
||||
auto thunkFn = ThreadState->CTX->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
auto thunkFn = static_cast<Context::ContextImpl*>(ThreadState->CTX)->ThunkHandler->LookupThunk(Op->ThunkNameHash);
|
||||
|
||||
mov(rax, reinterpret_cast<uintptr_t>(thunkFn));
|
||||
call(rax);
|
||||
@@ -321,7 +305,6 @@ DEF_OP(CPUID) {
|
||||
#undef DEF_OP
|
||||
void X86JITCore::RegisterBranchHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &X86JITCore::Op_##x
|
||||
REGISTER_OP(SIGNALRETURN, SignalReturn);
|
||||
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
|
||||
REGISTER_OP(EXITFUNCTION, ExitFunction);
|
||||
REGISTER_OP(JUMP, Jump);
|
||||
|
||||
@@ -111,6 +111,53 @@ DEF_OP(VCastFromGPR) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VDupFromGPR) {
|
||||
const auto Op = IROp->C<IR::IROp_VDupFromGPR>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = IROp->ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Src = GetSrc<RA_64>(Op->Src.ID()).cvt64();
|
||||
|
||||
vmovq(Dst, Src);
|
||||
|
||||
switch (ElementSize) {
|
||||
case 1:
|
||||
if (Is256Bit) {
|
||||
vpbroadcastb(ToYMM(Dst), Dst);
|
||||
} else {
|
||||
vpbroadcastb(Dst, Dst);
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
if (Is256Bit) {
|
||||
vpbroadcastw(ToYMM(Dst), Dst);
|
||||
} else {
|
||||
vpbroadcastw(Dst, Dst);
|
||||
}
|
||||
break;
|
||||
case 4:
|
||||
if (Is256Bit) {
|
||||
vpbroadcastd(ToYMM(Dst), Dst);
|
||||
} else {
|
||||
vpbroadcastd(Dst, Dst);
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
if (Is256Bit) {
|
||||
vpbroadcastq(ToYMM(Dst), Dst);
|
||||
} else {
|
||||
vpbroadcastq(Dst, Dst);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled element size: {}", ElementSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(Float_FromGPR_S) {
|
||||
const auto Op = IROp->C<IR::IROp_Float_FromGPR_S>();
|
||||
|
||||
@@ -357,6 +404,7 @@ 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(VDUPFROMGPR, VDupFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
|
||||
REGISTER_OP(FLOAT_FTOF, Float_FToF);
|
||||
REGISTER_OP(VECTOR_STOF, Vector_SToF);
|
||||
|
||||
@@ -21,23 +21,67 @@ DEF_OP(AESImc) {
|
||||
}
|
||||
|
||||
DEF_OP(AESEnc) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEnc>();
|
||||
vaesenc(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
const auto Op = IROp->C<IR::IROp_VAESEnc>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Key = GetSrc(Op->Key.ID());
|
||||
const auto State = GetSrc(Op->State.ID());
|
||||
|
||||
if (Is256Bit) {
|
||||
vaesenc(ToYMM(Dst), ToYMM(State), ToYMM(Key));
|
||||
} else {
|
||||
vaesenc(Dst, State, Key);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(AESEncLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESEncLast>();
|
||||
vaesenclast(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
const auto Op = IROp->C<IR::IROp_VAESEncLast>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Key = GetSrc(Op->Key.ID());
|
||||
const auto State = GetSrc(Op->State.ID());
|
||||
|
||||
if (Is256Bit) {
|
||||
vaesenclast(ToYMM(Dst), ToYMM(State), ToYMM(Key));
|
||||
} else {
|
||||
vaesenclast(Dst, State, Key);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(AESDec) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDec>();
|
||||
vaesdec(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
const auto Op = IROp->C<IR::IROp_VAESDec>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Key = GetSrc(Op->Key.ID());
|
||||
const auto State = GetSrc(Op->State.ID());
|
||||
|
||||
if (Is256Bit) {
|
||||
vaesdec(ToYMM(Dst), ToYMM(State), ToYMM(Key));
|
||||
} else {
|
||||
vaesdec(Dst, State, Key);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(AESDecLast) {
|
||||
auto Op = IROp->C<IR::IROp_VAESDecLast>();
|
||||
vaesdeclast(GetDst(Node), GetSrc(Op->State.ID()), GetSrc(Op->Key.ID()));
|
||||
const auto Op = IROp->C<IR::IROp_VAESDecLast>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Key = GetSrc(Op->Key.ID());
|
||||
const auto State = GetSrc(Op->State.ID());
|
||||
|
||||
if (Is256Bit) {
|
||||
vaesdeclast(ToYMM(Dst), ToYMM(State), ToYMM(Key));
|
||||
} else {
|
||||
vaesdeclast(Dst, State, Key);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(AESKeyGenAssist) {
|
||||
@@ -76,18 +120,24 @@ DEF_OP(CRC32) {
|
||||
}
|
||||
|
||||
DEF_OP(PCLMUL) {
|
||||
auto Op = IROp->C<IR::IROp_PCLMUL>();
|
||||
const auto Op = IROp->C<IR::IROp_PCLMUL>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
auto Dst = GetDst(Node);
|
||||
auto Src1 = GetSrc(Op->Src1.ID());
|
||||
auto Src2 = GetSrc(Op->Src2.ID());
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto Src1 = GetSrc(Op->Src1.ID());
|
||||
const auto Src2 = GetSrc(Op->Src2.ID());
|
||||
|
||||
switch (Op->Selector) {
|
||||
case 0b00000000:
|
||||
case 0b00000001:
|
||||
case 0b00010000:
|
||||
case 0b00010001:
|
||||
vpclmulqdq(Dst, Src1, Src2, Op->Selector);
|
||||
if (Is256Bit) {
|
||||
vpclmulqdq(ToYMM(Dst), ToYMM(Src1), ToYMM(Src2), Op->Selector);
|
||||
} else {
|
||||
vpclmulqdq(Dst, Src1, Src2, Op->Selector);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown PCLMUL selector: {}", Op->Selector);
|
||||
|
||||
+46
-15
@@ -330,7 +330,7 @@ static uint64_t X86JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame,
|
||||
}
|
||||
|
||||
auto LinkerAddress = Frame->Pointers.Common.ExitFunctionLinker;
|
||||
Context::Context::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
Context::ContextImpl::ThreadAddBlockLink(Thread, GuestRip, (uintptr_t)record, [record, LinkerAddress]{
|
||||
// undo the link
|
||||
record[0] = LinkerAddress;
|
||||
});
|
||||
@@ -342,7 +342,7 @@ static uint64_t X86JITCore_ExitFunctionLink(FEXCore::Core::CpuStateFrame *Frame,
|
||||
void X86JITCore::Op_NoOp(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
}
|
||||
|
||||
X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
X86JITCore::X86JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread)
|
||||
: CPUBackend(Thread, INITIAL_CODE_SIZE, MAX_CODE_SIZE)
|
||||
, CodeGenerator(0, this, nullptr) // this is not used here
|
||||
, CTX {ctx} {
|
||||
@@ -379,7 +379,7 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
|
||||
|
||||
Common.PrintValue = reinterpret_cast<uint64_t>(PrintValue);
|
||||
Common.PrintVectorValue = reinterpret_cast<uint64_t>(PrintVectorValue);
|
||||
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::Context::ThreadRemoveCodeEntryFromJit);
|
||||
Common.ThreadRemoveCodeEntryFromJIT = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadRemoveCodeEntryFromJit);
|
||||
Common.CPUIDObj = reinterpret_cast<uint64_t>(&CTX->CPUID);
|
||||
|
||||
{
|
||||
@@ -389,7 +389,7 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
|
||||
|
||||
Common.SyscallHandlerObj = reinterpret_cast<uint64_t>(CTX->SyscallHandler);
|
||||
Common.SyscallHandlerFunc = reinterpret_cast<uint64_t>(FEXCore::Context::HandleSyscall);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::Context::ThreadExitFunctionLink<X86JITCore_ExitFunctionLink>);
|
||||
Common.ExitFunctionLink = reinterpret_cast<uintptr_t>(&Context::ContextImpl::ThreadExitFunctionLink<X86JITCore_ExitFunctionLink>);
|
||||
|
||||
// Fill in the fallback handlers
|
||||
InterpreterOps::FillFallbackIndexPointers(Common.FallbackHandlerPointers);
|
||||
@@ -399,9 +399,9 @@ X86JITCore::X86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalTh
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
void X86JITCore::InitializeSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
void X86JITCore::InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
|
||||
CTX->SignalDelegation->RegisterHostSignalHandler(SIGILL, [](FEXCore::Core::InternalThreadState *Thread, int Signal, void *info, void *ucontext) -> bool {
|
||||
return Thread->CTX->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
|
||||
return static_cast<Context::ContextImpl*>(Thread->CTX)->Dispatcher->HandleSIGILL(Thread, Signal, info, ucontext);
|
||||
}, true);
|
||||
}
|
||||
|
||||
@@ -587,7 +587,7 @@ std::tuple<X86JITCore::SetCC, X86JITCore::CMovCC, X86JITCore::JCC> X86JITCore::G
|
||||
return { &CodeGenerator::sete , &CodeGenerator::cmove , &CodeGenerator::je };
|
||||
}
|
||||
|
||||
void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
|
||||
CPUBackend::CompiledCode X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRListView const *IR, [[maybe_unused]] FEXCore::Core::DebugData *DebugData, FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) {
|
||||
|
||||
FEXCORE_PROFILE_SCOPED("x86::CompileCode");
|
||||
JumpTargets.clear();
|
||||
@@ -603,12 +603,27 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
|
||||
CTX->ClearCodeCache(ThreadState);
|
||||
}
|
||||
|
||||
GuestEntry = getCurr<uint8_t*>();
|
||||
CodeData.BlockBegin = getCurr<uint8_t*>();
|
||||
|
||||
// Put the code header at the start of the data block.
|
||||
Label JITCodeHeaderLabel{};
|
||||
L(JITCodeHeaderLabel);
|
||||
|
||||
JITCodeHeader *CodeHeader = getCurr<JITCodeHeader *>();
|
||||
setSize(getSize() + sizeof(JITCodeHeader));
|
||||
|
||||
CodeData.BlockEntry = getCurr<uint8_t*>();
|
||||
|
||||
// Get the address of the JITCodeHeader and store in to the core state.
|
||||
// Only two instructions, so very low overhead.
|
||||
lea(TMP1, ptr [rip + JITCodeHeaderLabel]);
|
||||
mov(qword [STATE + offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader)], TMP1);
|
||||
|
||||
CursorEntry = getSize();
|
||||
this->IR = IR;
|
||||
|
||||
if (GDBEnabled) {
|
||||
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(GuestEntry, Entry);
|
||||
auto GDBSize = CTX->Dispatcher->GenerateGDBPauseCheck(CodeData.BlockBegin, Entry);
|
||||
setSize(getSize() + GDBSize);
|
||||
}
|
||||
|
||||
@@ -731,7 +746,7 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
|
||||
|
||||
if (DebugData) {
|
||||
DebugData->Subblocks.push_back({
|
||||
static_cast<uint32_t>(BlockStartHostCode - GuestEntry),
|
||||
static_cast<uint32_t>(BlockStartHostCode - CodeData.BlockBegin),
|
||||
static_cast<uint32_t>(getCurr<uint8_t *>() - BlockStartHostCode)
|
||||
});
|
||||
}
|
||||
@@ -744,20 +759,36 @@ void *X86JITCore::CompileCode(uint64_t Entry, [[maybe_unused]] FEXCore::IR::IRLi
|
||||
}
|
||||
PendingTargetLabel = nullptr;
|
||||
|
||||
void *GuestExit = getCurr<void*>();
|
||||
// Add the JitCodeTail
|
||||
auto JITBlockTailLocation = getCurr<uint8_t *>();
|
||||
auto JITBlockTail = getCurr<JITCodeTail*>();
|
||||
setSize(getSize() + sizeof(JITCodeTail));
|
||||
|
||||
// Put the block's RIP entry in the tail.
|
||||
// This will be used for RIP reconstruction in the future.
|
||||
// TODO: This needs to be a data RIP relocation once code caching works.
|
||||
// Current relocation code doesn't support this feature yet.
|
||||
JITBlockTail->RIP = Entry;
|
||||
|
||||
CodeHeader->OffsetToBlockTail = JITBlockTailLocation - CodeData.BlockBegin;
|
||||
|
||||
CodeData.Size = getCurr<uint8_t*>() - CodeData.BlockBegin;
|
||||
|
||||
JITBlockTail->Size = CodeData.Size;
|
||||
|
||||
this->IR = nullptr;
|
||||
|
||||
ready();
|
||||
|
||||
if (DebugData) {
|
||||
DebugData->HostCodeSize = reinterpret_cast<uintptr_t>(GuestExit) - reinterpret_cast<uintptr_t>(GuestEntry);
|
||||
DebugData->HostCodeSize = CodeData.Size;
|
||||
DebugData->Relocations = &Relocations;
|
||||
}
|
||||
|
||||
return GuestEntry;
|
||||
return CodeData;
|
||||
}
|
||||
|
||||
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
std::unique_ptr<CPUBackend> CreateX86JITCore(FEXCore::Context::ContextImpl *ctx, FEXCore::Core::InternalThreadState *Thread) {
|
||||
return std::make_unique<X86JITCore>(ctx, Thread);
|
||||
}
|
||||
|
||||
@@ -765,7 +796,7 @@ CPUBackendFeatures GetX86JITBackendFeatures() {
|
||||
return CPUBackendFeatures { };
|
||||
}
|
||||
|
||||
void InitializeX86JITSignalHandlers(FEXCore::Context::Context *CTX) {
|
||||
void InitializeX86JITSignalHandlers(FEXCore::Context::ContextImpl *CTX) {
|
||||
X86JITCore::InitializeSignalHandlers(CTX);
|
||||
}
|
||||
|
||||
|
||||
@@ -51,13 +51,13 @@ const std::array<Xbyak::Xmm, 11> RAXMM_x = { xmm1, xmm2, xmm3, xmm4, xmm5, xmm6
|
||||
|
||||
class X86JITCore final : public CPUBackend, public Xbyak::CodeGenerator {
|
||||
public:
|
||||
explicit X86JITCore(FEXCore::Context::Context *ctx,
|
||||
explicit X86JITCore(FEXCore::Context::ContextImpl *ctx,
|
||||
FEXCore::Core::InternalThreadState *Thread);
|
||||
~X86JITCore() override;
|
||||
|
||||
[[nodiscard]] std::string GetName() override { return "JIT"; }
|
||||
|
||||
[[nodiscard]] void *CompileCode(uint64_t Entry,
|
||||
[[nodiscard]] CPUBackend::CompiledCode CompileCode(uint64_t Entry,
|
||||
FEXCore::IR::IRListView const *IR,
|
||||
FEXCore::Core::DebugData *DebugData,
|
||||
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) override;
|
||||
@@ -68,7 +68,7 @@ public:
|
||||
|
||||
void ClearCache() override;
|
||||
|
||||
static void InitializeSignalHandlers(FEXCore::Context::Context *CTX);
|
||||
static void InitializeSignalHandlers(FEXCore::Context::ContextImpl *CTX);
|
||||
|
||||
void ClearRelocations() override { Relocations.clear(); }
|
||||
|
||||
@@ -135,9 +135,10 @@ private:
|
||||
/** @} */
|
||||
|
||||
Label* PendingTargetLabel{};
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
FEXCore::IR::IRListView const *IR;
|
||||
uint64_t Entry;
|
||||
CPUBackend::CompiledCode CodeData{};
|
||||
|
||||
std::unordered_map<IR::NodeID, Label> JumpTargets;
|
||||
Xbyak::util::Cpu Features{};
|
||||
@@ -205,10 +206,6 @@ private:
|
||||
void EmitDetectionString();
|
||||
|
||||
uint32_t SpillSlots{};
|
||||
/**
|
||||
* @brief Current guest RIP entrypoint
|
||||
*/
|
||||
uint8_t *GuestEntry{};
|
||||
|
||||
using SetCC = void (X86JITCore::*)(const Operand& op);
|
||||
using CMovCC = void (X86JITCore::*)(const Reg& reg, const Operand& op);
|
||||
@@ -308,7 +305,6 @@ private:
|
||||
DEF_OP(AtomicFetchNeg);
|
||||
|
||||
///< Branch ops
|
||||
DEF_OP(SignalReturn);
|
||||
DEF_OP(CallbackReturn);
|
||||
DEF_OP(ExitFunction);
|
||||
DEF_OP(Jump);
|
||||
@@ -322,6 +318,7 @@ private:
|
||||
///< Conversion ops
|
||||
DEF_OP(VInsGPR);
|
||||
DEF_OP(VCastFromGPR);
|
||||
DEF_OP(VDupFromGPR);
|
||||
DEF_OP(Float_FromGPR_S);
|
||||
DEF_OP(Float_FToF);
|
||||
DEF_OP(Vector_UToF);
|
||||
@@ -347,6 +344,7 @@ private:
|
||||
DEF_OP(StoreFlag);
|
||||
DEF_OP(LoadMem);
|
||||
DEF_OP(StoreMem);
|
||||
DEF_OP(MemSet);
|
||||
DEF_OP(CacheLineClear);
|
||||
DEF_OP(CacheLineClean);
|
||||
DEF_OP(CacheLineZero);
|
||||
@@ -409,6 +407,8 @@ private:
|
||||
DEF_OP(VZip2);
|
||||
DEF_OP(VUnZip);
|
||||
DEF_OP(VUnZip2);
|
||||
DEF_OP(VTrn);
|
||||
DEF_OP(VTrn2);
|
||||
DEF_OP(VBSL);
|
||||
DEF_OP(VCMPEQ);
|
||||
DEF_OP(VCMPEQZ);
|
||||
|
||||
@@ -766,6 +766,94 @@ DEF_OP(StoreMem) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(MemSet) {
|
||||
const auto Op = IROp->C<IR::IROp_MemSet>();
|
||||
|
||||
const int32_t Size = Op->Size;
|
||||
const auto MemReg = GetSrc<RA_64>(Op->Addr.ID());
|
||||
const auto Value = GetSrc<RA_64>(Op->Value.ID());
|
||||
const auto Length = GetSrc<RA_64>(Op->Length.ID());
|
||||
const auto Direction = GetSrc<RA_64>(Op->Direction.ID());
|
||||
const auto Dst = GetSrc<RA_64>(Node);
|
||||
|
||||
// If Direction == 0 then:
|
||||
// MemReg is incremented (by size)
|
||||
// else:
|
||||
// MemReg is decremented (by size)
|
||||
//
|
||||
// Counter is decremented regardless.
|
||||
|
||||
// TMP1 = rax
|
||||
// TMP2 = rcx
|
||||
// TMP4 = rdi
|
||||
// That leaves us with TMP3 and TMP5
|
||||
mov(rax, Value);
|
||||
mov(rcx, Length);
|
||||
mov(rdi, MemReg);
|
||||
|
||||
{
|
||||
mov(TMP3, Length);
|
||||
auto CalculateDest = [&]() {
|
||||
mov(Dst, MemReg);
|
||||
switch (Size) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
shl(TMP3, 1);
|
||||
break;
|
||||
case 4:
|
||||
shl(TMP3, 2);
|
||||
break;
|
||||
case 8:
|
||||
shl(TMP3, 3);
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Label AfterDir;
|
||||
Label BackwardDir;
|
||||
|
||||
cmp(Direction, 0);
|
||||
jne(BackwardDir);
|
||||
// Incrementing DF flag.
|
||||
cld();
|
||||
CalculateDest();
|
||||
add(Dst, TMP3);
|
||||
jmp(AfterDir);
|
||||
|
||||
L(BackwardDir);
|
||||
// Decrementing DF flag.
|
||||
std();
|
||||
CalculateDest();
|
||||
sub(Dst, TMP3);
|
||||
|
||||
L(AfterDir);
|
||||
}
|
||||
|
||||
switch (Size) {
|
||||
case 1:
|
||||
rep(); stosb();
|
||||
break;
|
||||
case 2:
|
||||
rep(); stosw();
|
||||
break;
|
||||
case 4:
|
||||
rep(); stosd();
|
||||
break;
|
||||
case 8:
|
||||
rep(); stosq();
|
||||
break;
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unhandled {} size: {}", __func__, Size);
|
||||
break;
|
||||
}
|
||||
// Ensure we set DF back to zero. Required by the ABI.
|
||||
cld();
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClear) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineClear>();
|
||||
|
||||
@@ -820,6 +908,7 @@ void X86JITCore::RegisterMemoryHandlers() {
|
||||
REGISTER_OP(STOREMEM, StoreMem);
|
||||
REGISTER_OP(LOADMEMTSO, LoadMem);
|
||||
REGISTER_OP(STOREMEMTSO, StoreMem);
|
||||
REGISTER_OP(MEMSET, MemSet);
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
|
||||
REGISTER_OP(CACHELINEZERO, CacheLineZero);
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(GuestOpcode) {
|
||||
auto Op = IROp->C<IR::IROp_GuestOpcode>();
|
||||
// metadata
|
||||
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, getCurr<uint8_t*>() - GuestEntry});
|
||||
DebugData->GuestOpcodes.push_back({Op->GuestEntryOffset, getCurr<uint8_t*>() - CodeData.BlockBegin});
|
||||
}
|
||||
|
||||
DEF_OP(Fence) {
|
||||
|
||||
+205
-1
@@ -1944,7 +1944,7 @@ DEF_OP(VUnZip2) {
|
||||
}
|
||||
case 8: {
|
||||
if (Is256Bit) {
|
||||
vshufpd(ToYMM(Dst), ToYMM(VectorLower), ToYMM(VectorUpper), 0b1'1);
|
||||
vshufpd(ToYMM(Dst), ToYMM(VectorLower), ToYMM(VectorUpper), 0b11'11);
|
||||
vpermq(ToYMM(Dst), ToYMM(Dst), 0b11'01'10'00);
|
||||
} else {
|
||||
vshufpd(Dst, VectorLower, VectorUpper, 0b1'1);
|
||||
@@ -1958,6 +1958,191 @@ DEF_OP(VUnZip2) {
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VTrn) {
|
||||
const auto Op = IROp->C<IR::IROp_VTrn>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto VectorLower = GetSrc(Op->VectorLower.ID());
|
||||
const auto VectorUpper = GetSrc(Op->VectorUpper.ID());
|
||||
|
||||
const auto LoadPshufbReg = [&](Xbyak::Xmm reg, uint64_t lower) {
|
||||
mov(rax, lower);
|
||||
mov(rcx, 0x80'80'80'80'80'80'80'80);
|
||||
vmovq(reg, rax);
|
||||
pinsrq(reg, rcx, 1);
|
||||
};
|
||||
|
||||
switch (ElementSize) {
|
||||
case 1: {
|
||||
LoadPshufbReg(xmm15, 0x0E'0C'0A'08'06'04'02'00);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpcklbw(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpcklbw(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
LoadPshufbReg(xmm15, 0x0D'0C'09'08'05'04'01'00);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpcklwd(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpcklwd(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
LoadPshufbReg(xmm15, 0x0B'0A'09'08'03'02'01'00);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpckldq(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpckldq(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
LoadPshufbReg(xmm15, 0x07'06'05'04'03'02'01'00);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpcklqdq(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpcklqdq(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VTrn2) {
|
||||
const auto Op = IROp->C<IR::IROp_VTrn2>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = Op->Header.ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto VectorLower = GetSrc(Op->VectorLower.ID());
|
||||
const auto VectorUpper = GetSrc(Op->VectorUpper.ID());
|
||||
|
||||
const auto LoadPshufbReg = [&](Xbyak::Xmm reg, uint64_t lower) {
|
||||
mov(rax, lower);
|
||||
mov(rcx, 0x80'80'80'80'80'80'80'80);
|
||||
vmovq(reg, rax);
|
||||
pinsrq(reg, rcx, 1);
|
||||
};
|
||||
|
||||
switch (ElementSize) {
|
||||
case 1: {
|
||||
LoadPshufbReg(xmm15, 0x0F'0D'0B'09'07'05'03'01);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpcklbw(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpcklbw(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
LoadPshufbReg(xmm15, 0x0F'0E'0B'0A'07'06'03'02);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpcklwd(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpcklwd(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
LoadPshufbReg(xmm15, 0x0F'0E'0D'0C'07'06'05'04);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpckldq(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpckldq(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
LoadPshufbReg(xmm15, 0x0F'0E'0D'0C'0B'0A'09'08);
|
||||
|
||||
if (Is256Bit) {
|
||||
vinserti128(ymm15, ymm15, xmm15, 1);
|
||||
|
||||
vpshufb(ymm14, ToYMM(VectorLower), ymm15);
|
||||
vpshufb(ymm13, ToYMM(VectorUpper), ymm15);
|
||||
|
||||
vpunpcklqdq(ToYMM(Dst), ymm14, ymm13);
|
||||
} else {
|
||||
vpshufb(xmm14, VectorLower, xmm15);
|
||||
vpshufb(xmm13, VectorUpper, xmm15);
|
||||
vpunpcklqdq(Dst, xmm14, xmm13);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Element Size: {}", ElementSize);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(VBSL) {
|
||||
const auto Op = IROp->C<IR::IROp_VBSL>();
|
||||
@@ -3147,6 +3332,23 @@ DEF_OP(VShlI) {
|
||||
const auto Vector = GetSrc(Op->Vector.ID());
|
||||
|
||||
switch (ElementSize) {
|
||||
case 1: {
|
||||
const auto Mask = 0xFFU >> BitShift;
|
||||
|
||||
mov(rax, Mask);
|
||||
vmovq(xmm15, rax);
|
||||
|
||||
if (Is256Bit) {
|
||||
vpsllw(ToYMM(Dst), ToYMM(Vector), BitShift);
|
||||
vpbroadcastb(ymm15, xmm15);
|
||||
vpand(ToYMM(Dst), ToYMM(Dst), ymm15);
|
||||
} else {
|
||||
vpsllw(Dst, Vector, BitShift);
|
||||
vpbroadcastb(xmm15, xmm15);
|
||||
vpand(Dst, Dst, ymm15);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (Is256Bit) {
|
||||
vpsllw(ToYMM(Dst), ToYMM(Vector), BitShift);
|
||||
@@ -4304,6 +4506,8 @@ void X86JITCore::RegisterVectorHandlers() {
|
||||
REGISTER_OP(VZIP2, VZip2);
|
||||
REGISTER_OP(VUNZIP, VUnZip);
|
||||
REGISTER_OP(VUNZIP2, VUnZip2);
|
||||
REGISTER_OP(VTRN, VTrn);
|
||||
REGISTER_OP(VTRN2, VTrn2);
|
||||
REGISTER_OP(VBSL, VBSL);
|
||||
REGISTER_OP(VCMPEQ, VCMPEQ);
|
||||
REGISTER_OP(VCMPEQZ, VCMPEQZ);
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ $end_info$
|
||||
#include <sys/mman.h>
|
||||
|
||||
namespace FEXCore {
|
||||
LookupCache::LookupCache(FEXCore::Context::Context *CTX)
|
||||
LookupCache::LookupCache(FEXCore::Context::ContextImpl *CTX)
|
||||
: ctx {CTX} {
|
||||
|
||||
TotalCacheSize = ctx->Config.VirtualMemSize / 4096 * 8 + CODE_SIZE + L1_SIZE;
|
||||
|
||||
+3
-5
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "Interface/Context/Context.h"
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
|
||||
#include <cstdint>
|
||||
@@ -12,9 +13,6 @@
|
||||
#include <tsl/robin_map.h>
|
||||
|
||||
namespace FEXCore {
|
||||
namespace Context {
|
||||
struct Context;
|
||||
}
|
||||
|
||||
class LookupCache {
|
||||
public:
|
||||
@@ -24,7 +22,7 @@ public:
|
||||
uintptr_t GuestCode;
|
||||
};
|
||||
|
||||
LookupCache(FEXCore::Context::Context *CTX);
|
||||
LookupCache(FEXCore::Context::ContextImpl *CTX);
|
||||
~LookupCache();
|
||||
|
||||
uintptr_t FindBlock(uint64_t Address) {
|
||||
@@ -260,7 +258,7 @@ private:
|
||||
|
||||
size_t AllocateOffset {};
|
||||
|
||||
FEXCore::Context::Context *ctx;
|
||||
FEXCore::Context::ContextImpl *ctx;
|
||||
uint64_t VirtualMemSize{};
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
#include <FEXCore/Config/Config.h>
|
||||
|
||||
namespace FEXCore::CodeSerialize {
|
||||
NamedRegionObjectHandler::NamedRegionObjectHandler(FEXCore::Context::Context *ctx) {
|
||||
NamedRegionObjectHandler::NamedRegionObjectHandler(FEXCore::Context::ContextImpl *ctx) {
|
||||
DefaultSerializationConfig.Cookie = CODE_COOKIE;
|
||||
|
||||
// Initialize the Arch from CPUID
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace {
|
||||
}
|
||||
|
||||
namespace FEXCore::CodeSerialize {
|
||||
CodeObjectSerializeService::CodeObjectSerializeService(FEXCore::Context::Context *ctx)
|
||||
CodeObjectSerializeService::CodeObjectSerializeService(FEXCore::Context::ContextImpl *ctx)
|
||||
: CTX {ctx}
|
||||
, AsyncHandler { &NamedRegionHandler , this }
|
||||
, NamedRegionHandler { ctx } {
|
||||
|
||||
@@ -253,7 +253,7 @@ namespace FEXCore::CodeSerialize {
|
||||
|
||||
class NamedRegionObjectHandler final {
|
||||
public:
|
||||
NamedRegionObjectHandler(FEXCore::Context::Context *ctx);
|
||||
NamedRegionObjectHandler(FEXCore::Context::ContextImpl *ctx);
|
||||
|
||||
void HandleNamedRegionObjectJobs();
|
||||
|
||||
@@ -338,7 +338,7 @@ namespace FEXCore::CodeSerialize {
|
||||
*/
|
||||
class CodeObjectSerializeService final {
|
||||
public:
|
||||
CodeObjectSerializeService(FEXCore::Context::Context *ctx);
|
||||
CodeObjectSerializeService(FEXCore::Context::ContextImpl *ctx);
|
||||
|
||||
/**
|
||||
* @brief Initialize the internal interface
|
||||
@@ -440,7 +440,7 @@ namespace FEXCore::CodeSerialize {
|
||||
void NotifyWork() { WorkAvailable.NotifyOne(); }
|
||||
|
||||
private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
|
||||
Event WorkAvailable{};
|
||||
std::unique_ptr<FEXCore::Threads::Thread> WorkerThread;
|
||||
|
||||
+99
-218
@@ -277,18 +277,6 @@ void OpDispatchBuilder::IRETOp(OpcodeArgs) {
|
||||
BlockSetRIP = true;
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::SIGRETOp(OpcodeArgs) {
|
||||
uint8_t Literal = Op->Src[0].Data.Literal.Value;
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
// Store the new RIP
|
||||
bool IsRT = CTX->Config.Is64BitMode() || Literal;
|
||||
_SignalReturn(IsRT);
|
||||
auto NewRIP = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, rip));
|
||||
// This ExitFunction won't actually get hit but needs to exist
|
||||
_ExitFunction(NewRIP);
|
||||
BlockSetRIP = true;
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CallbackReturnOp(OpcodeArgs) {
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
// Store the new RIP
|
||||
@@ -876,7 +864,7 @@ OrderedNode *OpDispatchBuilder::SelectCC(uint8_t OP, OrderedNode *TrueValue, Ord
|
||||
case 0x7: { // JA - Jump if CF == 0 && ZF == 0
|
||||
auto Flag1 = GetRFLAG(FEXCore::X86State::RFLAG_ZF_LOC);
|
||||
auto Flag2 = GetRFLAG(FEXCore::X86State::RFLAG_CF_LOC);
|
||||
auto Check = _Or(Flag1, _Lshl(Flag2, _Constant(1)));
|
||||
auto Check = _Or(Flag1, Flag2);
|
||||
SrcCond = _Select(FEXCore::IR::COND_EQ,
|
||||
Check, ZeroConst, TrueValue, FalseValue);
|
||||
break;
|
||||
@@ -3805,73 +3793,21 @@ void OpDispatchBuilder::STOSOp(OpcodeArgs) {
|
||||
StoreGPRRegister(X86State::REG_RDI, TailDest);
|
||||
}
|
||||
else {
|
||||
// Calculate deffered flags.
|
||||
// This block is ending and it needs flag status
|
||||
CalculateDeferredFlags();
|
||||
// FEX doesn't support partial faulting REP instructions.
|
||||
// Converting this to a `MemSet` IR op optimizes this quite significantly in our codegen.
|
||||
// If FEX is to gain support for faulting REP instructions, then this implementation needs to change significantly.
|
||||
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Dest = LoadGPRRegister(X86State::REG_RDI);
|
||||
|
||||
// Create all our blocks
|
||||
auto LoopHead = CreateNewCodeBlockAfter(GetCurrentBlock());
|
||||
auto LoopTail = CreateNewCodeBlockAfter(LoopHead);
|
||||
auto LoopEnd = CreateNewCodeBlockAfter(LoopTail);
|
||||
// Only ES prefix
|
||||
auto Segment = GetSegment(0, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
|
||||
|
||||
// At the time this was written, our RA can't handle accessing nodes across blocks.
|
||||
// So we need to re-load and re-calculate essential values each iteration of the loop.
|
||||
|
||||
// First thing we need to do is finish this block and jump to the start of the loop.
|
||||
|
||||
// RA can now better allocate things, move these ops before the header, to avoid accessing
|
||||
// DF on every iteration
|
||||
auto SizeConst = _Constant(Size);
|
||||
auto NegSizeConst = _Constant(-Size);
|
||||
|
||||
// Calculate direction.
|
||||
OrderedNode *Counter = LoadGPRRegister(X86State::REG_RCX);
|
||||
auto DF = GetRFLAG(FEXCore::X86State::RFLAG_DF_LOC);
|
||||
auto PtrDir = _Select(FEXCore::IR::COND_EQ,
|
||||
DF, _Constant(0),
|
||||
SizeConst, NegSizeConst);
|
||||
|
||||
_Jump(LoopHead);
|
||||
|
||||
SetCurrentCodeBlock(LoopHead);
|
||||
{
|
||||
OrderedNode *Counter = LoadGPRRegister(X86State::REG_RCX);
|
||||
|
||||
// Can we end the block?
|
||||
_CondJump(Counter, LoopEnd, LoopTail, {COND_EQ});
|
||||
}
|
||||
|
||||
SetCurrentCodeBlock(LoopTail);
|
||||
{
|
||||
OrderedNode *Src = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Dest = LoadGPRRegister(X86State::REG_RDI);
|
||||
|
||||
// Only ES prefix
|
||||
Dest = AppendSegmentOffset(Dest, 0, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX, true);
|
||||
|
||||
// Store to memory where RDI points
|
||||
_StoreMemAutoTSO(GPRClass, Size, Dest, Src, Size);
|
||||
|
||||
OrderedNode *TailCounter = LoadGPRRegister(X86State::REG_RCX);
|
||||
OrderedNode *TailDest = LoadGPRRegister(X86State::REG_RDI);
|
||||
|
||||
// Decrement counter
|
||||
TailCounter = _Sub(TailCounter, _Constant(1));
|
||||
|
||||
// Store the counter so we don't have to deal with PHI here
|
||||
StoreGPRRegister(X86State::REG_RCX, TailCounter);
|
||||
|
||||
|
||||
// Offset the pointer
|
||||
TailDest = _Add(TailDest, PtrDir);
|
||||
StoreGPRRegister(X86State::REG_RDI, TailDest);
|
||||
|
||||
// Jump back to the start, we have more work to do
|
||||
_Jump(LoopHead);
|
||||
}
|
||||
|
||||
// Make sure to start a new block after ending this one
|
||||
|
||||
SetCurrentCodeBlock(LoopEnd);
|
||||
auto Result = _MemSet(CTX->IsTSOEnabled(), Size, Segment ?: InvalidNode, Dest, Src, Counter, DF);
|
||||
StoreGPRRegister(X86State::REG_RCX, _Constant(0));
|
||||
StoreGPRRegister(X86State::REG_RDI, Result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4844,20 +4780,19 @@ uint32_t OpDispatchBuilder::GetDstBitSize(X86Tables::DecodedOp Op) const {
|
||||
return GetDstSize(Op) * 8;
|
||||
}
|
||||
|
||||
OrderedNode *OpDispatchBuilder::AppendSegmentOffset(OrderedNode *Value, uint32_t Flags, uint32_t DefaultPrefix, bool Override) {
|
||||
OrderedNode *OpDispatchBuilder::GetSegment(uint32_t Flags, uint32_t DefaultPrefix, bool Override) {
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
if (Flags & FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX) {
|
||||
Value = _Add(Value, _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached)));
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached));
|
||||
}
|
||||
else if (Flags & FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX) {
|
||||
Value = _Add(Value, _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached)));
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached));
|
||||
}
|
||||
// If there was any other segment in 64bit then it is ignored
|
||||
}
|
||||
else {
|
||||
OrderedNode *Segment{};
|
||||
uint32_t Prefix = Flags & FEXCore::X86Tables::DecodeFlags::FLAG_SEGMENTS;
|
||||
if (!Prefix || Override) {
|
||||
// If there was no prefix then use the default one if available
|
||||
@@ -4867,29 +4802,28 @@ OrderedNode *OpDispatchBuilder::AppendSegmentOffset(OrderedNode *Value, uint32_t
|
||||
// With the segment register optimization we store the GDT bases directly in the segment register to remove indexed loads
|
||||
switch (Prefix) {
|
||||
case FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX:
|
||||
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, es_cached));
|
||||
break;
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, es_cached));
|
||||
case FEXCore::X86Tables::DecodeFlags::FLAG_CS_PREFIX:
|
||||
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, cs_cached));
|
||||
break;
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, cs_cached));
|
||||
case FEXCore::X86Tables::DecodeFlags::FLAG_SS_PREFIX:
|
||||
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ss_cached));
|
||||
break;
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ss_cached));
|
||||
case FEXCore::X86Tables::DecodeFlags::FLAG_DS_PREFIX:
|
||||
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ds_cached));
|
||||
break;
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, ds_cached));
|
||||
case FEXCore::X86Tables::DecodeFlags::FLAG_FS_PREFIX:
|
||||
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached));
|
||||
break;
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, fs_cached));
|
||||
case FEXCore::X86Tables::DecodeFlags::FLAG_GS_PREFIX:
|
||||
Segment = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached));
|
||||
break;
|
||||
default: break; // Do nothing
|
||||
return _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, gs_cached));
|
||||
default:
|
||||
break; // Do nothing
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (Segment) {
|
||||
Value = _Add(Value, Segment);
|
||||
}
|
||||
OrderedNode *OpDispatchBuilder::AppendSegmentOffset(OrderedNode *Value, uint32_t Flags, uint32_t DefaultPrefix, bool Override) {
|
||||
auto Segment = GetSegment(Flags, DefaultPrefix, Override);
|
||||
if (Segment) {
|
||||
Value = _Add(Value, Segment);
|
||||
}
|
||||
|
||||
return Value;
|
||||
@@ -4957,21 +4891,9 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
|
||||
else if (gpr >= FEXCore::X86State::REG_XMM_0) {
|
||||
const auto gprIndex = gpr - X86State::REG_XMM_0;
|
||||
|
||||
const auto regSize = CTX->HostFeatures.SupportsAVX ?
|
||||
Core::CPUState::XMM_AVX_REG_SIZE :
|
||||
Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
|
||||
// Load the full register size if it is a XMM register source.
|
||||
Src = LoadXMMRegister(gprIndex);
|
||||
|
||||
// If we are wanting a high-index then we need to extract an element from the upper half of the reg.
|
||||
// We can only extract an element size here.
|
||||
// TODO: Have the instruction doing this load do the extract instead of here.
|
||||
// We don't have enough information here to know if we can avoid this dup.
|
||||
if (highIndex && OpSize < Core::CPUState::XMM_SSE_REG_SIZE) {
|
||||
Src = _VDupElement(regSize, OpSize, Src, 1);
|
||||
}
|
||||
|
||||
// Now extract the subregister if it was a partial load /smaller/ than SSE size
|
||||
// TODO: Instead of doing the VMov implicitly on load, hunt down all use cases that require partial loads and do it after load.
|
||||
// We don't have information here to know if the operation needs zero upper bits or can contain data.
|
||||
@@ -5173,28 +5095,23 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
}
|
||||
else if (gpr >= FEXCore::X86State::REG_XMM_0) {
|
||||
const auto gprIndex = gpr - X86State::REG_XMM_0;
|
||||
const auto highIndex = Operand.Data.GPR.HighBits ? 1 : 0;
|
||||
|
||||
const auto VectorSize = CTX->HostFeatures.SupportsAVX ? 32 : 16;
|
||||
|
||||
auto Result = Src;
|
||||
if (highIndex || OpSize != VectorSize) {
|
||||
// Partial writes can come from GPR or FPR.
|
||||
if (OpSize != VectorSize) {
|
||||
// Partial writes can come from FPRs.
|
||||
// TODO: Fix the instructions doing partial writes rather than dealing with it here.
|
||||
auto SrcVector = LoadXMMRegister(gprIndex);
|
||||
|
||||
if (Class == IR::GPRClass) {
|
||||
Result = _VInsGPR(VectorSize, OpSize, highIndex, SrcVector, Src);
|
||||
}
|
||||
else {
|
||||
// OpSize of 16 is special in that it is expected to zero the upper bits of the 256-bit operation.
|
||||
// TODO: Longer term we should enforce the difference between zero and insert.
|
||||
if (VectorSize == Core::CPUState::XMM_AVX_REG_SIZE && OpSize == Core::CPUState::XMM_SSE_REG_SIZE) {
|
||||
Result = _VMov(OpSize, Src);
|
||||
}
|
||||
else {
|
||||
Result = _VInsElement(VectorSize, OpSize, highIndex, 0, SrcVector, Src);
|
||||
}
|
||||
LOGMAN_THROW_AA_FMT(Class != IR::GPRClass, "Partial writes from GPR not allowed. Instruction: {}",
|
||||
Op->TableInfo->Name);
|
||||
|
||||
// OpSize of 16 is special in that it is expected to zero the upper bits of the 256-bit operation.
|
||||
// TODO: Longer term we should enforce the difference between zero and insert.
|
||||
if (VectorSize == Core::CPUState::XMM_AVX_REG_SIZE && OpSize == Core::CPUState::XMM_SSE_REG_SIZE) {
|
||||
Result = _VMov(OpSize, Src);
|
||||
} else {
|
||||
Result = _VInsElement(VectorSize, OpSize, 0, 0, SrcVector, Src);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5330,7 +5247,7 @@ void OpDispatchBuilder::StoreResult(FEXCore::IR::RegisterClassType Class, FEXCor
|
||||
StoreResult(Class, Op, Op->Dest, Src, Align, AccessType);
|
||||
}
|
||||
|
||||
OpDispatchBuilder::OpDispatchBuilder(FEXCore::Context::Context *ctx)
|
||||
OpDispatchBuilder::OpDispatchBuilder(FEXCore::Context::ContextImpl *ctx)
|
||||
: IREmitter {ctx->OpDispatcherAllocator}
|
||||
, CTX {ctx} {
|
||||
ResetWorkingList();
|
||||
@@ -5365,58 +5282,7 @@ void OpDispatchBuilder::MOVGPRNTOp(OpcodeArgs) {
|
||||
StoreResult(GPRClass, Op, Src, 1, MemoryAccessType::ACCESS_STREAM);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
bool RequiresMask = false;
|
||||
FEXCore::IR::IROps IROp;
|
||||
switch (Op->OP) {
|
||||
case 0x0:
|
||||
case 0x1:
|
||||
case 0x2:
|
||||
case 0x3:
|
||||
case 0x4:
|
||||
case 0x5:
|
||||
IROp = FEXCore::IR::IROps::OP_ADD;
|
||||
RequiresMask = true;
|
||||
break;
|
||||
case 0x8:
|
||||
case 0x9:
|
||||
case 0xA:
|
||||
case 0xB:
|
||||
case 0xC:
|
||||
case 0xD:
|
||||
IROp = FEXCore::IR::IROps::OP_OR;
|
||||
break;
|
||||
case 0x20:
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x24:
|
||||
case 0x25:
|
||||
IROp = FEXCore::IR::IROps::OP_AND;
|
||||
break;
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
case 0x2C:
|
||||
case 0x2D:
|
||||
IROp = FEXCore::IR::IROps::OP_SUB;
|
||||
RequiresMask = true;
|
||||
break;
|
||||
case 0x30:
|
||||
case 0x31:
|
||||
case 0x32:
|
||||
case 0x33:
|
||||
case 0x34:
|
||||
case 0x35:
|
||||
IROp = FEXCore::IR::IROps::OP_XOR;
|
||||
break;
|
||||
default:
|
||||
IROp = FEXCore::IR::IROps::OP_LAST;
|
||||
LOGMAN_MSG_A_FMT("Unknown ALU Op: 0x{:x}", Op->OP);
|
||||
break;
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask) {
|
||||
auto Size = GetDstSize(Op);
|
||||
|
||||
// X86 basic ALU ops just do the operation between the destination and a single source
|
||||
@@ -5429,43 +5295,24 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
HandledLock = true;
|
||||
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
|
||||
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
|
||||
switch (IROp) {
|
||||
case FEXCore::IR::IROps::OP_ADD: {
|
||||
Dest = _AtomicFetchAdd(Size, Src, DestMem);
|
||||
Result = _Add(Dest, Src);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::IROps::OP_SUB: {
|
||||
Dest = _AtomicFetchSub(Size, Src, DestMem);
|
||||
Result = _Sub(Dest, Src);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::IROps::OP_OR: {
|
||||
Dest = _AtomicFetchOr(Size, Src, DestMem);
|
||||
Result = _Or(Dest, Src);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::IROps::OP_AND: {
|
||||
Dest = _AtomicFetchAnd(Size, Src, DestMem);
|
||||
Result = _And(Dest, Src);
|
||||
break;
|
||||
}
|
||||
case FEXCore::IR::IROps::OP_XOR: {
|
||||
Dest = _AtomicFetchXor(Size, Src, DestMem);
|
||||
Result = _Xor(Dest, Src);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
LOGMAN_MSG_A_FMT("Unknown Atomic IR Op: {}", ToUnderlying(IROp));
|
||||
break;
|
||||
}
|
||||
|
||||
auto FetchOp = _AtomicFetchAdd(Size, Src, DestMem);
|
||||
// Overwrite our atomic op type
|
||||
FetchOp.first->Header.Op = AtomicFetchOp;
|
||||
Dest = FetchOp;
|
||||
|
||||
auto ALUOp = _Add(Dest, Src);
|
||||
// Overwrite our IR's op type
|
||||
ALUOp.first->Header.Op = ALUIROp;
|
||||
|
||||
Result = ALUOp;
|
||||
}
|
||||
else {
|
||||
Dest = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
|
||||
auto ALUOp = _Add(Dest, Src);
|
||||
// Overwrite our IR's op type
|
||||
ALUOp.first->Header.Op = IROp;
|
||||
ALUOp.first->Header.Op = ALUIROp;
|
||||
|
||||
Result = ALUOp;
|
||||
StoreResult(GPRClass, Op, Result, -1);
|
||||
@@ -5477,7 +5324,7 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
|
||||
// Flags set
|
||||
{
|
||||
switch (IROp) {
|
||||
switch (ALUIROp) {
|
||||
case FEXCore::IR::IROps::OP_ADD:
|
||||
GenerateFlags_ADD(Op, Result, Dest, Src);
|
||||
break;
|
||||
@@ -5495,6 +5342,11 @@ void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
}
|
||||
}
|
||||
|
||||
template<FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask>
|
||||
void OpDispatchBuilder::ALUOp(OpcodeArgs) {
|
||||
ALUOpImpl(Op, ALUIROp, AtomicFetchOp, RequiresMask);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::INTOp(OpcodeArgs) {
|
||||
IR::BreakDefinition Reason;
|
||||
bool SetRIPToNext = false;
|
||||
@@ -5826,8 +5678,12 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
static constexpr std::tuple<uint16_t, uint8_t, FEXCore::X86Tables::OpDispatchPtr> AVXTable[] = {
|
||||
{OPD(1, 0b00, 0x10), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
|
||||
{OPD(1, 0b01, 0x10), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
|
||||
{OPD(1, 0b10, 0x10), 1, &OpDispatchBuilder::VMOVSSOp},
|
||||
{OPD(1, 0b11, 0x10), 1, &OpDispatchBuilder::VMOVSDOp},
|
||||
{OPD(1, 0b00, 0x11), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
|
||||
{OPD(1, 0b01, 0x11), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
|
||||
{OPD(1, 0b10, 0x11), 1, &OpDispatchBuilder::VMOVSSOp},
|
||||
{OPD(1, 0b11, 0x11), 1, &OpDispatchBuilder::VMOVSDOp},
|
||||
|
||||
{OPD(1, 0b00, 0x12), 1, &OpDispatchBuilder::VMOVLPOp},
|
||||
{OPD(1, 0b01, 0x12), 1, &OpDispatchBuilder::VMOVLPOp},
|
||||
@@ -5946,6 +5802,10 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
{OPD(1, 0b01, 0x6F), 1, &OpDispatchBuilder::VMOVAPS_VMOVAPD_Op},
|
||||
{OPD(1, 0b10, 0x6F), 1, &OpDispatchBuilder::VMOVUPS_VMOVUPD_Op},
|
||||
|
||||
{OPD(1, 0b01, 0x70), 1, &OpDispatchBuilder::VPSHUFWOp<4, true>},
|
||||
{OPD(1, 0b10, 0x70), 1, &OpDispatchBuilder::VPSHUFWOp<2, false>},
|
||||
{OPD(1, 0b11, 0x70), 1, &OpDispatchBuilder::VPSHUFWOp<2, true>},
|
||||
|
||||
{OPD(1, 0b01, 0x74), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPEQ, 1>},
|
||||
{OPD(1, 0b01, 0x75), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPEQ, 2>},
|
||||
{OPD(1, 0b01, 0x76), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPEQ, 4>},
|
||||
@@ -5954,6 +5814,8 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
|
||||
{OPD(1, 0b01, 0x7C), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VFADDP, 8>},
|
||||
{OPD(1, 0b11, 0x7C), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VFADDP, 4>},
|
||||
{OPD(1, 0b01, 0x7D), 1, &OpDispatchBuilder::VHSUBPOp<8>},
|
||||
{OPD(1, 0b11, 0x7D), 1, &OpDispatchBuilder::VHSUBPOp<4>},
|
||||
|
||||
{OPD(1, 0b01, 0x7E), 1, &OpDispatchBuilder::MOVBetweenGPR_FPR},
|
||||
{OPD(1, 0b10, 0x7E), 1, &OpDispatchBuilder::MOVQOp},
|
||||
@@ -5968,6 +5830,9 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
|
||||
{OPD(1, 0b01, 0xC5), 1, &OpDispatchBuilder::PExtrOp<2>},
|
||||
|
||||
{OPD(1, 0b00, 0xC6), 1, &OpDispatchBuilder::VSHUFOp<4>},
|
||||
{OPD(1, 0b01, 0xC6), 1, &OpDispatchBuilder::VSHUFOp<8>},
|
||||
|
||||
{OPD(1, 0b01, 0xD0), 1, &OpDispatchBuilder::VADDSUBPOp<8>},
|
||||
{OPD(1, 0b11, 0xD0), 1, &OpDispatchBuilder::VADDSUBPOp<4>},
|
||||
|
||||
@@ -6015,6 +5880,7 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
{OPD(1, 0b01, 0xF2), 1, &OpDispatchBuilder::VPSLLOp<4>},
|
||||
{OPD(1, 0b01, 0xF3), 1, &OpDispatchBuilder::VPSLLOp<8>},
|
||||
{OPD(1, 0b01, 0xF4), 1, &OpDispatchBuilder::VPMULLOp<4, false>},
|
||||
{OPD(1, 0b01, 0xF5), 1, &OpDispatchBuilder::VPMADDWDOp},
|
||||
{OPD(1, 0b01, 0xF7), 1, &OpDispatchBuilder::MASKMOVOp},
|
||||
|
||||
{OPD(1, 0b01, 0xF8), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VSUB, 1>},
|
||||
@@ -6025,17 +5891,24 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
{OPD(1, 0b01, 0xFD), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VADD, 2>},
|
||||
{OPD(1, 0b01, 0xFE), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VADD, 4>},
|
||||
|
||||
{OPD(2, 0b01, 0x00), 1, &OpDispatchBuilder::VPSHUFBOp},
|
||||
{OPD(2, 0b01, 0x01), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VADDP, 2>},
|
||||
{OPD(2, 0b01, 0x02), 1, &OpDispatchBuilder::VHADDPOp<IR::OP_VADDP, 4>},
|
||||
{OPD(2, 0b01, 0x03), 1, &OpDispatchBuilder::VPHADDSWOp},
|
||||
|
||||
{OPD(2, 0b01, 0x05), 1, &OpDispatchBuilder::VPHSUBOp<2>},
|
||||
{OPD(2, 0b01, 0x06), 1, &OpDispatchBuilder::VPHSUBOp<4>},
|
||||
{OPD(2, 0b01, 0x07), 1, &OpDispatchBuilder::VPHSUBSWOp},
|
||||
|
||||
{OPD(2, 0b01, 0x08), 1, &OpDispatchBuilder::VPSIGN<1>},
|
||||
{OPD(2, 0b01, 0x09), 1, &OpDispatchBuilder::VPSIGN<2>},
|
||||
{OPD(2, 0b01, 0x0A), 1, &OpDispatchBuilder::VPSIGN<4>},
|
||||
{OPD(2, 0b01, 0x0B), 1, &OpDispatchBuilder::VPMULHRSWOp},
|
||||
{OPD(2, 0b01, 0x0C), 1, &OpDispatchBuilder::VPERMILRegOp<4>},
|
||||
{OPD(2, 0b01, 0x0D), 1, &OpDispatchBuilder::VPERMILRegOp<8>},
|
||||
|
||||
{OPD(2, 0b01, 0x16), 1, &OpDispatchBuilder::VPERMDOp},
|
||||
{OPD(2, 0b01, 0x17), 1, &OpDispatchBuilder::PTestOp},
|
||||
{OPD(2, 0b01, 0x18), 1, &OpDispatchBuilder::VBROADCASTOp<4>},
|
||||
{OPD(2, 0b01, 0x19), 1, &OpDispatchBuilder::VBROADCASTOp<8>},
|
||||
{OPD(2, 0b01, 0x1A), 1, &OpDispatchBuilder::VBROADCASTOp<16>},
|
||||
@@ -6061,6 +5934,7 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
{OPD(2, 0b01, 0x33), 1, &OpDispatchBuilder::AVXExtendVectorElements<2, 4, false>},
|
||||
{OPD(2, 0b01, 0x34), 1, &OpDispatchBuilder::AVXExtendVectorElements<2, 8, false>},
|
||||
{OPD(2, 0b01, 0x35), 1, &OpDispatchBuilder::AVXExtendVectorElements<4, 8, false>},
|
||||
{OPD(2, 0b01, 0x36), 1, &OpDispatchBuilder::VPERMDOp},
|
||||
|
||||
{OPD(2, 0b01, 0x37), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VCMPGT, 8>},
|
||||
{OPD(2, 0b01, 0x38), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VSMIN, 1>},
|
||||
@@ -6100,6 +5974,9 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
{OPD(3, 0b01, 0x0A), 1, &OpDispatchBuilder::AVXVectorRound<4, true>},
|
||||
{OPD(3, 0b01, 0x0B), 1, &OpDispatchBuilder::AVXVectorRound<8, true>},
|
||||
{OPD(3, 0b01, 0x0C), 1, &OpDispatchBuilder::VPBLENDDOp},
|
||||
{OPD(3, 0b01, 0x0D), 1, &OpDispatchBuilder::VBLENDPDOp},
|
||||
{OPD(3, 0b01, 0x0E), 1, &OpDispatchBuilder::VPBLENDWOp},
|
||||
{OPD(3, 0b01, 0x0F), 1, &OpDispatchBuilder::VPALIGNROp},
|
||||
|
||||
{OPD(3, 0b01, 0x14), 1, &OpDispatchBuilder::PExtrOp<1>},
|
||||
{OPD(3, 0b01, 0x15), 1, &OpDispatchBuilder::PExtrOp<2>},
|
||||
@@ -6107,16 +5984,22 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
{OPD(3, 0b01, 0x17), 1, &OpDispatchBuilder::PExtrOp<4>},
|
||||
|
||||
{OPD(3, 0b01, 0x18), 1, &OpDispatchBuilder::VINSERTOp},
|
||||
{OPD(3, 0b01, 0x19), 1, &OpDispatchBuilder::VEXTRACT128Op},
|
||||
|
||||
{OPD(3, 0b01, 0x21), 1, &OpDispatchBuilder::VINSERTPSOp},
|
||||
|
||||
{OPD(3, 0b01, 0x38), 1, &OpDispatchBuilder::VINSERTOp},
|
||||
{OPD(3, 0b01, 0x39), 1, &OpDispatchBuilder::VEXTRACT128Op},
|
||||
|
||||
{OPD(3, 0b01, 0x40), 1, &OpDispatchBuilder::VDPPOp<4>},
|
||||
{OPD(3, 0b01, 0x41), 1, &OpDispatchBuilder::VDPPOp<8>},
|
||||
|
||||
{OPD(3, 0b01, 0x46), 1, &OpDispatchBuilder::VPERM2Op},
|
||||
|
||||
{OPD(3, 0b01, 0x4A), 1, &OpDispatchBuilder::AVXVectorVariableBlend<4>},
|
||||
{OPD(3, 0b01, 0x4B), 1, &OpDispatchBuilder::AVXVectorVariableBlend<8>},
|
||||
{OPD(3, 0b01, 0x4C), 1, &OpDispatchBuilder::AVXVectorVariableBlend<1>},
|
||||
|
||||
{OPD(3, 0b01, 0xDF), 1, &OpDispatchBuilder::VAESKeyGenAssistOp},
|
||||
};
|
||||
#undef OPD
|
||||
@@ -6186,19 +6069,19 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
void InstallOpcodeHandlers(Context::OperatingMode Mode) {
|
||||
constexpr std::tuple<uint8_t, uint8_t, X86Tables::OpDispatchPtr> BaseOpTable[] = {
|
||||
// Instructions
|
||||
{0x00, 6, &OpDispatchBuilder::ALUOp},
|
||||
{0x00, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_ADD, FEXCore::IR::IROps::OP_ATOMICFETCHADD, true>},
|
||||
|
||||
{0x08, 6, &OpDispatchBuilder::ALUOp},
|
||||
{0x08, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_OR, FEXCore::IR::IROps::OP_ATOMICFETCHOR, false>},
|
||||
|
||||
{0x10, 6, &OpDispatchBuilder::ADCOp<0>},
|
||||
|
||||
{0x18, 6, &OpDispatchBuilder::SBBOp<0>},
|
||||
|
||||
{0x20, 6, &OpDispatchBuilder::ALUOp},
|
||||
{0x20, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_AND, FEXCore::IR::IROps::OP_ATOMICFETCHAND, false>},
|
||||
|
||||
{0x28, 6, &OpDispatchBuilder::ALUOp},
|
||||
{0x28, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_SUB, FEXCore::IR::IROps::OP_ATOMICFETCHSUB, true>},
|
||||
|
||||
{0x30, 6, &OpDispatchBuilder::ALUOp},
|
||||
{0x30, 6, &OpDispatchBuilder::ALUOp<FEXCore::IR::IROps::OP_XOR, FEXCore::IR::IROps::OP_ATOMICFETCHXOR, false>},
|
||||
|
||||
{0x38, 6, &OpDispatchBuilder::CMPOp<0>},
|
||||
{0x50, 8, &OpDispatchBuilder::PUSHREGOp},
|
||||
@@ -6327,9 +6210,8 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
|
||||
{0x12, 2, &OpDispatchBuilder::MOVLPOp},
|
||||
{0x14, 1, &OpDispatchBuilder::PUNPCKLOp<4>},
|
||||
{0x15, 1, &OpDispatchBuilder::PUNPCKHOp<4>},
|
||||
{0x16, 1, &OpDispatchBuilder::MOVLHPSOp},
|
||||
{0x17, 1, &OpDispatchBuilder::MOVUPSOp},
|
||||
{0x28, 2, &OpDispatchBuilder::MOVUPSOp},
|
||||
{0x16, 2, &OpDispatchBuilder::MOVHPDOp},
|
||||
{0x28, 2, &OpDispatchBuilder::MOVAPSOp},
|
||||
{0x2A, 1, &OpDispatchBuilder::MMX_To_XMM_Vector_CVT_Int_To_Float<4, false>},
|
||||
{0x2B, 1, &OpDispatchBuilder::MOVVectorNTOp},
|
||||
{0x2C, 1, &OpDispatchBuilder::Vector_CVT_Float_To_Int<4, false, false>},
|
||||
@@ -6419,7 +6301,6 @@ void InstallOpcodeHandlers(Context::OperatingMode Mode) {
|
||||
{0xFE, 1, &OpDispatchBuilder::VectorALUOp<IR::OP_VADD, 4>},
|
||||
|
||||
// FEX reserved instructions
|
||||
{0x36, 1, &OpDispatchBuilder::SIGRETOp},
|
||||
{0x37, 1, &OpDispatchBuilder::CallbackReturnOp},
|
||||
};
|
||||
|
||||
|
||||
+65
-5
@@ -75,7 +75,7 @@ public:
|
||||
OrderedNode* flagsOpDestSigned{};
|
||||
OrderedNode* flagsOpSrcSigned{};
|
||||
|
||||
FEXCore::Context::Context *CTX{};
|
||||
FEXCore::Context::ContextImpl *CTX{};
|
||||
|
||||
// Used during new op bringup
|
||||
bool ShouldDump {false};
|
||||
@@ -149,7 +149,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
OpDispatchBuilder(FEXCore::Context::Context *ctx);
|
||||
OpDispatchBuilder(FEXCore::Context::ContextImpl *ctx);
|
||||
OpDispatchBuilder(FEXCore::Utils::IntrusivePooledAllocator &Allocator);
|
||||
|
||||
void ResetWorkingList();
|
||||
@@ -168,6 +168,7 @@ public:
|
||||
void MOVGPRNTOp(OpcodeArgs);
|
||||
void MOVVectorOp(OpcodeArgs);
|
||||
void MOVVectorNTOp(OpcodeArgs);
|
||||
template<FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask>
|
||||
void ALUOp(OpcodeArgs);
|
||||
void INTOp(OpcodeArgs);
|
||||
void SyscallOp(OpcodeArgs);
|
||||
@@ -176,7 +177,6 @@ public:
|
||||
void NOPOp(OpcodeArgs);
|
||||
void RETOp(OpcodeArgs);
|
||||
void IRETOp(OpcodeArgs);
|
||||
void SIGRETOp(OpcodeArgs);
|
||||
void CallbackReturnOp(OpcodeArgs);
|
||||
void SecondaryALUOp(OpcodeArgs);
|
||||
template<uint32_t SrcIndex>
|
||||
@@ -304,7 +304,6 @@ public:
|
||||
// SSE
|
||||
void MOVAPSOp(OpcodeArgs);
|
||||
void MOVUPSOp(OpcodeArgs);
|
||||
void MOVLHPSOp(OpcodeArgs);
|
||||
void MOVLPOp(OpcodeArgs);
|
||||
void MOVSHDUPOp(OpcodeArgs);
|
||||
void MOVSLDUPOp(OpcodeArgs);
|
||||
@@ -437,14 +436,22 @@ public:
|
||||
|
||||
void VANDNOp(OpcodeArgs);
|
||||
|
||||
void VBLENDPDOp(OpcodeArgs);
|
||||
void VPBLENDDOp(OpcodeArgs);
|
||||
void VPBLENDWOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void VBROADCASTOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void VDPPOp(OpcodeArgs);
|
||||
|
||||
void VEXTRACT128Op(OpcodeArgs);
|
||||
|
||||
template <IROps IROp, size_t ElementSize>
|
||||
void VHADDPOp(OpcodeArgs);
|
||||
template <size_t ElementSize>
|
||||
void VHSUBPOp(OpcodeArgs);
|
||||
|
||||
void VINSERTOp(OpcodeArgs);
|
||||
void VINSERTPSOp(OpcodeArgs);
|
||||
@@ -459,6 +466,9 @@ public:
|
||||
void VMOVSHDUPOp(OpcodeArgs);
|
||||
void VMOVSLDUPOp(OpcodeArgs);
|
||||
|
||||
void VMOVSDOp(OpcodeArgs);
|
||||
void VMOVSSOp(OpcodeArgs);
|
||||
|
||||
void VMOVVectorNTOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
@@ -467,18 +477,26 @@ public:
|
||||
template <size_t ElementSize>
|
||||
void VPACKUSOp(OpcodeArgs);
|
||||
|
||||
void VPBLENDDOp(OpcodeArgs);
|
||||
void VPALIGNROp(OpcodeArgs);
|
||||
|
||||
void VPERM2Op(OpcodeArgs);
|
||||
void VPERMDOp(OpcodeArgs);
|
||||
void VPERMQOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void VPERMILImmOp(OpcodeArgs);
|
||||
template <size_t ElementSize>
|
||||
void VPERMILRegOp(OpcodeArgs);
|
||||
|
||||
void VPHADDSWOp(OpcodeArgs);
|
||||
|
||||
void VPHMINPOSUWOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void VPHSUBOp(OpcodeArgs);
|
||||
void VPHSUBSWOp(OpcodeArgs);
|
||||
|
||||
void VPMADDWDOp(OpcodeArgs);
|
||||
|
||||
void VPMULHRSWOp(OpcodeArgs);
|
||||
|
||||
@@ -488,6 +506,11 @@ public:
|
||||
template <size_t ElementSize, bool Signed>
|
||||
void VPMULLOp(OpcodeArgs);
|
||||
|
||||
void VPSHUFBOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize, bool Low>
|
||||
void VPSHUFWOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void VPSLLOp(OpcodeArgs);
|
||||
void VPSLLDQOp(OpcodeArgs);
|
||||
@@ -515,6 +538,9 @@ public:
|
||||
template <size_t ElementSize>
|
||||
void VPSRLIOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void VSHUFOp(OpcodeArgs);
|
||||
|
||||
void VZEROOp(OpcodeArgs);
|
||||
|
||||
// X87 Ops
|
||||
@@ -755,6 +781,8 @@ private:
|
||||
FEXCore::IR::IROp_IRHeader *Current_Header{};
|
||||
OrderedNode *Current_HeaderNode{};
|
||||
|
||||
void ALUOpImpl(OpcodeArgs, FEXCore::IR::IROps ALUIROp, FEXCore::IR::IROps AtomicFetchOp, bool RequiresMask);
|
||||
|
||||
// Opcode helpers for generalizing behavior across VEX and non-VEX variants.
|
||||
|
||||
OrderedNode* ADDSUBPOpImpl(OpcodeArgs, size_t ElementSize,
|
||||
@@ -764,6 +792,9 @@ private:
|
||||
void AVXVectorScalarALUOpImpl(OpcodeArgs, IROps IROp, size_t ElementSize);
|
||||
void AVXVectorUnaryOpImpl(OpcodeArgs, IROps IROp, size_t ElementSize, bool Scalar);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void AVXVectorVariableBlend(OpcodeArgs);
|
||||
|
||||
OrderedNode* AESKeyGenAssistImpl(OpcodeArgs);
|
||||
OrderedNode* AESIMCImpl(OpcodeArgs);
|
||||
|
||||
@@ -774,6 +805,10 @@ private:
|
||||
OrderedNode* ExtendVectorElementsImpl(OpcodeArgs, size_t ElementSize,
|
||||
size_t DstElementSize, bool Signed);
|
||||
|
||||
OrderedNode* HSUBPOpImpl(OpcodeArgs, size_t ElementSize,
|
||||
const X86Tables::DecodedOperand& Src1Op,
|
||||
const X86Tables::DecodedOperand& Src2Op);
|
||||
|
||||
OrderedNode* InsertPSOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
|
||||
const X86Tables::DecodedOperand& Src2,
|
||||
const X86Tables::DecodedOperand& Imm);
|
||||
@@ -784,11 +819,24 @@ private:
|
||||
OrderedNode* PACKUSOpImpl(OpcodeArgs, size_t ElementSize,
|
||||
OrderedNode *Src1, OrderedNode *Src2);
|
||||
|
||||
OrderedNode* PALIGNROpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
|
||||
const X86Tables::DecodedOperand& Src2,
|
||||
const X86Tables::DecodedOperand& Imm);
|
||||
|
||||
OrderedNode* PHADDSOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
|
||||
const X86Tables::DecodedOperand& Src2);
|
||||
|
||||
OrderedNode* PHMINPOSUWOpImpl(OpcodeArgs);
|
||||
|
||||
OrderedNode* PHSUBOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
|
||||
const X86Tables::DecodedOperand& Src2, size_t ElementSize);
|
||||
|
||||
OrderedNode* PHSUBSOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1Op,
|
||||
const X86Tables::DecodedOperand& Src2Op);
|
||||
|
||||
OrderedNode* PMADDWDOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
|
||||
const X86Tables::DecodedOperand& Src2);
|
||||
|
||||
OrderedNode* PMULHRSWOpImpl(OpcodeArgs, OrderedNode *Src1, OrderedNode *Src2);
|
||||
|
||||
OrderedNode* PMULHWOpImpl(OpcodeArgs, bool Signed,
|
||||
@@ -797,6 +845,9 @@ private:
|
||||
OrderedNode* PMULLOpImpl(OpcodeArgs, size_t ElementSize, bool Signed,
|
||||
OrderedNode *Src1, OrderedNode *Src2);
|
||||
|
||||
OrderedNode* PSHUFBOpImpl(OpcodeArgs, const X86Tables::DecodedOperand& Src1,
|
||||
const X86Tables::DecodedOperand& Src2);
|
||||
|
||||
OrderedNode* PSIGNImpl(OpcodeArgs, size_t ElementSize,
|
||||
OrderedNode *Src1, OrderedNode *Src2);
|
||||
|
||||
@@ -812,6 +863,13 @@ private:
|
||||
OrderedNode* PSRLDOpImpl(OpcodeArgs, size_t ElementSize,
|
||||
OrderedNode *Src, OrderedNode *ShiftVec);
|
||||
|
||||
OrderedNode* SHUFOpImpl(OpcodeArgs, size_t ElementSize,
|
||||
const X86Tables::DecodedOperand& Src1,
|
||||
const X86Tables::DecodedOperand& Src2,
|
||||
const X86Tables::DecodedOperand& Imm);
|
||||
|
||||
void VMOVScalarOpImpl(OpcodeArgs, size_t ElementSize);
|
||||
|
||||
OrderedNode* VFCMPOpImpl(OpcodeArgs, size_t ElementSize, bool Scalar,
|
||||
OrderedNode *Src1, OrderedNode *Src2, uint8_t CompType);
|
||||
|
||||
@@ -831,6 +889,8 @@ private:
|
||||
#undef OpcodeArgs
|
||||
|
||||
OrderedNode *AppendSegmentOffset(OrderedNode *Value, uint32_t Flags, uint32_t DefaultPrefix = 0, bool Override = false);
|
||||
OrderedNode *GetSegment(uint32_t Flags, uint32_t DefaultPrefix = 0, bool Override = false);
|
||||
|
||||
void UpdatePrefixFromSegment(OrderedNode *Segment, uint32_t SegmentReg);
|
||||
|
||||
enum class MemoryAccessType {
|
||||
|
||||
@@ -280,7 +280,7 @@ void OpDispatchBuilder::VAESIMCOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESEncOp(OpcodeArgs) {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESEnc(Dest, Src);
|
||||
OrderedNode *Result = _VAESEnc(16, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -293,7 +293,7 @@ void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESEnc(State, Key);
|
||||
OrderedNode *Result = _VAESEnc(DstSize, State, Key);
|
||||
|
||||
if (Is128Bit) {
|
||||
Result = _VMov(16, Result);
|
||||
@@ -304,7 +304,7 @@ void OpDispatchBuilder::VAESEncOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESEncLastOp(OpcodeArgs) {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESEncLast(Dest, Src);
|
||||
OrderedNode *Result = _VAESEncLast(16, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -317,7 +317,7 @@ void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESEncLast(State, Key);
|
||||
OrderedNode *Result = _VAESEncLast(DstSize, State, Key);
|
||||
|
||||
if (Is128Bit) {
|
||||
Result = _VMov(16, Result);
|
||||
@@ -328,7 +328,7 @@ void OpDispatchBuilder::VAESEncLastOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESDecOp(OpcodeArgs) {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESDec(Dest, Src);
|
||||
OrderedNode *Result = _VAESDec(16, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESDec(State, Key);
|
||||
OrderedNode *Result = _VAESDec(DstSize, State, Key);
|
||||
|
||||
if (Is128Bit) {
|
||||
Result = _VMov(16, Result);
|
||||
@@ -352,7 +352,7 @@ void OpDispatchBuilder::VAESDecOp(OpcodeArgs) {
|
||||
void OpDispatchBuilder::AESDecLastOp(OpcodeArgs) {
|
||||
OrderedNode *Dest = LoadSource(FPRClass, Op, Op->Dest, Op->Flags, -1);
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESDecLast(Dest, Src);
|
||||
OrderedNode *Result = _VAESDecLast(16, Dest, Src);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ void OpDispatchBuilder::VAESDecLastOp(OpcodeArgs) {
|
||||
|
||||
OrderedNode *State = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Key = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
|
||||
OrderedNode *Result = _VAESDecLast(State, Key);
|
||||
OrderedNode *Result = _VAESDecLast(DstSize, State, Key);
|
||||
|
||||
if (Is128Bit) {
|
||||
Result = _VMov(16, Result);
|
||||
@@ -399,7 +399,7 @@ void OpDispatchBuilder::PCLMULQDQOp(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
const auto Selector = static_cast<uint8_t>(Op->Src[1].Data.Literal.Value);
|
||||
|
||||
auto Res = _PCLMUL(Dest, Src, Selector);
|
||||
auto Res = _PCLMUL(16, Dest, Src, Selector);
|
||||
StoreResult(FPRClass, Op, Res, -1);
|
||||
}
|
||||
|
||||
@@ -413,7 +413,7 @@ void OpDispatchBuilder::VPCLMULQDQOp(OpcodeArgs) {
|
||||
OrderedNode *Src2 = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
|
||||
const auto Selector = static_cast<uint8_t>(Op->Src[2].Data.Literal.Value);
|
||||
|
||||
OrderedNode *Res = _PCLMUL(Src1, Src2, Selector);
|
||||
OrderedNode *Res = _PCLMUL(DstSize, Src1, Src2, Selector);
|
||||
if (Is128Bit) {
|
||||
Res = _VMov(16, Res);
|
||||
}
|
||||
|
||||
@@ -271,10 +271,8 @@ void OpDispatchBuilder::CalculcateFlags_ADC(uint8_t SrcSize, OrderedNode *Res, O
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(Size - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
|
||||
// PF
|
||||
@@ -342,10 +340,8 @@ void OpDispatchBuilder::CalculcateFlags_SBB(uint8_t SrcSize, OrderedNode *Res, O
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(SrcSize * 8 - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
|
||||
// PF
|
||||
@@ -412,10 +408,8 @@ void OpDispatchBuilder::CalculcateFlags_SUB(uint8_t SrcSize, OrderedNode *Res, O
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(SrcSize * 8 - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
|
||||
// PF
|
||||
@@ -469,10 +463,8 @@ void OpDispatchBuilder::CalculcateFlags_ADD(uint8_t SrcSize, OrderedNode *Res, O
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(SrcSize * 8 - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
|
||||
// PF
|
||||
@@ -583,10 +575,8 @@ void OpDispatchBuilder::CalculcateFlags_Logical(uint8_t SrcSize, OrderedNode *Re
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(SrcSize * 8 - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
|
||||
// PF
|
||||
@@ -750,10 +740,8 @@ void OpDispatchBuilder::CalculcateFlags_SignShiftRight(uint8_t SrcSize, OrderedN
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(SrcSize * 8 - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
COND_FLAG_SET(Src2, RFLAG_SF_LOC, LshrOp);
|
||||
auto SignBitOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
COND_FLAG_SET(Src2, RFLAG_SF_LOC, SignBitOp);
|
||||
}
|
||||
|
||||
// OF
|
||||
@@ -802,15 +790,14 @@ void OpDispatchBuilder::CalculcateFlags_ShiftLeftImmediate(uint8_t SrcSize, Orde
|
||||
|
||||
// SF
|
||||
{
|
||||
auto LshrOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignOp);
|
||||
|
||||
// OF
|
||||
// In the case of left shift. OF is only set from the result of <Top Source Bit> XOR <Top Result Bit>
|
||||
if (Shift == 1) {
|
||||
auto SourceBit = _Bfe(1, SrcSize * 8 - 1, Src1);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_OF_LOC>(_Xor(SourceBit, LshrOp));
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_OF_LOC>(_Xor(SourceBit, SignOp));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -851,10 +838,8 @@ void OpDispatchBuilder::CalculcateFlags_SignShiftRightImmediate(uint8_t SrcSize,
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(SrcSize * 8 - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignBitOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignBitOp);
|
||||
|
||||
// OF
|
||||
// Only defined when Shift is 1 else undefined
|
||||
@@ -902,10 +887,8 @@ void OpDispatchBuilder::CalculcateFlags_ShiftRightImmediate(uint8_t SrcSize, Ord
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBitConst = _Constant(SrcSize * 8 - 1);
|
||||
|
||||
auto LshrOp = _Lshr(Res, SignBitConst);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(LshrOp);
|
||||
auto SignBitOp = _Bfe(1, SrcSize * 8 - 1, Res);
|
||||
SetRFLAG<FEXCore::X86State::RFLAG_SF_LOC>(SignBitOp);
|
||||
}
|
||||
|
||||
// OF
|
||||
@@ -1115,10 +1098,8 @@ void OpDispatchBuilder::CalculcateFlags_BLSI(uint8_t SrcSize, OrderedNode *Src)
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBit = _Constant(SrcSize * 8 - 1);
|
||||
auto SFOp = _Lshr(Src, SignBit);
|
||||
|
||||
SetRFLAG<X86State::RFLAG_SF_LOC>(SFOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Src);
|
||||
SetRFLAG<X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1174,10 +1155,8 @@ void OpDispatchBuilder::CalculcateFlags_BLSR(uint8_t SrcSize, OrderedNode *Resul
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SignBit = _Constant(SrcSize * 8 - 1);
|
||||
auto SFOp = _Lshr(Result, SignBit);
|
||||
|
||||
SetRFLAG<X86State::RFLAG_SF_LOC>(SFOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Result);
|
||||
SetRFLAG<X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1230,9 +1209,8 @@ void OpDispatchBuilder::CalculcateFlags_BZHI(uint8_t SrcSize, OrderedNode *Resul
|
||||
|
||||
// SF
|
||||
{
|
||||
auto SFOp = _Lshr(Result, Bounds);
|
||||
|
||||
SetRFLAG<X86State::RFLAG_SF_LOC>(SFOp);
|
||||
auto SignOp = _Bfe(1, SrcSize * 8 - 1, Result);
|
||||
SetRFLAG<X86State::RFLAG_SF_LOC>(SignOp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+640
-166
File diff suppressed because it is too large.
Load diff
@@ -1388,7 +1388,7 @@ void OpDispatchBuilder::X87FCMOV(OpcodeArgs) {
|
||||
|
||||
auto a = _LoadContextIndexed(top, 16, MMBaseOffset(), 16, FPRClass);
|
||||
auto b = _LoadContextIndexed(arg, 16, MMBaseOffset(), 16, FPRClass);
|
||||
auto Result = _VBSL(VecCond, b, a);
|
||||
auto Result = _VBSL(16, VecCond, b, a);
|
||||
|
||||
// Write to ST[TOP]
|
||||
_StoreContextIndexed(Result, top, 16, MMBaseOffset(), 16, FPRClass);
|
||||
|
||||
+29
-8
@@ -23,17 +23,38 @@ X86GeneratedCode::X86GeneratedCode() {
|
||||
// Allocate a page for our emulated guest
|
||||
CodePtr = AllocateGuestCodeSpace(CODE_SIZE);
|
||||
|
||||
SignalReturn = reinterpret_cast<uint64_t>(CodePtr);
|
||||
SignalReturnRT = reinterpret_cast<uint64_t>(CodePtr) + 3;
|
||||
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr) + 6;
|
||||
|
||||
const std::vector<uint8_t> SignalReturnCode = {
|
||||
0x0F, 0x36, 0x0, // SIGRET FEX instruction (Non-RT)
|
||||
0x0F, 0x36, 0x1, // SIGRET FEX instruction (RT)
|
||||
constexpr std::array<uint8_t, 2> SignalReturnCode = {
|
||||
0x0F, 0x37, // CALLBACKRET FEX Instruction
|
||||
};
|
||||
|
||||
memcpy(CodePtr, &SignalReturnCode.at(0), SignalReturnCode.size());
|
||||
// Signal return handlers need to be bit-exact to what the Linux kernel provides in VDSO.
|
||||
// GDB and unwinding libraries key off of these instructions to understand if the stack frame is a signal frame or not.
|
||||
// This two code sections match exactly what libSegFault expects.
|
||||
//
|
||||
// Typically this handlers are provided by the 32-bit VDSO thunk library, but that isn't available in all cases.
|
||||
// Falling back to this generated code segment still allows a backtrace to work, just might not show
|
||||
// the symbol as VDSO since there is no ELF to parse.
|
||||
constexpr std::array<uint8_t, 9> sigreturn_32_code = {
|
||||
0x58, // pop eax
|
||||
0xb8, 0x77, 0x00, 0x00, 0x00, // mov eax, 0x77
|
||||
0xcd, 0x80, // int 0x80
|
||||
0x90, // nop
|
||||
};
|
||||
|
||||
constexpr std::array<uint8_t, 7> rt_sigreturn_32_code = {
|
||||
0xb8, 0xad, 0x00, 0x00, 0x00, // mov eax, 0xad
|
||||
0xcd, 0x80, // int 0x80
|
||||
};
|
||||
|
||||
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr);
|
||||
sigreturn_32 = CallbackReturn + SignalReturnCode.size();
|
||||
rt_sigreturn_32 = sigreturn_32 + sigreturn_32_code.size();
|
||||
|
||||
memcpy(reinterpret_cast<void*>(CallbackReturn), &SignalReturnCode.at(0), SignalReturnCode.size());
|
||||
memcpy(reinterpret_cast<void*>(sigreturn_32), &sigreturn_32_code.at(0), sigreturn_32_code.size());
|
||||
memcpy(reinterpret_cast<void*>(rt_sigreturn_32), &rt_sigreturn_32_code.at(0), rt_sigreturn_32_code.size());
|
||||
|
||||
mprotect(CodePtr, CODE_SIZE, PROT_READ);
|
||||
}
|
||||
|
||||
X86GeneratedCode::~X86GeneratedCode() {
|
||||
|
||||
+2
-2
@@ -15,9 +15,9 @@ public:
|
||||
X86GeneratedCode();
|
||||
~X86GeneratedCode();
|
||||
|
||||
uint64_t SignalReturn{};
|
||||
uint64_t SignalReturnRT{};
|
||||
uint64_t CallbackReturn{};
|
||||
uint64_t sigreturn_32{};
|
||||
uint64_t rt_sigreturn_32{};
|
||||
|
||||
private:
|
||||
void *CodePtr{};
|
||||
|
||||
@@ -42,7 +42,7 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
|
||||
{0x14, 1, X86InstInfo{"UNPCKLPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x15, 1, X86InstInfo{"UNPCKHPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x16, 1, X86InstInfo{"MOVLHPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x17, 1, X86InstInfo{"MOVHPS", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_SF_HIGH_XMM_REG | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x17, 1, X86InstInfo{"MOVHPS", TYPE_INST, GenFlagsSizes(SIZE_64BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{0x18, 1, X86InstInfo{"", TYPE_GROUP_16, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
{0x19, 7, X86InstInfo{"NOP", TYPE_INST, FLAGS_DEBUG | FLAGS_MODRM | FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
|
||||
@@ -64,6 +64,7 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
|
||||
{0x33, 1, X86InstInfo{"RDPMC", TYPE_PRIV, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
{0x34, 1, X86InstInfo{"SYSENTER", TYPE_PRIV, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
{0x35, 1, X86InstInfo{"SYSEXIT", TYPE_PRIV, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
{0x36, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
{0x38, 1, X86InstInfo{"", TYPE_0F38_TABLE, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
{0x39, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
{0x3A, 1, X86InstInfo{"", TYPE_0F3A_TABLE, FLAGS_NO_OVERLAY, 0, nullptr}},
|
||||
@@ -257,8 +258,6 @@ void InitializeSecondaryTables(Context::OperatingMode Mode) {
|
||||
|
||||
// FEX reserved instructions
|
||||
// Unused x86 encoding instruction.
|
||||
// Used by FEX to know when to do a signal return
|
||||
{0x36, 1, X86InstInfo{"SIGRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 1, nullptr}},
|
||||
|
||||
{0x37, 1, X86InstInfo{"CALLBACKRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0, nullptr}},
|
||||
|
||||
|
||||
+28
-30
@@ -19,13 +19,13 @@ void InitializeVEXTables() {
|
||||
// VEX Map 1
|
||||
{OPD(1, 0b00, 0x10), 1, X86InstInfo{"VMOVUPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x10), 1, X86InstInfo{"VMOVUPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b10, 0x10), 1, X86InstInfo{"VMOVSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x10), 1, X86InstInfo{"VMOVSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b10, 0x10), 1, X86InstInfo{"VMOVSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x10), 1, X86InstInfo{"VMOVSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(1, 0b00, 0x11), 1, X86InstInfo{"VMOVUPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x11), 1, X86InstInfo{"VMOVUPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b10, 0x11), 1, X86InstInfo{"VMOVSS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x11), 1, X86InstInfo{"VMOVSD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b10, 0x11), 1, X86InstInfo{"VMOVSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x11), 1, X86InstInfo{"VMOVSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(1, 0b00, 0x12), 1, X86InstInfo{"VMOVLPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x12), 1, X86InstInfo{"VMOVLPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_MEM_ONLY | FLAGS_XMM_FLAGS | FLAGS_VEX_1ST_SRC, 0, nullptr}},
|
||||
@@ -83,9 +83,9 @@ void InitializeVEXTables() {
|
||||
{OPD(1, 0b01, 0x66), 1, X86InstInfo{"VPCMPGTD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x67), 1, X86InstInfo{"VPACKUSWB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(1, 0b01, 0x70), 1, X86InstInfo{"VPSHUFD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b10, 0x70), 1, X86InstInfo{"VPSHUFHW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x70), 1, X86InstInfo{"VPSHUFLW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x70), 1, X86InstInfo{"VPSHUFD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(1, 0b10, 0x70), 1, X86InstInfo{"VPSHUFHW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(1, 0b11, 0x70), 1, X86InstInfo{"VPSHUFLW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(1, 0b01, 0x71), 1, X86InstInfo{"", TYPE_VEX_GROUP_12, FLAGS_NONE, 0, nullptr}}, // VEX Group 12
|
||||
{OPD(1, 0b01, 0x72), 1, X86InstInfo{"", TYPE_VEX_GROUP_13, FLAGS_NONE, 0, nullptr}}, // VEX Group 13
|
||||
@@ -105,8 +105,8 @@ void InitializeVEXTables() {
|
||||
{OPD(1, 0b01, 0xC4), 1, X86InstInfo{"VPINSRW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xC5), 1, X86InstInfo{"VPEXTRW", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_REG_ONLY | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(1, 0b00, 0xC6), 1, X86InstInfo{"VSHUFPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xC6), 1, X86InstInfo{"VSHUFPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b00, 0xC6), 1, X86InstInfo{"VSHUFPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(1, 0b01, 0xC6), 1, X86InstInfo{"VSHUFPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
// The above ops are defined from `Table A-17. VEX Opcode Map 1, Low Nibble = [0h:7h]` of AMD Architecture programmer's manual Volume 3
|
||||
// This table doesn't state which VEX.pp is for which instruction
|
||||
@@ -184,8 +184,8 @@ void InitializeVEXTables() {
|
||||
{OPD(1, 0b01, 0x7C), 1, X86InstInfo{"VHADDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x7C), 1, X86InstInfo{"VHADDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(1, 0b01, 0x7D), 1, X86InstInfo{"VHSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x7D), 1, X86InstInfo{"VHSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0x7D), 1, X86InstInfo{"VHSUBPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b11, 0x7D), 1, X86InstInfo{"VHSUBPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(1, 0b01, 0x7E), 1, X86InstInfo{"VMOV*", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b10, 0x7E), 1, X86InstInfo{"VMOVQ", TYPE_INST, GenFlagsSameSize(SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
@@ -246,7 +246,7 @@ void InitializeVEXTables() {
|
||||
{OPD(1, 0b01, 0xF2), 1, X86InstInfo{"VPSLLD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xF3), 1, X86InstInfo{"VPSLLQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xF4), 1, X86InstInfo{"VPMULUDQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xF5), 1, X86InstInfo{"VPMADDWD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xF5), 1, X86InstInfo{"VPMADDWD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xF6), 1, X86InstInfo{"VPSADBW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(1, 0b01, 0xF7), 1, X86InstInfo{"VMASKMOVDQU", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_REG_ONLY | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
@@ -259,27 +259,27 @@ void InitializeVEXTables() {
|
||||
{OPD(1, 0b01, 0xFE), 1, X86InstInfo{"VPADDD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
// VEX Map 2
|
||||
{OPD(2, 0b01, 0x00), 1, X86InstInfo{"VPSHUFB", TYPE_UNDEC, FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x00), 1, X86InstInfo{"VPSHUFB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x01), 1, X86InstInfo{"VPHADDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x02), 1, X86InstInfo{"VPHADDD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x03), 1, X86InstInfo{"VPHADDSW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x03), 1, X86InstInfo{"VPHADDSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x04), 1, X86InstInfo{"VPMADDUBSW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x05), 1, X86InstInfo{"VPHSUBW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x06), 1, X86InstInfo{"VPHSUBD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x07), 1, X86InstInfo{"VPHSUBSW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x07), 1, X86InstInfo{"VPHSUBSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x08), 1, X86InstInfo{"VPSIGNB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x09), 1, X86InstInfo{"VPSIGNW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0A), 1, X86InstInfo{"VPSIGND", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0B), 1, X86InstInfo{"VPMULHRSW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0C), 1, X86InstInfo{"VPERMILPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0D), 1, X86InstInfo{"VPERMILPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0C), 1, X86InstInfo{"VPERMILPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0D), 1, X86InstInfo{"VPERMILPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0E), 1, X86InstInfo{"VTESTPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x0F), 1, X86InstInfo{"VTESTPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x13), 1, X86InstInfo{"VCVTPH2PS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x16), 1, X86InstInfo{"VPERMPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x17), 1, X86InstInfo{"VPTEST", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x16), 1, X86InstInfo{"VPERMPS", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x17), 1, X86InstInfo{"VPTEST", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x18), 1, X86InstInfo{"VBROADCASTSS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x19), 1, X86InstInfo{"VBROADCASTSD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
@@ -310,7 +310,7 @@ void InitializeVEXTables() {
|
||||
{OPD(2, 0b01, 0x33), 1, X86InstInfo{"VPMOVZXWD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x34), 1, X86InstInfo{"VPMOVZXWQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x35), 1, X86InstInfo{"VPMOVZXDQ", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x36), 1, X86InstInfo{"VPERMD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x36), 1, X86InstInfo{"VPERMD", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x37), 1, X86InstInfo{"VPCMPGTQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x38), 1, X86InstInfo{"VPMINSB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
@@ -417,9 +417,9 @@ void InitializeVEXTables() {
|
||||
{OPD(3, 0b01, 0x0A), 1, X86InstInfo{"VROUNDSS", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_32BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x0B), 1, X86InstInfo{"VROUNDSD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x0C), 1, X86InstInfo{"VBLENDPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x0D), 1, X86InstInfo{"VBLENDPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x0E), 1, X86InstInfo{"VBLENDW", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x0F), 1, X86InstInfo{"VPALIGNR", TYPE_INST, FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x0D), 1, X86InstInfo{"VBLENDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x0E), 1, X86InstInfo{"VPBLENDW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x0F), 1, X86InstInfo{"VPALIGNR", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x14), 1, X86InstInfo{"VPEXTRB", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x15), 1, X86InstInfo{"VPEXTRW", TYPE_INST, GenFlagsSizes(SIZE_16BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
@@ -427,7 +427,7 @@ void InitializeVEXTables() {
|
||||
{OPD(3, 0b01, 0x17), 1, X86InstInfo{"VEXTRACTPS", TYPE_INST, GenFlagsSizes(SIZE_32BIT, SIZE_128BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_DST_GPR | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x18), 1, X86InstInfo{"VINSERTF128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x19), 1, X86InstInfo{"VEXTRACTF128", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x19), 1, X86InstInfo{"VEXTRACTF128", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_256BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x1D), 1, X86InstInfo{"VCVTPS2PH", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x20), 1, X86InstInfo{"VPINSRB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
@@ -435,7 +435,7 @@ void InitializeVEXTables() {
|
||||
{OPD(3, 0b01, 0x22), 1, X86InstInfo{"VPINSRD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x38), 1, X86InstInfo{"VINSERTI128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x39), 1, X86InstInfo{"VEXTRACTI128", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x39), 1, X86InstInfo{"VEXTRACTI128", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_256BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x40), 1, X86InstInfo{"VDPPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x41), 1, X86InstInfo{"VDPPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
@@ -443,11 +443,9 @@ void InitializeVEXTables() {
|
||||
{OPD(3, 0b01, 0x44), 1, X86InstInfo{"VPCLMULQDQ", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x46), 1, X86InstInfo{"VPERM2I128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x48), 1, X86InstInfo{"VPERMILzz2PS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x49), 1, X86InstInfo{"VPERMILzz2PD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x4A), 1, X86InstInfo{"VBLENDVPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x4B), 1, X86InstInfo{"VBLENDVPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x4C), 1, X86InstInfo{"VBLENDVB", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x4A), 1, X86InstInfo{"VBLENDVPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x4B), 1, X86InstInfo{"VBLENDVPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x4C), 1, X86InstInfo{"VPBLENDVB", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
|
||||
{OPD(3, 0b01, 0x5C), 1, X86InstInfo{"VFMADDSUBPS", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x5D), 1, X86InstInfo{"VFMADDSUBPD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
+4
-4
@@ -179,7 +179,7 @@ namespace FEXCore {
|
||||
};
|
||||
|
||||
auto args = reinterpret_cast<args_t*>(argsv);
|
||||
auto CTX = Thread->CTX;
|
||||
auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(args->original_callee, "Tried to link null pointer address to guest function");
|
||||
LOGMAN_THROW_AA_FMT(args->target_addr, "Tried to link address to null pointer guest function");
|
||||
@@ -264,7 +264,7 @@ namespace FEXCore {
|
||||
}
|
||||
|
||||
static void LoadLib(void *ArgsV) {
|
||||
auto CTX = Thread->CTX;
|
||||
auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
|
||||
|
||||
auto Args = reinterpret_cast<LoadlibArgs*>(ArgsV);
|
||||
|
||||
@@ -321,7 +321,7 @@ namespace FEXCore {
|
||||
|
||||
auto &[Name, rv] = *reinterpret_cast<ArgsRV_t*>(ArgsRV);
|
||||
|
||||
auto CTX = Thread->CTX;
|
||||
auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
|
||||
auto That = reinterpret_cast<ThunkHandler_impl*>(CTX->ThunkHandler.get());
|
||||
|
||||
{
|
||||
@@ -385,7 +385,7 @@ namespace FEXCore {
|
||||
HostToGuestTrampolinePtr* MakeHostTrampolineForGuestFunction(void* HostPacker, uintptr_t GuestTarget, uintptr_t GuestUnpacker) {
|
||||
LOGMAN_THROW_AA_FMT(GuestTarget, "Tried to create host-trampoline to null pointer guest function");
|
||||
|
||||
const auto CTX = Thread->CTX;
|
||||
const auto CTX = static_cast<Context::ContextImpl*>(Thread->CTX);
|
||||
const auto ThunkHandler = reinterpret_cast<ThunkHandler_impl *>(CTX->ThunkHandler.get());
|
||||
|
||||
const GuestcallInfo gci = { GuestUnpacker, GuestTarget };
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ $end_info$
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core {
|
||||
|
||||
+5
-2
@@ -17,6 +17,9 @@
|
||||
namespace FEXCore::Core {
|
||||
struct DebugData;
|
||||
}
|
||||
namespace FEXCore::Context {
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::IR {
|
||||
class RegisterAllocationData;
|
||||
@@ -87,7 +90,7 @@ namespace FEXCore::IR {
|
||||
class AOTIRCaptureCache final {
|
||||
public:
|
||||
|
||||
AOTIRCaptureCache(FEXCore::Context::Context *ctx) : CTX {ctx} {}
|
||||
AOTIRCaptureCache(FEXCore::Context::ContextImpl *ctx) : CTX {ctx} {}
|
||||
|
||||
void FinalizeAOTIRCache();
|
||||
void AOTIRCaptureCacheWriteoutQueue_Flush();
|
||||
@@ -131,7 +134,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
|
||||
private:
|
||||
FEXCore::Context::Context *CTX;
|
||||
FEXCore::Context::ContextImpl *CTX;
|
||||
|
||||
std::shared_mutex AOTIRCacheLock;
|
||||
std::shared_mutex AOTIRCaptureCacheWriteoutLock;
|
||||
|
||||
+35
-16
@@ -22,7 +22,7 @@
|
||||
"",
|
||||
"Eg:",
|
||||
"IR op with no result and no arguments",
|
||||
" SignalReturn",
|
||||
" CallbackReturn",
|
||||
"",
|
||||
"IR op with result and no arguments",
|
||||
" GPR = ProcessorID",
|
||||
@@ -264,9 +264,6 @@
|
||||
"Break BreakDefinition:$Reason": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"SignalReturn i8:$IsRT": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"CallbackReturn": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
@@ -479,6 +476,14 @@
|
||||
]
|
||||
},
|
||||
|
||||
"GPR = MemSet i1:$IsAtomic, u8:$Size, GPR:$Prefix, GPR:$Addr, GPR:$Value, GPR:$Length, GPR:$Direction": {
|
||||
"Desc": ["Duplicates behaviour of x86 STOS repeat",
|
||||
"Returns the final address that gets generated without the prefix appended."
|
||||
],
|
||||
"HasSideEffects": true,
|
||||
"DestSize": "8"
|
||||
},
|
||||
|
||||
"CacheLineClear GPR:$Addr, i1:$Serialize": {
|
||||
"Desc": ["Does a 64 byte cacheline clear at the address specified",
|
||||
"Only clears the data cachelines. Doesn't do any zeroing",
|
||||
@@ -1191,6 +1196,14 @@
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VTrn u8:#RegisterSize, u8:#ElementSize, FPR:$VectorLower, FPR:$VectorUpper": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
"FPR = VTrn2 u8:#RegisterSize, u8:#ElementSize, FPR:$VectorLower, FPR:$VectorUpper": {
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
|
||||
"FPR = VFAdd u8:#RegisterSize, u8:#ElementSize, FPR:$Vector1, FPR:$Vector2": {
|
||||
"DestSize": "RegisterSize",
|
||||
@@ -1353,12 +1366,12 @@
|
||||
"DestSize": "RegisterSize"
|
||||
},
|
||||
|
||||
"FPR = VBSL FPR:$VectorMask, FPR:$VectorTrue, FPR:$VectorFalse": {
|
||||
"FPR = VBSL u8:#RegisterSize, FPR:$VectorMask, FPR:$VectorTrue, FPR:$VectorFalse": {
|
||||
"Desc": ["Does a vector bitwise select.",
|
||||
"If the bit in the field is 1 then the corresponding bit is pulled from VectorTrue",
|
||||
"If the bit in the field is 0 then the corresponding bit is pulled from VectorFalse"
|
||||
],
|
||||
"DestSize": "16"
|
||||
"DestSize": "RegisterSize"
|
||||
}
|
||||
},
|
||||
"Conv": {
|
||||
@@ -1370,6 +1383,12 @@
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
|
||||
"FPR = VDupFromGPR u8:#RegisterSize, u8:#ElementSize, GPR:$Src": {
|
||||
"Desc": ["Broadcasts a value in a GPR into each ElementSize-sized element in a vector"],
|
||||
"DestSize": "RegisterSize",
|
||||
"NumElements": "RegisterSize / ElementSize"
|
||||
},
|
||||
|
||||
"FPR = Float_FromGPR_S u8:#DstElementSize, u8:$SrcElementSize, GPR:$Src": {
|
||||
"Desc": ["Scalar op: Converts signed GPR to Scalar float",
|
||||
"Zeroes the upper bits of the vector register"
|
||||
@@ -1418,21 +1437,21 @@
|
||||
"Desc": "Does a stage of the inverse mix column transformation",
|
||||
"DestSize": "16"
|
||||
},
|
||||
"FPR = VAESEnc FPR:$State, FPR:$Key": {
|
||||
"FPR = VAESEnc u8:#RegisterSize, FPR:$State, FPR:$Key": {
|
||||
"Desc": "Does a step of AES encryption",
|
||||
"DestSize": "16"
|
||||
"DestSize": "RegisterSize"
|
||||
},
|
||||
"FPR = VAESEncLast FPR:$State, FPR:$Key": {
|
||||
"FPR = VAESEncLast u8:#RegisterSize, FPR:$State, FPR:$Key": {
|
||||
"Desc": "Does the last step of AES encryption",
|
||||
"DestSize": "16"
|
||||
"DestSize": "RegisterSize"
|
||||
},
|
||||
"FPR = VAESDec FPR:$State, FPR:$Key": {
|
||||
"FPR = VAESDec u8:#RegisterSize, FPR:$State, FPR:$Key": {
|
||||
"Desc": "Does a step of AES decryption",
|
||||
"DestSize": "16"
|
||||
"DestSize": "RegisterSize"
|
||||
},
|
||||
"FPR = VAESDecLast FPR:$State, FPR:$Key": {
|
||||
"FPR = VAESDecLast u8:#RegisterSize, FPR:$State, FPR:$Key": {
|
||||
"Desc": "Does the last step of AES decryption",
|
||||
"DestSize": "16"
|
||||
"DestSize": "RegisterSize"
|
||||
},
|
||||
"FPR = VAESKeyGenAssist FPR:$Src, u8:$RCON": {
|
||||
"Desc": "Assists in key generation",
|
||||
@@ -1443,7 +1462,7 @@
|
||||
],
|
||||
"DestSize": "std::max<uint8_t>(4, GetOpSize(_Src1))"
|
||||
},
|
||||
"FPR = PCLMUL FPR:$Src1, FPR:$Src2, u8:$Selector": {
|
||||
"FPR = PCLMUL u8:#RegisterSize, FPR:$Src1, FPR:$Src2, u8:$Selector": {
|
||||
"Desc": [
|
||||
"Performs carryless multiplication of 64-bit elements depending on the selector.",
|
||||
"Selector = 0b00000000: Uses low 64-bit elements from both input vectors",
|
||||
@@ -1451,7 +1470,7 @@
|
||||
"Selector = 0b00010000: Uses low 64-bit element from Src1 and high 64-bit element from Src2",
|
||||
"Selector = 0b00010001: Uses high 64-bit elements from both input vectors"
|
||||
],
|
||||
"DestSize": "16"
|
||||
"DestSize": "RegisterSize"
|
||||
}
|
||||
},
|
||||
"F64": {
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@ $end_info$
|
||||
namespace FEXCore::IR {
|
||||
class IREmitter;
|
||||
|
||||
void PassManager::AddDefaultPasses(FEXCore::Context::Context *ctx, bool InlineConstants, bool StaticRegisterAllocation) {
|
||||
void PassManager::AddDefaultPasses(FEXCore::Context::ContextImpl *ctx, bool InlineConstants, bool StaticRegisterAllocation) {
|
||||
FEX_CONFIG_OPT(DisablePasses, O0);
|
||||
|
||||
if (!DisablePasses()) {
|
||||
|
||||
+5
-1
@@ -14,6 +14,10 @@ $end_info$
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace FEXCore::Context {
|
||||
class ContextImpl;
|
||||
}
|
||||
|
||||
namespace FEXCore::HLE {
|
||||
class SyscallHandler;
|
||||
}
|
||||
@@ -40,7 +44,7 @@ protected:
|
||||
class PassManager final {
|
||||
friend class SyscallOptimization;
|
||||
public:
|
||||
void AddDefaultPasses(FEXCore::Context::Context *ctx, bool InlineConstants, bool StaticRegisterAllocation);
|
||||
void AddDefaultPasses(FEXCore::Context::ContextImpl *ctx, bool InlineConstants, bool StaticRegisterAllocation);
|
||||
void AddDefaultValidationPasses();
|
||||
Pass* InsertPass(std::unique_ptr<Pass> Pass, std::string Name = "") {
|
||||
Pass->RegisterPassManager(this);
|
||||
|
||||
+2
-2
@@ -219,8 +219,8 @@ namespace {
|
||||
|
||||
ContextClassification->emplace_back(ContextMemberInfo{
|
||||
ContextMemberClassification {
|
||||
offsetof(FEXCore::Core::CPUState, _pad2),
|
||||
sizeof(FEXCore::Core::CPUState::_pad2),
|
||||
offsetof(FEXCore::Core::CPUState, InlineJITBlockHeader),
|
||||
sizeof(FEXCore::Core::CPUState::InlineJITBlockHeader),
|
||||
},
|
||||
ACCESS_INVALID,
|
||||
FEXCore::IR::InvalidClass,
|
||||
|
||||
+39
-4
@@ -36,7 +36,7 @@ namespace CPU {
|
||||
struct CPUBackendFeatures {
|
||||
bool SupportsStaticRegisterAllocation = false;
|
||||
};
|
||||
|
||||
|
||||
class CPUBackend {
|
||||
public:
|
||||
struct CodeBuffer {
|
||||
@@ -55,6 +55,42 @@ namespace CPU {
|
||||
* @return The name of this backend
|
||||
*/
|
||||
[[nodiscard]] virtual std::string GetName() = 0;
|
||||
|
||||
struct CompiledCode {
|
||||
// Where this code block begins.
|
||||
uint8_t* BlockBegin;
|
||||
/**
|
||||
* The function entrypoint to this codeblock.
|
||||
*
|
||||
* This may or may not equal `BlockBegin` above. Depending on the CPU backend, it may stick data
|
||||
* prior to the BlockEntry.
|
||||
*
|
||||
* Is actually a function pointer of type `void (FEXCore::Core::ThreadState *Thread)`
|
||||
*/
|
||||
uint8_t* BlockEntry;
|
||||
// The total size of the codeblock from [BlockBegin, BlockBegin+Size).
|
||||
size_t Size;
|
||||
};
|
||||
|
||||
// Header that can live at the start of a JIT block.
|
||||
// We want the header to be quite small, with most data living in the tail object.
|
||||
struct JITCodeHeader {
|
||||
// Offset from the start of this header to where the tail lives.
|
||||
// Only 32-bit since the tail block won't ever be more than 4GB away.
|
||||
uint32_t OffsetToBlockTail;
|
||||
};
|
||||
|
||||
// Header that can live at the end of the JIT block.
|
||||
// For any state reconstruction or other data, this is where it should live.
|
||||
// Any data that is explicitly tied to the JIT code and needs to be cached with it
|
||||
// should end up in this data structure.
|
||||
struct JITCodeTail {
|
||||
// The total size of the codeblock from [BlockBegin, BlockBegin+Size).
|
||||
size_t Size;
|
||||
// RIP that the block's entry comes from.
|
||||
uint64_t RIP;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Tells this CPUBackend to compile code for the provided IR and DebugData
|
||||
*
|
||||
@@ -69,10 +105,9 @@ namespace CPU {
|
||||
* @param IR - IR that maps to the IR for this RIP
|
||||
* @param DebugData - Debug data that is available for this IR indirectly
|
||||
*
|
||||
* @return An executable function pointer that is theoretically compiled from this point.
|
||||
* Is actually a function pointer of type `void (FEXCore::Core::ThreadState *Thread)
|
||||
* @return Information about the compiled code block.
|
||||
*/
|
||||
[[nodiscard]] virtual void *CompileCode(uint64_t Entry,
|
||||
[[nodiscard]] virtual CompiledCode CompileCode(uint64_t Entry,
|
||||
FEXCore::IR::IRListView const *IR,
|
||||
FEXCore::Core::DebugData *DebugData,
|
||||
FEXCore::IR::RegisterAllocationData *RAData, bool GDBEnabled) = 0;
|
||||
|
||||
+214
-212
@@ -41,7 +41,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class Context;
|
||||
enum ExitReason {
|
||||
EXIT_NONE,
|
||||
EXIT_WAITING,
|
||||
@@ -69,224 +69,226 @@ namespace FEXCore::Context {
|
||||
std::unique_lock<std::shared_mutex> lock;
|
||||
};
|
||||
|
||||
struct VDSOSigReturn {
|
||||
void *VDSO_kernel_sigreturn;
|
||||
void *VDSO_kernel_rt_sigreturn;
|
||||
};
|
||||
|
||||
using CustomCPUFactoryType = std::function<std::unique_ptr<FEXCore::CPU::CPUBackend> (FEXCore::Context::Context*, FEXCore::Core::InternalThreadState *Thread)>;
|
||||
|
||||
using ExitHandler = std::function<void(uint64_t ThreadId, FEXCore::Context::ExitReason)>;
|
||||
|
||||
class Context {
|
||||
public:
|
||||
virtual ~Context() = default;
|
||||
/**
|
||||
* @brief [[threadsafe]] Create a new FEXCore context object
|
||||
*
|
||||
* This is necessary to do when running threaded contexts
|
||||
*
|
||||
* @return a new context object
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY static FEXCore::Context::Context *CreateNewContext();
|
||||
|
||||
/**
|
||||
* @brief Post creation context initialization
|
||||
* Once configurations have been set, do the post-creation initialization with that configuration
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return true if we managed to initialize correctly
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual bool InitializeContext() = 0;
|
||||
|
||||
/**
|
||||
* @brief Destroy the context object
|
||||
*
|
||||
* @param CTX
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY static void DestroyContext(FEXCore::Context::Context * CTX);
|
||||
FEX_DEFAULT_VISIBILITY virtual void DestroyContext() = 0;
|
||||
|
||||
/**
|
||||
* @brief Allows setting up in memory code and other things prior to launchign code execution
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param Loader The loader that will be doing all the code loading
|
||||
*
|
||||
* @return true if we loaded code
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::Core::InternalThreadState* InitCore(uint64_t InitialRIP, uint64_t StackPointer) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetExitHandler(ExitHandler handler) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual ExitHandler GetExitHandler() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Pauses execution on the CPU core
|
||||
*
|
||||
* Blocks until all threads have paused.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void Pause() = 0;
|
||||
|
||||
/**
|
||||
* @brief Starts (or continues) the CPU core
|
||||
*
|
||||
* This function is async and returns immediately.
|
||||
* Use RunUntilExit() for synchonous executions
|
||||
*
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void Run() = 0;
|
||||
|
||||
/**
|
||||
* @brief Tells the core to shutdown
|
||||
*
|
||||
* Blocks until shutdown
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void Stop() = 0;
|
||||
|
||||
/**
|
||||
* @brief Executes one instruction
|
||||
*
|
||||
* Returns once execution is complete.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void Step() = 0;
|
||||
|
||||
/**
|
||||
* @brief Runs the CPU core until it exits
|
||||
*
|
||||
* If an Exit handler has been registered, this function won't return until the core
|
||||
* has shutdown.
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return The ExitReason for the parentthread.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual ExitReason RunUntilExit() = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP) = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets the program exit status
|
||||
*
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return The program exit status
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual int GetProgramStatus() const = 0;
|
||||
|
||||
/**
|
||||
* @brief [[threadsafe]] Returns the ExitReason of the parent thread. Typically used for async result status
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return The ExitReason for the parentthread
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual ExitReason GetExitReason() = 0;
|
||||
|
||||
/**
|
||||
* @brief [[theadsafe]] Checks if the Context is either done working or paused(in the case of single stepping)
|
||||
*
|
||||
* Use this when the context is async running to determine if it is done
|
||||
*
|
||||
* @param CTX the context that we created
|
||||
*
|
||||
* @return true if the core is done or paused
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual bool IsDone() const = 0;
|
||||
|
||||
/**
|
||||
* @brief Gets a copy the CPUState of the parent thread
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param State The state object to populate
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void GetCPUState(FEXCore::Core::CPUState *State) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Copies the CPUState provided to the parent thread
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param State The satate object to copy from
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetCPUState(const FEXCore::Core::CPUState *State) = 0;
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to pass in a custom CPUBackend creation factory
|
||||
*
|
||||
* This allows the frontend to have its own frontend. Typically for debugging
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param Factory The factory that the context will call if the DefaultCore config ise set to CUSTOM
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetCustomCPUBackendFactory(CustomCPUFactoryType Factory) = 0;
|
||||
|
||||
/**
|
||||
* @brief Sets up memory regions on the guest for mirroring within the guest's VM space
|
||||
*
|
||||
* @param VirtualAddress The address we want to set to mirror a physical memory region
|
||||
* @param PhysicalAddress The physical memory region we are mapping
|
||||
* @param Size Size of the region to mirror
|
||||
*
|
||||
* @return true when successfully mapped. false if there was an error adding
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual bool AddVirtualMemoryMapping(uint64_t VirtualAddress, uint64_t PhysicalAddress, uint64_t Size) = 0;
|
||||
|
||||
/**
|
||||
* @brief Retrieves a feature struct indicating certain supported aspects from
|
||||
* the hose.
|
||||
*
|
||||
* @param CTX A valid non-null context instance.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual HostFeatures GetHostFeatures() const = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void HandleCallback(FEXCore::Core::InternalThreadState *Thread, uint64_t RIP) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void RegisterHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) = 0;
|
||||
[[noreturn]] FEX_DEFAULT_VISIBILITY virtual void HandleSignalHandlerReturn(bool RT) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void RegisterFrontendHostSignalHandler(int Signal, HostSignalDelegatorFunction Func, bool Required) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void ExecutionThread(FEXCore::Core::InternalThreadState *Thread) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void InitializeThread(FEXCore::Core::InternalThreadState *Thread) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void RunThread(FEXCore::Core::InternalThreadState *Thread) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void StopThread(FEXCore::Core::InternalThreadState *Thread) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void DestroyThread(FEXCore::Core::InternalThreadState *Thread) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void CleanupAfterFork(FEXCore::Core::InternalThreadState *Thread) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetSignalDelegator(FEXCore::SignalDelegator *SignalDelegation) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetSyscallHandler(FEXCore::HLE::SyscallHandler *Handler) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunction(uint32_t Function, uint32_t Leaf) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual FEXCore::IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(const std::string& Name) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void UnloadAOTIRCacheEntry(FEXCore::IR::AOTIRCacheEntry *Entry) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetAOTIRLoader(std::function<int(const std::string&)> CacheReader) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetAOTIRWriter(std::function<std::unique_ptr<std::ofstream>(const std::string&)> CacheWriter) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetAOTIRRenamer(std::function<void(const std::string&)> CacheRenamer) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void FinalizeAOTIRCache() = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void WriteFilesWithCode(std::function<void(const std::string& fileid, const std::string& filename)> Writer) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void InvalidateGuestCodeRange(uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> callback) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual void MarkMemoryShared() = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress) = 0;
|
||||
FEX_DEFAULT_VISIBILITY virtual CustomIRResult AddCustomIREntrypoint(uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr) = 0;
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
|
||||
*
|
||||
* @param CTX A valid non-null context instance.
|
||||
* @param Definitions A vector of thunk definitions that the frontend controls
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY virtual void AppendThunkDefinitions(std::vector<FEXCore::IR::ThunkDefinition> const& Definitions) = 0;
|
||||
|
||||
FEX_DEFAULT_VISIBILITY virtual void SetVDSOSigReturn(const VDSOSigReturn &Pointers) = 0;
|
||||
private:
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief This initializes internal FEXCore state that is shared between contexts and requires overhead to setup
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void InitializeStaticTables(OperatingMode Mode = MODE_64BIT);
|
||||
FEX_DEFAULT_VISIBILITY void ShutdownStaticTables();
|
||||
|
||||
/**
|
||||
* @brief [[threadsafe]] Create a new FEXCore context object
|
||||
*
|
||||
* This is necessary to do when running threaded contexts
|
||||
*
|
||||
* @return a new context object
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::Context::Context *CreateNewContext();
|
||||
|
||||
/**
|
||||
* @brief Post creation context initialization
|
||||
* Once configurations have been set, do the post-creation initialization with that configuration
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return true if we managed to initialize correctly
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY bool InitializeContext(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Destroy the context object
|
||||
*
|
||||
* @param 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
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param Loader The loader that will be doing all the code loading
|
||||
*
|
||||
* @return true if we loaded code
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::Core::InternalThreadState* InitCore(FEXCore::Context::Context *CTX, uint64_t InitialRIP, uint64_t StackPointer);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void SetExitHandler(FEXCore::Context::Context *CTX, ExitHandler handler);
|
||||
FEX_DEFAULT_VISIBILITY ExitHandler GetExitHandler(const FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Pauses execution on the CPU core
|
||||
*
|
||||
* Blocks until all threads have paused.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void Pause(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Starts (or continues) the CPU core
|
||||
*
|
||||
* This function is async and returns immediately.
|
||||
* Use RunUntilExit() for synchonous executions
|
||||
*
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void Run(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Runs the CPU core until it exits
|
||||
*
|
||||
* If an Exit handler has been registered, this function won't return until the core
|
||||
* has shutdown.
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return The ExitReason for the parentthread.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY ExitReason RunUntilExit(FEXCore::Context::Context *CTX);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void CompileRIP(FEXCore::Core::InternalThreadState *Thread, uint64_t GuestRIP);
|
||||
|
||||
/**
|
||||
* @brief Gets the program exit status
|
||||
*
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return The program exit status
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY int GetProgramStatus(const FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Tells the core to shutdown
|
||||
*
|
||||
* Blocks until shutdown
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void Stop(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Executes one instruction
|
||||
*
|
||||
* Returns once execution is complete.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void Step(FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief [[threadsafe]] Returns the ExitReason of the parent thread. Typically used for async result status
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
*
|
||||
* @return The ExitReason for the parentthread
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY ExitReason GetExitReason(const FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief [[theadsafe]] Checks if the Context is either done working or paused(in the case of single stepping)
|
||||
*
|
||||
* Use this when the context is async running to determine if it is done
|
||||
*
|
||||
* @param CTX the context that we created
|
||||
*
|
||||
* @return true if the core is done or paused
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY bool IsDone(const FEXCore::Context::Context *CTX);
|
||||
|
||||
/**
|
||||
* @brief Gets a copy the CPUState of the parent thread
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param State The state object to populate
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void GetCPUState(const FEXCore::Context::Context *CTX,
|
||||
FEXCore::Core::CPUState *State);
|
||||
|
||||
/**
|
||||
* @brief Copies the CPUState provided to the parent thread
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param State The satate object to copy from
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void SetCPUState(FEXCore::Context::Context *CTX,
|
||||
const FEXCore::Core::CPUState *State);
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to pass in a custom CPUBackend creation factory
|
||||
*
|
||||
* This allows the frontend to have its own frontend. Typically for debugging
|
||||
*
|
||||
* @param CTX The context that we created
|
||||
* @param Factory The factory that the context will call if the DefaultCore config ise set to CUSTOM
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @param VirtualAddress The address we want to set to mirror a physical memory region
|
||||
* @param PhysicalAddress The physical memory region we are mapping
|
||||
* @param Size Size of the region to mirror
|
||||
*
|
||||
* @return true when successfully mapped. false if there was an error adding
|
||||
*/
|
||||
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
|
||||
*
|
||||
* Useful for debugging purposes. May not work if the syscall ID exceeds the maximum number of syscalls in the lookup table
|
||||
*
|
||||
* @param Syscall Which syscall ID to install a visitor to
|
||||
* @param Visitor The Visitor to install
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void RegisterExternalSyscallVisitor(FEXCore::Context::Context *CTX, uint64_t Syscall, FEXCore::HLE::SyscallVisitor *Visitor);
|
||||
|
||||
/**
|
||||
* @brief Retrieves a feature struct indicating certain supported aspects from
|
||||
* the hose.
|
||||
*
|
||||
* @param CTX A valid non-null context instance.
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY HostFeatures GetHostFeatures(const FEXCore::Context::Context *CTX);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void HandleCallback(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread, uint64_t RIP);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void RegisterHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required);
|
||||
FEX_DEFAULT_VISIBILITY void RegisterFrontendHostSignalHandler(FEXCore::Context::Context *CTX, int Signal, HostSignalDelegatorFunction Func, bool Required);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::Core::InternalThreadState* CreateThread(FEXCore::Context::Context *CTX, FEXCore::Core::CPUState *NewThreadState, uint64_t ParentTID);
|
||||
FEX_DEFAULT_VISIBILITY void ExecutionThread(FEXCore::Context::Context *CTX, FEXCore::Core::InternalThreadState *Thread);
|
||||
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);
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::CPUID::FunctionResults RunCPUIDFunctionName(FEXCore::Context::Context *CTX, uint32_t Function, uint32_t Leaf, uint32_t CPU);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY FEXCore::IR::AOTIRCacheEntry *LoadAOTIRCacheEntry(FEXCore::Context::Context *CTX, const std::string& Name);
|
||||
FEX_DEFAULT_VISIBILITY void UnloadAOTIRCacheEntry(FEXCore::Context::Context *CTX, FEXCore::IR::AOTIRCacheEntry *Entry);
|
||||
|
||||
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::ofstream>(const std::string&)> CacheWriter);
|
||||
FEX_DEFAULT_VISIBILITY void SetAOTIRRenamer(FEXCore::Context::Context *CTX, std::function<void(const std::string&)> CacheRenamer);
|
||||
|
||||
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 InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length);
|
||||
FEX_DEFAULT_VISIBILITY void InvalidateGuestCodeRange(FEXCore::Context::Context *CTX, uint64_t Start, uint64_t Length, std::function<void(uint64_t start, uint64_t Length)> callback);
|
||||
FEX_DEFAULT_VISIBILITY void MarkMemoryShared(FEXCore::Context::Context *CTX);
|
||||
|
||||
FEX_DEFAULT_VISIBILITY void ConfigureAOTGen(FEXCore::Core::InternalThreadState *Thread, std::set<uint64_t> *ExternalBranches, uint64_t SectionMaxAddress);
|
||||
FEX_DEFAULT_VISIBILITY CustomIRResult AddCustomIREntrypoint(FEXCore::Context::Context *CTX, uintptr_t Entrypoint, std::function<void(uintptr_t Entrypoint, FEXCore::IR::IREmitter *)> Handler, void *Creator = nullptr, void *Data = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Allows the frontend to register its own thunk handlers independent of what is controlled in the backend.
|
||||
*
|
||||
* @param CTX A valid non-null context instance.
|
||||
* @param Definitions A vector of thunk definitions that the frontend controls
|
||||
*/
|
||||
FEX_DEFAULT_VISIBILITY void AppendThunkDefinitions(FEXCore::Context::Context *CTX, std::vector<FEXCore::IR::ThunkDefinition> const& Definitions);
|
||||
}
|
||||
+1
-1
@@ -37,7 +37,7 @@ namespace FEXCore::Core {
|
||||
uint32_t es_cached, cs_cached, ss_cached, ds_cached;
|
||||
uint64_t gs_cached;
|
||||
uint64_t fs_cached;
|
||||
uint64_t _pad2[1];
|
||||
uint64_t InlineJITBlockHeader;
|
||||
XMMRegs xmm;
|
||||
uint8_t flags[48];
|
||||
uint64_t mm[8][2];
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ namespace FEXCore::Core {
|
||||
}
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class Context;
|
||||
|
||||
namespace Debug {
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ namespace FEXCore {
|
||||
}
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace FEXCore::CPU {
|
||||
|
||||
+1
-1
@@ -303,7 +303,7 @@ constexpr InstFlagType FLAGS_X87_FLAGS = (1ULL << 10);
|
||||
constexpr InstFlagType FLAGS_SF_REX_IN_BYTE = (1ULL << 15);
|
||||
|
||||
// XMM subflags
|
||||
constexpr InstFlagType FLAGS_SF_HIGH_XMM_REG = (1ULL << 11);
|
||||
constexpr InstFlagType FLAGS_SF_UNUSED = (1ULL << 11); // No assigned behavior yet
|
||||
constexpr InstFlagType FLAGS_SF_DST_GPR = (1ULL << 12);
|
||||
constexpr InstFlagType FLAGS_SF_SRC_GPR = (1ULL << 13);
|
||||
constexpr InstFlagType FLAGS_SF_MMX_DST = (1ULL << 14);
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ namespace FEXCore::IR {
|
||||
}
|
||||
|
||||
namespace FEXCore::Context {
|
||||
struct Context;
|
||||
class Context;
|
||||
}
|
||||
|
||||
namespace FEXCore::Core {
|
||||
|
||||
+3
-3
@@ -390,7 +390,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Bitfield") {
|
||||
TEST_SINGLE(sxth(Size::i32Bit, Reg::r29, Reg::r28), "sxth w29, w28");
|
||||
TEST_SINGLE(sxth(Size::i64Bit, Reg::r29, Reg::r28), "sxth x29, w28");
|
||||
|
||||
TEST_SINGLE(sxtw(XReg::x29, XReg::x28), "sxtw x29, w28");
|
||||
TEST_SINGLE(sxtw(XReg::x29, WReg::w28), "sxtw x29, w28");
|
||||
|
||||
TEST_SINGLE(sbfx(Size::i32Bit, Reg::r29, Reg::r28, 4, 16), "sbfx w29, w28, #4, #16");
|
||||
TEST_SINGLE(sbfx(Size::i64Bit, Reg::r29, Reg::r28, 4, 16), "sbfx x29, x28, #4, #16");
|
||||
@@ -1504,8 +1504,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Rotate right into flags") {
|
||||
TEST_SINGLE(rmif(XReg::x30, 63, 0b1111), "rmif x30, #63, #NZCV");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Evaluate into flags") {
|
||||
TEST_SINGLE(setf8(XReg::x30), "setf8 w30");
|
||||
TEST_SINGLE(setf16(XReg::x30), "setf16 w30");
|
||||
TEST_SINGLE(setf8(WReg::w30), "setf8 w30");
|
||||
TEST_SINGLE(setf16(WReg::w30), "setf16 w30");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Conditional compare - register") {
|
||||
TEST_SINGLE(ccmn(Size::i32Bit, Reg::r29, Reg::r28, StatusFlags::None, Condition::CC_AL), "ccmn w29, w28, #nzcv, al");
|
||||
|
||||
+15
-200
@@ -40,18 +40,33 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD table lookup")
|
||||
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q25), "tbx v30.16b, {v26.16b}, v25.16b");
|
||||
TEST_SINGLE(tbx(DReg::d30, QReg::q26, DReg::d25), "tbx v30.8b, {v26.16b}, v25.8b");
|
||||
|
||||
TEST_SINGLE(tbl(QReg::q30, QReg::q31, QReg::q0, QReg::q25), "tbl v30.16b, {v31.16b, v0.16b}, v25.16b");
|
||||
TEST_SINGLE(tbl(DReg::d30, QReg::q31, QReg::q0, DReg::d25), "tbl v30.8b, {v31.16b, v0.16b}, v25.8b");
|
||||
TEST_SINGLE(tbl(QReg::q30, QReg::q26, QReg::q27, QReg::q25), "tbl v30.16b, {v26.16b, v27.16b}, v25.16b");
|
||||
TEST_SINGLE(tbl(DReg::d30, QReg::q26, QReg::q27, DReg::d25), "tbl v30.8b, {v26.16b, v27.16b}, v25.8b");
|
||||
|
||||
TEST_SINGLE(tbx(QReg::q30, QReg::q31, QReg::q0, QReg::q25), "tbx v30.16b, {v31.16b, v0.16b}, v25.16b");
|
||||
TEST_SINGLE(tbx(DReg::d30, QReg::q31, QReg::q0, DReg::d25), "tbx v30.8b, {v31.16b, v0.16b}, v25.8b");
|
||||
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q27, QReg::q25), "tbx v30.16b, {v26.16b, v27.16b}, v25.16b");
|
||||
TEST_SINGLE(tbx(DReg::d30, QReg::q26, QReg::q27, DReg::d25), "tbx v30.8b, {v26.16b, v27.16b}, v25.8b");
|
||||
|
||||
TEST_SINGLE(tbl(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q25), "tbl v30.16b, {v31.16b, v0.16b, v1.16b}, v25.16b");
|
||||
TEST_SINGLE(tbl(DReg::d30, QReg::q31, QReg::q0, QReg::q1, DReg::d25), "tbl v30.8b, {v31.16b, v0.16b, v1.16b}, v25.8b");
|
||||
TEST_SINGLE(tbl(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q25), "tbl v30.16b, {v26.16b, v27.16b, v28.16b}, v25.16b");
|
||||
TEST_SINGLE(tbl(DReg::d30, QReg::q26, QReg::q27, QReg::q28, DReg::d25), "tbl v30.8b, {v26.16b, v27.16b, v28.16b}, v25.8b");
|
||||
|
||||
TEST_SINGLE(tbx(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q25), "tbx v30.16b, {v31.16b, v0.16b, v1.16b}, v25.16b");
|
||||
TEST_SINGLE(tbx(DReg::d30, QReg::q31, QReg::q0, QReg::q1, DReg::d25), "tbx v30.8b, {v31.16b, v0.16b, v1.16b}, v25.8b");
|
||||
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q25), "tbx v30.16b, {v26.16b, v27.16b, v28.16b}, v25.16b");
|
||||
TEST_SINGLE(tbx(DReg::d30, QReg::q26, QReg::q27, QReg::q28, DReg::d25), "tbx v30.8b, {v26.16b, v27.16b, v28.16b}, v25.8b");
|
||||
|
||||
TEST_SINGLE(tbl(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, QReg::q25), "tbl v30.16b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.16b");
|
||||
TEST_SINGLE(tbl(DReg::d30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, DReg::d25), "tbl v30.8b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.8b");
|
||||
TEST_SINGLE(tbl(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, QReg::q25), "tbl v30.16b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.16b");
|
||||
TEST_SINGLE(tbl(DReg::d30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, DReg::d25), "tbl v30.8b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.8b");
|
||||
|
||||
TEST_SINGLE(tbx(QReg::q30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, QReg::q25), "tbx v30.16b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.16b");
|
||||
TEST_SINGLE(tbx(DReg::d30, QReg::q31, QReg::q0, QReg::q1, QReg::q2, DReg::d25), "tbx v30.8b, {v31.16b, v0.16b, v1.16b, v2.16b}, v25.8b");
|
||||
TEST_SINGLE(tbx(QReg::q30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, QReg::q25), "tbx v30.16b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.16b");
|
||||
TEST_SINGLE(tbx(DReg::d30, QReg::q26, QReg::q27, QReg::q28, QReg::q29, DReg::d25), "tbx v30.8b, {v26.16b, v27.16b, v28.16b, v29.16b}, v25.8b");
|
||||
}
|
||||
@@ -815,11 +830,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register m
|
||||
TEST_SINGLE(sqxtun2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "sqxtun2 v30.4s, v29.2d");
|
||||
//TEST_SINGLE(sqxtun2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "sqxtun2 v30.2d, v29.1d");
|
||||
|
||||
//TEST_SINGLE(shll(SubRegSize::i8Bit, QReg::q30, QReg::q29), "shll v30.16b, v29.16b, #0");
|
||||
TEST_SINGLE(shll(SubRegSize::i16Bit, QReg::q30, QReg::q29), "shll v30.8h, v29.8b, #8");
|
||||
TEST_SINGLE(shll(SubRegSize::i32Bit, QReg::q30, QReg::q29), "shll v30.4s, v29.4h, #16");
|
||||
TEST_SINGLE(shll(SubRegSize::i64Bit, QReg::q30, QReg::q29), "shll v30.2d, v29.2s, #32");
|
||||
|
||||
//TEST_SINGLE(shll(SubRegSize::i8Bit, DReg::d30, DReg::d29), "shll v30.8b, v29.8b, #0");
|
||||
TEST_SINGLE(shll(SubRegSize::i16Bit, DReg::d30, DReg::d29), "shll v30.8h, v29.8b, #8");
|
||||
TEST_SINGLE(shll(SubRegSize::i32Bit, DReg::d30, DReg::d29), "shll v30.4s, v29.4h, #16");
|
||||
@@ -830,11 +840,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register m
|
||||
TEST_SINGLE(shll2(SubRegSize::i32Bit, QReg::q30, QReg::q29), "shll2 v30.4s, v29.8h, #16");
|
||||
TEST_SINGLE(shll2(SubRegSize::i64Bit, QReg::q30, QReg::q29), "shll2 v30.2d, v29.4s, #32");
|
||||
|
||||
//TEST_SINGLE(shll2(SubRegSize::i8Bit, DReg::d30, DReg::d29), "shll2 v30.8b, v29.8b, #0");
|
||||
TEST_SINGLE(shll2(SubRegSize::i16Bit, DReg::d30, DReg::d29), "shll2 v30.8h, v29.16b, #8");
|
||||
TEST_SINGLE(shll2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "shll2 v30.4s, v29.8h, #16");
|
||||
TEST_SINGLE(shll2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "shll2 v30.2d, v29.4s, #32");
|
||||
|
||||
TEST_SINGLE(uqxtn(SubRegSize::i8Bit, QReg::q30, QReg::q29), "uqxtn v30.8b, v29.8h");
|
||||
TEST_SINGLE(uqxtn(SubRegSize::i16Bit, QReg::q30, QReg::q29), "uqxtn v30.4h, v29.4s");
|
||||
TEST_SINGLE(uqxtn(SubRegSize::i32Bit, QReg::q30, QReg::q29), "uqxtn v30.2s, v29.2d");
|
||||
@@ -2221,15 +2226,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(sqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqshl v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(sqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqshl v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(shrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "shrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(shrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "shrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(shrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "shrn v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(shrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "shrn v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(shrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "shrn v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(shrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "shrn v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(shrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "shrn v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(shrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "shrn v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(shrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "shrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(shrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "shrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(shrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "shrn v30.4h, v29.4s, #1");
|
||||
@@ -2248,24 +2244,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "shrn2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "shrn2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(shrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(shrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "shrn2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(shrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(shrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "shrn2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(shrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(shrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "shrn2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "shrn2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(shrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "shrn2 v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(rshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "rshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(rshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "rshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(rshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "rshrn v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(rshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "rshrn v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(rshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "rshrn v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(rshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "rshrn v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(rshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "rshrn v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(rshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "rshrn v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(rshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "rshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(rshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "rshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(rshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "rshrn v30.4h, v29.4s, #1");
|
||||
@@ -2284,24 +2262,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "rshrn2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "rshrn2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(rshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(rshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "rshrn2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(rshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(rshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "rshrn2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(rshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(rshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "rshrn2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "rshrn2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(rshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "rshrn2 v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqshrn v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqshrn v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrn v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrn v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrn v30.4h, v29.4s, #1");
|
||||
@@ -2320,24 +2280,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrn2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrn2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(sqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrn2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(sqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(sqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqshrn2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(sqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(sqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqshrn2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqshrn2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(sqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqshrn2 v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqrshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqrshrn v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqrshrn v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrn v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrn v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqrshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrn v30.4h, v29.4s, #1");
|
||||
@@ -2356,24 +2298,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrn2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrn2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(sqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrn2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(sqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(sqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqrshrn2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(sqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(sqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqrshrn2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqrshrn2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(sqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqrshrn2 v30.1d, v29.1d, #63");
|
||||
|
||||
//TEST_SINGLE(sshll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sshll v30.8b, v29.8h, #1");
|
||||
//TEST_SINGLE(sshll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sshll v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sshll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sshll v30.8h, v29.8b, #1");
|
||||
TEST_SINGLE(sshll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 7), "sshll v30.8h, v29.8b, #7");
|
||||
TEST_SINGLE(sshll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sshll v30.4s, v29.4h, #1");
|
||||
TEST_SINGLE(sshll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 15), "sshll v30.4s, v29.4h, #15");
|
||||
TEST_SINGLE(sshll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sshll v30.2d, v29.2s, #1");
|
||||
TEST_SINGLE(sshll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "sshll v30.2d, v29.2s, #31");
|
||||
|
||||
//TEST_SINGLE(sshll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sshll v30.8b, v29.8h, #1");
|
||||
//TEST_SINGLE(sshll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sshll v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sshll(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sshll v30.8h, v29.8b, #1");
|
||||
@@ -2392,15 +2316,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
TEST_SINGLE(sshll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sshll2 v30.2d, v29.4s, #1");
|
||||
TEST_SINGLE(sshll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "sshll2 v30.2d, v29.4s, #31");
|
||||
|
||||
//TEST_SINGLE(sshll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.16b, v29.8h, #1");
|
||||
//TEST_SINGLE(sshll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sshll2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(sshll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.8h, v29.16b, #1");
|
||||
TEST_SINGLE(sshll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 7), "sshll2 v30.8h, v29.16b, #7");
|
||||
TEST_SINGLE(sshll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.4s, v29.8h, #1");
|
||||
TEST_SINGLE(sshll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 15), "sshll2 v30.4s, v29.8h, #15");
|
||||
TEST_SINGLE(sshll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sshll2 v30.2d, v29.4s, #1");
|
||||
TEST_SINGLE(sshll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 31), "sshll2 v30.2d, v29.4s, #31");
|
||||
|
||||
//TEST_SINGLE(sxtl(SubRegSize::i8Bit, QReg::q30, QReg::q29), "sxtl v30.8b, v29.8h");
|
||||
TEST_SINGLE(sxtl(SubRegSize::i16Bit, QReg::q30, QReg::q29), "sxtl v30.8h, v29.8b");
|
||||
TEST_SINGLE(sxtl(SubRegSize::i32Bit, QReg::q30, QReg::q29), "sxtl v30.4s, v29.4h");
|
||||
@@ -2601,15 +2516,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(uqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "uqshl v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(uqshl(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "uqshl v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqshrun v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqshrun v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqshrun v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrun v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrun v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrun v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrun v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqshrun(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrun v30.4h, v29.4s, #1");
|
||||
@@ -2628,24 +2534,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqshrun2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqshrun2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(sqshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqshrun2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(sqshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(sqshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqshrun2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(sqshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(sqshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqshrun2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqshrun2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(sqshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqshrun2 v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "sqrshrun v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "sqrshrun v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "sqrshrun v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqrshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrun v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqrshrun(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrun v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrun v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrun v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(sqrshrun(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrun v30.4h, v29.4s, #1");
|
||||
@@ -2664,24 +2552,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "sqrshrun2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "sqrshrun2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(sqrshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(sqrshrun2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "sqrshrun2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(sqrshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(sqrshrun2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "sqrshrun2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(sqrshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(sqrshrun2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "sqrshrun2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "sqrshrun2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(sqrshrun2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "sqrshrun2 v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "uqshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "uqshrn v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "uqshrn v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(uqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqshrn v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(uqshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqshrn v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(uqshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqshrn v30.4h, v29.4s, #1");
|
||||
@@ -2700,24 +2570,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqshrn2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqshrn2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(uqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(uqshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqshrn2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(uqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(uqshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "uqshrn2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(uqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(uqshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "uqshrn2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "uqshrn2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(uqshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "uqshrn2 v30.1d, v29.1d, #63");
|
||||
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "uqrshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.4h, v29.4s, #1");
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "uqrshrn v30.4h, v29.4s, #15");
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.2s, v29.2d, #1");
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "uqrshrn v30.2s, v29.2d, #31");
|
||||
//TEST_SINGLE(uqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqrshrn v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(uqrshrn(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqrshrn v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqrshrn v30.8b, v29.8h, #1");
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqrshrn v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(uqrshrn(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqrshrn v30.4h, v29.4s, #1");
|
||||
@@ -2736,24 +2588,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "uqrshrn2 v30.2d, v29.2d, #1");
|
||||
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "uqrshrn2 v30.2d, v29.2d, #63");
|
||||
|
||||
TEST_SINGLE(uqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.16b, v29.8h, #1");
|
||||
TEST_SINGLE(uqrshrn2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "uqrshrn2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(uqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.8h, v29.4s, #1");
|
||||
TEST_SINGLE(uqrshrn2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "uqrshrn2 v30.8h, v29.4s, #15");
|
||||
TEST_SINGLE(uqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.4s, v29.2d, #1");
|
||||
TEST_SINGLE(uqrshrn2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "uqrshrn2 v30.4s, v29.2d, #31");
|
||||
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "uqrshrn2 v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(uqrshrn2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "uqrshrn2 v30.1d, v29.1d, #63");
|
||||
|
||||
//TEST_SINGLE(ushll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "ushll v30.8b, v29.8h, #1");
|
||||
//TEST_SINGLE(ushll(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "ushll v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(ushll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "ushll v30.8h, v29.8b, #1");
|
||||
TEST_SINGLE(ushll(SubRegSize::i16Bit, QReg::q30, QReg::q29, 7), "ushll v30.8h, v29.8b, #7");
|
||||
TEST_SINGLE(ushll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "ushll v30.4s, v29.4h, #1");
|
||||
TEST_SINGLE(ushll(SubRegSize::i32Bit, QReg::q30, QReg::q29, 15), "ushll v30.4s, v29.4h, #15");
|
||||
TEST_SINGLE(ushll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "ushll v30.2d, v29.2s, #1");
|
||||
TEST_SINGLE(ushll(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "ushll v30.2d, v29.2s, #31");
|
||||
|
||||
//TEST_SINGLE(ushll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "ushll v30.8b, v29.8h, #1");
|
||||
//TEST_SINGLE(ushll(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "ushll v30.8b, v29.8h, #7");
|
||||
TEST_SINGLE(ushll(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "ushll v30.8h, v29.8b, #1");
|
||||
@@ -2772,20 +2606,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
TEST_SINGLE(ushll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "ushll2 v30.2d, v29.4s, #1");
|
||||
TEST_SINGLE(ushll2(SubRegSize::i64Bit, QReg::q30, QReg::q29, 31), "ushll2 v30.2d, v29.4s, #31");
|
||||
|
||||
//TEST_SINGLE(ushll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.16b, v29.8h, #1");
|
||||
//TEST_SINGLE(ushll2(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "ushll2 v30.16b, v29.8h, #7");
|
||||
TEST_SINGLE(ushll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.8h, v29.16b, #1");
|
||||
TEST_SINGLE(ushll2(SubRegSize::i16Bit, DReg::d30, DReg::d29, 7), "ushll2 v30.8h, v29.16b, #7");
|
||||
TEST_SINGLE(ushll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.4s, v29.8h, #1");
|
||||
TEST_SINGLE(ushll2(SubRegSize::i32Bit, DReg::d30, DReg::d29, 15), "ushll2 v30.4s, v29.8h, #15");
|
||||
TEST_SINGLE(ushll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "ushll2 v30.2d, v29.4s, #1");
|
||||
TEST_SINGLE(ushll2(SubRegSize::i64Bit, DReg::d30, DReg::d29, 31), "ushll2 v30.2d, v29.4s, #31");
|
||||
|
||||
//TEST_SINGLE(uxtl(SubRegSize::i8Bit, QReg::q30, QReg::q29), "uxtl v30.8b, v29.8h");
|
||||
TEST_SINGLE(uxtl(SubRegSize::i16Bit, QReg::q30, QReg::q29), "uxtl v30.8h, v29.8b");
|
||||
TEST_SINGLE(uxtl(SubRegSize::i32Bit, QReg::q30, QReg::q29), "uxtl v30.4s, v29.4h");
|
||||
TEST_SINGLE(uxtl(SubRegSize::i64Bit, QReg::q30, QReg::q29), "uxtl v30.2d, v29.2s");
|
||||
|
||||
//TEST_SINGLE(uxtl(SubRegSize::i8Bit, DReg::d30, DReg::d29), "uxtl v30.8b, v29.8h");
|
||||
TEST_SINGLE(uxtl(SubRegSize::i16Bit, DReg::d30, DReg::d29), "uxtl v30.8h, v29.8b");
|
||||
TEST_SINGLE(uxtl(SubRegSize::i32Bit, DReg::d30, DReg::d29), "uxtl v30.4s, v29.4h");
|
||||
@@ -2796,11 +2616,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
TEST_SINGLE(uxtl2(SubRegSize::i32Bit, QReg::q30, QReg::q29), "uxtl2 v30.4s, v29.8h");
|
||||
TEST_SINGLE(uxtl2(SubRegSize::i64Bit, QReg::q30, QReg::q29), "uxtl2 v30.2d, v29.4s");
|
||||
|
||||
//TEST_SINGLE(uxtl2(SubRegSize::i8Bit, DReg::d30, DReg::d29), "uxtl2 v30.16b, v29.8h");
|
||||
TEST_SINGLE(uxtl2(SubRegSize::i16Bit, DReg::d30, DReg::d29), "uxtl2 v30.8h, v29.16b");
|
||||
TEST_SINGLE(uxtl2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "uxtl2 v30.4s, v29.8h");
|
||||
TEST_SINGLE(uxtl2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "uxtl2 v30.2d, v29.4s");
|
||||
|
||||
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.16b, v29.16b, #1");
|
||||
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "ucvtf v30.16b, v29.16b, #7");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.8h, v29.8h, #1");
|
||||
|
||||
+210
-102
@@ -31,6 +31,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, Reg::r30), "ld1 {v26.2d}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, Reg::r30), "ld1 {v26.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "ld1 {v31.16b, v0.16b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "ld1 {v31.8b, v0.8b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "ld1 {v26.16b, v27.16b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "ld1 {v26.8b, v27.8b}, [x30]");
|
||||
|
||||
@@ -43,6 +45,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "ld1 {v26.2d, v27.2d}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "ld1 {v26.1d, v27.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "ld1 {v31.16b, v0.16b, v1.16b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "ld1 {v31.8b, v0.8b, v1.8b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld1 {v26.16b, v27.16b, v28.16b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld1 {v26.8b, v27.8b, v28.8b}, [x30]");
|
||||
|
||||
@@ -55,6 +59,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld1 {v26.2d, v27.2d, v28.2d}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld1 {v26.1d, v27.1d, v28.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "ld1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "ld1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
|
||||
|
||||
@@ -79,6 +85,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, Reg::r30), "st1 {v26.2d}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, Reg::r30), "st1 {v26.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "st1 {v31.16b, v0.16b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "st1 {v31.8b, v0.8b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "st1 {v26.16b, v27.16b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "st1 {v26.8b, v27.8b}, [x30]");
|
||||
|
||||
@@ -91,6 +99,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "st1 {v26.2d, v27.2d}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "st1 {v26.1d, v27.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "st1 {v31.16b, v0.16b, v1.16b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "st1 {v31.8b, v0.8b, v1.8b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st1 {v26.16b, v27.16b, v28.16b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "st1 {v26.8b, v27.8b, v28.8b}, [x30]");
|
||||
|
||||
@@ -103,6 +113,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st1 {v26.2d, v27.2d, v28.2d}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "st1 {v26.1d, v27.1d, v28.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "st1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "st1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "st1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "st1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
|
||||
|
||||
@@ -115,6 +127,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "st1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "st1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "ld2 {v31.16b, v0.16b}, [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "ld2 {v31.8b, v0.8b}, [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2 {v26.16b, v27.16b}, [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2 {v26.8b, v27.8b}, [x30]");
|
||||
|
||||
@@ -127,6 +141,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2 {v26.2d, v27.2d}, [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "st2 {v31.16b, v0.16b}, [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "st2 {v31.8b, v0.8b}, [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "st2 {v26.16b, v27.16b}, [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "st2 {v26.8b, v27.8b}, [x30]");
|
||||
|
||||
@@ -139,6 +155,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "st2 {v26.2d, v27.2d}, [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "ld3 {v31.16b, v0.16b, v1.16b}, [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "ld3 {v31.8b, v0.8b, v1.8b}, [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3 {v26.16b, v27.16b, v28.16b}, [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3 {v26.8b, v27.8b, v28.8b}, [x30]");
|
||||
|
||||
@@ -151,6 +169,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3 {v26.2d, v27.2d, v28.2d}, [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "st3 {v31.16b, v0.16b, v1.16b}, [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "st3 {v31.8b, v0.8b, v1.8b}, [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st3 {v26.16b, v27.16b, v28.16b}, [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "st3 {v26.8b, v27.8b, v28.8b}, [x30]");
|
||||
|
||||
@@ -163,6 +183,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "st3 {v26.2d, v27.2d, v28.2d}, [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "ld4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "ld4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
|
||||
|
||||
@@ -175,6 +197,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "unallocated (NEONLoadStoreMultiStruct)");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "st4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "st4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "st4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "st4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
|
||||
|
||||
@@ -213,6 +237,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, Reg::r30, 16), "ld1 {v26.2d}, [x30], #16");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, Reg::r30, 8), "ld1 {v26.1d}, [x30], #8");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "ld1 {v31.16b, v0.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "ld1 {v31.8b, v0.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld1 {v26.16b, v27.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld1 {v26.8b, v27.8b}, [x30], x29");
|
||||
|
||||
@@ -225,6 +251,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld1 {v26.2d, v27.2d}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld1 {v26.1d, v27.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "ld1 {v31.16b, v0.16b}, [x30], #32");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "ld1 {v31.8b, v0.8b}, [x30], #16");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld1 {v26.16b, v27.16b}, [x30], #32");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld1 {v26.8b, v27.8b}, [x30], #16");
|
||||
|
||||
@@ -237,6 +265,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld1 {v26.2d, v27.2d}, [x30], #32");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld1 {v26.1d, v27.1d}, [x30], #16");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "ld1 {v31.16b, v0.16b, v1.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "ld1 {v31.8b, v0.8b, v1.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld1 {v26.16b, v27.16b, v28.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld1 {v26.8b, v27.8b, v28.8b}, [x30], x29");
|
||||
|
||||
@@ -249,6 +279,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld1 {v26.2d, v27.2d, v28.2d}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld1 {v26.1d, v27.1d, v28.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "ld1 {v31.16b, v0.16b, v1.16b}, [x30], #48");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "ld1 {v31.8b, v0.8b, v1.8b}, [x30], #24");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld1 {v26.16b, v27.16b, v28.16b}, [x30], #48");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld1 {v26.8b, v27.8b, v28.8b}, [x30], #24");
|
||||
|
||||
@@ -261,6 +293,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld1 {v26.2d, v27.2d, v28.2d}, [x30], #48");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld1 {v26.1d, v27.1d, v28.1d}, [x30], #24");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "ld1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "ld1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
|
||||
|
||||
@@ -273,6 +307,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "ld1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "ld1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "ld1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "ld1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
|
||||
|
||||
@@ -309,6 +345,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, Reg::r30, 16), "st1 {v26.2d}, [x30], #16");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, Reg::r30, 8), "st1 {v26.1d}, [x30], #8");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "st1 {v31.16b, v0.16b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "st1 {v31.8b, v0.8b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st1 {v26.16b, v27.16b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "st1 {v26.8b, v27.8b}, [x30], x29");
|
||||
|
||||
@@ -321,6 +359,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st1 {v26.2d, v27.2d}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "st1 {v26.1d, v27.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "st1 {v31.16b, v0.16b}, [x30], #32");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "st1 {v31.8b, v0.8b}, [x30], #16");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st1 {v26.16b, v27.16b}, [x30], #32");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "st1 {v26.8b, v27.8b}, [x30], #16");
|
||||
|
||||
@@ -333,6 +373,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st1 {v26.2d, v27.2d}, [x30], #32");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "st1 {v26.1d, v27.1d}, [x30], #16");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "st1 {v31.16b, v0.16b, v1.16b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "st1 {v31.8b, v0.8b, v1.8b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st1 {v26.16b, v27.16b, v28.16b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "st1 {v26.8b, v27.8b, v28.8b}, [x30], x29");
|
||||
|
||||
@@ -345,6 +387,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st1 {v26.2d, v27.2d, v28.2d}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "st1 {v26.1d, v27.1d, v28.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "st1 {v31.16b, v0.16b, v1.16b}, [x30], #48");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "st1 {v31.8b, v0.8b, v1.8b}, [x30], #24");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st1 {v26.16b, v27.16b, v28.16b}, [x30], #48");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "st1 {v26.8b, v27.8b, v28.8b}, [x30], #24");
|
||||
|
||||
@@ -357,6 +401,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st1 {v26.2d, v27.2d, v28.2d}, [x30], #48");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "st1 {v26.1d, v27.1d, v28.1d}, [x30], #24");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "st1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "st1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "st1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
|
||||
|
||||
@@ -369,6 +415,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "st1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "st1 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "st1 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "st1 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "st1 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
|
||||
|
||||
@@ -381,6 +429,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "st1 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], #64");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "st1 {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], #32");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "ld2 {v31.16b, v0.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "ld2 {v31.8b, v0.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2 {v26.16b, v27.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2 {v26.8b, v27.8b}, [x30], x29");
|
||||
|
||||
@@ -393,6 +443,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2 {v26.2d, v27.2d}, [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "ld2 {v31.16b, v0.16b}, [x30], #32");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "ld2 {v31.8b, v0.8b}, [x30], #16");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld2 {v26.16b, v27.16b}, [x30], #32");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld2 {v26.8b, v27.8b}, [x30], #16");
|
||||
|
||||
@@ -405,6 +457,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "ld2 {v26.2d, v27.2d}, [x30], #32");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "st2 {v31.16b, v0.16b}, [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "st2 {v31.8b, v0.8b}, [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st2 {v26.16b, v27.16b}, [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "st2 {v26.8b, v27.8b}, [x30], x29");
|
||||
|
||||
@@ -417,6 +471,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "st2 {v26.2d, v27.2d}, [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 32), "st2 {v31.16b, v0.16b}, [x30], #32");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 16), "st2 {v31.8b, v0.8b}, [x30], #16");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st2 {v26.16b, v27.16b}, [x30], #32");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "st2 {v26.8b, v27.8b}, [x30], #16");
|
||||
|
||||
@@ -429,6 +485,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 32), "st2 {v26.2d, v27.2d}, [x30], #32");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "ld3 {v31.16b, v0.16b, v1.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "ld3 {v31.8b, v0.8b, v1.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3 {v26.16b, v27.16b, v28.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3 {v26.8b, v27.8b, v28.8b}, [x30], x29");
|
||||
|
||||
@@ -441,6 +499,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3 {v26.2d, v27.2d, v28.2d}, [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "ld3 {v31.16b, v0.16b, v1.16b}, [x30], #48");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "ld3 {v31.8b, v0.8b, v1.8b}, [x30], #24");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld3 {v26.16b, v27.16b, v28.16b}, [x30], #48");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld3 {v26.8b, v27.8b, v28.8b}, [x30], #24");
|
||||
|
||||
@@ -453,6 +513,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "ld3 {v26.2d, v27.2d, v28.2d}, [x30], #48");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "st3 {v31.16b, v0.16b, v1.16b}, [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "st3 {v31.8b, v0.8b, v1.8b}, [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st3 {v26.16b, v27.16b, v28.16b}, [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "st3 {v26.8b, v27.8b, v28.8b}, [x30], x29");
|
||||
|
||||
@@ -465,6 +527,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "st3 {v26.2d, v27.2d, v28.2d}, [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 48), "st3 {v31.16b, v0.16b, v1.16b}, [x30], #48");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 24), "st3 {v31.8b, v0.8b, v1.8b}, [x30], #24");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st3 {v26.16b, v27.16b, v28.16b}, [x30], #48");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "st3 {v26.8b, v27.8b, v28.8b}, [x30], #24");
|
||||
|
||||
@@ -477,6 +541,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 48), "st3 {v26.2d, v27.2d, v28.2d}, [x30], #48");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "ld4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "ld4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
|
||||
|
||||
@@ -489,6 +555,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "ld4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "ld4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "ld4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "ld4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
|
||||
|
||||
@@ -501,6 +569,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "ld4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], #64");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "st4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "st4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "st4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
|
||||
|
||||
@@ -513,6 +583,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "st4 {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "unallocated (NEONLoadStoreMultiStructPostIndex)");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 64), "st4 {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #64");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 32), "st4 {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #32");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 64), "st4 {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #64");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "st4 {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #32");
|
||||
|
||||
@@ -531,7 +603,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: ASIMD loadstore single")
|
||||
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30), "ld1 {v26.s}[0], [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30), "ld1 {v26.d}[0], [x30]");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "ld1 {v26.b}[15], [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "ld1 {v26.b}[15], [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30), "ld1 {v26.h}[7], [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30), "ld1 {v26.s}[3], [x30]");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30), "ld1 {v26.d}[1], [x30]");
|
||||
@@ -551,71 +623,80 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: ASIMD loadstore single")
|
||||
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30), "st1 {v26.s}[0], [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30), "st1 {v26.d}[0], [x30]");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "st1 {v26.b}[15], [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30), "st1 {v26.b}[15], [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30), "st1 {v26.h}[7], [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30), "st1 {v26.s}[3], [x30]");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30), "st1 {v26.d}[1], [x30]");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30), "ld2 {v31.b, v0.b}[0], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.b, v27.b}[0], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.h, v27.h}[0], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.s, v27.s}[0], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "ld2 {v26.d, v27.d}[0], [x30]");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "ld2 {v26.b, v27.b}[15], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "ld2 {v26.b, v27.b}[15], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30), "ld2 {v26.h, v27.h}[7], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30), "ld2 {v26.s, v27.s}[3], [x30]");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30), "ld2 {v26.d, v27.d}[1], [x30]");
|
||||
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30), "ld2r {v31.8b, v0.8b}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.8b, v27.8b}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.4h, v27.4h}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.2s, v27.2s}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30), "ld2r {v26.1d, v27.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30), "ld2r {v31.16b, v0.16b}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.16b, v27.16b}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.8h, v27.8h}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.4s, v27.4s}, [x30]");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30), "ld2r {v26.2d, v27.2d}, [x30]");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30), "st2 {v31.b, v0.b}[0], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.b, v27.b}[0], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.h, v27.h}[0], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.s, v27.s}[0], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30), "st2 {v26.d, v27.d}[0], [x30]");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "st2 {v26.b, v27.b}[15], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30), "st2 {v26.b, v27.b}[15], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30), "st2 {v26.h, v27.h}[7], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30), "st2 {v26.s, v27.s}[3], [x30]");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30), "st2 {v26.d, v27.d}[1], [x30]");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30), "ld3 {v31.b, v0.b, v1.b}[0], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.b, v27.b, v28.b}[0], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.h, v27.h, v28.h}[0], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.s, v27.s, v28.s}[0], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "ld3 {v26.d, v27.d, v28.d}[0], [x30]");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "ld3 {v26.b, v27.b, v28.b}[15], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "ld3 {v26.b, v27.b, v28.b}[15], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30), "ld3 {v26.h, v27.h, v28.h}[7], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30), "ld3 {v26.s, v27.s, v28.s}[3], [x30]");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30), "ld3 {v26.d, v27.d, v28.d}[1], [x30]");
|
||||
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30), "ld3r {v31.8b, v0.8b, v1.8b}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.8b, v27.8b, v28.8b}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.4h, v27.4h, v28.4h}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.2s, v27.2s, v28.2s}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30), "ld3r {v26.1d, v27.1d, v28.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30), "ld3r {v31.16b, v0.16b, v1.16b}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.16b, v27.16b, v28.16b}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.8h, v27.8h, v28.8h}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.4s, v27.4s, v28.4s}, [x30]");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30), "ld3r {v26.2d, v27.2d, v28.2d}, [x30]");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30), "st3 {v31.b, v0.b, v1.b}[0], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.b, v27.b, v28.b}[0], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.h, v27.h, v28.h}[0], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.s, v27.s, v28.s}[0], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30), "st3 {v26.d, v27.d, v28.d}[0], [x30]");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "st3 {v26.b, v27.b, v28.b}[15], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30), "st3 {v26.b, v27.b, v28.b}[15], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30), "st3 {v26.h, v27.h, v28.h}[7], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30), "st3 {v26.s, v27.s, v28.s}[3], [x30]");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30), "st3 {v26.d, v27.d, v28.d}[1], [x30]");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30), "ld4 {v31.b, v0.b, v1.b, v2.b}[0], [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "ld4 {v26.s, v27.s, v28.s, v29.s}[0], [x30]");
|
||||
@@ -626,22 +707,25 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: ASIMD loadstore single")
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30]");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30]");
|
||||
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30), "ld4r {v31.8b, v0.8b, v1.8b, v2.8b}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.2s, v27.2s, v28.2s, v29.2s}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30), "ld4r {v26.1d, v27.1d, v28.1d, v29.1d}, [x30]");
|
||||
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30), "ld4r {v31.16b, v0.16b, v1.16b, v2.16b}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.4s, v27.4s, v28.4s, v29.4s}, [x30]");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30), "ld4r {v26.2d, v27.2d, v28.2d, v29.2d}, [x30]");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30), "st4 {v31.b, v0.b, v1.b, v2.b}[0], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.s, v27.s, v28.s, v29.s}[0], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30), "st4 {v26.d, v27.d, v28.d, v29.d}[0], [x30]");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30]");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30]");
|
||||
@@ -652,7 +736,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30, 4), "ld1 {v26.s}[0], [x30], #4");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30, 8), "ld1 {v26.d}[0], [x30], #8");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "ld1 {v26.b}[15], [x30], #1");
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "ld1 {v26.b}[15], [x30], #1");
|
||||
TEST_SINGLE(ld1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30, 2), "ld1 {v26.h}[7], [x30], #2");
|
||||
TEST_SINGLE(ld1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30, 4), "ld1 {v26.s}[3], [x30], #4");
|
||||
TEST_SINGLE(ld1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30, 8), "ld1 {v26.d}[1], [x30], #8");
|
||||
@@ -672,100 +756,112 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 0, Reg::r30, 4), "st1 {v26.s}[0], [x30], #4");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 0, Reg::r30, 8), "st1 {v26.d}[0], [x30], #8");
|
||||
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "st1 {v26.b}[15], [x30], #1");
|
||||
TEST_SINGLE(st1<SubRegSize::i8Bit>(VReg::v26, 15, Reg::r30, 1), "st1 {v26.b}[15], [x30], #1");
|
||||
TEST_SINGLE(st1<SubRegSize::i16Bit>(VReg::v26, 7, Reg::r30, 2), "st1 {v26.h}[7], [x30], #2");
|
||||
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30, 4), "st1 {v26.s}[3], [x30], #4");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30, 8), "st1 {v26.d}[1], [x30], #8");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "ld2 {v26.b, v27.b}[0], [x30], #2");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "ld2 {v26.h, v27.h}[0], [x30], #4");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "ld2 {v26.s, v27.s}[0], [x30], #8");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, 2), "ld2 {v31.b, v0.b}[0], [x30], #2");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "ld2 {v26.b, v27.b}[0], [x30], #2");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "ld2 {v26.h, v27.h}[0], [x30], #4");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "ld2 {v26.s, v27.s}[0], [x30], #8");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 16), "ld2 {v26.d, v27.d}[0], [x30], #16");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, 2), "ld2 {v26.b, v27.b}[15], [x30], #2");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "ld2 {v26.h, v27.h}[7], [x30], #4");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "ld2 {v26.s, v27.s}[3], [x30], #8");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "ld2 {v26.h, v27.h}[7], [x30], #4");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "ld2 {v26.s, v27.s}[3], [x30], #8");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, 16), "ld2 {v26.d, v27.d}[1], [x30], #16");
|
||||
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 2), "ld2r {v26.8b, v27.8b}, [x30], #2");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30, 4), "ld2r {v26.4h, v27.4h}, [x30], #4");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30, 8), "ld2r {v26.2s, v27.2s}, [x30], #8");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, 2), "ld2r {v31.8b, v0.8b}, [x30], #2");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, 2), "ld2r {v26.8b, v27.8b}, [x30], #2");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30, 4), "ld2r {v26.4h, v27.4h}, [x30], #4");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30, 8), "ld2r {v26.2s, v27.2s}, [x30], #8");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, 16), "ld2r {v26.1d, v27.1d}, [x30], #16");
|
||||
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 2), "ld2r {v26.16b, v27.16b}, [x30], #2");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30, 4), "ld2r {v26.8h, v27.8h}, [x30], #4");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30, 8), "ld2r {v26.4s, v27.4s}, [x30], #8");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, 2), "ld2r {v31.16b, v0.16b}, [x30], #2");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, 2), "ld2r {v26.16b, v27.16b}, [x30], #2");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30, 4), "ld2r {v26.8h, v27.8h}, [x30], #4");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30, 8), "ld2r {v26.4s, v27.4s}, [x30], #8");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, 16), "ld2r {v26.2d, v27.2d}, [x30], #16");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "st2 {v26.b, v27.b}[0], [x30], #2");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "st2 {v26.h, v27.h}[0], [x30], #4");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "st2 {v26.s, v27.s}[0], [x30], #8");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, 2), "st2 {v31.b, v0.b}[0], [x30], #2");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 2), "st2 {v26.b, v27.b}[0], [x30], #2");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 4), "st2 {v26.h, v27.h}[0], [x30], #4");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 8), "st2 {v26.s, v27.s}[0], [x30], #8");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, 16), "st2 {v26.d, v27.d}[0], [x30], #16");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, 2), "st2 {v26.b, v27.b}[15], [x30], #2");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "st2 {v26.h, v27.h}[7], [x30], #4");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "st2 {v26.s, v27.s}[3], [x30], #8");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, 16), "st2 {v26.d, v27.d}[1], [x30], #16");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, 2), "st2 {v26.b, v27.b}[15], [x30], #2");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, 4), "st2 {v26.h, v27.h}[7], [x30], #4");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, 8), "st2 {v26.s, v27.s}[3], [x30], #8");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, 16), "st2 {v26.d, v27.d}[1], [x30], #16");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "ld3 {v26.b, v27.b, v28.b}[0], [x30], #3");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "ld3 {v26.h, v27.h, v28.h}[0], [x30], #6");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, 3), "ld3 {v31.b, v0.b, v1.b}[0], [x30], #3");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "ld3 {v26.b, v27.b, v28.b}[0], [x30], #3");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "ld3 {v26.h, v27.h, v28.h}[0], [x30], #6");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 12), "ld3 {v26.s, v27.s, v28.s}[0], [x30], #12");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 24), "ld3 {v26.d, v27.d, v28.d}[0], [x30], #24");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 1), "ld3 {v26.b, v27.b, v28.b}[15], [x30], #3");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 2), "ld3 {v26.h, v27.h, v28.h}[7], [x30], #6");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 4), "ld3 {v26.s, v27.s, v28.s}[3], [x30], #12");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 8), "ld3 {v26.d, v27.d, v28.d}[1], [x30], #24");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 1), "ld3 {v26.b, v27.b, v28.b}[15], [x30], #3");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 2), "ld3 {v26.h, v27.h, v28.h}[7], [x30], #6");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 4), "ld3 {v26.s, v27.s, v28.s}[3], [x30], #12");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 8), "ld3 {v26.d, v27.d, v28.d}[1], [x30], #24");
|
||||
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 3), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], #3");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 6), "ld3r {v26.4h, v27.4h, v28.4h}, [x30], #6");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, 3), "ld3r {v31.8b, v0.8b, v1.8b}, [x30], #3");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 3), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], #3");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 6), "ld3r {v26.4h, v27.4h, v28.4h}, [x30], #6");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 12), "ld3r {v26.2s, v27.2s, v28.2s}, [x30], #12");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, 24), "ld3r {v26.1d, v27.1d, v28.1d}, [x30], #24");
|
||||
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 3), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], #3");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 6), "ld3r {v26.8h, v27.8h, v28.8h}, [x30], #6");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, 3), "ld3r {v31.16b, v0.16b, v1.16b}, [x30], #3");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 3), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], #3");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 6), "ld3r {v26.8h, v27.8h, v28.8h}, [x30], #6");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 12), "ld3r {v26.4s, v27.4s, v28.4s}, [x30], #12");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, 24), "ld3r {v26.2d, v27.2d, v28.2d}, [x30], #24");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[0], [x30], #3");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[0], [x30], #6");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, 3), "st3 {v31.b, v0.b, v1.b}[0], [x30], #3");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[0], [x30], #3");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[0], [x30], #6");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 12), "st3 {v26.s, v27.s, v28.s}[0], [x30], #12");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, 24), "st3 {v26.d, v27.d, v28.d}[0], [x30], #24");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[15], [x30], #3");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[7], [x30], #6");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 12), "st3 {v26.s, v27.s, v28.s}[3], [x30], #12");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 24), "st3 {v26.d, v27.d, v28.d}[1], [x30], #24");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, 3), "st3 {v26.b, v27.b, v28.b}[15], [x30], #3");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, 6), "st3 {v26.h, v27.h, v28.h}[7], [x30], #6");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, 12), "st3 {v26.s, v27.s, v28.s}[3], [x30], #12");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, 24), "st3 {v26.d, v27.d, v28.d}[1], [x30], #24");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, 4), "ld4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], #4");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 16), "ld4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], #16");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 32), "ld4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], #32");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
|
||||
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 4), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #4");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 8), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30], #8");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, 4), "ld4r {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], #4");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 4), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], #4");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 8), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30], #8");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 16), "ld4r {v26.2s, v27.2s, v28.2s, v29.2s}, [x30], #16");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, 32), "ld4r {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], #32");
|
||||
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 4), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #4");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 8), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30], #8");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, 4), "ld4r {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], #4");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 4), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], #4");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 8), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30], #8");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 16), "ld4r {v26.4s, v27.4s, v28.4s, v29.4s}, [x30], #16");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, 32), "ld4r {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], #32");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, 4), "st4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], #4");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], #4");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], #8");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 16), "st4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], #16");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, 32), "st4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], #32");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, 4), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], #4");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, 8), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], #8");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, 16), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], #16");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, 32), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], #32");
|
||||
|
||||
TEST_SINGLE(ld1<SubRegSize::i8Bit>(VReg::v26, 0, Reg::r30, Reg::r29), "ld1 {v26.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld1<SubRegSize::i16Bit>(VReg::v26, 0, Reg::r30, Reg::r29), "ld1 {v26.h}[0], [x30], x29");
|
||||
@@ -797,95 +893,107 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Advanced SIMD load/store
|
||||
TEST_SINGLE(st1<SubRegSize::i32Bit>(VReg::v26, 3, Reg::r30, Reg::r29), "st1 {v26.s}[3], [x30], x29");
|
||||
TEST_SINGLE(st1<SubRegSize::i64Bit>(VReg::v26, 1, Reg::r30, Reg::r29), "st1 {v26.d}[1], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, Reg::r29), "ld2 {v31.b, v0.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.h, v27.h}[0], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.s, v27.s}[0], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "ld2 {v26.d, v27.d}[0], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[15], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "ld2 {v26.h, v27.h}[7], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "ld2 {v26.s, v27.s}[3], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "ld2 {v26.d, v27.d}[1], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "ld2 {v26.b, v27.b}[15], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "ld2 {v26.h, v27.h}[7], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "ld2 {v26.s, v27.s}[3], [x30], x29");
|
||||
TEST_SINGLE(ld2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "ld2 {v26.d, v27.d}[1], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.8b, v27.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, Reg::r30, Reg::r29), "ld2r {v31.8b, v0.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.8b, v27.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.4h, v27.4h}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.2s, v27.2s}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, Reg::r30, Reg::r29), "ld2r {v26.1d, v27.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.16b, v27.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, Reg::r30, Reg::r29), "ld2r {v31.16b, v0.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.16b, v27.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.8h, v27.8h}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.4s, v27.4s}, [x30], x29");
|
||||
TEST_SINGLE(ld2r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, Reg::r30, Reg::r29), "ld2r {v26.2d, v27.2d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v31, VReg::v0, 0, Reg::r30, Reg::r29), "st2 {v31.b, v0.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.h, v27.h}[0], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.s, v27.s}[0], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 0, Reg::r30, Reg::r29), "st2 {v26.d, v27.d}[0], [x30], x29");
|
||||
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[15], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "st2 {v26.h, v27.h}[7], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "st2 {v26.s, v27.s}[3], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "st2 {v26.d, v27.d}[1], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i8Bit>(VReg::v26, VReg::v27, 15, Reg::r30, Reg::r29), "st2 {v26.b, v27.b}[15], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i16Bit>(VReg::v26, VReg::v27, 7, Reg::r30, Reg::r29), "st2 {v26.h, v27.h}[7], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i32Bit>(VReg::v26, VReg::v27, 3, Reg::r30, Reg::r29), "st2 {v26.s, v27.s}[3], [x30], x29");
|
||||
TEST_SINGLE(st2<SubRegSize::i64Bit>(VReg::v26, VReg::v27, 1, Reg::r30, Reg::r29), "st2 {v26.d, v27.d}[1], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, Reg::r29), "ld3 {v31.b, v0.b, v1.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.h, v27.h, v28.h}[0], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.s, v27.s, v28.s}[0], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "ld3 {v26.d, v27.d, v28.d}[0], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[15], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "ld3 {v26.h, v27.h, v28.h}[7], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "ld3 {v26.s, v27.s, v28.s}[3], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "ld3 {v26.d, v27.d, v28.d}[1], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "ld3 {v26.b, v27.b, v28.b}[15], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "ld3 {v26.h, v27.h, v28.h}[7], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "ld3 {v26.s, v27.s, v28.s}[3], [x30], x29");
|
||||
TEST_SINGLE(ld3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "ld3 {v26.d, v27.d, v28.d}[1], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, Reg::r30, Reg::r29), "ld3r {v31.8b, v0.8b, v1.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.8b, v27.8b, v28.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.4h, v27.4h, v28.4h}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.2s, v27.2s, v28.2s}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, Reg::r30, Reg::r29), "ld3r {v26.1d, v27.1d, v28.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, Reg::r30, Reg::r29), "ld3r {v31.16b, v0.16b, v1.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.16b, v27.16b, v28.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.8h, v27.8h, v28.8h}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.4s, v27.4s, v28.4s}, [x30], x29");
|
||||
TEST_SINGLE(ld3r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, Reg::r30, Reg::r29), "ld3r {v26.2d, v27.2d, v28.2d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, 0, Reg::r30, Reg::r29), "st3 {v31.b, v0.b, v1.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.h, v27.h, v28.h}[0], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.s, v27.s, v28.s}[0], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 0, Reg::r30, Reg::r29), "st3 {v26.d, v27.d, v28.d}[0], [x30], x29");
|
||||
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[15], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "st3 {v26.h, v27.h, v28.h}[7], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "st3 {v26.s, v27.s, v28.s}[3], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "st3 {v26.d, v27.d, v28.d}[1], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, 15, Reg::r30, Reg::r29), "st3 {v26.b, v27.b, v28.b}[15], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, 7, Reg::r30, Reg::r29), "st3 {v26.h, v27.h, v28.h}[7], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, 3, Reg::r30, Reg::r29), "st3 {v26.s, v27.s, v28.s}[3], [x30], x29");
|
||||
TEST_SINGLE(st3<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, 1, Reg::r30, Reg::r29), "st3 {v26.d, v27.d, v28.d}[1], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, Reg::r29), "ld4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "ld4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "ld4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "ld4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "ld4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
|
||||
TEST_SINGLE(ld4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "ld4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d31, DReg::d0, DReg::d1, DReg::d2, Reg::r30, Reg::r29), "ld4r {v31.8b, v0.8b, v1.8b, v2.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.8b, v27.8b, v28.8b, v29.8b}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.4h, v27.4h, v28.4h, v29.4h}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.2s, v27.2s, v28.2s, v29.2s}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(DReg::d26, DReg::d27, DReg::d28, DReg::d29, Reg::r30, Reg::r29), "ld4r {v26.1d, v27.1d, v28.1d, v29.1d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q31, QReg::q0, QReg::q1, QReg::q2, Reg::r30, Reg::r29), "ld4r {v31.16b, v0.16b, v1.16b, v2.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i8Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.16b, v27.16b, v28.16b, v29.16b}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i16Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.8h, v27.8h, v28.8h, v29.8h}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i32Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.4s, v27.4s, v28.4s, v29.4s}, [x30], x29");
|
||||
TEST_SINGLE(ld4r<SubRegSize::i64Bit>(QReg::q26, QReg::q27, QReg::q28, QReg::q29, Reg::r30, Reg::r29), "ld4r {v26.2d, v27.2d, v28.2d, v29.2d}, [x30], x29");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v31, VReg::v0, VReg::v1, VReg::v2, 0, Reg::r30, Reg::r29), "st4 {v31.b, v0.b, v1.b, v2.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[0], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.h, v27.h, v28.h, v29.h}[0], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.s, v27.s, v28.s, v29.s}[0], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 0, Reg::r30, Reg::r29), "st4 {v26.d, v27.d, v28.d, v29.d}[0], [x30], x29");
|
||||
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i8Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 15, Reg::r30, Reg::r29), "st4 {v26.b, v27.b, v28.b, v29.b}[15], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i16Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 7, Reg::r30, Reg::r29), "st4 {v26.h, v27.h, v28.h, v29.h}[7], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i32Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 3, Reg::r30, Reg::r29), "st4 {v26.s, v27.s, v28.s, v29.s}[3], [x30], x29");
|
||||
TEST_SINGLE(st4<SubRegSize::i64Bit>(VReg::v26, VReg::v27, VReg::v28, VReg::v29, 1, Reg::r30, Reg::r29), "st4 {v26.d, v27.d, v28.d, v29.d}[1], [x30], x29");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore exclusive pair") {
|
||||
TEST_SINGLE(stxp(Size::i32Bit, Reg::r28, Reg::r29, Reg::r30, Reg::r28), "stxp w28, w29, w30, [x28]");
|
||||
@@ -920,7 +1028,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore exclusive regi
|
||||
TEST_SINGLE(ldaxr(WReg::w30, Reg::r29), "ldaxr w30, [x29]");
|
||||
|
||||
TEST_SINGLE(stxr(XReg::x30, XReg::x29, Reg::r28), "stxr w30, x29, [x28]");
|
||||
TEST_SINGLE(stlxr(XReg::x30, XReg::x29, Reg::r28), "stlxr w30, x29, [x28]");
|
||||
TEST_SINGLE(stlxr(WReg::w30, XReg::x29, Reg::r28), "stlxr w30, x29, [x28]");
|
||||
|
||||
TEST_SINGLE(ldxr(XReg::x30, Reg::r29), "ldxr x30, [x29]");
|
||||
TEST_SINGLE(ldaxr(XReg::x30, Reg::r29), "ldaxr x30, [x29]");
|
||||
@@ -1211,8 +1319,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore register pair
|
||||
TEST_SINGLE(ldp<IndexType::POST>(WReg::w30, WReg::w28, Reg::r29, -256), "ldp w30, w28, [x29], #-256");
|
||||
TEST_SINGLE(ldp<IndexType::POST>(WReg::w30, WReg::w28, Reg::r29, 252), "ldp w30, w28, [x29], #252");
|
||||
|
||||
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, WReg::w28, Reg::r29, -256), "ldpsw x30, x28, [x29], #-256");
|
||||
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, WReg::w28, Reg::r29, 252), "ldpsw x30, x28, [x29], #252");
|
||||
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, -256), "ldpsw x30, x28, [x29], #-256");
|
||||
TEST_SINGLE(ldpsw<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, 252), "ldpsw x30, x28, [x29], #252");
|
||||
|
||||
TEST_SINGLE(stp<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, -512), "stp x30, x28, [x29], #-512");
|
||||
TEST_SINGLE(stp<IndexType::POST>(XReg::x30, XReg::x28, Reg::r29, 504), "stp x30, x28, [x29], #504");
|
||||
@@ -1245,8 +1353,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore register pair
|
||||
TEST_SINGLE(ldp<IndexType::OFFSET>(WReg::w30, WReg::w28, Reg::r29, -256), "ldp w30, w28, [x29, #-256]");
|
||||
TEST_SINGLE(ldp<IndexType::OFFSET>(WReg::w30, WReg::w28, Reg::r29, 252), "ldp w30, w28, [x29, #252]");
|
||||
|
||||
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, WReg::w28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]");
|
||||
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, WReg::w28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]");
|
||||
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]");
|
||||
TEST_SINGLE(ldpsw<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]");
|
||||
|
||||
TEST_SINGLE(stp<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, -512), "stp x30, x28, [x29, #-512]");
|
||||
TEST_SINGLE(stp<IndexType::OFFSET>(XReg::x30, XReg::x28, Reg::r29, 504), "stp x30, x28, [x29, #504]");
|
||||
@@ -1279,8 +1387,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Loadstore: Loadstore register pair
|
||||
TEST_SINGLE(ldp<IndexType::PRE>(WReg::w30, WReg::w28, Reg::r29, -256), "ldp w30, w28, [x29, #-256]!");
|
||||
TEST_SINGLE(ldp<IndexType::PRE>(WReg::w30, WReg::w28, Reg::r29, 252), "ldp w30, w28, [x29, #252]!");
|
||||
|
||||
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, WReg::w28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]!");
|
||||
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, WReg::w28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]!");
|
||||
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, -256), "ldpsw x30, x28, [x29, #-256]!");
|
||||
TEST_SINGLE(ldpsw<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, 252), "ldpsw x30, x28, [x29, #252]!");
|
||||
|
||||
TEST_SINGLE(stp<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, -512), "stp x30, x28, [x29, #-512]!");
|
||||
TEST_SINGLE(stp<IndexType::PRE>(XReg::x30, XReg::x28, Reg::r29, 504), "stp x30, x28, [x29, #504]!");
|
||||
|
||||
+777
-95
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -658,7 +658,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: Scalar: Floating-point data-process
|
||||
TEST_SINGLE(fcvt(SReg::s30, DReg::d29), "fcvt s30, d29");
|
||||
if (false) {
|
||||
// vixl doesn't support this instruction.
|
||||
TEST_SINGLE(bfcvt(HReg::h30, DReg::d29), "bfcvt h30, d29");
|
||||
TEST_SINGLE(bfcvt(HReg::h30, SReg::s29), "bfcvt h30, s29");
|
||||
}
|
||||
TEST_SINGLE(fcvt(HReg::h30, DReg::d29), "fcvt h30, d29");
|
||||
TEST_SINGLE(frintn(DReg::d30, DReg::d29), "frintn d30, d29");
|
||||
|
||||
Vendored
+1
-1
Submodule External/fex-gcc-target-tests-bins updated: 9f83d474bb...442678a134.
@@ -66,6 +66,9 @@ DefinitionRenameDict = {
|
||||
"shmctl": "_shmctl",
|
||||
"shmat": "_shmat",
|
||||
"shmdt": "_shmdt",
|
||||
# musl/Alpine Linux defines `fstatat64` as a define that points to `fstatat`.
|
||||
# Rename it to avoid global define conflicts.
|
||||
"fstatat64": "fstatat_64",
|
||||
}
|
||||
|
||||
Definitions_x64 = []
|
||||
|
||||
@@ -15,7 +15,16 @@ BigCoreIDs = {
|
||||
tuple([0x41, 0xd0b]): "cortex-a76",
|
||||
tuple([0x41, 0xd0d]): "cortex-a77",
|
||||
tuple([0x41, 0xd41]): "cortex-a78",
|
||||
tuple([0x41, 0xd41]):
|
||||
[ ["cortex-a78", "0.0"],
|
||||
["cortex-x1c", "14.0"], # Claim to be x1c as an alternative if available.
|
||||
["cortex-a78c", "9999.0"], # Doesn't exist in clang as of version 15.0
|
||||
],
|
||||
tuple([0x41, 0xd44]): "cortex-x1",
|
||||
tuple([0x41, 0xd4c]):
|
||||
[ ["cortex-x1", "0.0"],
|
||||
["cortex-x1c", "14.0"],
|
||||
],
|
||||
tuple([0x41, 0xd47]):
|
||||
[ ["cortex-a78", "0.0"],
|
||||
["cortex-a710", "14.0"],
|
||||
|
||||
Loaded 100 of 177 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user