mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 20:00:16 +02:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e9e88968d7 |
No files matched your search
@@ -166,17 +166,6 @@ jobs:
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_APITests.log || true
|
||||
|
||||
- name: ARMEmitter tests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
run: cmake --build . --config $BUILD_TYPE --target emitter_tests
|
||||
|
||||
- name: ARMEmitter Test Results move
|
||||
if: ${{ always() }}
|
||||
shell: bash
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
run: mv ${{runner.workspace}}/build/Testing/Temporary/LastTest.log ${{runner.workspace}}/build/Testing/Temporary/LastTest_ARMEmitterTests.log || true
|
||||
|
||||
- name: FEXLinuxTests
|
||||
working-directory: ${{runner.workspace}}/build
|
||||
shell: bash
|
||||
|
||||
@@ -30,7 +30,6 @@ option(ENABLE_CCACHE "Enables ccache for compile caching" TRUE)
|
||||
option(ENABLE_TERMUX_BUILD "Forces building for Termux on a non-Termux build machine" FALSE)
|
||||
option(ENABLE_VIXL_SIMULATOR "Forces the FEX JIT to use the VIXL simulator" FALSE)
|
||||
option(ENABLE_VIXL_DISASSEMBLER "Enables debug disassembler output with VIXL" FALSE)
|
||||
option(COMPILE_VIXL_DISASSEMBLER "Compiles the vixl disassembler in to vixl" FALSE)
|
||||
option(ENABLE_FEXCORE_PROFILER "Enables use of the FEXCore timeline profiling capabilities" FALSE)
|
||||
set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend you want to use for the FEXCore profiler")
|
||||
|
||||
@@ -198,11 +197,6 @@ set (CMAKE_LINKER_FLAGS_RELEASE "${CMAKE_LINKER_FLAGS_RELEASE} -fomit-frame-poin
|
||||
|
||||
include_directories(External/robin-map/include/)
|
||||
|
||||
if (BUILD_TESTS)
|
||||
# Enable vixl disassembler if tests are enabled.
|
||||
set(COMPILE_VIXL_DISASSEMBLER TRUE)
|
||||
endif()
|
||||
|
||||
add_subdirectory(External/vixl/)
|
||||
include_directories(External/vixl/src/)
|
||||
|
||||
|
||||
+11
-38
@@ -281,7 +281,9 @@ def print_ir_structs(defines):
|
||||
output_file.write("\tvoid* Data[0];\n")
|
||||
output_file.write("\tIROps Op;\n\n")
|
||||
output_file.write("\tuint8_t Size;\n")
|
||||
output_file.write("\tuint8_t ElementSize;\n")
|
||||
output_file.write("\tuint8_t NumArgs;\n")
|
||||
output_file.write("\tuint8_t ElementSize : 7;\n")
|
||||
output_file.write("\tbool HasDest : 1;\n")
|
||||
|
||||
output_file.write("\ttemplate<typename T>\n")
|
||||
output_file.write("\tT const* C() const { return reinterpret_cast<T const*>(Data); }\n")
|
||||
@@ -356,10 +358,8 @@ def print_ir_sizes():
|
||||
|
||||
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] std::string_view const& GetName(IROps Op);\n")
|
||||
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] uint8_t GetArgs(IROps Op);\n")
|
||||
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] uint8_t GetRAArgs(IROps Op);\n")
|
||||
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] FEXCore::IR::RegisterClassType GetRegClass(IROps Op);\n\n")
|
||||
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] bool HasSideEffects(IROps Op);\n")
|
||||
output_file.write("[[nodiscard, gnu::const, gnu::visibility(\"default\")]] bool GetHasDest(IROps Op);\n")
|
||||
|
||||
output_file.write("#undef IROP_SIZES\n")
|
||||
output_file.write("#endif\n\n")
|
||||
@@ -417,7 +417,7 @@ def print_ir_getname():
|
||||
def print_ir_getraargs():
|
||||
output_file.write("#ifdef IROP_GETRAARGS_IMPL\n")
|
||||
|
||||
output_file.write("constexpr std::array<uint8_t, OP_LAST + 1> IRRAArgs = {\n")
|
||||
output_file.write("constexpr std::array<uint8_t, OP_LAST + 1> IRArgs = {\n")
|
||||
for op in IROps:
|
||||
SSAArgs = op.SSAArgNum
|
||||
|
||||
@@ -430,18 +430,6 @@ def print_ir_getraargs():
|
||||
|
||||
output_file.write("};\n\n")
|
||||
|
||||
|
||||
output_file.write("constexpr std::array<uint8_t, OP_LAST + 1> IRArgs = {\n")
|
||||
for op in IROps:
|
||||
SSAArgs = op.SSAArgNum
|
||||
output_file.write("\t{},\n".format(SSAArgs))
|
||||
|
||||
output_file.write("};\n\n")
|
||||
|
||||
output_file.write("uint8_t GetRAArgs(IROps Op) {\n")
|
||||
output_file.write(" return IRRAArgs[Op];\n")
|
||||
output_file.write("}\n")
|
||||
|
||||
output_file.write("uint8_t GetArgs(IROps Op) {\n")
|
||||
output_file.write(" return IRArgs[Op];\n")
|
||||
output_file.write("}\n")
|
||||
@@ -465,25 +453,6 @@ def print_ir_hassideeffects():
|
||||
output_file.write("#undef IROP_HASSIDEEFFECTS_IMPL\n")
|
||||
output_file.write("#endif\n\n")
|
||||
|
||||
def print_ir_gethasdest():
|
||||
output_file.write("#ifdef IROP_GETHASDEST_IMPL\n")
|
||||
|
||||
output_file.write("constexpr std::array<bool, OP_LAST + 1> IRDest = {\n")
|
||||
for op in IROps:
|
||||
if op.HasDest:
|
||||
output_file.write("\ttrue,\n")
|
||||
else:
|
||||
output_file.write("\tfalse,\n")
|
||||
|
||||
output_file.write("};\n\n")
|
||||
|
||||
output_file.write("bool GetHasDest(IROps Op) {\n")
|
||||
output_file.write(" return IRDest[Op];\n")
|
||||
output_file.write("}\n")
|
||||
|
||||
output_file.write("#undef IROP_GETHASDEST_IMPL\n")
|
||||
output_file.write("#endif\n\n")
|
||||
|
||||
# Print out IR argument printing
|
||||
def print_ir_arg_printer():
|
||||
output_file.write("#ifdef IROP_ARGPRINTER_HELPER\n")
|
||||
@@ -578,13 +547,13 @@ def print_ir_allocator_helpers():
|
||||
|
||||
output_file.write("\tuint8_t GetOpElements(const OrderedNode *Op) const {\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
|
||||
output_file.write("\t\tLOGMAN_THROW_A_FMT(OpHasDest(Op), \"Op {} has no dest\\n\", GetName(HeaderOp->Op));\n")
|
||||
output_file.write("\t\tLOGMAN_THROW_A_FMT(HeaderOp->HasDest, \"Op {} has no dest\\n\", GetName(HeaderOp->Op));\n")
|
||||
output_file.write("\t\treturn HeaderOp->Size / HeaderOp->ElementSize;\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
output_file.write("\tbool OpHasDest(const OrderedNode *Op) const {\n")
|
||||
output_file.write("\t\tauto HeaderOp = Op->Header.Value.GetNode(DualListData.DataBegin());\n")
|
||||
output_file.write("\t\treturn GetHasDest(HeaderOp->Op);\n")
|
||||
output_file.write("\t\treturn HeaderOp->HasDest;\n")
|
||||
output_file.write("\t}\n\n")
|
||||
|
||||
output_file.write("\tIROps GetOpType(const OrderedNode *Op) const {\n")
|
||||
@@ -662,6 +631,8 @@ def print_ir_allocator_helpers():
|
||||
|
||||
output_file.write("\t\tOp.first->Header.Size = InferSize;\n")
|
||||
|
||||
output_file.write("\t\tOp.first->Header.NumArgs = {};\n".format(op.SSAArgNum))
|
||||
|
||||
# Some ops without a destination still need an operating size
|
||||
# Effectively reusing the destination size value for operation size
|
||||
if op.DestSize != None:
|
||||
@@ -672,6 +643,9 @@ def print_ir_allocator_helpers():
|
||||
else:
|
||||
output_file.write("\t\tOp.first->Header.ElementSize = Op.first->Header.Size / ({});\n".format(op.NumElements))
|
||||
|
||||
if (op.HasDest):
|
||||
output_file.write("\t\tOp.first->Header.HasDest = true;\n")
|
||||
|
||||
# Insert validation here
|
||||
if op.EmitValidation != None:
|
||||
output_file.write("\t\t#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED\n")
|
||||
@@ -759,7 +733,6 @@ print_ir_reg_classes()
|
||||
print_ir_getname()
|
||||
print_ir_getraargs()
|
||||
print_ir_hassideeffects()
|
||||
print_ir_gethasdest()
|
||||
print_ir_arg_printer()
|
||||
print_ir_allocator_helpers()
|
||||
print_ir_parser_switch_helper()
|
||||
|
||||
+17
-39
@@ -1,86 +1,64 @@
|
||||
#include "Common/JitSymbols.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
namespace FEXCore {
|
||||
JITSymbols::JITSymbols() {
|
||||
JITSymbols::JITSymbols() : fp{nullptr, std::fclose} {
|
||||
}
|
||||
|
||||
JITSymbols::~JITSymbols() {
|
||||
if (fd != -1) {
|
||||
close(fd);
|
||||
}
|
||||
}
|
||||
JITSymbols::~JITSymbols() = default;
|
||||
|
||||
void JITSymbols::InitFile() {
|
||||
// We can't use FILE here since we must be robust against forking processes closing our FD from under us.
|
||||
const auto PerfMap = fmt::format("/tmp/perf-{}.map", getpid());
|
||||
|
||||
fd = open(PerfMap.c_str(), O_CREAT | O_TRUNC | O_WRONLY | O_APPEND, 0644);
|
||||
fp.reset(fopen(PerfMap.c_str(), "wb"));
|
||||
if (fp) {
|
||||
// Disable buffering on this file
|
||||
setvbuf(fp.get(), nullptr, _IONBF, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void JITSymbols::Register(const void *HostAddr, uint64_t GuestAddr, uint32_t CodeSize) {
|
||||
if (fd == -1) return;
|
||||
if (!fp) return;
|
||||
|
||||
// Linux perf format is very straightforward
|
||||
// `<HostPtr> <Size> <Name>\n`
|
||||
const auto Buffer = fmt::format("{} {:x} JIT_0x{:x}_{}\n", HostAddr, CodeSize, GuestAddr, HostAddr);
|
||||
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
||||
if (Result == -1 && errno == EBADF) {
|
||||
fd = -1;
|
||||
}
|
||||
fmt::print(fp.get(), "{} {:x} JIT_0x{:x}_{}\n", HostAddr, CodeSize, GuestAddr, HostAddr);
|
||||
}
|
||||
|
||||
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
|
||||
if (fd == -1) return;
|
||||
if (!fp) return;
|
||||
|
||||
// Linux perf format is very straightforward
|
||||
// `<HostPtr> <Size> <Name>\n`
|
||||
const auto Buffer = fmt::format("{} {:x} {}_{}\n", HostAddr, CodeSize, Name, HostAddr);
|
||||
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
||||
if (Result == -1 && errno == EBADF) {
|
||||
fd = -1;
|
||||
}
|
||||
fmt::print(fp.get(), "{} {:x} {}_{}\n", HostAddr, CodeSize, Name, HostAddr);
|
||||
}
|
||||
|
||||
void JITSymbols::Register(const void *HostAddr, uint32_t CodeSize, std::string_view Name, uintptr_t Offset) {
|
||||
if (fd == -1) return;
|
||||
if (!fp) return;
|
||||
|
||||
// Linux perf format is very straightforward
|
||||
// `<HostPtr> <Size> <Name>\n`
|
||||
const auto Buffer = fmt::format("{} {:x} {}+0x{:x} ({})\n", HostAddr, CodeSize, Name, Offset, HostAddr);
|
||||
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
||||
if (Result == -1 && errno == EBADF) {
|
||||
fd = -1;
|
||||
}
|
||||
fmt::print(fp.get(), "{} {:x} {}+0x{:x} ({})\n", HostAddr, CodeSize, Name, Offset, HostAddr);
|
||||
}
|
||||
|
||||
void JITSymbols::RegisterNamedRegion(const void *HostAddr, uint32_t CodeSize, std::string_view Name) {
|
||||
if (fd == -1) return;
|
||||
if (!fp) return;
|
||||
|
||||
// Linux perf format is very straightforward
|
||||
// `<HostPtr> <Size> <Name>\n`
|
||||
const auto Buffer = fmt::format("{} {:x} {}\n", HostAddr, CodeSize, Name);
|
||||
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
||||
if (Result == -1 && errno == EBADF) {
|
||||
fd = -1;
|
||||
}
|
||||
fmt::print(fp.get(), "{} {:x} {}\n", HostAddr, CodeSize, Name);
|
||||
}
|
||||
|
||||
void JITSymbols::RegisterJITSpace(const void *HostAddr, uint32_t CodeSize) {
|
||||
if (fd == -1) return;
|
||||
if (!fp) return;
|
||||
|
||||
// Linux perf format is very straightforward
|
||||
// `<HostPtr> <Size> <Name>\n`
|
||||
const auto Buffer = fmt::format("{} {:x} FEXJIT\n", HostAddr, CodeSize);
|
||||
auto Result = write(fd, Buffer.c_str(), Buffer.size());
|
||||
if (Result == -1 && errno == EBADF) {
|
||||
fd = -1;
|
||||
}
|
||||
fmt::print(fp.get(), "{} {:x} FEXJIT\n", HostAddr, CodeSize);
|
||||
}
|
||||
|
||||
} // namespace FEXCore
|
||||
+3
-1
@@ -19,6 +19,8 @@ public:
|
||||
void RegisterJITSpace(const void *HostAddr, uint32_t CodeSize);
|
||||
|
||||
private:
|
||||
int fd{-1};
|
||||
using FILEPtr = std::unique_ptr<FILE, decltype(&std::fclose)>;
|
||||
|
||||
FILEPtr fp;
|
||||
};
|
||||
}
|
||||
+1
-1
@@ -123,7 +123,7 @@ namespace FEXCore::Context {
|
||||
FEXCore::HostFeatures HostFeatures;
|
||||
|
||||
std::mutex ThreadCreationMutex;
|
||||
FEXCore::Core::InternalThreadState* ParentThread{};
|
||||
FEXCore::Core::InternalThreadState* ParentThread;
|
||||
std::vector<FEXCore::Core::InternalThreadState*> Threads;
|
||||
std::atomic_bool CoreShuttingDown{false};
|
||||
bool NeedToCheckXID{true};
|
||||
|
||||
@@ -10,16 +10,6 @@
|
||||
* FEX-Emu ALU operations usually have a 32-bit or 64-bit operating size encoded in the IR operation,
|
||||
* This allows FEX to use a single helper function which decodes to both handlers.
|
||||
*/
|
||||
private:
|
||||
static bool IsADRRange(int64_t Imm) {
|
||||
return Imm >= -1048576 && Imm <= 1048575;
|
||||
}
|
||||
static bool IsADRPRange(int64_t Imm) {
|
||||
return Imm >= -4294967296 && Imm <= 4294963200;
|
||||
}
|
||||
static bool IsADRPAligned(int64_t Imm) {
|
||||
return (Imm & 0xFFF) == 0;
|
||||
}
|
||||
public:
|
||||
// PC relative
|
||||
void adr(FEXCore::ARMEmitter::Register rd, uint32_t Imm) {
|
||||
@@ -29,7 +19,7 @@ public:
|
||||
|
||||
void adr(FEXCore::ARMEmitter::Register rd, BackwardLabel const* Label) {
|
||||
int32_t Imm = static_cast<int32_t>(Label->Location - GetCursorAddress<uint8_t*>());
|
||||
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
|
||||
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575, "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b0001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
@@ -56,7 +46,7 @@ public:
|
||||
|
||||
void adrp(FEXCore::ARMEmitter::Register rd, BackwardLabel const* Label) {
|
||||
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>() & ~0xFFFLL);
|
||||
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
|
||||
LOGMAN_THROW_A_FMT(Imm >= -4294967296 && Imm <= 4294963200 && (Imm & 0xFFF) == 0, "Unscaled offset too large");
|
||||
|
||||
constexpr uint32_t Op = 0b1001'0000 << 24;
|
||||
DataProcessing_PCRel_Imm(Op, rd, Imm);
|
||||
@@ -76,49 +66,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void LongAddressGen(FEXCore::ARMEmitter::Register rd, BackwardLabel const* Label) {
|
||||
int64_t Imm = reinterpret_cast<int64_t>(Label->Location) - (GetCursorAddress<int64_t>());
|
||||
if (IsADRRange(Imm)) {
|
||||
// If the range is in ADR range then we can just use ADR.
|
||||
adr(rd, Label);
|
||||
}
|
||||
else if (IsADRPRange(Imm)) {
|
||||
int64_t ADRPImm = (reinterpret_cast<int64_t>(Label->Location) & ~0xFFFLL)
|
||||
- (GetCursorAddress<int64_t>() & ~0xFFFLL);
|
||||
|
||||
// If the range is in the ADRP range then we can use ADRP.
|
||||
bool NeedsOffset = !IsADRPAligned(reinterpret_cast<uint64_t>(Label->Location));
|
||||
uint64_t AlignedOffset = reinterpret_cast<uint64_t>(Label->Location) & 0xFFFULL;
|
||||
|
||||
// First emit ADRP
|
||||
adrp(rd, ADRPImm >> 12);
|
||||
|
||||
if (NeedsOffset) {
|
||||
// Now even an add
|
||||
add(ARMEmitter::Size::i64Bit, rd, rd, AlignedOffset);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGMAN_MSG_A_FMT("Unscaled offset too large");
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
}
|
||||
void LongAddressGen(FEXCore::ARMEmitter::Register rd, ForwardLabel* Label) {
|
||||
Label->Insts.emplace_back(ForwardLabel::Instructions{ .Location = GetCursorAddress<uint8_t*>(), .Type = ForwardLabel::Instructions::InstType::LONG_ADDRESS_GEN });
|
||||
// Emit a register index and a nop. These will be backpatched.
|
||||
dc32(rd.Idx());
|
||||
nop();
|
||||
}
|
||||
|
||||
void LongAddressGen(FEXCore::ARMEmitter::Register rd, BiDirectionalLabel *Label) {
|
||||
if (Label->Backward.Location) {
|
||||
LongAddressGen(rd, &Label->Backward);
|
||||
}
|
||||
else {
|
||||
LongAddressGen(rd, &Label->Forward);
|
||||
}
|
||||
}
|
||||
|
||||
// Add/subtract immediate
|
||||
void add(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rd, FEXCore::ARMEmitter::Register rn, uint32_t Imm, bool LSL12 = false) {
|
||||
constexpr uint32_t Op = 0b0001'0001'0 << 23;
|
||||
@@ -637,30 +584,10 @@ public:
|
||||
constexpr uint32_t Op = 0b0111'1010'000U << 21;
|
||||
DataProcessing_Extended_Reg(Op, s, rd, rn, rm, FEXCore::ARMEmitter::ExtendedType::UXTB, 0);
|
||||
}
|
||||
|
||||
// Rotate right into flags
|
||||
void rmif(XRegister rn, uint32_t shift, uint32_t mask) {
|
||||
LOGMAN_THROW_AA_FMT(shift <= 63, "Shift must be within 0-63. Shift: {}", shift);
|
||||
LOGMAN_THROW_AA_FMT(mask <= 15, "Mask must be within 0-15. Mask: {}", mask);
|
||||
|
||||
uint32_t Op = 0b1011'1010'0000'0000'0000'0100'0000'0000;
|
||||
Op |= rn.Idx() << 5;
|
||||
Op |= shift << 15;
|
||||
Op |= mask;
|
||||
|
||||
dc32(Op);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Evaluate into flags
|
||||
void setf8(WRegister rn) {
|
||||
constexpr uint32_t Op = 0b0011'1010'0000'0000'0000'1000'0000'1101;
|
||||
EvaluateIntoFlags(Op, 0, rn);
|
||||
}
|
||||
void setf16(WRegister rn) {
|
||||
constexpr uint32_t Op = 0b0011'1010'0000'0000'0000'1000'0000'1101;
|
||||
EvaluateIntoFlags(Op, 1, rn);
|
||||
}
|
||||
|
||||
// TODO
|
||||
// Conditional compare - register
|
||||
void ccmn(FEXCore::ARMEmitter::Size s, FEXCore::ARMEmitter::Register rn, FEXCore::ARMEmitter::Register rm, FEXCore::ARMEmitter::StatusFlags flags, FEXCore::ARMEmitter::Condition Cond) {
|
||||
constexpr uint32_t Op = 0b0011'1010'010 << 21;
|
||||
@@ -982,11 +909,4 @@ private:
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
void EvaluateIntoFlags(uint32_t op, uint32_t size, WRegister rn) {
|
||||
uint32_t Instr = op;
|
||||
Instr |= size << 14;
|
||||
Instr |= rn.Idx() << 5;
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
|
||||
+7
-1549
File diff suppressed because it is too large.
Load diff
@@ -87,11 +87,6 @@ namespace FEXCore::ARMEmitter {
|
||||
return Size;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
size_t GetCursorOffsetFromAddress(const T* Address) const {
|
||||
return static_cast<size_t>(reinterpret_cast<const uint8_t*>(Address) - BufferBase);
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void ResetBuffer() {
|
||||
|
||||
@@ -519,7 +519,6 @@ namespace FEXCore::ARMEmitter {
|
||||
BC,
|
||||
TEST_BRANCH,
|
||||
RELATIVE_LOAD,
|
||||
LONG_ADDRESS_GEN,
|
||||
};
|
||||
uint8_t *Location{};
|
||||
InstType Type;
|
||||
@@ -536,14 +535,6 @@ namespace FEXCore::ARMEmitter {
|
||||
ForwardLabel Forward;
|
||||
};
|
||||
|
||||
// Some FCMA ASIMD instructions support a rotation argument.
|
||||
enum class Rotation : uint32_t {
|
||||
ROTATE_0 = 0b00,
|
||||
ROTATE_90 = 0b01,
|
||||
ROTATE_180 = 0b10,
|
||||
ROTATE_270 = 0b11,
|
||||
};
|
||||
|
||||
// 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.
|
||||
|
||||
@@ -580,7 +571,7 @@ namespace FEXCore::ARMEmitter {
|
||||
case ForwardLabel::Instructions::InstType::ADR: {
|
||||
uint32_t *Instruction = reinterpret_cast<uint32_t*>(Inst.Location);
|
||||
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
|
||||
LOGMAN_THROW_A_FMT(IsADRRange(Imm), "Unscaled offset too large");
|
||||
LOGMAN_THROW_A_FMT(Imm >= -1048576 && Imm <= 1048575, "Unscaled offset too large");
|
||||
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
|
||||
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
|
||||
uint32_t Inst = *Instruction & ~InstMask;
|
||||
@@ -592,7 +583,7 @@ namespace FEXCore::ARMEmitter {
|
||||
case ForwardLabel::Instructions::InstType::ADRP: {
|
||||
uint32_t *Instruction = reinterpret_cast<uint32_t*>(Inst.Location);
|
||||
int64_t Imm = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(Instruction);
|
||||
LOGMAN_THROW_A_FMT(IsADRPRange(Imm) && IsADRPAligned(Imm), "Unscaled offset too large");
|
||||
LOGMAN_THROW_A_FMT(Imm >= -4294967296 && Imm <= 4294963200 && (Imm & 0xFFF) == 0, "Unscaled offset too large");
|
||||
Imm >>= 12;
|
||||
uint32_t InstMask = 0b11 << 29 | 0b1111'1111'1111'1111'111 << 5;
|
||||
uint32_t Offset = static_cast<uint32_t>(Imm) & 0x3F'FFFF;
|
||||
@@ -643,47 +634,6 @@ namespace FEXCore::ARMEmitter {
|
||||
*Instruction = Inst;
|
||||
break;
|
||||
}
|
||||
case ForwardLabel::Instructions::InstType::LONG_ADDRESS_GEN: {
|
||||
uint32_t *Instructions = reinterpret_cast<uint32_t*>(Inst.Location);
|
||||
int64_t ImmInstOne = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(&Instructions[0]);
|
||||
int64_t ImmInstTwo = reinterpret_cast<int64_t>(CurrentAddress) - reinterpret_cast<int64_t>(&Instructions[1]);
|
||||
auto OriginalOffset = GetCursorOffset();
|
||||
|
||||
auto InstOffset = GetCursorOffsetFromAddress(Instructions);
|
||||
SetCursorOffset(InstOffset);
|
||||
|
||||
// We encoded the destination register in to the first instruction space.
|
||||
// Read it back.
|
||||
ARMEmitter::Register DestReg(Instructions[0]);
|
||||
|
||||
if (IsADRRange(ImmInstTwo)) {
|
||||
// If within ADR range from the second instruction, then we can emit NOP+ADR
|
||||
nop();
|
||||
adr(DestReg, static_cast<uint32_t>(ImmInstTwo) & 0x7FFF);
|
||||
}
|
||||
else if (IsADRPRange(ImmInstOne)) {
|
||||
|
||||
// If within ADRP range from the first instruction, then we are /definitely/ in range for the second instruction.
|
||||
// First check if we are in non-offset range for second instruction.
|
||||
if (IsADRPAligned(reinterpret_cast<uint64_t>(CurrentAddress))) {
|
||||
// We can emit nop + adrp
|
||||
nop();
|
||||
adrp(DestReg, static_cast<uint32_t>(ImmInstTwo >> 12) & 0x7FFF);
|
||||
}
|
||||
else {
|
||||
// Not aligned, need adrp + add
|
||||
adrp(DestReg, static_cast<uint32_t>(ImmInstOne >> 12) & 0x7FFF);
|
||||
add(ARMEmitter::Size::i64Bit, DestReg, DestReg, ImmInstOne & 0xFFF);
|
||||
}
|
||||
}
|
||||
else {
|
||||
LOGMAN_MSG_A_FMT("Unscaled offset is too large");
|
||||
FEX_UNREACHABLE;
|
||||
}
|
||||
|
||||
SetCursorOffset(OriginalOffset);
|
||||
break;
|
||||
}
|
||||
default: LOGMAN_MSG_A_FMT("Unexpected inst type in label fixup");
|
||||
}
|
||||
}
|
||||
|
||||
+7
-167
@@ -66,87 +66,9 @@ public:
|
||||
SVESel(Op, size, zd, pv, zn, zd);
|
||||
}
|
||||
|
||||
void histcnt(SubRegSize size, ZRegister zd, PRegisterZero pv, ZRegister zn, ZRegister zm) {
|
||||
LOGMAN_THROW_AA_FMT(size == SubRegSize::i32Bit || size == SubRegSize::i64Bit, "SubRegSize must be 32-bit or 64-bit");
|
||||
LOGMAN_THROW_AA_FMT(pv <= PReg::p7, "histcnt can only use p0 to p7");
|
||||
|
||||
uint32_t Op = 0b0100'0101'0010'0000'1100'0000'0000'0000;
|
||||
Op |= FEXCore::ToUnderlying(size) << 22;
|
||||
Op |= zm.Idx() << 16;
|
||||
Op |= pv.Idx() << 10;
|
||||
Op |= zn.Idx() << 5;
|
||||
Op |= zd.Idx();
|
||||
dc32(Op);
|
||||
}
|
||||
|
||||
void histseg(ZRegister zd, ZRegister zn, ZRegister zm) {
|
||||
uint32_t Op = 0b0100'0101'0010'0000'1010'0000'0000'0000;
|
||||
Op |= zm.Idx() << 16;
|
||||
Op |= zn.Idx() << 5;
|
||||
Op |= zd.Idx();
|
||||
dc32(Op);
|
||||
}
|
||||
|
||||
void fcmla(SubRegSize size, ZRegister zda, ZRegister zn, ZRegister zm, uint32_t index, Rotation rot) {
|
||||
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit,
|
||||
"SubRegSize must be 16-bit or 32-bit");
|
||||
|
||||
const auto IsHalfPrecision = size == SubRegSize::i16Bit;
|
||||
|
||||
if (IsHalfPrecision) {
|
||||
LOGMAN_THROW_AA_FMT(index <= 3, "Index for half-precision fcmla must be within 0-3. Index={}", index);
|
||||
LOGMAN_THROW_AA_FMT(zm.Idx() <= 7, "zm must be within z0-z7. zm=z{}", zm.Idx());
|
||||
} else {
|
||||
LOGMAN_THROW_AA_FMT(index <= 1, "Index for single-precision fcmla must be within 0-1. Index={}", index);
|
||||
LOGMAN_THROW_AA_FMT(zm.Idx() <= 15, "zm must be within z0-z15. zm=z{}", zm.Idx());
|
||||
}
|
||||
|
||||
uint32_t Op = 0b0110'0100'1010'0000'0001'0000'0000'0000;
|
||||
Op |= (IsHalfPrecision ? 0 : 1) << 22;
|
||||
Op |= index << (19 + int(!IsHalfPrecision));
|
||||
Op |= zm.Idx() << 16;
|
||||
Op |= FEXCore::ToUnderlying(rot) << 10;
|
||||
Op |= zn.Idx() << 5;
|
||||
Op |= zda.Idx();
|
||||
|
||||
dc32(Op);
|
||||
}
|
||||
|
||||
void fcmla(SubRegSize size, ZRegister zda, PRegisterMerge pv, ZRegister zn, ZRegister zm, Rotation rot) {
|
||||
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit || size == SubRegSize::i64Bit,
|
||||
"SubRegSize must be 16-bit, 32-bit, or 64-bit");
|
||||
LOGMAN_THROW_AA_FMT(pv <= PReg::p7, "fcmla can only use p0 to p7");
|
||||
|
||||
uint32_t Op = 0b0110'0100'0000'0000'0000'0000'0000'0000;
|
||||
Op |= FEXCore::ToUnderlying(size) << 22;
|
||||
Op |= zm.Idx() << 16;
|
||||
Op |= FEXCore::ToUnderlying(rot) << 13;
|
||||
Op |= pv.Idx() << 10;
|
||||
Op |= zn.Idx() << 5;
|
||||
Op |= zda.Idx();
|
||||
|
||||
dc32(Op);
|
||||
}
|
||||
|
||||
void fcadd(SubRegSize size, ZRegister zd, PRegisterMerge pv, ZRegister zn, ZRegister zm, Rotation rot) {
|
||||
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit || size == SubRegSize::i64Bit,
|
||||
"SubRegSize must be 16-bit, 32-bit, or 64-bit");
|
||||
LOGMAN_THROW_AA_FMT(pv <= PReg::p7, "fcadd can only use p0 to p7");
|
||||
LOGMAN_THROW_AA_FMT(rot == Rotation::ROTATE_90 || rot == Rotation::ROTATE_270,
|
||||
"fcadd rotation may only be 90 or 270 degrees");
|
||||
LOGMAN_THROW_AA_FMT(zd.Idx() == zn.Idx(), "fcadd zd and zn must be the same register");
|
||||
|
||||
const uint32_t ConvertedRotation = rot == Rotation::ROTATE_90 ? 0 : 1;
|
||||
|
||||
uint32_t Op = 0b0110'0100'0000'0000'1000'0000'0000'0000;
|
||||
Op |= FEXCore::ToUnderlying(size) << 22;
|
||||
Op |= ConvertedRotation << 16;
|
||||
Op |= pv.Idx() << 10;
|
||||
Op |= zm.Idx() << 5;
|
||||
Op |= zd.Idx();
|
||||
|
||||
dc32(Op);
|
||||
}
|
||||
// TODO: HISTCNT
|
||||
// TODO: FCMLA
|
||||
// TODO: FCADD
|
||||
|
||||
// SVE integer add/subtract vectors (unpredicated)
|
||||
void add(FEXCore::ARMEmitter::SubRegSize size, FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::ZRegister zn, FEXCore::ARMEmitter::ZRegister zm) {
|
||||
@@ -392,21 +314,12 @@ public:
|
||||
SVEPredicateLogical(Op, 1, 1, 1, 1, pm, pg, pn, pd);
|
||||
}
|
||||
|
||||
// XXX:
|
||||
// SVE broadcast predicate element
|
||||
// XXX:
|
||||
// SVE integer clamp
|
||||
|
||||
// XXX:
|
||||
// SVE2 character match
|
||||
void match(SubRegSize size, PRegister pd, PRegisterZero pg, ZRegister zn, ZRegister zm) {
|
||||
constexpr uint32_t Op = 0b0100'0101'0010'0000'1000'0000'0000'0000;
|
||||
SVECharacterMatch(Op, 0, size, pd, pg, zn, zm);
|
||||
}
|
||||
void nmatch(SubRegSize size, PRegister pd, PRegisterZero pg, ZRegister zn, ZRegister zm) {
|
||||
constexpr uint32_t Op = 0b0100'0101'0010'0000'1000'0000'0000'0000;
|
||||
SVECharacterMatch(Op, 1, size, pd, pg, zn, zm);
|
||||
}
|
||||
|
||||
// XXX:
|
||||
// SVE floating-point convert precision odd elements
|
||||
void fcvtxnt(FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::PRegisterMerge pg, FEXCore::ARMEmitter::ZRegister zn) {
|
||||
constexpr uint32_t Op = 0b0110'0100'0000'1000'101 << 13;
|
||||
@@ -573,26 +486,7 @@ public:
|
||||
}
|
||||
|
||||
// SVE floating-point recursive reduction
|
||||
void faddv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
|
||||
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
|
||||
SVEFPRecursiveReduction(Op, 0b000, size, vd, pg, zn);
|
||||
}
|
||||
void fmaxnmv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
|
||||
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
|
||||
SVEFPRecursiveReduction(Op, 0b100, size, vd, pg, zn);
|
||||
}
|
||||
void fminnmv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
|
||||
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
|
||||
SVEFPRecursiveReduction(Op, 0b101, size, vd, pg, zn);
|
||||
}
|
||||
void fmaxv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
|
||||
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
|
||||
SVEFPRecursiveReduction(Op, 0b110, size, vd, pg, zn);
|
||||
}
|
||||
void fminv(SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
|
||||
constexpr uint32_t Op = 0b0110'0101'0000'0000'0010'0000'0000'0000;
|
||||
SVEFPRecursiveReduction(Op, 0b111, size, vd, pg, zn);
|
||||
}
|
||||
// XXX:
|
||||
|
||||
// SVE integer Multiply-Add - Predicated
|
||||
// SVE integer multiply-accumulate writing addend (predicated)
|
||||
@@ -602,19 +496,7 @@ public:
|
||||
|
||||
// SVE Integer Binary Arithmetic - Predicated
|
||||
// SVE integer add/subtract vectors (predicated)
|
||||
void add(SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
|
||||
constexpr uint32_t Op = 0b0000'0100'0000'0000'0000'0000'0000'0000;
|
||||
SVEAddSubVectorsPredicated(Op, 0b000, size, zd, pg, zn, zm);
|
||||
}
|
||||
void sub(SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
|
||||
constexpr uint32_t Op = 0b0000'0100'0000'0000'0000'0000'0000'0000;
|
||||
SVEAddSubVectorsPredicated(Op, 0b001, size, zd, pg, zn, zm);
|
||||
}
|
||||
void subr(SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
|
||||
constexpr uint32_t Op = 0b0000'0100'0000'0000'0000'0000'0000'0000;
|
||||
SVEAddSubVectorsPredicated(Op, 0b011, size, zd, pg, zn, zm);
|
||||
}
|
||||
|
||||
// XXX:
|
||||
// SVE integer min/max/difference (predicated)
|
||||
void smax(FEXCore::ARMEmitter::SubRegSize size, FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::PRegisterMerge pg, FEXCore::ARMEmitter::ZRegister zdn, FEXCore::ARMEmitter::ZRegister zm) {
|
||||
LOGMAN_THROW_AA_FMT(size != FEXCore::ARMEmitter::SubRegSize::i128Bit, "Can't use 128-bit size");
|
||||
@@ -3945,48 +3827,6 @@ private:
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
void SVECharacterMatch(uint32_t op, uint32_t opc, SubRegSize size, PRegister pd, PRegisterZero pg, ZRegister zn, ZRegister zm) {
|
||||
LOGMAN_THROW_AA_FMT(size == SubRegSize::i8Bit || size == SubRegSize::i16Bit,
|
||||
"match/nmatch can only use 8-bit or 16-bit element sizes");
|
||||
LOGMAN_THROW_AA_FMT(pg <= PReg::p7, "match/nmatch can only use p0-p7 as a governing predicate");
|
||||
|
||||
uint32_t Instr = op;
|
||||
Instr |= FEXCore::ToUnderlying(size) << 22;
|
||||
Instr |= opc << 4;
|
||||
Instr |= zm.Idx() << 16;
|
||||
Instr |= pg.Idx() << 10;
|
||||
Instr |= zn.Idx() << 5;
|
||||
Instr |= pd.Idx();
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
void SVEFPRecursiveReduction(uint32_t op, uint32_t opc, SubRegSize size, VRegister vd, PRegister pg, ZRegister zn) {
|
||||
LOGMAN_THROW_AA_FMT(size == SubRegSize::i16Bit || size == SubRegSize::i32Bit || size == SubRegSize::i64Bit,
|
||||
"FP reduction operation can only use 16-bit, 32-bit, or 64-bit element sizes");
|
||||
LOGMAN_THROW_AA_FMT(pg <= PReg::p7, "FP reduction operation can only use p0-p7 as a governing predicate");
|
||||
|
||||
uint32_t Instr = op;
|
||||
Instr |= FEXCore::ToUnderlying(size) << 22;
|
||||
Instr |= opc << 16;
|
||||
Instr |= pg.Idx() << 10;
|
||||
Instr |= zn.Idx() << 5;
|
||||
Instr |= vd.Idx();
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
void SVEAddSubVectorsPredicated(uint32_t op, uint32_t opc, SubRegSize size, ZRegister zd, PRegisterMerge pg, ZRegister zn, ZRegister zm) {
|
||||
LOGMAN_THROW_AA_FMT(zd.Idx() == zn.Idx(), "zd and zn must be the same register");
|
||||
LOGMAN_THROW_AA_FMT(pg <= PReg::p7, "Add/Sub operation can only use p0-p7 as a governing predicate");
|
||||
|
||||
uint32_t Instr = op;
|
||||
Instr |= FEXCore::ToUnderlying(size) << 22;
|
||||
Instr |= opc << 16;
|
||||
Instr |= pg.Idx() << 10;
|
||||
Instr |= zm.Idx() << 5;
|
||||
Instr |= zd.Idx();
|
||||
dc32(Instr);
|
||||
}
|
||||
|
||||
// SVE floating-point round to integral value
|
||||
void frintX(uint32_t opc, FEXCore::ARMEmitter::SubRegSize size, FEXCore::ARMEmitter::ZRegister zd, FEXCore::ARMEmitter::PRegister pg, FEXCore::ARMEmitter::ZRegister zn) {
|
||||
// opc = round mode
|
||||
|
||||
@@ -18,19 +18,8 @@ enum ContextFlags : uint32_t {
|
||||
CONTEXT_FLAG_32BIT = (1U << 1),
|
||||
};
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
constexpr uint64_t STACK_COOKIE_MAGIC = 0x4142434445464748ULL;
|
||||
#endif
|
||||
|
||||
struct X86ContextBackup {
|
||||
// Host State
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
// During debug builds, insert a cookie on the stack.
|
||||
// This is useful for validation that the stack is trying to be restored from the correct location.
|
||||
// During stack restore, we ensure this is set to the value we expect.
|
||||
// If given an incorrect stack location, or corrupted stack then this cookie will be wrong.
|
||||
uint64_t StackCookie;
|
||||
#endif
|
||||
// RIP and RSP is stored in GPRs here
|
||||
uint64_t GPRs[23];
|
||||
FEXCore::x86_64::_libc_fpstate FPRState;
|
||||
@@ -50,9 +39,6 @@ struct X86ContextBackup {
|
||||
|
||||
struct ArmContextBackup {
|
||||
// Host State
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
uint64_t StackCookie;
|
||||
#endif
|
||||
uint64_t GPRs[31];
|
||||
uint64_t PrevSP;
|
||||
uint64_t PrevPC;
|
||||
@@ -225,10 +211,6 @@ static inline void BackupContext(void* ucontext, T *Backup) {
|
||||
|
||||
// Save the signal mask so we can restore it
|
||||
memcpy(&Backup->sa_mask, &_ucontext->uc_sigmask, sizeof(uint64_t));
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
Backup->StackCookie = STACK_COOKIE_MAGIC;
|
||||
#endif
|
||||
} else {
|
||||
// This must be a runtime error
|
||||
ERROR_AND_DIE_FMT("Wrong context type");
|
||||
@@ -255,8 +237,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");
|
||||
@@ -322,10 +302,6 @@ static inline void BackupContext(void* ucontext, T *Backup) {
|
||||
|
||||
// Save the signal mask so we can restore it
|
||||
memcpy(&Backup->sa_mask, &_ucontext->uc_sigmask, sizeof(uint64_t));
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
Backup->StackCookie = STACK_COOKIE_MAGIC;
|
||||
#endif
|
||||
} else {
|
||||
// This must be a runtime error
|
||||
ERROR_AND_DIE_FMT("Wrong context type");
|
||||
@@ -345,8 +321,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");
|
||||
|
||||
+98
-18
@@ -88,6 +88,7 @@ static uint32_t CalculateNumberOfCPUs() {
|
||||
// when AVX implementations are further along.
|
||||
constexpr uint32_t SUPPORTS_AVX = 0;
|
||||
|
||||
// #define CPUID_AMD
|
||||
#ifdef CPUID_AMD
|
||||
constexpr uint32_t FAMILY_IDENTIFIER =
|
||||
0 | // Stepping
|
||||
@@ -121,24 +122,25 @@ void CPUIDEmu::SetupHostHybridFlag() {
|
||||
uint64_t MIDR{};
|
||||
for (size_t i = 0; i < CPUs; ++i) {
|
||||
std::error_code ec{};
|
||||
std::string MIDRPath = fmt::format("/sys/devices/system/cpu/cpu{}/regs/identification/midr_el1", i);
|
||||
std::string MIDRPath = "/sys/devices/system/cpu/cpu" + std::to_string(i) + "/regs/identification/midr_el1";
|
||||
if (std::filesystem::exists(MIDRPath, ec)) {
|
||||
std::vector<char> Data{};
|
||||
// Needs to be a fixed size since depending on kernel it will try to read a full page of data and fail
|
||||
// Only read 18 bytes for a 64bit value prefixed with 0x
|
||||
if (FEXCore::FileLoading::LoadFile(Data, MIDRPath, 18)) {
|
||||
uint64_t NewMIDR{};
|
||||
std::string_view MIDRView(&Data.at(0), 18);
|
||||
if (FEXCore::StrConv::Conv(MIDRView, &NewMIDR)) {
|
||||
if (MIDR != 0 && MIDR != NewMIDR) {
|
||||
// CPU mismatch, claim hybrid
|
||||
Hybrid = true;
|
||||
}
|
||||
|
||||
std::array<char, 18> Data;
|
||||
// Needs to be a fixed size since depending on kernel it will try to read a full page of data and fail
|
||||
// Only read 18 bytes for a 64bit value prefixed with 0x
|
||||
if (FEXCore::FileLoading::LoadFileToBuffer(MIDRPath, Data) == sizeof(Data)) {
|
||||
uint64_t NewMIDR{};
|
||||
std::string_view MIDRView(Data.data(), sizeof(Data));
|
||||
if (FEXCore::StrConv::Conv(MIDRView, &NewMIDR)) {
|
||||
if (MIDR != 0 && MIDR != NewMIDR) {
|
||||
// CPU mismatch, claim hybrid
|
||||
Hybrid = true;
|
||||
// Truncate to 32-bits, top 32-bits are all reserved in MIDR
|
||||
PerCPUData[i].ProductName = ProductNames::ARM_UNKNOWN;
|
||||
PerCPUData[i].MIDR = NewMIDR;
|
||||
MIDR = NewMIDR;
|
||||
}
|
||||
|
||||
// Truncate to 32-bits, top 32-bits are all reserved in MIDR
|
||||
PerCPUData[i].ProductName = ProductNames::ARM_UNKNOWN;
|
||||
PerCPUData[i].MIDR = NewMIDR;
|
||||
MIDR = NewMIDR;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -655,8 +657,8 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) {
|
||||
(0 << 20) | // SMAP Supervisor mode access prevention and CLAC/STAC instructions
|
||||
(0 << 21) | // Reserved
|
||||
(0 << 22) | // Reserved
|
||||
(1 << 23) | // CLFLUSHOPT instruction
|
||||
(CTX->HostFeatures.SupportsCLWB << 24) | // CLWB instruction
|
||||
(0 << 23) | // CLFLUSHOPT instruction
|
||||
(0 << 24) | // CLWB instruction
|
||||
(0 << 25) | // Intel processor trace
|
||||
(0 << 26) | // Reserved
|
||||
(0 << 27) | // Reserved
|
||||
@@ -1213,6 +1215,84 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_Reserved(uint32_t Leaf) {
|
||||
void CPUIDEmu::Init(FEXCore::Context::Context *ctx) {
|
||||
CTX = ctx;
|
||||
|
||||
RegisterFunction(0, &CPUIDEmu::Function_0h);
|
||||
RegisterFunction(1, &CPUIDEmu::Function_01h);
|
||||
RegisterFunction(2, &CPUIDEmu::Function_02h);
|
||||
// 3: Serial Number(previously), now reserved
|
||||
#ifndef CPUID_AMD
|
||||
// Deterministic cache parameters for each level
|
||||
RegisterFunction(0x4, &CPUIDEmu::Function_04h);
|
||||
#endif
|
||||
// 5: Monitor/mwait
|
||||
// Thermal and power management
|
||||
RegisterFunction(6, &CPUIDEmu::Function_06h);
|
||||
// Extended feature flags
|
||||
RegisterFunction(7, &CPUIDEmu::Function_07h);
|
||||
// 9: Direct Cache Access information
|
||||
// 0x0A: Architectural performance monitoring
|
||||
// 0x0B: Extended topology enumeration
|
||||
// 0x0D: Processor extended state enumeration
|
||||
RegisterFunction(0x0D, &CPUIDEmu::Function_0Dh);
|
||||
// 0x0F: Intel RDT monitoring
|
||||
// 0x10: Intel RDT allocation enumeration
|
||||
// 0x12: Intel SGX capability enumeration
|
||||
// 0x13: Reserved
|
||||
// 0x14: Intel Processor trace
|
||||
#ifndef CPUID_AMD
|
||||
// Timestamp counter information
|
||||
// Doesn't exist on AMD hardware
|
||||
RegisterFunction(0x15, &CPUIDEmu::Function_15h);
|
||||
#endif
|
||||
// 0x16: Processor frequency information
|
||||
// 0x17: SoC vendor attribute enumeration
|
||||
|
||||
// 0x1A: Hybrid Information Sub-leaf
|
||||
#ifndef CPUID_AMD
|
||||
RegisterFunction(0x1A, &CPUIDEmu::Function_1Ah);
|
||||
#endif
|
||||
// Hypervisor CPUID information leaf
|
||||
RegisterFunction(0x4000'0000, &CPUIDEmu::Function_4000_0000h);
|
||||
RegisterFunction(0x4000'0001, &CPUIDEmu::Function_4000_0001h);
|
||||
|
||||
// Largest extended function number
|
||||
RegisterFunction(0x8000'0000, &CPUIDEmu::Function_8000_0000h);
|
||||
// Processor vendor
|
||||
RegisterFunction(0x8000'0001, &CPUIDEmu::Function_8000_0001h);
|
||||
// Processor brand string
|
||||
RegisterFunction(0x8000'0002, &CPUIDEmu::Function_8000_0002h);
|
||||
// Processor brand string continued
|
||||
RegisterFunction(0x8000'0003, &CPUIDEmu::Function_8000_0003h);
|
||||
// Processor brand string continued
|
||||
RegisterFunction(0x8000'0004, &CPUIDEmu::Function_8000_0004h);
|
||||
// 0x8000'0005: L1 Cache and TLB identifiers
|
||||
#ifdef CPUID_AMD
|
||||
RegisterFunction(0x8000'0005, &CPUIDEmu::Function_8000_0005h);
|
||||
#else
|
||||
// This is full reserved on Intel platforms
|
||||
RegisterFunction(0x8000'0005, &CPUIDEmu::Function_Reserved);
|
||||
#endif
|
||||
// 0x8000'0006: L2 Cache identifiers
|
||||
RegisterFunction(0x8000'0006, &CPUIDEmu::Function_8000_0006h);
|
||||
// Advanced power management information
|
||||
RegisterFunction(0x8000'0007, &CPUIDEmu::Function_8000_0007h);
|
||||
// Virtual and physical address sizes
|
||||
RegisterFunction(0x8000'0008, &CPUIDEmu::Function_8000_0008h);
|
||||
|
||||
// 0x8000'000A: SVM Revision
|
||||
// TLB 1GB page identifiers
|
||||
RegisterFunction(0x8000'0019, &CPUIDEmu::Function_8000_0019h);
|
||||
|
||||
// 0x8000'001A: Performance optimization identifiers
|
||||
// 0x8000'001B: Instruction based sampling identifiers
|
||||
// 0x8000'001C: Lightweight profiling capabilities
|
||||
// 0x8000'001D: Cache properties
|
||||
#ifdef CPUID_AMD
|
||||
// Deterministic cache parameters for each level
|
||||
RegisterFunction(0x8000'001D, &CPUIDEmu::Function_8000_001Dh);
|
||||
#endif
|
||||
// 0x8000'001E: Extended APIC ID
|
||||
// 0x8000'001F: AMD Secure Encryption
|
||||
|
||||
// Setup some state tracking
|
||||
SetupHostHybridFlag();
|
||||
}
|
||||
|
||||
+11
-169
@@ -13,9 +13,6 @@ namespace Context {
|
||||
struct Context;
|
||||
}
|
||||
|
||||
// Debugging define to switch what family of CPU we execute as.
|
||||
// Might be useful if an application makes an assumption about a CPU.
|
||||
// #define CPUID_AMD
|
||||
class CPUIDEmu final {
|
||||
private:
|
||||
constexpr static uint32_t CPUID_VENDOR_INTEL1 = 0x756E6547; // "Genu"
|
||||
@@ -34,24 +31,13 @@ public:
|
||||
void Init(FEXCore::Context::Context *ctx);
|
||||
|
||||
FEXCore::CPUID::FunctionResults RunFunction(uint32_t Function, uint32_t Leaf) {
|
||||
if (Function < Primary.size()) {
|
||||
const auto Handler = Primary[Function];
|
||||
return (this->*Handler)(Leaf);
|
||||
const auto Handler = FunctionHandlers.find(Function);
|
||||
|
||||
if (Handler == FunctionHandlers.end()) {
|
||||
return Function_Reserved(Leaf);
|
||||
}
|
||||
|
||||
constexpr uint32_t HypervisorBase = 0x4000'0000;
|
||||
if (Function >= HypervisorBase && Function < (HypervisorBase + Hypervisor.size())) {
|
||||
const auto Handler = Hypervisor[Function - HypervisorBase];
|
||||
return (this->*Handler)(Leaf);
|
||||
}
|
||||
|
||||
constexpr uint32_t ExtendedBase = 0x8000'0000;
|
||||
if (Function >= ExtendedBase && Function < (ExtendedBase + Extended.size())) {
|
||||
const auto Handler = Extended[Function - ExtendedBase];
|
||||
return (this->*Handler)(Leaf);
|
||||
}
|
||||
|
||||
return Function_Reserved(Leaf);
|
||||
return (this->*Handler->second)(Leaf);
|
||||
}
|
||||
|
||||
FEXCore::CPUID::FunctionResults RunFunctionName(uint32_t Function, uint32_t Leaf, uint32_t CPU) {
|
||||
@@ -69,6 +55,11 @@ private:
|
||||
FEX_CONFIG_OPT(Cores, THREADS);
|
||||
|
||||
using FunctionHandler = FEXCore::CPUID::FunctionResults (CPUIDEmu::*)(uint32_t Leaf);
|
||||
void RegisterFunction(uint32_t Function, FunctionHandler Handler) {
|
||||
FunctionHandlers.insert_or_assign(Function, Handler);
|
||||
}
|
||||
|
||||
std::unordered_map<uint32_t, FunctionHandler> FunctionHandlers;
|
||||
struct CPUData {
|
||||
const char *ProductName{};
|
||||
#ifdef _M_ARM_64
|
||||
@@ -104,161 +95,12 @@ private:
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0006h(uint32_t Leaf);
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0007h(uint32_t Leaf);
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0008h(uint32_t Leaf);
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0009h(uint32_t Leaf);
|
||||
FEXCore::CPUID::FunctionResults Function_8000_0019h(uint32_t Leaf);
|
||||
FEXCore::CPUID::FunctionResults Function_8000_001Dh(uint32_t Leaf);
|
||||
FEXCore::CPUID::FunctionResults Function_Reserved(uint32_t Leaf);
|
||||
|
||||
void SetupHostHybridFlag();
|
||||
static constexpr std::array<FunctionHandler, 27> Primary = {
|
||||
// 0: Highest function parameter and ID
|
||||
&CPUIDEmu::Function_0h,
|
||||
// 1: Processor info
|
||||
&CPUIDEmu::Function_01h,
|
||||
// 2: Cache and TLB info
|
||||
&CPUIDEmu::Function_02h,
|
||||
// 3: Serial Number(previously), now reserved
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#ifndef CPUID_AMD
|
||||
// 4: Deterministic cache parameters for each level
|
||||
&CPUIDEmu::Function_04h,
|
||||
#else
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#endif
|
||||
// 5: Monitor/mwait
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 6: Thermal and power management
|
||||
&CPUIDEmu::Function_06h,
|
||||
// 7: Extended feature flags
|
||||
&CPUIDEmu::Function_07h,
|
||||
// 0x08: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 9: Direct Cache Access information
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x0A: Architectural performance monitoring
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x0B: Extended topology enumeration
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x0C: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x0D: Processor extended state enumeration
|
||||
&CPUIDEmu::Function_0Dh,
|
||||
// 0x0E: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x0F: Intel RDT monitoring
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x10: Intel RDT allocation enumeration
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x12: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x12: Intel SGX capability enumeration
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x13: Reserved
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x14: Intel Processor trace
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#ifndef CPUID_AMD
|
||||
// Timestamp counter information
|
||||
// Doesn't exist on AMD hardware
|
||||
&CPUIDEmu::Function_15h,
|
||||
#else
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#endif
|
||||
// 0x16: Processor frequency information
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x17: SoC vendor attribute enumeration
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x18: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x19: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#ifndef CPUID_AMD
|
||||
// 0x1A: Hybrid Information Sub-leaf
|
||||
&CPUIDEmu::Function_1Ah,
|
||||
#else
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#endif
|
||||
};
|
||||
|
||||
static constexpr std::array<FunctionHandler, 2> Hypervisor = {
|
||||
// Hypervisor CPUID information leaf
|
||||
&CPUIDEmu::Function_4000_0000h,
|
||||
// FEX-Emu specific leaf
|
||||
&CPUIDEmu::Function_4000_0001h,
|
||||
};
|
||||
|
||||
static constexpr std::array<FunctionHandler, 32> Extended = {
|
||||
// Largest extended function number
|
||||
&CPUIDEmu::Function_8000_0000h,
|
||||
// Processor vendor
|
||||
&CPUIDEmu::Function_8000_0001h,
|
||||
// Processor brand string
|
||||
&CPUIDEmu::Function_8000_0002h,
|
||||
// Processor brand string continued
|
||||
&CPUIDEmu::Function_8000_0003h,
|
||||
// Processor brand string continued
|
||||
&CPUIDEmu::Function_8000_0004h,
|
||||
#ifdef CPUID_AMD
|
||||
// 0x8000'0005: L1 Cache and TLB identifiers
|
||||
&CPUIDEmu::Function_8000_0005h,
|
||||
#else
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#endif
|
||||
// 0x8000'0006: L2 Cache identifiers
|
||||
&CPUIDEmu::Function_8000_0006h,
|
||||
// 0x8000'0007: Advanced power management information
|
||||
&CPUIDEmu::Function_8000_0007h,
|
||||
// 0x8000'0008: Virtual and physical address sizes
|
||||
&CPUIDEmu::Function_8000_0008h,
|
||||
// 0x8000'0009: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'000A: SVM Revision
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'000B: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'000C: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'000D: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'000E: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'000F: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0010: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0011: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0012: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0013: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0014: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0015: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0016: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0017: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0018: Reserved?
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'0019: TLB 1GB page identifiers
|
||||
&CPUIDEmu::Function_8000_0019h,
|
||||
// 0x8000'001A: Performance optimization identifiers
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'001B: Instruction based sampling identifiers
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'001C: Lightweight profiling capabilities
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#ifdef CPUID_AMD
|
||||
// 0x8000'001D: Cache properties
|
||||
&CPUIDEmu::Function_8000_001Dh,
|
||||
#else
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
#endif
|
||||
// 0x8000'001E: Extended APIC ID
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
// 0x8000'001F: AMD Secure Encryption
|
||||
&CPUIDEmu::Function_Reserved,
|
||||
};
|
||||
};
|
||||
}
|
||||
@@ -159,11 +159,6 @@ namespace FEXCore::Context {
|
||||
HostFeatures.SupportsAVX = false;
|
||||
}
|
||||
|
||||
if (!Config.Is64BitMode()) {
|
||||
// When operating in 32-bit mode, the virtual memory we care about is only the lower 32-bits.
|
||||
Config.VirtualMemSize = 1ULL << 32;
|
||||
}
|
||||
|
||||
if (Config.BlockJITNaming() ||
|
||||
Config.GlobalJITNaming() ||
|
||||
Config.LibraryJITNaming()) {
|
||||
|
||||
@@ -155,16 +155,14 @@ void Arm64Dispatcher::EmitDispatcher() {
|
||||
// Shift the offset by the size of the block cache entry
|
||||
add(ARMEmitter::XReg::x0, ARMEmitter::XReg::x0, ARMEmitter::XReg::x1, ARMEmitter::ShiftType::LSL, (int)log2(sizeof(FEXCore::LookupCache::LookupCacheEntry)));
|
||||
|
||||
// The the full LookupCacheEntry with a single LDP.
|
||||
// Check the guest address first to ensure it maps to the address we are currently at.
|
||||
// Load the guest address first to ensure it maps to the address we are currently at
|
||||
// This fixes aliasing problems
|
||||
ldp<ARMEmitter::IndexType::OFFSET>(ARMEmitter::XReg::x3, ARMEmitter::XReg::x1, ARMEmitter::Reg::r0, 0);
|
||||
|
||||
// If the guest address doesn't match, Compile the block.
|
||||
ldr(ARMEmitter::XReg::x1, ARMEmitter::Reg::r0, offsetof(FEXCore::LookupCache::LookupCacheEntry, GuestCode));
|
||||
cmp(ARMEmitter::XReg::x1, RipReg);
|
||||
b(ARMEmitter::Condition::CC_NE, &NoBlock);
|
||||
|
||||
// Check the host address to see if it matches, else compile the block.
|
||||
// Now load the actual host block to execute if we can
|
||||
ldr(ARMEmitter::XReg::x3, ARMEmitter::Reg::r0, offsetof(FEXCore::LookupCache::LookupCacheEntry, HostCode));
|
||||
cbz(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r3, &NoBlock);
|
||||
|
||||
// If we've made it here then we have a real compiled block
|
||||
@@ -320,14 +318,6 @@ void Arm64Dispatcher::EmitDispatcher() {
|
||||
hlt(0);
|
||||
}
|
||||
|
||||
{
|
||||
SignalHandlerReturnAddressRT = GetCursorAddress<uint64_t>();
|
||||
|
||||
// Now to get back to our old location we need to do a fault dance
|
||||
// We can't use SIGTRAP here since gdb catches it and never gives it to the application!
|
||||
hlt(0);
|
||||
}
|
||||
|
||||
{
|
||||
// Guest SIGILL handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
@@ -662,7 +652,6 @@ void Arm64Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Thr
|
||||
Common.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
|
||||
Common.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV;
|
||||
Common.SignalReturnHandler = SignalHandlerReturnAddress;
|
||||
Common.SignalReturnHandlerRT = SignalHandlerReturnAddressRT;
|
||||
|
||||
auto &AArch64 = Thread->CurrentFrame->Pointers.AArch64;
|
||||
AArch64.LUDIVHandler = LUDIVHandlerAddress;
|
||||
|
||||
+447
-727
File diff suppressed because it is too large.
Load diff
@@ -44,7 +44,6 @@ public:
|
||||
uint64_t ThreadPauseHandlerAddressSpillSRA{};
|
||||
uint64_t ExitFunctionLinkerAddress{};
|
||||
uint64_t SignalHandlerReturnAddress{};
|
||||
uint64_t SignalHandlerReturnAddressRT{};
|
||||
uint64_t GuestSignal_SIGILL{};
|
||||
uint64_t GuestSignal_SIGTRAP{};
|
||||
uint64_t GuestSignal_SIGSEGV{};
|
||||
@@ -91,71 +90,8 @@ protected:
|
||||
, config {Config}
|
||||
{}
|
||||
|
||||
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,
|
||||
GuestSigAction *GuestAction, stack_t *GuestStack,
|
||||
uint64_t NewGuestSP, const uint32_t eflags);
|
||||
|
||||
///< Setup the signal frame for a 32-bit signal without SA_SIGINFO.
|
||||
uint64_t SetupFrame_ia32(ArchHelpers::Context::ContextBackup* ContextBackup, FEXCore::Core::CpuStateFrame *Frame,
|
||||
int Signal, siginfo_t *HostSigInfo, void *ucontext,
|
||||
GuestSigAction *GuestAction, stack_t *GuestStack,
|
||||
uint64_t NewGuestSP, const uint32_t eflags);
|
||||
|
||||
///< Setup the signal frame for a 32-bit signal with SA_SIGINFO.
|
||||
uint64_t SetupRTFrame_ia32(ArchHelpers::Context::ContextBackup* ContextBackup, FEXCore::Core::CpuStateFrame *Frame,
|
||||
int Signal, siginfo_t *HostSigInfo, void *ucontext,
|
||||
GuestSigAction *GuestAction, stack_t *GuestStack,
|
||||
uint64_t NewGuestSP, const uint32_t eflags);
|
||||
|
||||
ArchHelpers::Context::ContextBackup* StoreThreadState(FEXCore::Core::InternalThreadState *Thread, int Signal, void *ucontext);
|
||||
enum class RestoreType {
|
||||
TYPE_REALTIME, ///< Signal restore type is from a `realtime` signal.
|
||||
TYPE_NONREALTIME, ///< Signal restore type is from a `non-realtime` signal.
|
||||
TYPE_PAUSE, ///< Signal restore type is from a GDB pause event.
|
||||
};
|
||||
|
||||
/*
|
||||
* Signal frames on 32-bit architecture needs to match exactly how the kernel generates the frame.
|
||||
* This is because large parts of the signal frame definition is part of the UAPI.
|
||||
* This means that when FEX sets up the signal frame, it needs to match the UAPI stack setup.
|
||||
*
|
||||
* The two signal stack frame types below describe the two different 32-bit frame types.
|
||||
*/
|
||||
|
||||
// The 32-bit non-realtime signal frame.
|
||||
// This frame type is used when the guest signal is used without the `SA_SIGINFO` flag.
|
||||
struct SigFrame_i32 {
|
||||
uint32_t pretcode; ///< sigreturn return branch point.
|
||||
int32_t Signal; ///< The signal hit.
|
||||
FEXCore::x86::sigcontext sc; ///< The signal context.
|
||||
x86::_libc_fpstate fpstate_unused; ///< Unused fpstate. Retained for backwards compatibility.
|
||||
uint32_t extramask[1]; ///< Upper 32-bits of the signal mask. Lower 32-bits is in the sigcontext.
|
||||
char retcode[8]; ///< Unused but needs to be filled. GDB seemingly uses as a debug marker.
|
||||
///< FP state now follows after this.
|
||||
};
|
||||
|
||||
// The 32-bit realtime signal frame.
|
||||
// This frame type is used when the guest signal is used with the `SA_SIGINFO` flag.
|
||||
struct RTSigFrame_i32 {
|
||||
uint32_t pretcode; ///< sigreturn return branch point.
|
||||
int32_t Signal; ///< The signal hit.
|
||||
uint32_t pinfo; ///< Pointer to siginfo_t
|
||||
uint32_t puc; ///< Pointer to ucontext_t
|
||||
FEXCore::x86::siginfo_t info;
|
||||
FEXCore::x86::ucontext_t uc;
|
||||
char retcode[8]; ///< Unused but needs to be filled. GDB seemingly uses as a debug marker.
|
||||
///< FP state now follows after this.
|
||||
};
|
||||
|
||||
void RestoreThreadState(FEXCore::Core::InternalThreadState *Thread, void *ucontext, RestoreType Type);
|
||||
void RestoreThreadState(FEXCore::Core::InternalThreadState *Thread, void *ucontext);
|
||||
std::stack<uint64_t, std::vector<uint64_t>> SignalFrames;
|
||||
|
||||
virtual void SpillSRA(FEXCore::Core::InternalThreadState *Thread, void *ucontext, uint32_t IgnoreMask) {}
|
||||
|
||||
@@ -344,12 +344,6 @@ X86Dispatcher::X86Dispatcher(FEXCore::Context::Context *ctx, const DispatcherCon
|
||||
ud2();
|
||||
}
|
||||
|
||||
{
|
||||
// RT Signal return handler
|
||||
SignalHandlerReturnAddressRT = getCurr<uint64_t>();
|
||||
ud2();
|
||||
}
|
||||
|
||||
{
|
||||
// Guest SIGILL handler
|
||||
// Needs to be distinct from the SignalHandlerReturnAddress
|
||||
@@ -492,7 +486,6 @@ void X86Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState *Threa
|
||||
Common.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
|
||||
Common.GuestSignal_SIGSEGV = GuestSignal_SIGSEGV;
|
||||
Common.SignalReturnHandler = SignalHandlerReturnAddress;
|
||||
Common.SignalReturnHandlerRT = SignalHandlerReturnAddressRT;
|
||||
|
||||
auto &Interpreter = Thread->CurrentFrame->Pointers.Interpreter;
|
||||
(uintptr_t&)Interpreter.CallbackReturn = IntCallbackReturnAddress;
|
||||
|
||||
+236
-131
@@ -32,6 +32,26 @@ using namespace FEXCore::X86Tables;
|
||||
static uint32_t MapModRMToReg(uint8_t REX, uint8_t bits, bool HighBits, bool HasREX, bool HasXMM, bool HasMM, uint8_t InvalidOffset = 16) {
|
||||
using GPRArray = std::array<uint32_t, 16>;
|
||||
|
||||
static constexpr GPRArray GPRIndexes = {
|
||||
// Classical ordering?
|
||||
FEXCore::X86State::REG_RAX,
|
||||
FEXCore::X86State::REG_RCX,
|
||||
FEXCore::X86State::REG_RDX,
|
||||
FEXCore::X86State::REG_RBX,
|
||||
FEXCore::X86State::REG_RSP,
|
||||
FEXCore::X86State::REG_RBP,
|
||||
FEXCore::X86State::REG_RSI,
|
||||
FEXCore::X86State::REG_RDI,
|
||||
FEXCore::X86State::REG_R8,
|
||||
FEXCore::X86State::REG_R9,
|
||||
FEXCore::X86State::REG_R10,
|
||||
FEXCore::X86State::REG_R11,
|
||||
FEXCore::X86State::REG_R12,
|
||||
FEXCore::X86State::REG_R13,
|
||||
FEXCore::X86State::REG_R14,
|
||||
FEXCore::X86State::REG_R15,
|
||||
};
|
||||
|
||||
static constexpr GPRArray GPR8BitHighIndexes = {
|
||||
// Classical ordering?
|
||||
FEXCore::X86State::REG_RAX,
|
||||
@@ -52,30 +72,108 @@ static uint32_t MapModRMToReg(uint8_t REX, uint8_t bits, bool HighBits, bool Has
|
||||
FEXCore::X86State::REG_R15,
|
||||
};
|
||||
|
||||
static constexpr GPRArray XMMIndexes = {
|
||||
FEXCore::X86State::REG_XMM_0,
|
||||
FEXCore::X86State::REG_XMM_1,
|
||||
FEXCore::X86State::REG_XMM_2,
|
||||
FEXCore::X86State::REG_XMM_3,
|
||||
FEXCore::X86State::REG_XMM_4,
|
||||
FEXCore::X86State::REG_XMM_5,
|
||||
FEXCore::X86State::REG_XMM_6,
|
||||
FEXCore::X86State::REG_XMM_7,
|
||||
FEXCore::X86State::REG_XMM_8,
|
||||
FEXCore::X86State::REG_XMM_9,
|
||||
FEXCore::X86State::REG_XMM_10,
|
||||
FEXCore::X86State::REG_XMM_11,
|
||||
FEXCore::X86State::REG_XMM_12,
|
||||
FEXCore::X86State::REG_XMM_13,
|
||||
FEXCore::X86State::REG_XMM_14,
|
||||
FEXCore::X86State::REG_XMM_15,
|
||||
};
|
||||
|
||||
static constexpr GPRArray MMIndexes = {
|
||||
FEXCore::X86State::REG_MM_0,
|
||||
FEXCore::X86State::REG_MM_1,
|
||||
FEXCore::X86State::REG_MM_2,
|
||||
FEXCore::X86State::REG_MM_3,
|
||||
FEXCore::X86State::REG_MM_4,
|
||||
FEXCore::X86State::REG_MM_5,
|
||||
FEXCore::X86State::REG_MM_6,
|
||||
FEXCore::X86State::REG_MM_7,
|
||||
FEXCore::X86State::REG_INVALID,
|
||||
FEXCore::X86State::REG_INVALID,
|
||||
FEXCore::X86State::REG_INVALID,
|
||||
FEXCore::X86State::REG_INVALID,
|
||||
FEXCore::X86State::REG_INVALID,
|
||||
FEXCore::X86State::REG_INVALID,
|
||||
FEXCore::X86State::REG_INVALID,
|
||||
FEXCore::X86State::REG_INVALID
|
||||
};
|
||||
|
||||
const GPRArray *GPRs = &GPRIndexes;
|
||||
if (HasXMM) {
|
||||
GPRs = &XMMIndexes;
|
||||
}
|
||||
else if (HasMM) {
|
||||
GPRs = &MMIndexes;
|
||||
}
|
||||
else if (HighBits && !HasREX) {
|
||||
GPRs = &GPR8BitHighIndexes;
|
||||
}
|
||||
|
||||
uint8_t Offset = (REX << 3) | bits;
|
||||
|
||||
if (Offset == InvalidOffset) {
|
||||
return FEXCore::X86State::REG_INVALID;
|
||||
}
|
||||
|
||||
if (HasXMM) {
|
||||
return FEXCore::X86State::REG_XMM_0 + Offset;
|
||||
}
|
||||
else if (HasMM) {
|
||||
return FEXCore::X86State::REG_MM_0 + Offset;
|
||||
}
|
||||
else if (!(HighBits && !HasREX)) {
|
||||
return FEXCore::X86State::REG_RAX + Offset;
|
||||
}
|
||||
|
||||
return GPR8BitHighIndexes[Offset];
|
||||
return (*GPRs)[(REX << 3) | bits];
|
||||
}
|
||||
|
||||
static uint32_t MapVEXToReg(uint8_t vvvv, bool HasXMM) {
|
||||
using GPRArray = std::array<uint32_t, 16>;
|
||||
|
||||
static constexpr GPRArray GPRIndexes = {
|
||||
FEXCore::X86State::REG_RAX,
|
||||
FEXCore::X86State::REG_RCX,
|
||||
FEXCore::X86State::REG_RDX,
|
||||
FEXCore::X86State::REG_RBX,
|
||||
FEXCore::X86State::REG_RSP,
|
||||
FEXCore::X86State::REG_RBP,
|
||||
FEXCore::X86State::REG_RSI,
|
||||
FEXCore::X86State::REG_RDI,
|
||||
FEXCore::X86State::REG_R8,
|
||||
FEXCore::X86State::REG_R9,
|
||||
FEXCore::X86State::REG_R10,
|
||||
FEXCore::X86State::REG_R11,
|
||||
FEXCore::X86State::REG_R12,
|
||||
FEXCore::X86State::REG_R13,
|
||||
FEXCore::X86State::REG_R14,
|
||||
FEXCore::X86State::REG_R15,
|
||||
};
|
||||
|
||||
static constexpr GPRArray XMMIndexes = {
|
||||
FEXCore::X86State::REG_XMM_0,
|
||||
FEXCore::X86State::REG_XMM_1,
|
||||
FEXCore::X86State::REG_XMM_2,
|
||||
FEXCore::X86State::REG_XMM_3,
|
||||
FEXCore::X86State::REG_XMM_4,
|
||||
FEXCore::X86State::REG_XMM_5,
|
||||
FEXCore::X86State::REG_XMM_6,
|
||||
FEXCore::X86State::REG_XMM_7,
|
||||
FEXCore::X86State::REG_XMM_8,
|
||||
FEXCore::X86State::REG_XMM_9,
|
||||
FEXCore::X86State::REG_XMM_10,
|
||||
FEXCore::X86State::REG_XMM_11,
|
||||
FEXCore::X86State::REG_XMM_12,
|
||||
FEXCore::X86State::REG_XMM_13,
|
||||
FEXCore::X86State::REG_XMM_14,
|
||||
FEXCore::X86State::REG_XMM_15,
|
||||
};
|
||||
|
||||
if (HasXMM) {
|
||||
return FEXCore::X86State::REG_XMM_0 + vvvv;
|
||||
return XMMIndexes[vvvv];
|
||||
} else {
|
||||
return FEXCore::X86State::REG_RAX + vvvv;
|
||||
return GPRIndexes[vvvv];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +206,7 @@ uint64_t Decoder::ReadData(uint8_t Size) {
|
||||
uint64_t Res = 0;
|
||||
std::memcpy(&Res, &InstStream[InstructionSize], Size);
|
||||
|
||||
#if defined(ASSERTIONS_ENABLED) && ASSERTIONS_ENABLED
|
||||
#ifndef NDEBUG
|
||||
for(size_t i = 0; i < Size; ++i) {
|
||||
ReadByte();
|
||||
}
|
||||
@@ -286,6 +384,12 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
DecodeInst->OP = Op;
|
||||
DecodeInst->TableInfo = Info;
|
||||
|
||||
// XXX: Once we support 32bit x86 then this will be necessary to support
|
||||
if (Info->Type == FEXCore::X86Tables::TYPE_LEGACY_PREFIX) {
|
||||
LogMan::Msg::DFmt("Legacy Prefix");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Info->Type == FEXCore::X86Tables::TYPE_UNKNOWN) {
|
||||
LogMan::Msg::DFmt("Unknown instruction: {} 0x{:04x} 0x{:x}", Info->Name ?: "UND", Op, DecodeInst->PC);
|
||||
return false;
|
||||
@@ -325,14 +429,6 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
|
||||
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
|
||||
|
||||
// This is used for ModRM register modification
|
||||
// For both modrm.reg and modrm.rm(when mod == 0b11) when value is >= 0b100
|
||||
// then it changes from expected registers to the high 8bits of the lower registers
|
||||
// Bit annoying to support
|
||||
// In the case of no modrm (REX in byte situation) then it is unaffected
|
||||
bool Is8BitSrc{};
|
||||
bool Is8BitDest{};
|
||||
|
||||
// If we require ModRM and haven't decoded it yet, do it now
|
||||
// Some instructions have to read modrm upfront, others do it later
|
||||
if (HasMODRM && !DecodeInst->DecodedModRM) {
|
||||
@@ -349,7 +445,6 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
if (DstSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_8BIT) {
|
||||
DecodeInst->Flags |= DecodeFlags::GenSizeDstSize(DecodeFlags::SIZE_8BIT);
|
||||
DestSize = 1;
|
||||
Is8BitDest = true;
|
||||
}
|
||||
else if (DstSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_16BIT) {
|
||||
DecodeInst->Flags |= DecodeFlags::GenSizeDstSize(DecodeFlags::SIZE_16BIT);
|
||||
@@ -392,7 +487,6 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
// Decode sources
|
||||
if (SrcSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_8BIT) {
|
||||
DecodeInst->Flags |= DecodeFlags::GenSizeSrcSize(DecodeFlags::SIZE_8BIT);
|
||||
Is8BitSrc = true;
|
||||
}
|
||||
else if (SrcSizeFlag == FEXCore::X86Tables::InstFlags::SIZE_16BIT) {
|
||||
DecodeInst->Flags |= DecodeFlags::GenSizeSrcSize(DecodeFlags::SIZE_16BIT);
|
||||
@@ -426,6 +520,14 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
}
|
||||
}
|
||||
|
||||
// This is used for ModRM register modification
|
||||
// For both modrm.reg and modrm.rm(when mod == 0b11) when value is >= 0b100
|
||||
// then it changes from expected registers to the high 8bits of the lower registers
|
||||
// Bit annoying to support
|
||||
// In the case of no modrm (REX in byte situation) then it is unaffected
|
||||
const bool Is8BitSrc = (DecodeFlags::GetSizeSrcFlags(DecodeInst->Flags) == DecodeFlags::SIZE_8BIT);
|
||||
const bool Is8BitDest = (DecodeFlags::GetSizeDstFlags(DecodeInst->Flags) == DecodeFlags::SIZE_8BIT);
|
||||
|
||||
auto *CurrentDest = &DecodeInst->Dest;
|
||||
|
||||
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ||
|
||||
@@ -436,7 +538,8 @@ bool Decoder::NormalOp(FEXCore::X86Tables::X86InstInfo const *Info, uint16_t Op,
|
||||
CurrentDest->Data.GPR.GPR = HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_DST_RAX) ? FEXCore::X86State::REG_RAX : FEXCore::X86State::REG_RDX;
|
||||
CurrentDest = &DecodeInst->Src[0];
|
||||
}
|
||||
else if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_REX_IN_BYTE)) {
|
||||
|
||||
if (HAS_NON_XMM_SUBFLAG(Info->Flags, FEXCore::X86Tables::InstFlags::FLAGS_SF_REX_IN_BYTE)) {
|
||||
LOGMAN_THROW_AA_FMT(!HasMODRM, "This instruction shouldn't have ModRM!");
|
||||
|
||||
// If the REX is in the byte that means the lower nibble of the OP contains the destination GPR
|
||||
@@ -581,6 +684,12 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
DecodeInst->OP = Op;
|
||||
DecodeInst->TableInfo = Info;
|
||||
|
||||
// XXX: Once we support 32bit x86 then this will be necessary to support
|
||||
if (Info->Type == FEXCore::X86Tables::TYPE_LEGACY_PREFIX) {
|
||||
LogMan::Msg::DFmt("Legacy Prefix");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Info->Type == FEXCore::X86Tables::TYPE_UNKNOWN) {
|
||||
LogMan::Msg::DFmt("Unknown instruction: {} 0x{:04x} 0x{:x}", Info->Name ?: "UND", Op, DecodeInst->PC);
|
||||
return false;
|
||||
@@ -594,11 +703,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
LOGMAN_THROW_AA_FMT(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX,
|
||||
"REX PREFIX should have been decoded before this!");
|
||||
|
||||
// A normal instruction is the most likely.
|
||||
if (Info->Type == FEXCore::X86Tables::TYPE_INST) [[likely]] {
|
||||
return NormalOp(Info, Op);
|
||||
}
|
||||
else if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
|
||||
if (Info->Type >= FEXCore::X86Tables::TYPE_GROUP_1 &&
|
||||
Info->Type <= FEXCore::X86Tables::TYPE_GROUP_11) {
|
||||
uint8_t ModRMByte = ReadByte();
|
||||
DecodeInst->ModRM = ModRMByte;
|
||||
@@ -746,8 +851,7 @@ bool Decoder::NormalOpHeader(FEXCore::X86Tables::X86InstInfo const *Info, uint16
|
||||
return NormalOp(&EVEXTableOps[EVEXOp], EVEXOp);
|
||||
}
|
||||
|
||||
LOGMAN_MSG_A_FMT("Invalid instruction decoding type");
|
||||
FEX_UNREACHABLE;
|
||||
return NormalOp(Info, Op);
|
||||
}
|
||||
|
||||
bool Decoder::DecodeInstruction(uint64_t PC) {
|
||||
@@ -766,106 +870,105 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
|
||||
case 0x0F: {// Escape Op
|
||||
uint8_t EscapeOp = ReadByte();
|
||||
switch (EscapeOp) {
|
||||
case 0x0F: [[unlikely]] { // 3DNow!
|
||||
// 3DNow! Instruction Encoding: 0F 0F [ModRM] [SIB] [Displacement] [Opcode]
|
||||
// Decode ModRM
|
||||
uint8_t ModRMByte = ReadByte();
|
||||
DecodeInst->ModRM = ModRMByte;
|
||||
DecodeInst->DecodedModRM = true;
|
||||
case 0x0F: [[unlikely]] { // 3DNow!
|
||||
// 3DNow! Instruction Encoding: 0F 0F [ModRM] [SIB] [Displacement] [Opcode]
|
||||
// Decode ModRM
|
||||
uint8_t ModRMByte = ReadByte();
|
||||
DecodeInst->ModRM = ModRMByte;
|
||||
DecodeInst->DecodedModRM = true;
|
||||
|
||||
FEXCore::X86Tables::ModRMDecoded ModRM;
|
||||
ModRM.Hex = DecodeInst->ModRM;
|
||||
FEXCore::X86Tables::ModRMDecoded ModRM;
|
||||
ModRM.Hex = DecodeInst->ModRM;
|
||||
|
||||
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
|
||||
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
|
||||
const bool Has16BitAddressing = !CTX->Config.Is64BitMode &&
|
||||
DecodeInst->Flags & DecodeFlags::FLAG_ADDRESS_SIZE;
|
||||
|
||||
// All 3DNow! instructions have the second argument as the rm handler
|
||||
// We need to decode it upfront to get the displacement out of the way
|
||||
if (ModRM.mod != 0b11) {
|
||||
auto Disp = DecodeModRMs_Disp[Has16BitAddressing];
|
||||
(this->*Disp)(&DecodeInst->Src[0], ModRM);
|
||||
}
|
||||
|
||||
// Take a peek at the op just past the displacement
|
||||
uint8_t LocalOp = ReadByte();
|
||||
return NormalOpHeader(&FEXCore::X86Tables::DDDNowOps[LocalOp], LocalOp);
|
||||
break;
|
||||
// All 3DNow! instructions have the second argument as the rm handler
|
||||
// We need to decode it upfront to get the displacement out of the way
|
||||
if (ModRM.mod != 0b11) {
|
||||
auto Disp = DecodeModRMs_Disp[Has16BitAddressing];
|
||||
(this->*Disp)(&DecodeInst->Src[0], ModRM);
|
||||
}
|
||||
case 0x38: { // F38 Table!
|
||||
constexpr uint16_t PF_38_NONE = 0;
|
||||
constexpr uint16_t PF_38_66 = (1U << 0);
|
||||
constexpr uint16_t PF_38_F2 = (1U << 1);
|
||||
constexpr uint16_t PF_38_F3 = (1U << 2);
|
||||
|
||||
uint16_t Prefix = PF_38_NONE;
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_OPERAND_SIZE) {
|
||||
Prefix |= PF_38_66;
|
||||
}
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_REPNE_PREFIX) {
|
||||
Prefix |= PF_38_F2;
|
||||
}
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_REP_PREFIX) {
|
||||
Prefix |= PF_38_F3;
|
||||
}
|
||||
// Take a peek at the op just past the displacement
|
||||
uint8_t LocalOp = ReadByte();
|
||||
return NormalOpHeader(&FEXCore::X86Tables::DDDNowOps[LocalOp], LocalOp);
|
||||
break;
|
||||
}
|
||||
case 0x38: { // F38 Table!
|
||||
constexpr uint16_t PF_38_NONE = 0;
|
||||
constexpr uint16_t PF_38_66 = (1U << 0);
|
||||
constexpr uint16_t PF_38_F2 = (1U << 1);
|
||||
constexpr uint16_t PF_38_F3 = (1U << 2);
|
||||
|
||||
uint16_t LocalOp = (Prefix << 8) | ReadByte();
|
||||
return NormalOpHeader(&FEXCore::X86Tables::H0F38TableOps[LocalOp], LocalOp);
|
||||
break;
|
||||
uint16_t Prefix = PF_38_NONE;
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_OPERAND_SIZE) {
|
||||
Prefix |= PF_38_66;
|
||||
}
|
||||
case 0x3A: { // F3A Table!
|
||||
constexpr uint16_t PF_3A_NONE = 0;
|
||||
constexpr uint16_t PF_3A_66 = (1 << 0);
|
||||
constexpr uint16_t PF_3A_REX = (1 << 1);
|
||||
|
||||
uint16_t Prefix = PF_3A_NONE;
|
||||
if (DecodeInst->LastEscapePrefix == 0x66) // Operand Size
|
||||
Prefix = PF_3A_66;
|
||||
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_REX_WIDENING)
|
||||
Prefix |= PF_3A_REX;
|
||||
|
||||
uint16_t LocalOp = (Prefix << 8) | ReadByte();
|
||||
return NormalOpHeader(&FEXCore::X86Tables::H0F3ATableOps[LocalOp], LocalOp);
|
||||
break;
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_REPNE_PREFIX) {
|
||||
Prefix |= PF_38_F2;
|
||||
}
|
||||
default: [[likely]] { // Two byte table!
|
||||
// x86-64 abuses three legacy prefixes to extend the table encodings
|
||||
// 0x66 - Operand Size prefix
|
||||
// 0xF2 - REPNE prefix
|
||||
// 0xF3 - REP prefix
|
||||
// If any of these three prefixes are used then it falls down the subtable
|
||||
// Additionally: If you hit repeat of differnt prefixes then only the LAST one before this one works for subtable selection
|
||||
|
||||
bool NoOverlay = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY) != 0;
|
||||
bool NoOverlay66 = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY66) != 0;
|
||||
|
||||
if (NoOverlay) { // This section of the table ignores prefix extention
|
||||
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else if (DecodeInst->LastEscapePrefix == 0xF3) { // REP
|
||||
// Remove prefix so it doesn't effect calculations.
|
||||
// This is only an escape prefix rather tan modifier now
|
||||
DecodeInst->Flags &= ~DecodeFlags::FLAG_REP_PREFIX;
|
||||
return NormalOpHeader(&FEXCore::X86Tables::RepModOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else if (DecodeInst->LastEscapePrefix == 0xF2) { // REPNE
|
||||
// Remove prefix so it doesn't effect calculations.
|
||||
// This is only an escape prefix rather tan modifier now
|
||||
DecodeInst->Flags &= ~DecodeFlags::FLAG_REPNE_PREFIX;
|
||||
return NormalOpHeader(&FEXCore::X86Tables::RepNEModOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else if (DecodeInst->LastEscapePrefix == 0x66 && !NoOverlay66) { // Operand Size
|
||||
// Remove prefix so it doesn't effect calculations.
|
||||
// This is only an escape prefix rather tan modifier now
|
||||
DecodeInst->Flags &= ~DecodeFlags::FLAG_OPERAND_SIZE;
|
||||
DecodeFlags::PopOpAddrIf(&DecodeInst->Flags, DecodeFlags::FLAG_OPERAND_SIZE_LAST);
|
||||
return NormalOpHeader(&FEXCore::X86Tables::OpSizeModOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else {
|
||||
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
break;
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_REP_PREFIX) {
|
||||
Prefix |= PF_38_F3;
|
||||
}
|
||||
|
||||
uint16_t LocalOp = (Prefix << 8) | ReadByte();
|
||||
return NormalOpHeader(&FEXCore::X86Tables::H0F38TableOps[LocalOp], LocalOp);
|
||||
break;
|
||||
}
|
||||
case 0x3A: { // F3A Table!
|
||||
constexpr uint16_t PF_3A_NONE = 0;
|
||||
constexpr uint16_t PF_3A_66 = (1 << 0);
|
||||
constexpr uint16_t PF_3A_REX = (1 << 1);
|
||||
|
||||
uint16_t Prefix = PF_3A_NONE;
|
||||
if (DecodeInst->LastEscapePrefix == 0x66) // Operand Size
|
||||
Prefix = PF_3A_66;
|
||||
|
||||
if (DecodeInst->Flags & DecodeFlags::FLAG_REX_WIDENING)
|
||||
Prefix |= PF_3A_REX;
|
||||
|
||||
uint16_t LocalOp = (Prefix << 8) | ReadByte();
|
||||
return NormalOpHeader(&FEXCore::X86Tables::H0F3ATableOps[LocalOp], LocalOp);
|
||||
break;
|
||||
}
|
||||
default: // Two byte table!
|
||||
// x86-64 abuses three legacy prefixes to extend the table encodings
|
||||
// 0x66 - Operand Size prefix
|
||||
// 0xF2 - REPNE prefix
|
||||
// 0xF3 - REP prefix
|
||||
// If any of these three prefixes are used then it falls down the subtable
|
||||
// Additionally: If you hit repeat of differnt prefixes then only the LAST one before this one works for subtable selection
|
||||
|
||||
bool NoOverlay = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY) != 0;
|
||||
bool NoOverlay66 = (FEXCore::X86Tables::SecondBaseOps[EscapeOp].Flags & InstFlags::FLAGS_NO_OVERLAY66) != 0;
|
||||
|
||||
if (NoOverlay) { // This section of the table ignores prefix extention
|
||||
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else if (DecodeInst->LastEscapePrefix == 0xF3) { // REP
|
||||
// Remove prefix so it doesn't effect calculations.
|
||||
// This is only an escape prefix rather tan modifier now
|
||||
DecodeInst->Flags &= ~DecodeFlags::FLAG_REP_PREFIX;
|
||||
return NormalOpHeader(&FEXCore::X86Tables::RepModOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else if (DecodeInst->LastEscapePrefix == 0xF2) { // REPNE
|
||||
// Remove prefix so it doesn't effect calculations.
|
||||
// This is only an escape prefix rather tan modifier now
|
||||
DecodeInst->Flags &= ~DecodeFlags::FLAG_REPNE_PREFIX;
|
||||
return NormalOpHeader(&FEXCore::X86Tables::RepNEModOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else if (DecodeInst->LastEscapePrefix == 0x66 && !NoOverlay66) { // Operand Size
|
||||
// Remove prefix so it doesn't effect calculations.
|
||||
// This is only an escape prefix rather tan modifier now
|
||||
DecodeInst->Flags &= ~DecodeFlags::FLAG_OPERAND_SIZE;
|
||||
DecodeFlags::PopOpAddrIf(&DecodeInst->Flags, DecodeFlags::FLAG_OPERAND_SIZE_LAST);
|
||||
return NormalOpHeader(&FEXCore::X86Tables::OpSizeModOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
else {
|
||||
return NormalOpHeader(&FEXCore::X86Tables::SecondBaseOps[EscapeOp], EscapeOp);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -918,7 +1021,7 @@ bool Decoder::DecodeInstruction(uint64_t PC) {
|
||||
case 0x65: // GS prefix
|
||||
DecodeInst->Flags |= DecodeFlags::FLAG_GS_PREFIX;
|
||||
break;
|
||||
default: [[likely]] { // Default base table
|
||||
default: { // Default base table
|
||||
auto Info = &FEXCore::X86Tables::BaseOps[Op];
|
||||
|
||||
if (Info->Type == FEXCore::X86Tables::TYPE_REX_PREFIX) {
|
||||
@@ -1137,19 +1240,24 @@ void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC,
|
||||
auto OpMinPage = OpMinAddress & FHU::FEX_PAGE_MASK;
|
||||
auto OpMaxPage = OpMaxAddress & FHU::FEX_PAGE_MASK;
|
||||
|
||||
|
||||
if (OpMinPage != CurrentCodePage) {
|
||||
CurrentCodePage = OpMinPage;
|
||||
CodePages.insert(CurrentCodePage);
|
||||
if (CodePages.insert(CurrentCodePage).second) {
|
||||
AddContainedCodePage(PC, CurrentCodePage, FHU::FEX_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
if (OpMaxPage != CurrentCodePage) {
|
||||
CurrentCodePage = OpMaxPage;
|
||||
CodePages.insert(CurrentCodePage);
|
||||
if (CodePages.insert(CurrentCodePage).second) {
|
||||
AddContainedCodePage(PC, CurrentCodePage, FHU::FEX_PAGE_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
bool ErrorDuringDecoding = !DecodeInstruction(RIPToDecode + PCOffset);
|
||||
|
||||
if (ErrorDuringDecoding) [[unlikely]] {
|
||||
if (ErrorDuringDecoding) {
|
||||
LogMan::Msg::DFmt("Couldn't Decode something at 0x{:x}, Started at 0x{:x}", RIPToDecode + PCOffset, PC);
|
||||
// Put an invalid instruction in the stream so the core can raise SIGILL if hit
|
||||
CurrentBlockDecoding.HasInvalidInstruction = true;
|
||||
@@ -1206,9 +1314,6 @@ void Decoder::DecodeInstructionsAtEntry(uint8_t const* _InstStream, uint64_t PC,
|
||||
CurrentBlockDecoding.DecodedInstructions = &DecodedBuffer[BlockStartOffset];
|
||||
}
|
||||
|
||||
for (auto CodePage : CodePages) {
|
||||
AddContainedCodePage(PC, CodePage, FHU::FEX_PAGE_SIZE);
|
||||
}
|
||||
|
||||
// sort for better branching
|
||||
std::sort(Blocks.begin(), Blocks.end(), [](const FEXCore::Frontend::Decoder::DecodedBlocks& a, const FEXCore::Frontend::Decoder::DecodedBlocks& b) {
|
||||
|
||||
@@ -79,7 +79,6 @@ HostFeatures::HostFeatures() {
|
||||
SupportsSHA = true;
|
||||
SupportsBMI1 = true;
|
||||
SupportsBMI2 = true;
|
||||
SupportsCLWB = true;
|
||||
|
||||
if (!SupportsAtomics) {
|
||||
WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance");
|
||||
@@ -129,7 +128,6 @@ HostFeatures::HostFeatures() {
|
||||
SupportsSHA = Features.has(Xbyak::util::Cpu::tSHA);
|
||||
SupportsBMI1 = Features.has(Xbyak::util::Cpu::tBMI1);
|
||||
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tBMI2);
|
||||
SupportsBMI2 = Features.has(Xbyak::util::Cpu::tCLWB);
|
||||
SupportsPMULL_128Bit = Features.has(Xbyak::util::Cpu::tPCLMULQDQ);
|
||||
|
||||
// xbyak doesn't know how to check for CLZero
|
||||
|
||||
@@ -18,8 +18,8 @@ $end_info$
|
||||
|
||||
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);
|
||||
static void SignalReturn(FEXCore::Core::InternalThreadState *Thread) {
|
||||
Thread->CTX->SignalThread(Thread, FEXCore::Core::SignalEvent::Return);
|
||||
|
||||
LOGMAN_MSG_A_FMT("unreachable");
|
||||
FEX_UNREACHABLE;
|
||||
@@ -28,9 +28,7 @@ static void SignalReturn(FEXCore::Core::InternalThreadState *Thread, bool RT) {
|
||||
#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);
|
||||
SignalReturn(Data->State);
|
||||
}
|
||||
|
||||
DEF_OP(CallbackReturn) {
|
||||
|
||||
@@ -155,7 +155,6 @@ constexpr OpHandlerArray InterpreterOpHandlers = [] {
|
||||
REGISTER_OP(LOADMEMTSO, LoadMem);
|
||||
REGISTER_OP(STOREMEMTSO, StoreMem);
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
|
||||
REGISTER_OP(CACHELINEZERO, CacheLineZero);
|
||||
|
||||
// Misc ops
|
||||
@@ -330,6 +329,7 @@ void InterpreterOps::InterpretIR(FEXCore::Core::CpuStateFrame *Frame, FEXCore::I
|
||||
|
||||
const uintptr_t ListSize = CurrentIR->GetSSACount();
|
||||
|
||||
static_assert(sizeof(FEXCore::IR::IROp_Header) == 4);
|
||||
static_assert(sizeof(FEXCore::IR::OrderedNode) == 16);
|
||||
|
||||
auto BlockEnd = CurrentIR->GetBlocks().end();
|
||||
|
||||
@@ -182,7 +182,6 @@ namespace FEXCore::CPU {
|
||||
DEF_OP(LoadMem);
|
||||
DEF_OP(StoreMem);
|
||||
DEF_OP(CacheLineClear);
|
||||
DEF_OP(CacheLineClean);
|
||||
DEF_OP(CacheLineZero);
|
||||
|
||||
///< Misc ops
|
||||
|
||||
@@ -23,22 +23,6 @@ static inline void CacheLineFlush(char *Addr) {
|
||||
#endif
|
||||
}
|
||||
|
||||
static inline void CacheLineClean(char *Addr) {
|
||||
#ifdef _M_X86_64
|
||||
__asm volatile (
|
||||
"clwb (%[Addr]);"
|
||||
:: [Addr] "r" (Addr)
|
||||
: "memory");
|
||||
#elif _M_ARM_64
|
||||
__asm volatile (
|
||||
"dc cvac, %[Addr]"
|
||||
:: [Addr] "r" (Addr)
|
||||
: "memory");
|
||||
#else
|
||||
LOGMAN_THROW_A_FMT("Unsupported architecture with cacheline clean");
|
||||
#endif
|
||||
}
|
||||
|
||||
#define DEF_OP(x) void InterpreterOps::Op_##x(IR::IROp_Header *IROp, IROpData *Data, IR::NodeID Node)
|
||||
DEF_OP(LoadContext) {
|
||||
const auto Op = IROp->C<IR::IROp_LoadContext>();
|
||||
@@ -297,15 +281,6 @@ DEF_OP(CacheLineClear) {
|
||||
CacheLineFlush(MemData);
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClean) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineClean>();
|
||||
|
||||
char *MemData = *GetSrc<char **>(Data->SSAData, Op->Addr);
|
||||
|
||||
// 64-byte cache line clear
|
||||
CacheLineClean(MemData);
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineZero) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineZero>();
|
||||
|
||||
|
||||
@@ -1274,4 +1274,57 @@ DEF_OP(FCmp) {
|
||||
|
||||
#undef DEF_OP
|
||||
|
||||
void Arm64JITCore::RegisterALUHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
|
||||
REGISTER_OP(CONSTANT, Constant);
|
||||
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
|
||||
REGISTER_OP(INLINECONSTANT, InlineConstant);
|
||||
REGISTER_OP(INLINEENTRYPOINTOFFSET, InlineEntrypointOffset);
|
||||
REGISTER_OP(CYCLECOUNTER, CycleCounter);
|
||||
REGISTER_OP(ADD, Add);
|
||||
REGISTER_OP(SUB, Sub);
|
||||
REGISTER_OP(NEG, Neg);
|
||||
REGISTER_OP(MUL, Mul);
|
||||
REGISTER_OP(UMUL, UMul);
|
||||
REGISTER_OP(DIV, Div);
|
||||
REGISTER_OP(UDIV, UDiv);
|
||||
REGISTER_OP(REM, Rem);
|
||||
REGISTER_OP(UREM, URem);
|
||||
REGISTER_OP(MULH, MulH);
|
||||
REGISTER_OP(UMULH, UMulH);
|
||||
REGISTER_OP(OR, Or);
|
||||
REGISTER_OP(AND, And);
|
||||
REGISTER_OP(ANDN, Andn);
|
||||
REGISTER_OP(XOR, Xor);
|
||||
REGISTER_OP(LSHL, Lshl);
|
||||
REGISTER_OP(LSHR, Lshr);
|
||||
REGISTER_OP(ASHR, Ashr);
|
||||
REGISTER_OP(ROR, Ror);
|
||||
REGISTER_OP(EXTR, Extr);
|
||||
REGISTER_OP(PDEP, PDep);
|
||||
REGISTER_OP(PEXT, PExt);
|
||||
REGISTER_OP(LDIV, LDiv);
|
||||
REGISTER_OP(LUDIV, LUDiv);
|
||||
REGISTER_OP(LREM, LRem);
|
||||
REGISTER_OP(LUREM, LURem);
|
||||
REGISTER_OP(NOT, Not);
|
||||
REGISTER_OP(POPCOUNT, Popcount);
|
||||
REGISTER_OP(FINDLSB, FindLSB);
|
||||
REGISTER_OP(FINDMSB, FindMSB);
|
||||
REGISTER_OP(FINDTRAILINGZEROS, FindTrailingZeros);
|
||||
REGISTER_OP(COUNTLEADINGZEROES, CountLeadingZeroes);
|
||||
REGISTER_OP(REV, Rev);
|
||||
REGISTER_OP(BFI, Bfi);
|
||||
REGISTER_OP(BFE, Bfe);
|
||||
REGISTER_OP(SBFE, Sbfe);
|
||||
REGISTER_OP(SELECT, Select);
|
||||
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
|
||||
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
|
||||
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
|
||||
REGISTER_OP(FCMP, FCmp);
|
||||
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
}
|
||||
@@ -438,5 +438,23 @@ DEF_OP(AtomicFetchNeg) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterAtomicHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(CASPAIR, CASPair);
|
||||
REGISTER_OP(CAS, CAS);
|
||||
REGISTER_OP(ATOMICADD, AtomicAdd);
|
||||
REGISTER_OP(ATOMICSUB, AtomicSub);
|
||||
REGISTER_OP(ATOMICAND, AtomicAnd);
|
||||
REGISTER_OP(ATOMICOR, AtomicOr);
|
||||
REGISTER_OP(ATOMICXOR, AtomicXor);
|
||||
REGISTER_OP(ATOMICSWAP, AtomicSwap);
|
||||
REGISTER_OP(ATOMICFETCHADD, AtomicFetchAdd);
|
||||
REGISTER_OP(ATOMICFETCHSUB, AtomicFetchSub);
|
||||
REGISTER_OP(ATOMICFETCHAND, AtomicFetchAnd);
|
||||
REGISTER_OP(ATOMICFETCHOR, AtomicFetchOr);
|
||||
REGISTER_OP(ATOMICFETCHXOR, AtomicFetchXor);
|
||||
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,19 +21,12 @@ 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));
|
||||
}
|
||||
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler));
|
||||
br(ARMEmitter::Reg::r0);
|
||||
}
|
||||
|
||||
@@ -458,5 +451,20 @@ DEF_OP(CPUID) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterBranchHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(SIGNALRETURN, SignalReturn);
|
||||
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
|
||||
REGISTER_OP(EXITFUNCTION, ExitFunction);
|
||||
REGISTER_OP(JUMP, Jump);
|
||||
REGISTER_OP(CONDJUMP, CondJump);
|
||||
REGISTER_OP(SYSCALL, Syscall);
|
||||
REGISTER_OP(INLINESYSCALL, InlineSyscall);
|
||||
REGISTER_OP(THUNK, Thunk);
|
||||
REGISTER_OP(VALIDATECODE, ValidateCode);
|
||||
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
|
||||
REGISTER_OP(CPUID, CPUID);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,5 +365,18 @@ DEF_OP(Vector_FToI) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterConversionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
|
||||
REGISTER_OP(FLOAT_FTOF, Float_FToF);
|
||||
REGISTER_OP(VECTOR_STOF, Vector_SToF);
|
||||
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
|
||||
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
|
||||
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
|
||||
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,4 +136,16 @@ DEF_OP(PCLMUL) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterEncryptionHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(VAESIMC, AESImc);
|
||||
REGISTER_OP(VAESENC, AESEnc);
|
||||
REGISTER_OP(VAESENCLAST, AESEncLast);
|
||||
REGISTER_OP(VAESDEC, AESDec);
|
||||
REGISTER_OP(VAESDECLAST, AESDecLast);
|
||||
REGISTER_OP(VAESKEYGENASSIST, AESKeyGenAssist);
|
||||
REGISTER_OP(CRC32, CRC32);
|
||||
REGISTER_OP(PCLMUL, PCLMUL);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
@@ -14,5 +14,10 @@ DEF_OP(GetHostFlag) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterFlagHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+20
-252
@@ -163,7 +163,7 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header const *IROp, IR::NodeID Node) {
|
||||
|
||||
const auto Src1 = GetReg(IROp->Args[0].ID());
|
||||
if (Info.ABI == FABI_F80_I16) {
|
||||
sxth(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, Src1);
|
||||
uxth(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, Src1);
|
||||
}
|
||||
else {
|
||||
mov(ARMEmitter::Size::i32Bit, ARMEmitter::Reg::r0, Src1);
|
||||
@@ -310,7 +310,7 @@ void Arm64JITCore::Op_Unhandled(IR::IROp_Header const *IROp, IR::NodeID Node) {
|
||||
FillStaticRegs();
|
||||
|
||||
const auto Dst = GetReg(Node);
|
||||
sxth(ARMEmitter::Size::i64Bit, Dst, ARMEmitter::Reg::r0);
|
||||
uxth(ARMEmitter::Size::i64Bit, Dst, ARMEmitter::Reg::r0);
|
||||
}
|
||||
break;
|
||||
case FABI_I32_F80:{
|
||||
@@ -535,6 +535,21 @@ Arm64JITCore::Arm64JITCore(FEXCore::Context::Context *ctx, FEXCore::Core::Intern
|
||||
RAPass->AddRegisterConflict(FEXCore::IR::GPRClass, i * 2 + 1, FEXCore::IR::GPRPairClass, i);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < FEXCore::IR::IROps::OP_LAST + 1; ++i) {
|
||||
OpHandlers[i] = &Arm64JITCore::Op_Unhandled;
|
||||
}
|
||||
|
||||
RegisterALUHandlers();
|
||||
RegisterAtomicHandlers();
|
||||
RegisterBranchHandlers();
|
||||
RegisterConversionHandlers();
|
||||
RegisterFlagHandlers();
|
||||
RegisterMemoryHandlers();
|
||||
RegisterMiscHandlers();
|
||||
RegisterMoveHandlers();
|
||||
RegisterVectorHandlers();
|
||||
RegisterEncryptionHandlers();
|
||||
|
||||
{
|
||||
// Set up pointers that the JIT needs to load
|
||||
|
||||
@@ -754,257 +769,10 @@ void *Arm64JITCore::CompileCode(uint64_t Entry,
|
||||
|
||||
for (auto [CodeNode, IROp] : IR->GetCode(BlockNode)) {
|
||||
const auto ID = IR->GetID(CodeNode);
|
||||
switch (IROp->Op) {
|
||||
#define REGISTER_OP(op, x) case FEXCore::IR::IROps::OP_##op: Op_##x(IROp, ID); break
|
||||
// ALU ops
|
||||
REGISTER_OP(TRUNCELEMENTPAIR, TruncElementPair);
|
||||
REGISTER_OP(CONSTANT, Constant);
|
||||
REGISTER_OP(ENTRYPOINTOFFSET, EntrypointOffset);
|
||||
REGISTER_OP(INLINECONSTANT, InlineConstant);
|
||||
REGISTER_OP(INLINEENTRYPOINTOFFSET, InlineEntrypointOffset);
|
||||
REGISTER_OP(CYCLECOUNTER, CycleCounter);
|
||||
REGISTER_OP(ADD, Add);
|
||||
REGISTER_OP(SUB, Sub);
|
||||
REGISTER_OP(NEG, Neg);
|
||||
REGISTER_OP(MUL, Mul);
|
||||
REGISTER_OP(UMUL, UMul);
|
||||
REGISTER_OP(DIV, Div);
|
||||
REGISTER_OP(UDIV, UDiv);
|
||||
REGISTER_OP(REM, Rem);
|
||||
REGISTER_OP(UREM, URem);
|
||||
REGISTER_OP(MULH, MulH);
|
||||
REGISTER_OP(UMULH, UMulH);
|
||||
REGISTER_OP(OR, Or);
|
||||
REGISTER_OP(AND, And);
|
||||
REGISTER_OP(ANDN, Andn);
|
||||
REGISTER_OP(XOR, Xor);
|
||||
REGISTER_OP(LSHL, Lshl);
|
||||
REGISTER_OP(LSHR, Lshr);
|
||||
REGISTER_OP(ASHR, Ashr);
|
||||
REGISTER_OP(ROR, Ror);
|
||||
REGISTER_OP(EXTR, Extr);
|
||||
REGISTER_OP(PDEP, PDep);
|
||||
REGISTER_OP(PEXT, PExt);
|
||||
REGISTER_OP(LDIV, LDiv);
|
||||
REGISTER_OP(LUDIV, LUDiv);
|
||||
REGISTER_OP(LREM, LRem);
|
||||
REGISTER_OP(LUREM, LURem);
|
||||
REGISTER_OP(NOT, Not);
|
||||
REGISTER_OP(POPCOUNT, Popcount);
|
||||
REGISTER_OP(FINDLSB, FindLSB);
|
||||
REGISTER_OP(FINDMSB, FindMSB);
|
||||
REGISTER_OP(FINDTRAILINGZEROS, FindTrailingZeros);
|
||||
REGISTER_OP(COUNTLEADINGZEROES, CountLeadingZeroes);
|
||||
REGISTER_OP(REV, Rev);
|
||||
REGISTER_OP(BFI, Bfi);
|
||||
REGISTER_OP(BFE, Bfe);
|
||||
REGISTER_OP(SBFE, Sbfe);
|
||||
REGISTER_OP(SELECT, Select);
|
||||
REGISTER_OP(VEXTRACTTOGPR, VExtractToGPR);
|
||||
REGISTER_OP(FLOAT_TOGPR_ZS, Float_ToGPR_ZS);
|
||||
REGISTER_OP(FLOAT_TOGPR_S, Float_ToGPR_S);
|
||||
REGISTER_OP(FCMP, FCmp);
|
||||
|
||||
// Atomic ops
|
||||
REGISTER_OP(CASPAIR, CASPair);
|
||||
REGISTER_OP(CAS, CAS);
|
||||
REGISTER_OP(ATOMICADD, AtomicAdd);
|
||||
REGISTER_OP(ATOMICSUB, AtomicSub);
|
||||
REGISTER_OP(ATOMICAND, AtomicAnd);
|
||||
REGISTER_OP(ATOMICOR, AtomicOr);
|
||||
REGISTER_OP(ATOMICXOR, AtomicXor);
|
||||
REGISTER_OP(ATOMICSWAP, AtomicSwap);
|
||||
REGISTER_OP(ATOMICFETCHADD, AtomicFetchAdd);
|
||||
REGISTER_OP(ATOMICFETCHSUB, AtomicFetchSub);
|
||||
REGISTER_OP(ATOMICFETCHAND, AtomicFetchAnd);
|
||||
REGISTER_OP(ATOMICFETCHOR, AtomicFetchOr);
|
||||
REGISTER_OP(ATOMICFETCHXOR, AtomicFetchXor);
|
||||
REGISTER_OP(ATOMICFETCHNEG, AtomicFetchNeg);
|
||||
|
||||
// Branch ops
|
||||
REGISTER_OP(SIGNALRETURN, SignalReturn);
|
||||
REGISTER_OP(CALLBACKRETURN, CallbackReturn);
|
||||
REGISTER_OP(EXITFUNCTION, ExitFunction);
|
||||
REGISTER_OP(JUMP, Jump);
|
||||
REGISTER_OP(CONDJUMP, CondJump);
|
||||
REGISTER_OP(SYSCALL, Syscall);
|
||||
REGISTER_OP(INLINESYSCALL, InlineSyscall);
|
||||
REGISTER_OP(THUNK, Thunk);
|
||||
REGISTER_OP(VALIDATECODE, ValidateCode);
|
||||
REGISTER_OP(THREADREMOVECODEENTRY, ThreadRemoveCodeEntry);
|
||||
REGISTER_OP(CPUID, CPUID);
|
||||
|
||||
// Conversion ops
|
||||
REGISTER_OP(VINSGPR, VInsGPR);
|
||||
REGISTER_OP(VCASTFROMGPR, VCastFromGPR);
|
||||
REGISTER_OP(FLOAT_FROMGPR_S, Float_FromGPR_S);
|
||||
REGISTER_OP(FLOAT_FTOF, Float_FToF);
|
||||
REGISTER_OP(VECTOR_STOF, Vector_SToF);
|
||||
REGISTER_OP(VECTOR_FTOZS, Vector_FToZS);
|
||||
REGISTER_OP(VECTOR_FTOS, Vector_FToS);
|
||||
REGISTER_OP(VECTOR_FTOF, Vector_FToF);
|
||||
REGISTER_OP(VECTOR_FTOI, Vector_FToI);
|
||||
|
||||
// Encryption ops
|
||||
REGISTER_OP(VAESIMC, AESImc);
|
||||
REGISTER_OP(VAESENC, AESEnc);
|
||||
REGISTER_OP(VAESENCLAST, AESEncLast);
|
||||
REGISTER_OP(VAESDEC, AESDec);
|
||||
REGISTER_OP(VAESDECLAST, AESDecLast);
|
||||
REGISTER_OP(VAESKEYGENASSIST, AESKeyGenAssist);
|
||||
REGISTER_OP(CRC32, CRC32);
|
||||
REGISTER_OP(PCLMUL, PCLMUL);
|
||||
|
||||
// Flag ops
|
||||
REGISTER_OP(GETHOSTFLAG, GetHostFlag);
|
||||
|
||||
// Memory ops
|
||||
REGISTER_OP(LOADCONTEXT, LoadContext);
|
||||
REGISTER_OP(STORECONTEXT, StoreContext);
|
||||
REGISTER_OP(LOADREGISTER, LoadRegister);
|
||||
REGISTER_OP(STOREREGISTER, StoreRegister);
|
||||
REGISTER_OP(LOADCONTEXTINDEXED, LoadContextIndexed);
|
||||
REGISTER_OP(STORECONTEXTINDEXED, StoreContextIndexed);
|
||||
REGISTER_OP(SPILLREGISTER, SpillRegister);
|
||||
REGISTER_OP(FILLREGISTER, FillRegister);
|
||||
REGISTER_OP(LOADFLAG, LoadFlag);
|
||||
REGISTER_OP(STOREFLAG, StoreFlag);
|
||||
REGISTER_OP(LOADMEM, LoadMem);
|
||||
REGISTER_OP(STOREMEM, StoreMem);
|
||||
case FEXCore::IR::IROps::OP_LOADMEMTSO:
|
||||
if (ParanoidTSO()) {
|
||||
Op_ParanoidLoadMemTSO(IROp, ID);
|
||||
}
|
||||
else {
|
||||
Op_LoadMemTSO(IROp, ID);
|
||||
}
|
||||
break;
|
||||
case FEXCore::IR::IROps::OP_STOREMEMTSO:
|
||||
if (ParanoidTSO()) {
|
||||
Op_ParanoidStoreMemTSO(IROp, ID);
|
||||
}
|
||||
else {
|
||||
Op_StoreMemTSO(IROp, ID);
|
||||
}
|
||||
break;
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
|
||||
REGISTER_OP(CACHELINEZERO, CacheLineZero);
|
||||
|
||||
// Misc ops
|
||||
REGISTER_OP(DUMMY, NoOp);
|
||||
REGISTER_OP(IRHEADER, NoOp);
|
||||
REGISTER_OP(CODEBLOCK, NoOp);
|
||||
REGISTER_OP(BEGINBLOCK, NoOp);
|
||||
REGISTER_OP(ENDBLOCK, NoOp);
|
||||
REGISTER_OP(GUESTOPCODE, GuestOpcode);
|
||||
REGISTER_OP(FENCE, Fence);
|
||||
REGISTER_OP(BREAK, Break);
|
||||
REGISTER_OP(PHI, NoOp);
|
||||
REGISTER_OP(PHIVALUE, NoOp);
|
||||
REGISTER_OP(PRINT, Print);
|
||||
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
|
||||
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
|
||||
REGISTER_OP(INVALIDATEFLAGS, NoOp);
|
||||
REGISTER_OP(PROCESSORID, ProcessorID);
|
||||
REGISTER_OP(RDRAND, RDRAND);
|
||||
REGISTER_OP(YIELD, Yield);
|
||||
|
||||
// Move ops
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
|
||||
// Vector ops
|
||||
REGISTER_OP(VECTORZERO, VectorZero);
|
||||
REGISTER_OP(VECTORIMM, VectorImm);
|
||||
REGISTER_OP(VMOV, VMov);
|
||||
REGISTER_OP(VAND, VAnd);
|
||||
REGISTER_OP(VBIC, VBic);
|
||||
REGISTER_OP(VOR, VOr);
|
||||
REGISTER_OP(VXOR, VXor);
|
||||
REGISTER_OP(VADD, VAdd);
|
||||
REGISTER_OP(VSUB, VSub);
|
||||
REGISTER_OP(VUQADD, VUQAdd);
|
||||
REGISTER_OP(VUQSUB, VUQSub);
|
||||
REGISTER_OP(VSQADD, VSQAdd);
|
||||
REGISTER_OP(VSQSUB, VSQSub);
|
||||
REGISTER_OP(VADDP, VAddP);
|
||||
REGISTER_OP(VADDV, VAddV);
|
||||
REGISTER_OP(VUMINV, VUMinV);
|
||||
REGISTER_OP(VURAVG, VURAvg);
|
||||
REGISTER_OP(VABS, VAbs);
|
||||
REGISTER_OP(VPOPCOUNT, VPopcount);
|
||||
REGISTER_OP(VFADD, VFAdd);
|
||||
REGISTER_OP(VFADDP, VFAddP);
|
||||
REGISTER_OP(VFSUB, VFSub);
|
||||
REGISTER_OP(VFMUL, VFMul);
|
||||
REGISTER_OP(VFDIV, VFDiv);
|
||||
REGISTER_OP(VFMIN, VFMin);
|
||||
REGISTER_OP(VFMAX, VFMax);
|
||||
REGISTER_OP(VFRECP, VFRecp);
|
||||
REGISTER_OP(VFSQRT, VFSqrt);
|
||||
REGISTER_OP(VFRSQRT, VFRSqrt);
|
||||
REGISTER_OP(VNEG, VNeg);
|
||||
REGISTER_OP(VFNEG, VFNeg);
|
||||
REGISTER_OP(VNOT, VNot);
|
||||
REGISTER_OP(VUMIN, VUMin);
|
||||
REGISTER_OP(VSMIN, VSMin);
|
||||
REGISTER_OP(VUMAX, VUMax);
|
||||
REGISTER_OP(VSMAX, VSMax);
|
||||
REGISTER_OP(VZIP, VZip);
|
||||
REGISTER_OP(VZIP2, VZip2);
|
||||
REGISTER_OP(VUNZIP, VUnZip);
|
||||
REGISTER_OP(VUNZIP2, VUnZip2);
|
||||
REGISTER_OP(VBSL, VBSL);
|
||||
REGISTER_OP(VCMPEQ, VCMPEQ);
|
||||
REGISTER_OP(VCMPEQZ, VCMPEQZ);
|
||||
REGISTER_OP(VCMPGT, VCMPGT);
|
||||
REGISTER_OP(VCMPGTZ, VCMPGTZ);
|
||||
REGISTER_OP(VCMPLTZ, VCMPLTZ);
|
||||
REGISTER_OP(VFCMPEQ, VFCMPEQ);
|
||||
REGISTER_OP(VFCMPNEQ, VFCMPNEQ);
|
||||
REGISTER_OP(VFCMPLT, VFCMPLT);
|
||||
REGISTER_OP(VFCMPGT, VFCMPGT);
|
||||
REGISTER_OP(VFCMPLE, VFCMPLE);
|
||||
REGISTER_OP(VFCMPORD, VFCMPORD);
|
||||
REGISTER_OP(VFCMPUNO, VFCMPUNO);
|
||||
REGISTER_OP(VUSHL, VUShl);
|
||||
REGISTER_OP(VUSHR, VUShr);
|
||||
REGISTER_OP(VSSHR, VSShr);
|
||||
REGISTER_OP(VUSHLS, VUShlS);
|
||||
REGISTER_OP(VUSHRS, VUShrS);
|
||||
REGISTER_OP(VSSHRS, VSShrS);
|
||||
REGISTER_OP(VINSELEMENT, VInsElement);
|
||||
REGISTER_OP(VDUPELEMENT, VDupElement);
|
||||
REGISTER_OP(VEXTR, VExtr);
|
||||
REGISTER_OP(VUSHRI, VUShrI);
|
||||
REGISTER_OP(VSSHRI, VSShrI);
|
||||
REGISTER_OP(VSHLI, VShlI);
|
||||
REGISTER_OP(VUSHRNI, VUShrNI);
|
||||
REGISTER_OP(VUSHRNI2, VUShrNI2);
|
||||
REGISTER_OP(VSXTL, VSXTL);
|
||||
REGISTER_OP(VSXTL2, VSXTL2);
|
||||
REGISTER_OP(VUXTL, VUXTL);
|
||||
REGISTER_OP(VUXTL2, VUXTL2);
|
||||
REGISTER_OP(VSQXTN, VSQXTN);
|
||||
REGISTER_OP(VSQXTN2, VSQXTN2);
|
||||
REGISTER_OP(VSQXTUN, VSQXTUN);
|
||||
REGISTER_OP(VSQXTUN2, VSQXTUN2);
|
||||
REGISTER_OP(VUMUL, VMul);
|
||||
REGISTER_OP(VSMUL, VMul);
|
||||
REGISTER_OP(VUMULL, VUMull);
|
||||
REGISTER_OP(VSMULL, VSMull);
|
||||
REGISTER_OP(VUMULL2, VUMull2);
|
||||
REGISTER_OP(VSMULL2, VSMull2);
|
||||
REGISTER_OP(VUABDL, VUABDL);
|
||||
REGISTER_OP(VTBL1, VTBL1);
|
||||
REGISTER_OP(VREV64, VRev64);
|
||||
#undef REGISTER_OP
|
||||
|
||||
default:
|
||||
Op_Unhandled(IROp, ID);
|
||||
break;
|
||||
}
|
||||
// Execute handler
|
||||
OpHandler Handler = OpHandlers[IROp->Op];
|
||||
(this->*Handler)(IROp, ID);
|
||||
}
|
||||
|
||||
if (DebugData) {
|
||||
|
||||
+12
-1
@@ -235,6 +235,18 @@ private:
|
||||
*/
|
||||
uint8_t *GuestEntry{};
|
||||
|
||||
using OpHandler = void (Arm64JITCore::*)(IR::IROp_Header const *IROp, IR::NodeID Node);
|
||||
std::array<OpHandler, IR::IROps::OP_LAST + 1> OpHandlers {};
|
||||
void RegisterALUHandlers();
|
||||
void RegisterAtomicHandlers();
|
||||
void RegisterBranchHandlers();
|
||||
void RegisterConversionHandlers();
|
||||
void RegisterFlagHandlers();
|
||||
void RegisterMemoryHandlers();
|
||||
void RegisterMiscHandlers();
|
||||
void RegisterMoveHandlers();
|
||||
void RegisterVectorHandlers();
|
||||
void RegisterEncryptionHandlers();
|
||||
#define DEF_OP(x) void Op_##x(IR::IROp_Header const *IROp, IR::NodeID Node)
|
||||
|
||||
///< Unhandled handler
|
||||
@@ -356,7 +368,6 @@ private:
|
||||
DEF_OP(ParanoidLoadMemTSO);
|
||||
DEF_OP(ParanoidStoreMemTSO);
|
||||
DEF_OP(CacheLineClear);
|
||||
DEF_OP(CacheLineClean);
|
||||
DEF_OP(CacheLineZero);
|
||||
|
||||
///< Misc ops
|
||||
|
||||
+42
-117
@@ -703,43 +703,19 @@ DEF_OP(SpillRegister) {
|
||||
const auto Src = GetReg(Op->Value.ID());
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
if (SlotOffset > 4095) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
strb(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
strb(Src, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
strb(Src, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (SlotOffset > 8190) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
strh(Src, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
strh(Src, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
strh(Src, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
str(Src.W(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
str(Src.W(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
str(Src.X(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
str(Src.X(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -751,33 +727,15 @@ DEF_OP(SpillRegister) {
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
str(Src.S(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
str(Src.S(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
str(Src.D(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
str(Src.D(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
if (SlotOffset > 65520) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
str(Src.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
str(Src.Q(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
str(Src.Q(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
@@ -803,43 +761,19 @@ DEF_OP(FillRegister) {
|
||||
const auto Dst = GetReg(Node);
|
||||
switch (OpSize) {
|
||||
case 1: {
|
||||
if (SlotOffset > 4095) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldrb(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
ldrb(Dst, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
ldrb(Dst, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 2: {
|
||||
if (SlotOffset > 8190) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldrh(Dst, ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
ldrh(Dst, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
ldrh(Dst, ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.W(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
ldr(Dst.W(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
ldr(Dst.W(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.X(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
ldr(Dst.X(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
ldr(Dst.X(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -851,33 +785,15 @@ DEF_OP(FillRegister) {
|
||||
|
||||
switch (OpSize) {
|
||||
case 4: {
|
||||
if (SlotOffset > 16380) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.S(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
ldr(Dst.S(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
ldr(Dst.S(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 8: {
|
||||
if (SlotOffset > 32760) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.D(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
ldr(Dst.D(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
ldr(Dst.D(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 16: {
|
||||
if (SlotOffset > 65520) {
|
||||
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, SlotOffset);
|
||||
ldr(Dst.Q(), ARMEmitter::Reg::rsp, TMP1.R(), ARMEmitter::ExtendedType::LSL_64, 0);
|
||||
}
|
||||
else {
|
||||
ldr(Dst.Q(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
}
|
||||
ldr(Dst.Q(), ARMEmitter::Reg::rsp, SlotOffset);
|
||||
break;
|
||||
}
|
||||
case 32: {
|
||||
@@ -1493,27 +1409,10 @@ DEF_OP(CacheLineClear) {
|
||||
// icache doesn't matter here since the guest application shouldn't be calling clflush on JIT code.
|
||||
mov(TMP1, MemReg.X());
|
||||
for (size_t i = 0; i < std::max(1U, CTX->HostFeatures.DCacheLineSize / 64U); ++i) {
|
||||
dc(ARMEmitter::DataCacheOperation::CIVAC, TMP1);
|
||||
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, CTX->HostFeatures.DCacheLineSize);
|
||||
}
|
||||
|
||||
if (Op->Serialize) {
|
||||
// If requested, serialized all of the data cache operations.
|
||||
dsb(FEXCore::ARMEmitter::BarrierScope::ISH);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClean) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineClean>();
|
||||
|
||||
auto MemReg = GetReg(Op->Addr.ID());
|
||||
|
||||
// Clean dcache only
|
||||
mov(TMP1, MemReg.X());
|
||||
for (size_t i = 0; i < std::max(1U, CTX->HostFeatures.DCacheLineSize / 64U); ++i) {
|
||||
dc(ARMEmitter::DataCacheOperation::CVAC, TMP1);
|
||||
dc(ARMEmitter::DataCacheOperation::CVAU, TMP1);
|
||||
add(ARMEmitter::Size::i64Bit, TMP1, TMP1, CTX->HostFeatures.DCacheLineSize);
|
||||
}
|
||||
dsb(FEXCore::ARMEmitter::BarrierScope::ISH);
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineZero) {
|
||||
@@ -1539,5 +1438,31 @@ DEF_OP(CacheLineZero) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterMemoryHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(LOADCONTEXT, LoadContext);
|
||||
REGISTER_OP(STORECONTEXT, StoreContext);
|
||||
REGISTER_OP(LOADREGISTER, LoadRegister);
|
||||
REGISTER_OP(STOREREGISTER, StoreRegister);
|
||||
REGISTER_OP(LOADCONTEXTINDEXED, LoadContextIndexed);
|
||||
REGISTER_OP(STORECONTEXTINDEXED, StoreContextIndexed);
|
||||
REGISTER_OP(SPILLREGISTER, SpillRegister);
|
||||
REGISTER_OP(FILLREGISTER, FillRegister);
|
||||
REGISTER_OP(LOADFLAG, LoadFlag);
|
||||
REGISTER_OP(STOREFLAG, StoreFlag);
|
||||
REGISTER_OP(LOADMEM, LoadMem);
|
||||
REGISTER_OP(STOREMEM, StoreMem);
|
||||
if (ParanoidTSO()) {
|
||||
REGISTER_OP(LOADMEMTSO, ParanoidLoadMemTSO);
|
||||
REGISTER_OP(STOREMEMTSO, ParanoidStoreMemTSO);
|
||||
}
|
||||
else {
|
||||
REGISTER_OP(LOADMEMTSO, LoadMemTSO);
|
||||
REGISTER_OP(STOREMEMTSO, StoreMemTSO);
|
||||
}
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
REGISTER_OP(CACHELINEZERO, CacheLineZero);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,5 +231,27 @@ DEF_OP(Yield) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterMiscHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(DUMMY, NoOp);
|
||||
REGISTER_OP(IRHEADER, NoOp);
|
||||
REGISTER_OP(CODEBLOCK, NoOp);
|
||||
REGISTER_OP(BEGINBLOCK, NoOp);
|
||||
REGISTER_OP(ENDBLOCK, NoOp);
|
||||
REGISTER_OP(GUESTOPCODE, GuestOpcode);
|
||||
REGISTER_OP(FENCE, Fence);
|
||||
REGISTER_OP(BREAK, Break);
|
||||
REGISTER_OP(PHI, NoOp);
|
||||
REGISTER_OP(PHIVALUE, NoOp);
|
||||
REGISTER_OP(PRINT, Print);
|
||||
REGISTER_OP(GETROUNDINGMODE, GetRoundingMode);
|
||||
REGISTER_OP(SETROUNDINGMODE, SetRoundingMode);
|
||||
REGISTER_OP(INVALIDATEFLAGS, NoOp);
|
||||
REGISTER_OP(PROCESSORID, ProcessorID);
|
||||
REGISTER_OP(RDRAND, RDRAND);
|
||||
REGISTER_OP(YIELD, Yield);
|
||||
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,5 +42,11 @@ DEF_OP(CreateElementPair) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterMoveHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(EXTRACTELEMENTPAIR, ExtractElementPair);
|
||||
REGISTER_OP(CREATEELEMENTPAIR, CreateElementPair);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
+89
-38
@@ -2020,44 +2020,7 @@ DEF_OP(VUShr) {
|
||||
}
|
||||
|
||||
DEF_OP(VSShr) {
|
||||
const auto Op = IROp->C<IR::IROp_VSShr>();
|
||||
const auto OpSize = IROp->Size;
|
||||
|
||||
const auto ElementSize = IROp->ElementSize;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
const auto MaxShift = (ElementSize * 8) - 1;
|
||||
|
||||
const auto Dst = GetVReg(Node);
|
||||
const auto ShiftVector = GetVReg(Op->ShiftVector.ID());
|
||||
const auto Vector = GetVReg(Op->Vector.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) {
|
||||
const auto Mask = PRED_TMP_32B.Merging();
|
||||
|
||||
dup_imm(SubRegSize, VTMP2.Z(), MaxShift);
|
||||
umin(SubRegSize, VTMP2.Z(), Mask, VTMP2.Z(), ShiftVector.Z());
|
||||
|
||||
movprfx(VTMP1.Z(), Vector.Z());
|
||||
asr(SubRegSize, VTMP1.Z(), Mask, VTMP1.Z(), VTMP2.Z());
|
||||
mov(Dst.Z(), VTMP1.Z());
|
||||
} else {
|
||||
LOGMAN_THROW_AA_FMT(ElementSize != 8, "Adv. SIMD UMIN doesn't handle 64-bit values");
|
||||
|
||||
movi(SubRegSize, VTMP1.Q(), MaxShift);
|
||||
umin(SubRegSize, VTMP1.Q(), VTMP1.Q(), ShiftVector.Q());
|
||||
|
||||
// Need to invert shift values to perform a right shift with SSHL
|
||||
// (SSHR only has an immediate variant).
|
||||
neg(SubRegSize, VTMP1.Q(), VTMP1.Q());
|
||||
sshl(SubRegSize, Dst.Q(), Vector.Q(), VTMP1.Q());
|
||||
}
|
||||
LOGMAN_MSG_A_FMT("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VUShlS) {
|
||||
@@ -2972,5 +2935,93 @@ DEF_OP(VRev64) {
|
||||
}
|
||||
|
||||
#undef DEF_OP
|
||||
void Arm64JITCore::RegisterVectorHandlers() {
|
||||
#define REGISTER_OP(op, x) OpHandlers[FEXCore::IR::IROps::OP_##op] = &Arm64JITCore::Op_##x
|
||||
REGISTER_OP(VECTORZERO, VectorZero);
|
||||
REGISTER_OP(VECTORIMM, VectorImm);
|
||||
REGISTER_OP(VMOV, VMov);
|
||||
REGISTER_OP(VAND, VAnd);
|
||||
REGISTER_OP(VBIC, VBic);
|
||||
REGISTER_OP(VOR, VOr);
|
||||
REGISTER_OP(VXOR, VXor);
|
||||
REGISTER_OP(VADD, VAdd);
|
||||
REGISTER_OP(VSUB, VSub);
|
||||
REGISTER_OP(VUQADD, VUQAdd);
|
||||
REGISTER_OP(VUQSUB, VUQSub);
|
||||
REGISTER_OP(VSQADD, VSQAdd);
|
||||
REGISTER_OP(VSQSUB, VSQSub);
|
||||
REGISTER_OP(VADDP, VAddP);
|
||||
REGISTER_OP(VADDV, VAddV);
|
||||
REGISTER_OP(VUMINV, VUMinV);
|
||||
REGISTER_OP(VURAVG, VURAvg);
|
||||
REGISTER_OP(VABS, VAbs);
|
||||
REGISTER_OP(VPOPCOUNT, VPopcount);
|
||||
REGISTER_OP(VFADD, VFAdd);
|
||||
REGISTER_OP(VFADDP, VFAddP);
|
||||
REGISTER_OP(VFSUB, VFSub);
|
||||
REGISTER_OP(VFMUL, VFMul);
|
||||
REGISTER_OP(VFDIV, VFDiv);
|
||||
REGISTER_OP(VFMIN, VFMin);
|
||||
REGISTER_OP(VFMAX, VFMax);
|
||||
REGISTER_OP(VFRECP, VFRecp);
|
||||
REGISTER_OP(VFSQRT, VFSqrt);
|
||||
REGISTER_OP(VFRSQRT, VFRSqrt);
|
||||
REGISTER_OP(VNEG, VNeg);
|
||||
REGISTER_OP(VFNEG, VFNeg);
|
||||
REGISTER_OP(VNOT, VNot);
|
||||
REGISTER_OP(VUMIN, VUMin);
|
||||
REGISTER_OP(VSMIN, VSMin);
|
||||
REGISTER_OP(VUMAX, VUMax);
|
||||
REGISTER_OP(VSMAX, VSMax);
|
||||
REGISTER_OP(VZIP, VZip);
|
||||
REGISTER_OP(VZIP2, VZip2);
|
||||
REGISTER_OP(VUNZIP, VUnZip);
|
||||
REGISTER_OP(VUNZIP2, VUnZip2);
|
||||
REGISTER_OP(VBSL, VBSL);
|
||||
REGISTER_OP(VCMPEQ, VCMPEQ);
|
||||
REGISTER_OP(VCMPEQZ, VCMPEQZ);
|
||||
REGISTER_OP(VCMPGT, VCMPGT);
|
||||
REGISTER_OP(VCMPGTZ, VCMPGTZ);
|
||||
REGISTER_OP(VCMPLTZ, VCMPLTZ);
|
||||
REGISTER_OP(VFCMPEQ, VFCMPEQ);
|
||||
REGISTER_OP(VFCMPNEQ, VFCMPNEQ);
|
||||
REGISTER_OP(VFCMPLT, VFCMPLT);
|
||||
REGISTER_OP(VFCMPGT, VFCMPGT);
|
||||
REGISTER_OP(VFCMPLE, VFCMPLE);
|
||||
REGISTER_OP(VFCMPORD, VFCMPORD);
|
||||
REGISTER_OP(VFCMPUNO, VFCMPUNO);
|
||||
REGISTER_OP(VUSHL, VUShl);
|
||||
REGISTER_OP(VUSHR, VUShr);
|
||||
REGISTER_OP(VSSHR, VSShr);
|
||||
REGISTER_OP(VUSHLS, VUShlS);
|
||||
REGISTER_OP(VUSHRS, VUShrS);
|
||||
REGISTER_OP(VSSHRS, VSShrS);
|
||||
REGISTER_OP(VINSELEMENT, VInsElement);
|
||||
REGISTER_OP(VDUPELEMENT, VDupElement);
|
||||
REGISTER_OP(VEXTR, VExtr);
|
||||
REGISTER_OP(VUSHRI, VUShrI);
|
||||
REGISTER_OP(VSSHRI, VSShrI);
|
||||
REGISTER_OP(VSHLI, VShlI);
|
||||
REGISTER_OP(VUSHRNI, VUShrNI);
|
||||
REGISTER_OP(VUSHRNI2, VUShrNI2);
|
||||
REGISTER_OP(VSXTL, VSXTL);
|
||||
REGISTER_OP(VSXTL2, VSXTL2);
|
||||
REGISTER_OP(VUXTL, VUXTL);
|
||||
REGISTER_OP(VUXTL2, VUXTL2);
|
||||
REGISTER_OP(VSQXTN, VSQXTN);
|
||||
REGISTER_OP(VSQXTN2, VSQXTN2);
|
||||
REGISTER_OP(VSQXTUN, VSQXTUN);
|
||||
REGISTER_OP(VSQXTUN2, VSQXTUN2);
|
||||
REGISTER_OP(VUMUL, VMul);
|
||||
REGISTER_OP(VSMUL, VMul);
|
||||
REGISTER_OP(VUMULL, VUMull);
|
||||
REGISTER_OP(VSMULL, VSMull);
|
||||
REGISTER_OP(VUMULL2, VUMull2);
|
||||
REGISTER_OP(VSMULL2, VSMull2);
|
||||
REGISTER_OP(VUABDL, VUABDL);
|
||||
REGISTER_OP(VTBL1, VTBL1);
|
||||
REGISTER_OP(VREV64, VRev64);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,19 +31,12 @@ 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)]);
|
||||
}
|
||||
jmp(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.SignalReturnHandler)]);
|
||||
}
|
||||
|
||||
DEF_OP(CallbackReturn) {
|
||||
|
||||
@@ -147,12 +147,7 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
case FABI_F80_I32: {
|
||||
PushRegs();
|
||||
|
||||
if (Info.ABI == FABI_F80_I16) {
|
||||
movsx(rdi, GetSrc<RA_32>(IROp->Args[0].ID()).cvt16());
|
||||
}
|
||||
else {
|
||||
mov(edi, GetSrc<RA_32>(IROp->Args[0].ID()));
|
||||
}
|
||||
mov(edi, GetSrc<RA_32>(IROp->Args[0].ID()));
|
||||
call(qword [STATE + offsetof(FEXCore::Core::CpuStateFrame, Pointers.Common.FallbackHandlerPointers[Info.HandlerIndex])]);
|
||||
|
||||
PopRegs();
|
||||
@@ -228,7 +223,7 @@ void X86JITCore::Op_Unhandled(IR::IROp_Header *IROp, IR::NodeID Node) {
|
||||
|
||||
PopRegs();
|
||||
|
||||
movsx(GetDst<RA_64>(Node), ax);
|
||||
movzx(GetDst<RA_64>(Node), ax);
|
||||
}
|
||||
break;
|
||||
case FABI_I32_F80:{
|
||||
|
||||
@@ -348,7 +348,6 @@ private:
|
||||
DEF_OP(LoadMem);
|
||||
DEF_OP(StoreMem);
|
||||
DEF_OP(CacheLineClear);
|
||||
DEF_OP(CacheLineClean);
|
||||
DEF_OP(CacheLineZero);
|
||||
|
||||
///< Misc ops
|
||||
|
||||
@@ -771,19 +771,7 @@ DEF_OP(CacheLineClear) {
|
||||
|
||||
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
|
||||
|
||||
if (Op->Serialize) {
|
||||
clflush(ptr [MemReg]);
|
||||
}
|
||||
else {
|
||||
clflushopt(ptr [MemReg]);
|
||||
}
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineClean) {
|
||||
auto Op = IROp->C<IR::IROp_CacheLineClean>();
|
||||
|
||||
Xbyak::Reg MemReg = GetSrc<RA_64>(Op->Addr.ID());
|
||||
clwb(ptr [MemReg]);
|
||||
clflush(ptr [MemReg]);
|
||||
}
|
||||
|
||||
DEF_OP(CacheLineZero) {
|
||||
@@ -821,7 +809,6 @@ void X86JITCore::RegisterMemoryHandlers() {
|
||||
REGISTER_OP(LOADMEMTSO, LoadMem);
|
||||
REGISTER_OP(STOREMEMTSO, StoreMem);
|
||||
REGISTER_OP(CACHELINECLEAR, CacheLineClear);
|
||||
REGISTER_OP(CACHELINECLEAN, CacheLineClean);
|
||||
REGISTER_OP(CACHELINEZERO, CacheLineZero);
|
||||
#undef REGISTER_OP
|
||||
}
|
||||
|
||||
@@ -2549,22 +2549,7 @@ DEF_OP(VUShr) {
|
||||
}
|
||||
|
||||
DEF_OP(VSShr) {
|
||||
const auto Op = IROp->C<IR::IROp_VSShr>();
|
||||
const auto OpSize = IROp->Size;
|
||||
const auto Is256Bit = OpSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
const auto ElementSize = IROp->ElementSize;
|
||||
LOGMAN_THROW_AA_FMT(ElementSize == 4, "VSShr only supports 32-bit elements");
|
||||
|
||||
const auto Dst = GetDst(Node);
|
||||
const auto ShiftVector = GetSrc(Op->ShiftVector.ID());
|
||||
const auto Vector = GetSrc(Op->Vector.ID());
|
||||
|
||||
if (Is256Bit) {
|
||||
vpsravd(ToYMM(Dst), ToYMM(Vector), ToYMM(ShiftVector));
|
||||
} else {
|
||||
vpsravd(Dst, Vector, ShiftVector);
|
||||
}
|
||||
LOGMAN_MSG_A_FMT("Unimplemented");
|
||||
}
|
||||
|
||||
DEF_OP(VUShlS) {
|
||||
|
||||
+54
-80
@@ -278,11 +278,9 @@ void OpDispatchBuilder::IRETOp(OpcodeArgs) {
|
||||
}
|
||||
|
||||
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);
|
||||
_SignalReturn();
|
||||
auto NewRIP = _LoadContext(GPRSize, GPRClass, offsetof(FEXCore::Core::CPUState, rip));
|
||||
// This ExitFunction won't actually get hit but needs to exist
|
||||
_ExitFunction(NewRIP);
|
||||
@@ -1615,17 +1613,17 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource_WithOpSize(GPRClass, Op, Op->Src[0], 2, Op->Flags, -1);
|
||||
|
||||
switch (Op->Dest.Data.GPR.GPR) {
|
||||
case FEXCore::X86State::REG_RAX: // ES
|
||||
case 0: // ES
|
||||
case FEXCore::X86State::REG_R8: // ES
|
||||
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, es_idx));
|
||||
UpdatePrefixFromSegment(Src, FEXCore::X86Tables::DecodeFlags::FLAG_ES_PREFIX);
|
||||
break;
|
||||
case FEXCore::X86State::REG_RBX: // DS
|
||||
case 1: // DS
|
||||
case FEXCore::X86State::REG_R11: // DS
|
||||
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, ds_idx));
|
||||
UpdatePrefixFromSegment(Src, FEXCore::X86Tables::DecodeFlags::FLAG_DS_PREFIX);
|
||||
break;
|
||||
case FEXCore::X86State::REG_RCX: // CS
|
||||
case 2: // CS
|
||||
case FEXCore::X86State::REG_R9: // CS
|
||||
// CPL3 can't write to this
|
||||
_Break(FEXCore::IR::BreakDefinition {
|
||||
@@ -1635,12 +1633,12 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
|
||||
.si_code = 0,
|
||||
});
|
||||
break;
|
||||
case FEXCore::X86State::REG_RDX: // SS
|
||||
case 3: // SS
|
||||
case FEXCore::X86State::REG_R10: // SS
|
||||
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, ss_idx));
|
||||
UpdatePrefixFromSegment(Src, FEXCore::X86Tables::DecodeFlags::FLAG_SS_PREFIX);
|
||||
break;
|
||||
case FEXCore::X86State::REG_RBP: // GS
|
||||
case 6: // GS
|
||||
case FEXCore::X86State::REG_R13: // GS
|
||||
if (!CTX->Config.Is64BitMode) {
|
||||
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, gs_idx));
|
||||
@@ -1650,7 +1648,7 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
|
||||
DecodeFailure = true;
|
||||
}
|
||||
break;
|
||||
case FEXCore::X86State::REG_RSP: // FS
|
||||
case 7: // FS
|
||||
case FEXCore::X86State::REG_R12: // FS
|
||||
if (!CTX->Config.Is64BitMode) {
|
||||
_StoreContext(2, GPRClass, Src, offsetof(FEXCore::Core::CPUState, fs_idx));
|
||||
@@ -1670,23 +1668,23 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
|
||||
OrderedNode *Segment{};
|
||||
|
||||
switch (Op->Src[0].Data.GPR.GPR) {
|
||||
case FEXCore::X86State::REG_RAX: // ES
|
||||
case 0: // ES
|
||||
case FEXCore::X86State::REG_R8: // ES
|
||||
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, es_idx));
|
||||
break;
|
||||
case FEXCore::X86State::REG_RBX: // DS
|
||||
case 1: // DS
|
||||
case FEXCore::X86State::REG_R11: // DS
|
||||
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, ds_idx));
|
||||
break;
|
||||
case FEXCore::X86State::REG_RCX: // CS
|
||||
case 2: // CS
|
||||
case FEXCore::X86State::REG_R9: // CS
|
||||
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, cs_idx));
|
||||
break;
|
||||
case FEXCore::X86State::REG_RDX: // SS
|
||||
case 3: // SS
|
||||
case FEXCore::X86State::REG_R10: // SS
|
||||
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, ss_idx));
|
||||
break;
|
||||
case FEXCore::X86State::REG_RBP: // GS
|
||||
case 6: // GS
|
||||
case FEXCore::X86State::REG_R13: // GS
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
Segment = _Constant(0);
|
||||
@@ -1695,7 +1693,7 @@ void OpDispatchBuilder::MOVSegOp(OpcodeArgs) {
|
||||
Segment = _LoadContext(2, GPRClass, offsetof(FEXCore::Core::CPUState, gs_idx));
|
||||
}
|
||||
break;
|
||||
case FEXCore::X86State::REG_RSP: // FS
|
||||
case 7: // FS
|
||||
case FEXCore::X86State::REG_R12: // FS
|
||||
if (CTX->Config.Is64BitMode) {
|
||||
Segment = _Constant(0);
|
||||
@@ -4961,8 +4959,12 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
|
||||
Core::CPUState::XMM_AVX_REG_SIZE :
|
||||
Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
|
||||
const auto VectorOffset = CTX->HostFeatures.SupportsAVX ?
|
||||
offsetof(Core::CPUState, xmm.avx.data[gprIndex][0]) :
|
||||
offsetof(Core::CPUState, xmm.sse.data[gprIndex][0]);
|
||||
|
||||
// Load the full register size if it is a XMM register source.
|
||||
Src = LoadXMMRegister(gprIndex);
|
||||
Src = _LoadRegister(false, VectorOffset, FPRClass, FPRFixedClass, regSize);
|
||||
|
||||
// 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.
|
||||
@@ -4980,11 +4982,11 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
|
||||
}
|
||||
}
|
||||
else {
|
||||
Src = LoadGPRRegister(gpr, OpSize, highIndex ? 8 : 0);
|
||||
Src = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[gpr]) + (highIndex ? 1 : 0), GPRClass, GPRFixedClass, OpSize);
|
||||
}
|
||||
}
|
||||
else if (Operand.IsGPRDirect()) {
|
||||
Src = LoadGPRRegister(Operand.Data.GPR.GPR, GPRSize);
|
||||
Src = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPR.GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
LoadableType = true;
|
||||
if (Operand.Data.GPR.GPR == FEXCore::X86State::REG_RSP && AccessType == MemoryAccessType::ACCESS_DEFAULT) {
|
||||
@@ -4992,7 +4994,7 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
|
||||
}
|
||||
}
|
||||
else if (Operand.IsGPRIndirect()) {
|
||||
auto GPR = LoadGPRRegister(Operand.Data.GPRIndirect.GPR, GPRSize);
|
||||
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPRIndirect.GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
auto Constant = _Constant(GPRSize * 8, Operand.Data.GPRIndirect.Displacement);
|
||||
|
||||
@@ -5017,7 +5019,7 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
|
||||
else if (Operand.IsSIB()) {
|
||||
OrderedNode *Tmp {};
|
||||
if (Operand.Data.SIB.Index != FEXCore::X86State::REG_INVALID) {
|
||||
Tmp = LoadGPRRegister(Operand.Data.SIB.Index, GPRSize);
|
||||
Tmp = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Index]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
if (Operand.Data.SIB.Scale != 1) {
|
||||
auto Constant = _Constant(GPRSize * 8, Operand.Data.SIB.Scale);
|
||||
@@ -5029,7 +5031,7 @@ OrderedNode *OpDispatchBuilder::LoadSource_WithOpSize(FEXCore::IR::RegisterClass
|
||||
}
|
||||
|
||||
if (Operand.Data.SIB.Base != FEXCore::X86State::REG_INVALID) {
|
||||
auto GPR = LoadGPRRegister(Operand.Data.SIB.Base, GPRSize);
|
||||
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Base]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
if (Tmp != nullptr) {
|
||||
Tmp = _Add(Tmp, GPR);
|
||||
@@ -5094,12 +5096,9 @@ OrderedNode *OpDispatchBuilder::GetRelocatedPC(FEXCore::X86Tables::DecodedOp con
|
||||
|
||||
OrderedNode *OpDispatchBuilder::LoadGPRRegister(uint32_t GPR, int8_t Size, uint8_t Offset) {
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
if (Size == -1) {
|
||||
Size = GPRSize;
|
||||
}
|
||||
OrderedNode *Reg = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
if (Size != GPRSize || Offset != 0) {
|
||||
if (Size != -1 || Offset != 0) {
|
||||
// Extract the subregister if requested.
|
||||
Reg = _Bfe(Size, Size * 8, Offset, Reg);
|
||||
}
|
||||
@@ -5118,18 +5117,15 @@ OrderedNode *OpDispatchBuilder::LoadXMMRegister(uint32_t XMM) {
|
||||
|
||||
void OpDispatchBuilder::StoreGPRRegister(uint32_t GPR, OrderedNode *const Src, int8_t Size, uint8_t Offset) {
|
||||
const uint8_t GPRSize = CTX->GetGPRSize();
|
||||
if (Size == -1) {
|
||||
Size = GPRSize;
|
||||
}
|
||||
|
||||
OrderedNode *Reg = Src;
|
||||
if (Size != GPRSize || Offset != 0) {
|
||||
if (Size != -1 || Offset != 0) {
|
||||
// Need to do an insert if not automatic size or zero offset.
|
||||
Reg = LoadGPRRegister(GPR);
|
||||
OrderedNode *Reg = LoadGPRRegister(GPR);
|
||||
Reg = _Bfi(GPRSize, Size * 8, Offset, Reg, Src);
|
||||
_StoreRegister(Reg, false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
}
|
||||
else {
|
||||
_StoreRegister(Src, false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
}
|
||||
|
||||
_StoreRegister(Reg, false, offsetof(FEXCore::Core::CPUState, gregs[GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::StoreXMMRegister(uint32_t XMM, OrderedNode *const Src) {
|
||||
@@ -5176,36 +5172,42 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
const auto highIndex = Operand.Data.GPR.HighBits ? 1 : 0;
|
||||
|
||||
const auto VectorSize = CTX->HostFeatures.SupportsAVX ? 32 : 16;
|
||||
const auto VectorOffset = CTX->HostFeatures.SupportsAVX ?
|
||||
offsetof(Core::CPUState, xmm.avx.data[gprIndex][highIndex]) :
|
||||
offsetof(Core::CPUState, xmm.sse.data[gprIndex][highIndex]);
|
||||
|
||||
auto Result = Src;
|
||||
if (highIndex || OpSize != VectorSize) {
|
||||
auto InsertResult = Src;
|
||||
// Partial writes can come from GPR or FPR.
|
||||
// TODO: Fix the instructions doing partial writes rather than dealing with it here.
|
||||
auto SrcVector = LoadXMMRegister(gprIndex);
|
||||
auto SrcVector = _LoadRegister(false, VectorOffset, FPRClass, FPRFixedClass, OpSize);
|
||||
|
||||
if (Class == IR::GPRClass) {
|
||||
Result = _VInsGPR(VectorSize, OpSize, highIndex, SrcVector, Src);
|
||||
InsertResult = _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);
|
||||
InsertResult = _VMov(OpSize, Src);
|
||||
}
|
||||
else {
|
||||
Result = _VInsElement(VectorSize, OpSize, highIndex, 0, SrcVector, Src);
|
||||
InsertResult = _VInsElement(VectorSize, OpSize, highIndex, 0, SrcVector, Src);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StoreXMMRegister(gprIndex, Result);
|
||||
_StoreRegister(InsertResult, false, VectorOffset, FPRClass, FPRFixedClass, VectorSize);
|
||||
}
|
||||
else {
|
||||
_StoreRegister(Src, false, VectorOffset, FPRClass, FPRFixedClass, VectorSize);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (GPRSize == 8 && OpSize == 4) {
|
||||
// If the Source IR op is 64 bits, we need to zext the upper bits
|
||||
// For all other sizes, the upper bits are guaranteed to already be zero
|
||||
OrderedNode *Value = GetOpSize(Src) == 8 ? _Bfe(4, 32, 0, Src) : Src;
|
||||
StoreGPRRegister(gpr, Value, GPRSize);
|
||||
_StoreRegister(Value, false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
LOGMAN_THROW_AA_FMT(!Operand.Data.GPR.HighBits, "Can't handle 32bit store to high 8bit register");
|
||||
}
|
||||
@@ -5218,24 +5220,25 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
// mov al, 2 ; Move in to lower 8-bits.
|
||||
// mov ah, 2 ; Move in to upper 8-bits of 16-bit reg.
|
||||
// mov ax, 2 ; Move in to lower 16-bits of reg.
|
||||
StoreGPRRegister(gpr, Src, OpSize, Operand.Data.GPR.HighBits * 8);
|
||||
auto RegDest = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, GPRSize);
|
||||
auto Result = _Bfi(GPRSize, OpSize * 8, Operand.Data.GPR.HighBits * 8, RegDest, Src);
|
||||
_StoreRegister(Result, false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, GPRSize);
|
||||
}
|
||||
else {
|
||||
StoreGPRRegister(gpr, Src, std::min(GPRSize, OpSize));
|
||||
_StoreRegister(Src, false, offsetof(FEXCore::Core::CPUState, gregs[gpr]), GPRClass, GPRFixedClass, std::min(GPRSize, OpSize));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (Operand.IsGPRDirect()) {
|
||||
MemStoreDst = LoadGPRRegister(Operand.Data.GPR.GPR, GPRSize);
|
||||
|
||||
MemStoreDst = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPR.GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
MemStore = true;
|
||||
if (Operand.Data.GPR.GPR == FEXCore::X86State::REG_RSP && AccessType == MemoryAccessType::ACCESS_DEFAULT) {
|
||||
AccessType = MemoryAccessType::ACCESS_NONTSO;
|
||||
}
|
||||
}
|
||||
else if (Operand.IsGPRIndirect()) {
|
||||
auto GPR = LoadGPRRegister(Operand.Data.GPRIndirect.GPR, GPRSize);
|
||||
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.GPRIndirect.GPR]), GPRClass, GPRFixedClass, GPRSize);
|
||||
auto Constant = _Constant(GPRSize * 8, Operand.Data.GPRIndirect.Displacement);
|
||||
|
||||
MemStoreDst = _Add(GPR, Constant);
|
||||
@@ -5257,7 +5260,7 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
else if (Operand.IsSIB()) {
|
||||
OrderedNode *Tmp {};
|
||||
if (Operand.Data.SIB.Index != FEXCore::X86State::REG_INVALID) {
|
||||
Tmp = LoadGPRRegister(Operand.Data.SIB.Index, GPRSize);
|
||||
Tmp = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Index]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
if (Operand.Data.SIB.Scale != 1) {
|
||||
auto Constant = _Constant(GPRSize * 8, Operand.Data.SIB.Scale);
|
||||
@@ -5266,7 +5269,7 @@ void OpDispatchBuilder::StoreResult_WithOpSize(FEXCore::IR::RegisterClassType Cl
|
||||
}
|
||||
|
||||
if (Operand.Data.SIB.Base != FEXCore::X86State::REG_INVALID) {
|
||||
auto GPR = LoadGPRRegister(Operand.Data.SIB.Base, GPRSize);
|
||||
auto GPR = _LoadRegister(false, offsetof(FEXCore::Core::CPUState, gregs[Operand.Data.SIB.Base]), GPRClass, GPRFixedClass, GPRSize);
|
||||
|
||||
if (Tmp != nullptr) {
|
||||
Tmp = _Add(Tmp, GPR);
|
||||
@@ -5620,29 +5623,6 @@ void OpDispatchBuilder::FenceOp(OpcodeArgs) {
|
||||
_Fence({FenceType});
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CLWB(OpcodeArgs) {
|
||||
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
|
||||
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
|
||||
_CacheLineClean(DestMem);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::CLFLUSHOPT(OpcodeArgs) {
|
||||
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
|
||||
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
|
||||
_CacheLineClear(DestMem, false);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::MemFenceOrXSAVEOPT(OpcodeArgs) {
|
||||
if (Op->ModRM == 0xF0) {
|
||||
// 0xF0 is MFENCE
|
||||
_Fence(FEXCore::IR::Fence_LoadStore);
|
||||
}
|
||||
else {
|
||||
LogMan::Msg::EFmt("Application tried using XSAVEOPT");
|
||||
UnimplementedOp(Op);
|
||||
}
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::StoreFenceOrCLFlush(OpcodeArgs) {
|
||||
if (Op->ModRM == 0xF8) {
|
||||
// 0xF8 is SFENCE
|
||||
@@ -5652,7 +5632,7 @@ void OpDispatchBuilder::StoreFenceOrCLFlush(OpcodeArgs) {
|
||||
// This is a CLFlush
|
||||
OrderedNode *DestMem = LoadSource(GPRClass, Op, Op->Dest, Op->Flags, -1, false);
|
||||
DestMem = AppendSegmentOffset(DestMem, Op->Flags);
|
||||
_CacheLineClear(DestMem, true);
|
||||
_CacheLineClear(DestMem);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6074,7 +6054,6 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
|
||||
{OPD(2, 0b01, 0x40), 1, &OpDispatchBuilder::AVXVectorALUOp<IR::OP_VSMUL, 4>},
|
||||
{OPD(2, 0b01, 0x41), 1, &OpDispatchBuilder::VPHMINPOSUWOp},
|
||||
{OPD(2, 0b01, 0x46), 1, &OpDispatchBuilder::VPSRAVDOp},
|
||||
|
||||
{OPD(2, 0b01, 0x58), 1, &OpDispatchBuilder::VBROADCASTOp<4>},
|
||||
{OPD(2, 0b01, 0x59), 1, &OpDispatchBuilder::VBROADCASTOp<8>},
|
||||
@@ -6091,7 +6070,6 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
|
||||
{OPD(3, 0b01, 0x00), 1, &OpDispatchBuilder::VPERMQOp},
|
||||
{OPD(3, 0b01, 0x01), 1, &OpDispatchBuilder::VPERMQOp},
|
||||
{OPD(3, 0b01, 0x02), 1, &OpDispatchBuilder::VPBLENDDOp},
|
||||
{OPD(3, 0b01, 0x04), 1, &OpDispatchBuilder::VPERMILImmOp<4>},
|
||||
{OPD(3, 0b01, 0x05), 1, &OpDispatchBuilder::VPERMILImmOp<8>},
|
||||
{OPD(3, 0b01, 0x06), 1, &OpDispatchBuilder::VPERM2Op},
|
||||
@@ -6099,7 +6077,6 @@ void OpDispatchBuilder::InstallHostSpecificOpcodeHandlers() {
|
||||
{OPD(3, 0b01, 0x09), 1, &OpDispatchBuilder::AVXVectorRound<8, false>},
|
||||
{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, 0x14), 1, &OpDispatchBuilder::PExtrOp<1>},
|
||||
{OPD(3, 0b01, 0x15), 1, &OpDispatchBuilder::PExtrOp<2>},
|
||||
@@ -6793,15 +6770,12 @@ constexpr uint16_t PF_F2 = 3;
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 2), 1, &OpDispatchBuilder::LDMXCSR},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 3), 1, &OpDispatchBuilder::STMXCSR},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 5), 1, &OpDispatchBuilder::FenceOp<FEXCore::IR::Fence_Load.Val>}, //LFENCE
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 6), 1, &OpDispatchBuilder::MemFenceOrXSAVEOPT}, //MFENCE
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 6), 1, &OpDispatchBuilder::FenceOp<FEXCore::IR::Fence_LoadStore.Val>}, //MFENCE
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_NONE, 7), 1, &OpDispatchBuilder::StoreFenceOrCLFlush}, //SFENCE
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 5), 1, &OpDispatchBuilder::UnimplementedOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_F3, 6), 1, &OpDispatchBuilder::UnimplementedOp},
|
||||
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_66, 6), 1, &OpDispatchBuilder::CLWB},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_15, PF_66, 7), 1, &OpDispatchBuilder::CLFLUSHOPT},
|
||||
|
||||
// GROUP 16
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_NONE, 0), 8, &OpDispatchBuilder::NOPOp},
|
||||
{OPD(FEXCore::X86Tables::TYPE_GROUP_16, PF_F3, 0), 8, &OpDispatchBuilder::NOPOp},
|
||||
|
||||
@@ -467,8 +467,6 @@ public:
|
||||
template <size_t ElementSize>
|
||||
void VPACKUSOp(OpcodeArgs);
|
||||
|
||||
void VPBLENDDOp(OpcodeArgs);
|
||||
|
||||
void VPERM2Op(OpcodeArgs);
|
||||
void VPERMQOp(OpcodeArgs);
|
||||
|
||||
@@ -500,8 +498,6 @@ public:
|
||||
template <size_t ElementSize>
|
||||
void VPSRAIOp(OpcodeArgs);
|
||||
|
||||
void VPSRAVDOp(OpcodeArgs);
|
||||
|
||||
template <size_t ElementSize>
|
||||
void VPSRLDOp(OpcodeArgs);
|
||||
void VPSRLDQOp(OpcodeArgs);
|
||||
@@ -693,9 +689,6 @@ public:
|
||||
template<uint8_t FenceType>
|
||||
void FenceOp(OpcodeArgs);
|
||||
|
||||
void CLWB(OpcodeArgs);
|
||||
void CLFLUSHOPT(OpcodeArgs);
|
||||
void MemFenceOrXSAVEOPT(OpcodeArgs);
|
||||
void StoreFenceOrCLFlush(OpcodeArgs);
|
||||
void CLZeroOp(OpcodeArgs);
|
||||
void RDTSCPOp(OpcodeArgs);
|
||||
|
||||
@@ -1705,20 +1705,6 @@ void OpDispatchBuilder::VPSRAIOp<2>(OpcodeArgs);
|
||||
template
|
||||
void OpDispatchBuilder::VPSRAIOp<4>(OpcodeArgs);
|
||||
|
||||
void OpDispatchBuilder::VPSRAVDOp(OpcodeArgs) {
|
||||
const auto SrcSize = GetSrcSize(Op);
|
||||
const auto Is128Bit = SrcSize == Core::CPUState::XMM_SSE_REG_SIZE;
|
||||
|
||||
OrderedNode *Vector = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *ShiftVector = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
|
||||
OrderedNode *Result = _VSShr(SrcSize, 4, Vector, ShiftVector);
|
||||
if (Is128Bit) {
|
||||
Result = _VMov(16, Result);
|
||||
}
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::MOVDDUPOp(OpcodeArgs) {
|
||||
OrderedNode *Src = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Res = _VDupElement(16, GetSrcSize(Op), Src, 0);
|
||||
@@ -3756,56 +3742,6 @@ void OpDispatchBuilder::VPERMQOp(OpcodeArgs) {
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::VPBLENDDOp(OpcodeArgs) {
|
||||
const auto DstSize = GetDstSize(Op);
|
||||
const auto Is256Bit = DstSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
OrderedNode *Src1 = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
OrderedNode *Src2 = LoadSource(FPRClass, Op, Op->Src[1], Op->Flags, -1);
|
||||
|
||||
LOGMAN_THROW_A_FMT(Op->Src[2].IsLiteral(), "Src[2] needs to be literal here");
|
||||
const auto Selector = Op->Src[2].Data.Literal.Value;
|
||||
|
||||
// Each bit in the selector chooses between Src1 and Src2.
|
||||
// If a bit is set, then we select it's corresponding 32-bit element from Src2
|
||||
// If a bit is not set, then we select it's corresponding 32-bit element from Src1
|
||||
|
||||
// Cases where we can exit out early, since the selector is indicating a copy
|
||||
// of an entire input vector. Unlikely to occur, since it's slower than
|
||||
// just an equivalent vector move instruction. but just in case something
|
||||
// silly is happening, we have your back.
|
||||
|
||||
if (Selector == 0) {
|
||||
OrderedNode *Result = Is256Bit ? Src1 : _VMov(16, Src1);
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
return;
|
||||
}
|
||||
if (Selector == 0xFF && Is256Bit) {
|
||||
StoreResult(FPRClass, Op, Src2, -1);
|
||||
return;
|
||||
}
|
||||
// The only bits we care about from the 8-bit immediate for 128-bit operations
|
||||
// are the first four bits. We do a bitwise check here to catch cases where
|
||||
// silliness is going on and the upper bits are being set even when they'll
|
||||
// be ignored
|
||||
if ((Selector & 0xF) == 0xF && !Is256Bit) {
|
||||
StoreResult(FPRClass, Op, _VMov(16, Src2), -1);
|
||||
return;
|
||||
}
|
||||
|
||||
const std::array Sources{Src1, Src2};
|
||||
|
||||
OrderedNode *Result = _VectorZero(DstSize);
|
||||
const int Num32BitElements = DstSize / 4;
|
||||
for (int i = 0; i < Num32BitElements; i++) {
|
||||
const auto SelectorIndex = (Selector >> i) & 1;
|
||||
|
||||
Result = _VInsElement(DstSize, 4, i, i, Result, Sources[SelectorIndex]);
|
||||
}
|
||||
|
||||
StoreResult(FPRClass, Op, Result, -1);
|
||||
}
|
||||
|
||||
void OpDispatchBuilder::VZEROOp(OpcodeArgs) {
|
||||
const auto DstSize = GetDstSize(Op);
|
||||
const auto IsVZEROALL = DstSize == Core::CPUState::XMM_AVX_REG_SIZE;
|
||||
|
||||
@@ -39,7 +39,7 @@ class OrderedNode;
|
||||
//FST(register to register)
|
||||
|
||||
// State loading duplicated from X87.cpp, setting host rounding mode
|
||||
// See issue
|
||||
// See issue
|
||||
void OpDispatchBuilder::FNINITF64(OpcodeArgs) {
|
||||
// Init FCW to 0x037F
|
||||
auto NewFCW = _Constant(16, 0x037F);
|
||||
@@ -76,7 +76,7 @@ void OpDispatchBuilder::X87LDENVF64(OpcodeArgs) {
|
||||
roundingMode = _And(roundingMode, roundMask);
|
||||
_SetRoundingMode(roundingMode);
|
||||
_F80LoadFCW(NewFCW);
|
||||
|
||||
|
||||
_StoreContext(2, GPRClass, NewFCW, offsetof(FEXCore::Core::CPUState, FCW));
|
||||
|
||||
OrderedNode *MemLocation = _Add(Mem, _Constant(Size * 1));
|
||||
@@ -184,7 +184,7 @@ void OpDispatchBuilder::FBLDF64(OpcodeArgs) {
|
||||
void OpDispatchBuilder::FBSTPF64(OpcodeArgs) {
|
||||
auto orig_top = GetX87Top();
|
||||
auto data = _LoadContextIndexed(orig_top, 8, MMBaseOffset(), 16, FPRClass);
|
||||
|
||||
|
||||
OrderedNode *converted = _F80CVTTo(data, 8);
|
||||
converted = _F80BCDStore(converted);
|
||||
|
||||
@@ -256,7 +256,7 @@ void OpDispatchBuilder::FSTF64(OpcodeArgs) {
|
||||
//Convert to 80-bit float
|
||||
auto result = _F80CVTTo(data, 8);
|
||||
StoreResult_WithOpSize(FPRClass, Op, Op->Dest, result, 10, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if ((Op->TableInfo->Flags & X86Tables::InstFlags::FLAGS_POP) != 0) {
|
||||
// if we are popping then we must first mark this location as empty
|
||||
@@ -315,10 +315,7 @@ void OpDispatchBuilder::FADDF64(OpcodeArgs) {
|
||||
// Memory arg
|
||||
if constexpr (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
if(width == 16) {
|
||||
arg = _Sext(16, arg);
|
||||
}
|
||||
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
|
||||
b = _Float_FromGPR_S(8, 8, arg);
|
||||
} else if constexpr (width == 32) {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
b = _Float_FToF(8, 4, arg);
|
||||
@@ -376,10 +373,7 @@ void OpDispatchBuilder::FMULF64(OpcodeArgs) {
|
||||
// Memory arg
|
||||
if constexpr (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
if(width == 16) {
|
||||
arg = _Sext(16, arg);
|
||||
}
|
||||
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
|
||||
b = _Float_FromGPR_S(8, 8, arg);
|
||||
} else if constexpr (width == 32) {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
b = _Float_FToF(8, 4, arg);
|
||||
@@ -440,10 +434,7 @@ void OpDispatchBuilder::FDIVF64(OpcodeArgs) {
|
||||
// Memory arg
|
||||
if constexpr (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
if(width == 16) {
|
||||
arg = _Sext(16, arg);
|
||||
}
|
||||
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
|
||||
b = _Float_FromGPR_S(8, 8, arg);
|
||||
} else if constexpr (width == 32) {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
b = _Float_FToF(8, 4, arg);
|
||||
@@ -526,10 +517,7 @@ void OpDispatchBuilder::FSUBF64(OpcodeArgs) {
|
||||
// Memory arg
|
||||
if constexpr (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
if(width == 16) {
|
||||
arg = _Sext(16, arg);
|
||||
}
|
||||
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
|
||||
b = _Float_FromGPR_S(8, 8, arg);
|
||||
} else if constexpr (width == 32) {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
b = _Float_FToF(8, 4, arg);
|
||||
@@ -688,10 +676,7 @@ void OpDispatchBuilder::FCOMIF64(OpcodeArgs) {
|
||||
// Memory arg
|
||||
if constexpr (Integer) {
|
||||
arg = LoadSource(GPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
if(width == 16) {
|
||||
arg = _Sext(16, arg);
|
||||
}
|
||||
b = _Float_FromGPR_S(8, width == 64 ? 8 : 4, arg);
|
||||
b = _Float_FromGPR_S(8, 8, arg);
|
||||
} else if constexpr (width == 32) {
|
||||
arg = LoadSource(FPRClass, Op, Op->Src[0], Op->Flags, -1);
|
||||
b = _Float_FToF(8, 4, arg);
|
||||
@@ -715,7 +700,7 @@ void OpDispatchBuilder::FCOMIF64(OpcodeArgs) {
|
||||
OrderedNode *HostFlag_CF = _GetHostFlag(Res, FCMP_FLAG_LT);
|
||||
OrderedNode *HostFlag_ZF = _GetHostFlag(Res, FCMP_FLAG_EQ);
|
||||
OrderedNode *HostFlag_Unordered = _GetHostFlag(Res, FCMP_FLAG_UNORDERED);
|
||||
|
||||
|
||||
HostFlag_CF = _Or(HostFlag_CF, HostFlag_Unordered);
|
||||
HostFlag_ZF = _Or(HostFlag_ZF, HostFlag_Unordered);
|
||||
|
||||
@@ -825,8 +810,8 @@ void OpDispatchBuilder::X87BinaryOpF64(OpcodeArgs) {
|
||||
// Overwrite the op
|
||||
result.first->Header.Op = IROp;
|
||||
|
||||
if constexpr (IROp == IR::OP_F64FPREM ||
|
||||
IROp == IR::OP_F64FPREM1) {
|
||||
if constexpr (IROp == IR::OP_F80FPREM ||
|
||||
IROp == IR::OP_F80FPREM1) {
|
||||
//TODO: Set C0 to Q2, C3 to Q1, C1 to Q0
|
||||
SetRFLAG<FEXCore::X86State::X87FLAG_C2_LOC>(_Constant(0));
|
||||
}
|
||||
|
||||
+2
-4
@@ -24,12 +24,10 @@ X86GeneratedCode::X86GeneratedCode() {
|
||||
CodePtr = AllocateGuestCodeSpace(CODE_SIZE);
|
||||
|
||||
SignalReturn = reinterpret_cast<uint64_t>(CodePtr);
|
||||
SignalReturnRT = reinterpret_cast<uint64_t>(CodePtr) + 3;
|
||||
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr) + 6;
|
||||
CallbackReturn = reinterpret_cast<uint64_t>(CodePtr) + 2;
|
||||
|
||||
const std::vector<uint8_t> SignalReturnCode = {
|
||||
0x0F, 0x36, 0x0, // SIGRET FEX instruction (Non-RT)
|
||||
0x0F, 0x36, 0x1, // SIGRET FEX instruction (RT)
|
||||
0x0F, 0x36, // SIGRET FEX instruction
|
||||
0x0F, 0x37, // CALLBACKRET FEX Instruction
|
||||
};
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ public:
|
||||
~X86GeneratedCode();
|
||||
|
||||
uint64_t SignalReturn{};
|
||||
uint64_t SignalReturnRT{};
|
||||
uint64_t CallbackReturn{};
|
||||
|
||||
private:
|
||||
|
||||
@@ -338,7 +338,7 @@ void InitializeSecondaryGroupTables() {
|
||||
{OPD(TYPE_GROUP_15, PF_NONE, 3), 1, X86InstInfo{"STMXCSR", TYPE_INST, GenFlagsSameSize(SIZE_32BIT) | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_MEM_ONLY, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_NONE, 4), 1, X86InstInfo{"XSAVE", TYPE_PRIV, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_NONE, 5), 1, X86InstInfo{"LFENCE/XRSTOR", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_NONE, 6), 1, X86InstInfo{"MFENCE/XSAVEOPT", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_NONE, 6), 1, X86InstInfo{"MFENCE/XSAVEOPT", TYPE_INST, FLAGS_MODRM, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_NONE, 7), 1, X86InstInfo{"SFENCE/CLFLUSH", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
|
||||
|
||||
{OPD(TYPE_GROUP_15, PF_F3, 0), 1, X86InstInfo{"RDFSBASE", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_SF_MOD_REG_ONLY, 0, nullptr}},
|
||||
@@ -356,8 +356,8 @@ void InitializeSecondaryGroupTables() {
|
||||
{OPD(TYPE_GROUP_15, PF_66, 3), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_66, 4), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_66, 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_66, 6), 1, X86InstInfo{"CLWB", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_66, 7), 1, X86InstInfo{"CLFLUSHOPT", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_66, 6), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_66, 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{OPD(TYPE_GROUP_15, PF_F2, 0), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(TYPE_GROUP_15, PF_F2, 1), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
@@ -258,7 +258,7 @@ 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}},
|
||||
{0x36, 1, X86InstInfo{"SIGRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0, nullptr}},
|
||||
|
||||
{0x37, 1, X86InstInfo{"CALLBACKRET", TYPE_INST, FLAGS_BLOCK_END | FLAGS_NO_OVERLAY | FLAGS_SETS_RIP, 0, nullptr}},
|
||||
|
||||
|
||||
@@ -325,7 +325,7 @@ void InitializeVEXTables() {
|
||||
{OPD(2, 0b01, 0x40), 1, X86InstInfo{"VPMULLD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x41), 1, X86InstInfo{"VPHMINPOSUW", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x45), 1, X86InstInfo{"VPSRLV", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x46), 1, X86InstInfo{"VPSRAVD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x46), 1, X86InstInfo{"VPSRAVD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(2, 0b01, 0x47), 1, X86InstInfo{"VPSLLV", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
|
||||
{OPD(2, 0b01, 0x58), 1, X86InstInfo{"VPBROADCASTD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 0, nullptr}},
|
||||
@@ -407,7 +407,7 @@ void InitializeVEXTables() {
|
||||
// VEX Map 3
|
||||
{OPD(3, 0b01, 0x00), 1, X86InstInfo{"VPERMQ", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x01), 1, X86InstInfo{"VPERMPD", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x02), 1, X86InstInfo{"VPBLENDD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x02), 1, X86InstInfo{"VPBLENDD", TYPE_UNDEC, FLAGS_NONE, 0, nullptr}},
|
||||
{OPD(3, 0b01, 0x04), 1, X86InstInfo{"VPERMILPS", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x05), 1, X86InstInfo{"VPERMILPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{OPD(3, 0b01, 0x06), 1, X86InstInfo{"VPERM2F128", TYPE_INST, GenFlagsSameSize(SIZE_256BIT) | FLAGS_MODRM | FLAGS_VEX_1ST_SRC | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
@@ -416,7 +416,7 @@ void InitializeVEXTables() {
|
||||
{OPD(3, 0b01, 0x09), 1, X86InstInfo{"VROUNDPD", TYPE_INST, GenFlagsSameSize(SIZE_128BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1, nullptr}},
|
||||
{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, 0x0C), 1, X86InstInfo{"VBLENDPS", TYPE_UNDEC, FLAGS_NONE, 0, 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}},
|
||||
|
||||
+3
-11
@@ -264,7 +264,7 @@
|
||||
"Break BreakDefinition:$Reason": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"SignalReturn i8:$IsRT": {
|
||||
"SignalReturn": {
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"CallbackReturn": {
|
||||
@@ -479,17 +479,9 @@
|
||||
]
|
||||
},
|
||||
|
||||
"CacheLineClear GPR:$Addr, i1:$Serialize": {
|
||||
"CacheLineClear GPR:$Addr": {
|
||||
"Desc": ["Does a 64 byte cacheline clear at the address specified",
|
||||
"Only clears the data cachelines. Doesn't do any zeroing",
|
||||
"Can skip serialization if requested."
|
||||
],
|
||||
"HasSideEffects": true
|
||||
},
|
||||
"CacheLineClean GPR:$Addr": {
|
||||
"Desc": ["Does a 64 byte cacheline cleanat the address specified",
|
||||
"Only cleans the data cachelines. Doesn't do any zeroing",
|
||||
"Skips the invalidation step of the CacheLineClear operation"
|
||||
"Only clears the data cachelines. Doesn't do any zeroing"
|
||||
],
|
||||
"HasSideEffects": true
|
||||
},
|
||||
|
||||
+2
-3
@@ -23,7 +23,6 @@ namespace FEXCore::IR {
|
||||
#define IROP_REG_CLASSES_IMPL
|
||||
#define IROP_HASSIDEEFFECTS_IMPL
|
||||
#define IROP_SIZES_IMPL
|
||||
#define IROP_GETHASDEST_IMPL
|
||||
|
||||
#include <FEXCore/IR/IRDefines.inc>
|
||||
|
||||
@@ -126,7 +125,7 @@ static void PrintArg(std::stringstream *out, IRListView const* IR, OrderedNodeWr
|
||||
}
|
||||
}
|
||||
|
||||
if (GetHasDest(IROp->Op)) {
|
||||
if (IROp->HasDest) {
|
||||
uint32_t ElementSize = IROp->ElementSize;
|
||||
uint32_t NumElements = IROp->Size;
|
||||
if (!IROp->ElementSize) {
|
||||
@@ -232,7 +231,7 @@ void Dump(std::stringstream *out, IRListView const* IR, IR::RegisterAllocationDa
|
||||
|
||||
if (!Skip) {
|
||||
AddIndent();
|
||||
if (GetHasDest(IROp->Op)) {
|
||||
if (IROp->HasDest) {
|
||||
|
||||
uint32_t ElementSize = IROp->ElementSize;
|
||||
uint32_t NumElements = IROp->Size;
|
||||
|
||||
+3
-2
@@ -112,7 +112,7 @@ void IREmitter::ReplaceAllUsesWithRange(OrderedNode *Node, OrderedNode *NewNode,
|
||||
while (Begin != End) {
|
||||
auto [RealNode, IROp] = Begin();
|
||||
|
||||
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
if (IROp->Args[i].ID() == NodeId) {
|
||||
Node->RemoveUse();
|
||||
@@ -148,7 +148,7 @@ void IREmitter::RemoveArgUses(OrderedNode *Node) {
|
||||
|
||||
FEXCore::IR::IROp_Header *IROp = Node->Op(DataBegin);
|
||||
|
||||
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
auto ArgNode = IROp->Args[i].GetNode(ListBegin);
|
||||
ArgNode->RemoveUse();
|
||||
@@ -201,6 +201,7 @@ void IREmitter::ReplaceWithConstant(OrderedNode *Node, uint64_t Value) {
|
||||
|
||||
// Overwrite data with the new constant op
|
||||
Header->Op = OP_CONSTANT;
|
||||
Header->NumArgs = 0;
|
||||
auto Const = Header->CW<IROp_Constant>();
|
||||
Const->Constant = Value;
|
||||
} else {
|
||||
|
||||
+18
-31
@@ -29,7 +29,6 @@ $end_info$
|
||||
#include <string.h>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <tsl/robin_map.h>
|
||||
#include <utility>
|
||||
|
||||
namespace FEXCore::IR {
|
||||
@@ -199,17 +198,6 @@ private:
|
||||
|
||||
std::unordered_map<uint64_t, OrderedNode*> ConstPool;
|
||||
std::map<OrderedNode*, uint64_t> AddressgenConsts;
|
||||
|
||||
// Pool inline constant generation. These are typically very small and pool efficiently.
|
||||
tsl::robin_map<uint64_t, OrderedNode*> InlineConstantGen;
|
||||
OrderedNode *CreateInlineConstant(IREmitter *IREmit, uint64_t Constant) {
|
||||
const auto it = InlineConstantGen.find(Constant);
|
||||
if (it != InlineConstantGen.end()) {
|
||||
return it->second;
|
||||
}
|
||||
auto Result = InlineConstantGen.insert_or_assign(Constant, IREmit->_InlineConstant(Constant));
|
||||
return Result.first->second;
|
||||
}
|
||||
bool SupportsTSOImm9{};
|
||||
};
|
||||
|
||||
@@ -245,7 +233,7 @@ void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& Cur
|
||||
for (auto [BlockNode, BlockIROp] : CurrentIR.GetBlocks()) {
|
||||
auto BlockOp = BlockIROp->CW<FEXCore::IR::IROp_CodeBlock>();
|
||||
for (auto [UnaryOpNode, UnaryOpHdr] : CurrentIR.GetCode(BlockNode)) {
|
||||
if (IR::GetArgs(UnaryOpHdr->Op) == 1 && !HasSideEffects(UnaryOpHdr->Op)) {
|
||||
if (UnaryOpHdr->NumArgs == 1 && !HasSideEffects(UnaryOpHdr->Op)) {
|
||||
// could be moved
|
||||
auto SelectOpNode = IREmit->UnwrapNode(UnaryOpHdr->Args[0]);
|
||||
auto SelectOpHdr = IREmit->GetOpHeader(UnaryOpHdr->Args[0]);
|
||||
@@ -267,7 +255,7 @@ void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& Cur
|
||||
// Copy over the op
|
||||
memcpy(NewUnaryOp1.first, UnaryOpHdr, OpSize);
|
||||
|
||||
for (int i = 0; i < IR::GetArgs(NewUnaryOp1.first->Op); i++) {
|
||||
for (int i = 0; i < NewUnaryOp1.first->NumArgs; i++) {
|
||||
NewUnaryOp1.first->Args[i] = IREmit->WrapNode(IREmit->Invalid());
|
||||
}
|
||||
// Set New Op to operate on the constant
|
||||
@@ -281,7 +269,7 @@ void ConstProp::CodeMotionAroundSelects(IREmitter *IREmit, const IRListView& Cur
|
||||
// Copy over the op
|
||||
memcpy(NewUnaryOp2.first, UnaryOpHdr, OpSize);
|
||||
|
||||
for (int i = 0; i < IR::GetArgs(NewUnaryOp2.first->Op); i++) {
|
||||
for (int i = 0; i < NewUnaryOp2.first->NumArgs; i++) {
|
||||
NewUnaryOp2.first->Args[i] = IREmit->WrapNode(IREmit->Invalid());
|
||||
}
|
||||
// Set New Op to operate on the constant
|
||||
@@ -378,7 +366,7 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& C
|
||||
case OP_ASHR:
|
||||
case OP_LSHL:
|
||||
case OP_ROR: {
|
||||
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto newArg = RemoveUselessMasking(IREmit, IROp->Args[i], getMask(IROp));
|
||||
if (newArg.ID() != IROp->Args[i].ID()) {
|
||||
IREmit->ReplaceNodeArgument(CodeNode, i, IREmit->UnwrapNode(newArg));
|
||||
@@ -390,7 +378,7 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& C
|
||||
|
||||
case OP_AND: {
|
||||
// if AND's arguments are imms, they are masking
|
||||
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto mask = getMask(IROp);
|
||||
uint64_t imm = 0;
|
||||
if (IREmit->IsValueConstant(IROp->Args[i^1], &imm))
|
||||
@@ -469,7 +457,7 @@ bool ConstProp::ZextAndMaskingElimination(IREmitter *IREmit, const IRListView& C
|
||||
case OP_VFDIV:
|
||||
case OP_FCMP: {
|
||||
auto flopSize = IROp->Size;
|
||||
for (int i = 0; i < IR::GetArgs(IROp->Op); i++) {
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
auto argHeader = IREmit->GetOpHeader(IROp->Args[i]);
|
||||
|
||||
if (argHeader->Op == OP_VMOV) {
|
||||
@@ -832,7 +820,6 @@ bool ConstProp::ConstantPropagation(IREmitter *IREmit, const IRListView& Current
|
||||
}
|
||||
|
||||
bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR) {
|
||||
InlineConstantGen.clear();
|
||||
bool Changed = false;
|
||||
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetAllCode()) {
|
||||
@@ -854,7 +841,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
else
|
||||
Constant2 &= 63;
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -870,7 +857,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -886,7 +873,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsImmAddSub(Constant1)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant1));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant1));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -901,8 +888,8 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
{
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[2]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 2, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 3, CreateInlineConstant(IREmit, Constant3));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 2, IREmit->_InlineConstant(Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 3, IREmit->_InlineConstant(Constant3));
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -916,7 +903,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsImmAddSub(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -932,7 +919,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->NewRIP));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, CreateInlineConstant(IREmit, Constant));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 0, IREmit->_InlineConstant(Constant));
|
||||
|
||||
Changed = true;
|
||||
} else {
|
||||
@@ -958,7 +945,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsImmLogical(Constant2, IROp->Size * 8)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Header.Args[1]));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, 1, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -974,7 +961,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsImmMemory(Constant2, IROp->Size)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -990,7 +977,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsImmMemory(Constant2, IROp->Size)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -1007,7 +994,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsTSOImm9(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
@@ -1025,7 +1012,7 @@ bool ConstProp::ConstantInlining(IREmitter *IREmit, const IRListView& CurrentIR)
|
||||
if (IsTSOImm9(Constant2)) {
|
||||
IREmit->SetWriteCursor(CurrentIR.GetNode(Op->Offset));
|
||||
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, CreateInlineConstant(IREmit, Constant2));
|
||||
IREmit->ReplaceNodeArgument(CodeNode, Op->Offset_Index, IREmit->_InlineConstant(Constant2));
|
||||
|
||||
Changed = true;
|
||||
}
|
||||
|
||||
@@ -174,9 +174,9 @@ bool IRCompaction::Run(IREmitter *IREmit) {
|
||||
for (auto [LocalNode, LocalIROp] : LocalIR.GetCode(Block.NewNode)) {
|
||||
|
||||
// Now that we have the op copied over, we need to modify SSA values to point to the new correct locations
|
||||
// This doesn't use IR::GetRAArgs(Op) because we need to remap all SSA nodes
|
||||
// This doesn't use IR::GetArgs(Op) because we need to remap all SSA nodes
|
||||
// Including ones that we don't RA
|
||||
const uint8_t NumArgs = IR::GetArgs(LocalIROp->Op);
|
||||
const uint8_t NumArgs = LocalIROp->NumArgs;
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
const auto OldArg = LocalIROp->Args[i].ID();
|
||||
const auto NewArg = OldToNewRemap[OldArg.Value].NodeID;
|
||||
|
||||
@@ -79,7 +79,7 @@ bool IRValidation::Run(IREmitter *IREmit) {
|
||||
const auto ID = CurrentIR.GetID(CodeNode);
|
||||
const uint8_t OpSize = IROp->Size;
|
||||
|
||||
if (GetHasDest(IROp->Op)) {
|
||||
if (IROp->HasDest) {
|
||||
HadError |= OpSize == 0;
|
||||
// Does the op have a destination of size 0?
|
||||
if (OpSize == 0) {
|
||||
@@ -120,8 +120,23 @@ bool IRValidation::Run(IREmitter *IREmit) {
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
|
||||
uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
|
||||
if (NumArgs != IROp->NumArgs) {
|
||||
switch (IROp->Op) {
|
||||
case OP_BEGINBLOCK:
|
||||
case OP_ENDBLOCK:
|
||||
case OP_PHI:
|
||||
case OP_PHIVALUE:
|
||||
case OP_CONDJUMP:
|
||||
case OP_JUMP:
|
||||
// These override the number of args for RA, so ignore them.
|
||||
break;
|
||||
default:
|
||||
HadError |= true;
|
||||
Errors << "%ssa" << ID << ": Has wrong number of Args" << std::endl;
|
||||
}
|
||||
}
|
||||
for (uint32_t i = 0; i < NumArgs; ++i) {
|
||||
OrderedNodeWrapper Arg = IROp->Args[i];
|
||||
const auto ArgID = Arg.ID();
|
||||
|
||||
@@ -257,7 +257,7 @@ namespace {
|
||||
void FindNodeClasses(RegisterGraph *Graph, FEXCore::IR::IRListView *IR) {
|
||||
for (auto [CodeNode, IROp] : IR->GetAllCode()) {
|
||||
// If the destination hasn't yet been set then set it now
|
||||
if (GetHasDest(IROp->Op)) {
|
||||
if (IROp->HasDest) {
|
||||
const auto ID = IR->GetID(CodeNode);
|
||||
Graph->AllocData->Map[ID.Value] = PhysicalRegister(GetRegClassFromNode(IR, IROp), INVALID_REG);
|
||||
} else {
|
||||
@@ -455,7 +455,7 @@ namespace {
|
||||
auto& NodeLiveRange = LiveRanges[Node.Value];
|
||||
|
||||
// If the destination hasn't yet been set then set it now
|
||||
if (GetHasDest(IROp->Op)) {
|
||||
if (IROp->HasDest) {
|
||||
LOGMAN_THROW_AA_FMT(NodeLiveRange.Begin.Value == UINT32_MAX,
|
||||
"Node begin already defined?");
|
||||
NodeLiveRange.Begin = Node;
|
||||
@@ -475,7 +475,7 @@ namespace {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
|
||||
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
const auto& Arg = IROp->Args[i];
|
||||
|
||||
@@ -679,7 +679,7 @@ namespace {
|
||||
auto& NodeLiveRange = LiveRanges[Node.Value];
|
||||
|
||||
// Check for read-after-write and demote if it happens
|
||||
const uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
|
||||
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
const auto& Arg = IROp->Args[i];
|
||||
|
||||
@@ -709,7 +709,7 @@ namespace {
|
||||
}
|
||||
|
||||
// This op defines a span
|
||||
if (GetHasDest(IROp->Op)) {
|
||||
if (IROp->HasDest) {
|
||||
// If this is a pre-write, update the StaticMap so we track writes
|
||||
if (!NodeLiveRange.PrefferedRegister.IsInvalid()) {
|
||||
SRA_DEBUG("ssa{} is a pre-write\n", Node);
|
||||
@@ -1037,7 +1037,7 @@ namespace {
|
||||
while(1) {
|
||||
auto [RealNode, IROp] = Begin();
|
||||
|
||||
const uint8_t NumArgs = FEXCore::IR::GetRAArgs(IROp->Op);
|
||||
const uint8_t NumArgs = FEXCore::IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
const auto ArgNode = IROp->Args[i].ID();
|
||||
if (ArgNode == SearchID) {
|
||||
@@ -1069,7 +1069,7 @@ namespace {
|
||||
return End;
|
||||
}
|
||||
|
||||
const uint8_t NumArgs = FEXCore::IR::GetRAArgs(IROp->Op);
|
||||
const uint8_t NumArgs = FEXCore::IR::GetArgs(IROp->Op);
|
||||
for (uint8_t i = 0; i < NumArgs; ++i) {
|
||||
const auto ArgNode = IROp->Args[i].ID();
|
||||
if (ArgNode == SearchID) {
|
||||
@@ -1297,7 +1297,7 @@ namespace {
|
||||
|
||||
CurrentNodes.insert(NodeOpBegin.ID());
|
||||
|
||||
for (int i = 0; i < IR::GetRAArgs(IROp->Op); i++) {
|
||||
for (int i = 0; i < IROp->NumArgs; i++) {
|
||||
CurrentNodes.insert(IROp->Args[i].ID());
|
||||
}
|
||||
}
|
||||
@@ -1370,7 +1370,7 @@ namespace {
|
||||
auto LastCursor = IREmit->GetWriteCursor();
|
||||
auto [CodeNode, IROp] = IR.at(SpillPointId)();
|
||||
|
||||
LOGMAN_THROW_AA_FMT(GetHasDest(IROp->Op), "Can't spill with no dest");
|
||||
LOGMAN_THROW_AA_FMT(IROp->HasDest, "Can't spill with no dest");
|
||||
|
||||
const auto Node = IR.GetID(CodeNode);
|
||||
RegisterNode *CurrentNode = &Graph->Nodes[Node.Value];
|
||||
|
||||
@@ -94,7 +94,7 @@ bool ValueDominanceValidation::Run(IREmitter *IREmit) {
|
||||
for (auto [CodeNode, IROp] : CurrentIR.GetCode(BlockNode)) {
|
||||
const auto CodeID = CurrentIR.GetID(CodeNode);
|
||||
|
||||
const uint8_t NumArgs = IR::GetRAArgs(IROp->Op);
|
||||
const uint8_t NumArgs = IR::GetArgs(IROp->Op);
|
||||
for (uint32_t i = 0; i < NumArgs; ++i) {
|
||||
if (IROp->Args[i].IsInvalid()) continue;
|
||||
if (CurrentIR.GetOp<IROp_Header>(IROp->Args[i])->Op == OP_IRHEADER) continue;
|
||||
|
||||
+24
-25
@@ -1,52 +1,51 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <span>
|
||||
#include <unistd.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
namespace FEXCore::FileLoading {
|
||||
bool LoadFile(std::vector<char> &Data, const std::string &Filepath, size_t FixedSize) {
|
||||
int FD = open(Filepath.c_str(), O_RDONLY);
|
||||
std::fstream ConfigFile;
|
||||
ConfigFile.open(Filepath, std::ios::in);
|
||||
|
||||
if (FD == -1) {
|
||||
if (!ConfigFile.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t FileSize{};
|
||||
|
||||
if (FixedSize == 0) {
|
||||
struct stat buf;
|
||||
if (fstat(FD, &buf) != 0) {
|
||||
close(FD);
|
||||
if (!ConfigFile.seekg(0, std::fstream::end)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
FileSize = buf.st_size;
|
||||
FileSize = ConfigFile.tellg();
|
||||
if (ConfigFile.fail()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ConfigFile.seekg(0, std::fstream::beg)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
FileSize = FixedSize;
|
||||
}
|
||||
|
||||
ssize_t Read = -1;
|
||||
if (FileSize > 0) {
|
||||
Data.resize(FileSize);
|
||||
Read = pread(FD, &Data.at(0), FileSize, 0);
|
||||
if (!ConfigFile.read(&Data.at(0), FileSize)) {
|
||||
// Probably means permissions aren't set. Just early exit
|
||||
return false;
|
||||
}
|
||||
ConfigFile.close();
|
||||
}
|
||||
close(FD);
|
||||
return Read == FileSize;
|
||||
}
|
||||
|
||||
ssize_t LoadFileToBuffer(const std::string &Filepath, std::span<char> Buffer) {
|
||||
int FD = open(Filepath.c_str(), O_RDONLY);
|
||||
|
||||
if (FD == -1) {
|
||||
return -1;
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
ssize_t Read = pread(FD, Buffer.data(), Buffer.size(), 0);
|
||||
close(FD);
|
||||
return Read;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-11
@@ -3,7 +3,6 @@
|
||||
#include <vector>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <span>
|
||||
|
||||
namespace FEXCore::FileLoading {
|
||||
/**
|
||||
@@ -15,15 +14,5 @@ namespace FEXCore::FileLoading {
|
||||
* @return true on file loaded, false on failure
|
||||
*/
|
||||
bool LoadFile(std::vector<char> &Data, const std::string &Filepath, size_t FixedSize = 0);
|
||||
|
||||
/**
|
||||
* @brief Loads a filepath in to a buffer of data with a fixed size
|
||||
*
|
||||
* @param Filepath The filepath to load
|
||||
* @param Buffer The buffer to load the data in to. Attempting to read the full size of the span
|
||||
*
|
||||
* @return The amount of data read or -1 on error.
|
||||
*/
|
||||
ssize_t LoadFileToBuffer(const std::string &Filepath, std::span<char> Buffer);
|
||||
}
|
||||
|
||||
@@ -159,7 +159,6 @@ namespace FEXCore::Core {
|
||||
uint64_t GuestSignal_SIGTRAP{};
|
||||
uint64_t GuestSignal_SIGSEGV{};
|
||||
uint64_t SignalReturnHandler{};
|
||||
uint64_t SignalReturnHandlerRT{};
|
||||
uint64_t L1Pointer{};
|
||||
uint64_t L2Pointer{};
|
||||
/** @} */
|
||||
|
||||
@@ -27,7 +27,6 @@ class HostFeatures final {
|
||||
bool SupportsSHA{};
|
||||
bool SupportsBMI1{};
|
||||
bool SupportsBMI2{};
|
||||
bool SupportsCLWB{};
|
||||
bool SupportsPMULL_128Bit{};
|
||||
|
||||
// Float exception behaviour
|
||||
|
||||
@@ -372,33 +372,5 @@ namespace FEXCore {
|
||||
};
|
||||
static_assert(sizeof(FEXCore::x86::ucontext_t) == 236, "This needs to be the right size");
|
||||
|
||||
///< Non-rt signal context.
|
||||
//
|
||||
// Needs to match the format expected from signal handlers without SA_SIGINFO set.
|
||||
struct sigcontext {
|
||||
uint32_t gs;
|
||||
uint32_t fs;
|
||||
uint32_t es;
|
||||
uint32_t ds;
|
||||
uint32_t di;
|
||||
uint32_t si;
|
||||
uint32_t bp;
|
||||
uint32_t sp;
|
||||
uint32_t bx;
|
||||
uint32_t dx;
|
||||
uint32_t cx;
|
||||
uint32_t ax;
|
||||
uint32_t trapno;
|
||||
uint32_t err;
|
||||
uint32_t ip;
|
||||
uint32_t cs;
|
||||
uint32_t flags;
|
||||
uint32_t sp_at_signal;
|
||||
uint32_t ss;
|
||||
|
||||
uint32_t fpstate;
|
||||
uint32_t oldmask;
|
||||
uint32_t cr2;
|
||||
};
|
||||
}
|
||||
}
|
||||
+7
-7
@@ -8,13 +8,13 @@ namespace FEXCore::X86State {
|
||||
* @{ */
|
||||
enum X86Reg : uint32_t {
|
||||
REG_RAX = 0,
|
||||
REG_RCX = 1,
|
||||
REG_RDX = 2,
|
||||
REG_RBX = 3,
|
||||
REG_RSP = 4,
|
||||
REG_RBP = 5,
|
||||
REG_RSI = 6,
|
||||
REG_RDI = 7,
|
||||
REG_RBX = 1,
|
||||
REG_RCX = 2,
|
||||
REG_RDX = 3,
|
||||
REG_RSI = 4,
|
||||
REG_RDI = 5,
|
||||
REG_RBP = 6,
|
||||
REG_RSP = 7,
|
||||
REG_R8 = 8,
|
||||
REG_R9 = 9,
|
||||
REG_R10 = 10,
|
||||
|
||||
@@ -68,7 +68,6 @@ namespace FEXCore::Core {
|
||||
Pause,
|
||||
Stop,
|
||||
Return,
|
||||
ReturnRT,
|
||||
};
|
||||
|
||||
struct LocalIREntry {
|
||||
|
||||
+12
-12
@@ -144,8 +144,8 @@ struct DecodedOperand {
|
||||
} GPR;
|
||||
|
||||
struct {
|
||||
int32_t Displacement;
|
||||
uint8_t GPR;
|
||||
int32_t Displacement;
|
||||
} GPRIndirect;
|
||||
|
||||
struct {
|
||||
@@ -156,33 +156,27 @@ struct DecodedOperand {
|
||||
} RIPLiteral;
|
||||
|
||||
struct {
|
||||
uint64_t Value;
|
||||
uint8_t Size;
|
||||
uint64_t Value;
|
||||
} Literal;
|
||||
|
||||
struct {
|
||||
int32_t Offset;
|
||||
uint8_t Scale;
|
||||
uint8_t Index; // ~0 invalid
|
||||
uint8_t Base; // ~0 invalid
|
||||
uint32_t Scale : 8;
|
||||
int32_t Offset;
|
||||
} SIB;
|
||||
};
|
||||
|
||||
TypeUnion Data;
|
||||
OpType Type;
|
||||
TypeUnion Data;
|
||||
};
|
||||
|
||||
struct DecodedInst {
|
||||
uint64_t PC;
|
||||
|
||||
DecodedOperand Dest;
|
||||
DecodedOperand Src[3];
|
||||
|
||||
// Constains the dispatcher handler pointer
|
||||
X86InstInfo const* TableInfo;
|
||||
|
||||
uint32_t Flags;
|
||||
uint16_t OP;
|
||||
uint32_t Flags;
|
||||
|
||||
uint8_t ModRM;
|
||||
uint8_t SIB;
|
||||
@@ -190,6 +184,12 @@ struct DecodedInst {
|
||||
uint8_t LastEscapePrefix;
|
||||
bool DecodedModRM;
|
||||
bool DecodedSIB;
|
||||
|
||||
DecodedOperand Dest;
|
||||
DecodedOperand Src[3];
|
||||
|
||||
// Constains the dispatcher handler pointer
|
||||
X86InstInfo const* TableInfo;
|
||||
};
|
||||
|
||||
union ModRMDecoded {
|
||||
|
||||
@@ -58,6 +58,8 @@ friend class FEXCore::IR::PassManager;
|
||||
Op.first->Constant = (Constant & Mask);
|
||||
Op.first->Header.Size = Size / 8;
|
||||
Op.first->Header.ElementSize = Size / 8;
|
||||
Op.first->Header.NumArgs = 0;
|
||||
Op.first->Header.HasDest = true;
|
||||
return Op;
|
||||
}
|
||||
IRPair<IROp_Bfe> _Bfe(uint8_t Width, uint8_t lsb, OrderedNode *ssa0) {
|
||||
|
||||
+2
-152
@@ -83,150 +83,6 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: PC relative") {
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb000001e);
|
||||
}
|
||||
|
||||
{
|
||||
// Will generate adr.
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
CHECK(DisassembleEncoding(1) == 0x10fffffe);
|
||||
}
|
||||
{
|
||||
// Will generate nop + adr.
|
||||
ForwardLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
CHECK(DisassembleEncoding(1) == 0x1000003e);
|
||||
}
|
||||
{
|
||||
// Will generate adr.
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
CHECK(DisassembleEncoding(1) == 0x10fffffe);
|
||||
}
|
||||
|
||||
{
|
||||
// Will generate nop + adr.
|
||||
BiDirectionalLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
CHECK(DisassembleEncoding(1) == 0x1000003e);
|
||||
}
|
||||
|
||||
{
|
||||
// Will generate adrp.
|
||||
BackwardLabel Label;
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
// Move adrp 1MB away.
|
||||
for (size_t i = 0; i < (1 * 1024 * 1024 / 4); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
nop();
|
||||
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
|
||||
CHECK(DisassembleEncoding(262146) == 0xd503201f);
|
||||
}
|
||||
|
||||
{
|
||||
// Will generate nop + adrp.
|
||||
ForwardLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, and then aligned to a page.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
CHECK(DisassembleEncoding(1) == 0x9000081e);
|
||||
}
|
||||
|
||||
{
|
||||
// Will generate adrp + add.
|
||||
ForwardLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, plus one instruction.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb000081e);
|
||||
CHECK(DisassembleEncoding(1) == 0x910013de);
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
// Will generate adrp.
|
||||
BiDirectionalLabel Label;
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
// Move adrp 1MB away.
|
||||
for (size_t i = 0; i < (1 * 1024 * 1024 / 4); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
nop();
|
||||
CHECK(DisassembleEncoding(262145) == 0x90fff81e);
|
||||
CHECK(DisassembleEncoding(262146) == 0xd503201f);
|
||||
}
|
||||
|
||||
{
|
||||
// Will generate nop + adrp.
|
||||
BiDirectionalLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, and then aligned to a page.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 2); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xd503201f);
|
||||
CHECK(DisassembleEncoding(1) == 0x9000081e);
|
||||
}
|
||||
|
||||
{
|
||||
// Will generate adrp + add.
|
||||
BiDirectionalLabel Label;
|
||||
LongAddressGen(Reg::r30, &Label);
|
||||
|
||||
// Move label 1MB away, plus a page, plus one instruction.
|
||||
for (size_t i = 0; i < ((1 * 1024 * 1024 + 4096) / 4 - 1); ++i) {
|
||||
nop();
|
||||
}
|
||||
|
||||
Bind(&Label);
|
||||
dc32(0);
|
||||
|
||||
CHECK(DisassembleEncoding(0) == 0xb000081e);
|
||||
CHECK(DisassembleEncoding(1) == 0x910013de);
|
||||
}
|
||||
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Add/subtract immediate") {
|
||||
TEST_SINGLE(add(Size::i32Bit, Reg::r29, Reg::r28, 0, false), "add w29, w28, #0x0 (0)");
|
||||
@@ -1496,16 +1352,10 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: AddSub - with carry") {
|
||||
TEST_SINGLE(sbcs(Size::i64Bit, Reg::r29, Reg::r28, Reg::r27), "sbcs x29, x28, x27");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Rotate right into flags") {
|
||||
TEST_SINGLE(rmif(XReg::x30, 63, 0b0000), "rmif x30, #63, #nzcv");
|
||||
TEST_SINGLE(rmif(XReg::x30, 63, 0b0001), "rmif x30, #63, #nzcV");
|
||||
TEST_SINGLE(rmif(XReg::x30, 63, 0b0010), "rmif x30, #63, #nzCv");
|
||||
TEST_SINGLE(rmif(XReg::x30, 63, 0b0100), "rmif x30, #63, #nZcv");
|
||||
TEST_SINGLE(rmif(XReg::x30, 63, 0b1000), "rmif x30, #63, #Nzcv");
|
||||
TEST_SINGLE(rmif(XReg::x30, 63, 0b1111), "rmif x30, #63, #NZCV");
|
||||
// TODO: Add support to emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ALU: Evaluate into flags") {
|
||||
TEST_SINGLE(setf8(XReg::x30), "setf8 w30");
|
||||
TEST_SINGLE(setf16(XReg::x30), "setf16 w30");
|
||||
// TODO: Add support to emitter.
|
||||
}
|
||||
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");
|
||||
|
||||
+7
-749
@@ -249,169 +249,13 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD copy") {
|
||||
TEST_SINGLE(ins(SubRegSize::i64Bit, VReg::v30, 1, VReg::v29, 0), "mov v30.d[1], v29.d[0]");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD three same (FP16)") {
|
||||
TEST_SINGLE(fmaxnm<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmaxnm v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmla<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmla v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fadd<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fadd v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmulx<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmulx v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fcmeq<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fcmeq v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmax<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmax v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(frecps<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "frecps v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fminnm<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fminnm v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmls<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmls v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fsub<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fsub v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmin<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmin v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(frsqrts<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "frsqrts v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmaxnmp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmaxnmp v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(faddp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "faddp v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmul<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmul v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fcmge<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fcmge v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(facge<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "facge v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fmaxp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fmaxp v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fdiv<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fdiv v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fminnmp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fminnmp v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fabd<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fabd v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fcmgt<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fcmgt v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(facgt<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "facgt v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(fminp<SubRegSize::i16Bit>(QReg::q30, QReg::q29, QReg::q28), "fminp v30.8h, v29.8h, v28.8h");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register miscellaneous (FP16)") {
|
||||
TEST_SINGLE(frintn<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintn v30.8h, v29.8h");
|
||||
TEST_SINGLE(frintm<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintm v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtns<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtns v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtms<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtms v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtas<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtas v30.8h, v29.8h");
|
||||
TEST_SINGLE(scvtf<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "scvtf v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcmgt<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmgt v30.8h, v29.8h, #0.0");
|
||||
TEST_SINGLE(fcmeq<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmeq v30.8h, v29.8h, #0.0");
|
||||
TEST_SINGLE(fcmlt<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmlt v30.8h, v29.8h, #0.0");
|
||||
TEST_SINGLE(fabs<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fabs v30.8h, v29.8h");
|
||||
TEST_SINGLE(frintp<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintp v30.8h, v29.8h");
|
||||
TEST_SINGLE(frintz<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintz v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtps<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtps v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtzs<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtzs v30.8h, v29.8h");
|
||||
TEST_SINGLE(frecpe<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frecpe v30.8h, v29.8h");
|
||||
TEST_SINGLE(frinta<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frinta v30.8h, v29.8h");
|
||||
TEST_SINGLE(frintx<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frintx v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtnu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtnu v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtmu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtmu v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtau<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtau v30.8h, v29.8h");
|
||||
TEST_SINGLE(ucvtf<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "ucvtf v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcmge<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmge v30.8h, v29.8h, #0.0");
|
||||
TEST_SINGLE(fcmle<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcmle v30.8h, v29.8h, #0.0");
|
||||
TEST_SINGLE(fneg<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fneg v30.8h, v29.8h");
|
||||
TEST_SINGLE(frinti<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frinti v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtpu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtpu v30.8h, v29.8h");
|
||||
TEST_SINGLE(fcvtzu<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fcvtzu v30.8h, v29.8h");
|
||||
TEST_SINGLE(frsqrte<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "frsqrte v30.8h, v29.8h");
|
||||
TEST_SINGLE(fsqrt<SubRegSize::i16Bit>(QReg::q30, QReg::q29), "fsqrt v30.8h, v29.8h");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD three-register extension") {
|
||||
TEST_SINGLE(sdot(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.16b, v29.16b, v28.16b");
|
||||
TEST_SINGLE(sdot(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.8h, v29.16b, v28.16b");
|
||||
TEST_SINGLE(sdot(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.4s, v29.16b, v28.16b");
|
||||
TEST_SINGLE(sdot(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sdot v30.2d, v29.16b, v28.16b");
|
||||
TEST_SINGLE(sdot(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.8b, v29.8b, v28.8b");
|
||||
TEST_SINGLE(sdot(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.4h, v29.8b, v28.8b");
|
||||
TEST_SINGLE(sdot(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.2s, v29.8b, v28.8b");
|
||||
//TEST_SINGLE(sdot(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sdot v30.1d, v29.8b, v28.8b");
|
||||
|
||||
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q28), "usdot v30.4s, v29.16b, v28.16b");
|
||||
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d28), "usdot v30.2s, v29.8b, v28.8b");
|
||||
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.16b, v29.16b, v28.16b");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.4s, v29.4s, v28.4s");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlah v30.2d, v29.2d, v28.2d");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.8b, v29.8b, v28.8b");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.4h, v29.4h, v28.4h");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.2s, v29.2s, v28.2s");
|
||||
//TEST_SINGLE(sqrdmlah(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlah v30.1d, v29.1d, v28.1d");
|
||||
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.16b, v29.16b, v28.16b");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.8h, v29.8h, v28.8h");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.4s, v29.4s, v28.4s");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "sqrdmlsh v30.2d, v29.2d, v28.2d");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.8b, v29.8b, v28.8b");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.4h, v29.4h, v28.4h");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.2s, v29.2s, v28.2s");
|
||||
//TEST_SINGLE(sqrdmlsh(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "sqrdmlsh v30.1d, v29.1d, v28.1d");
|
||||
|
||||
TEST_SINGLE(udot(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.16b, v29.16b, v28.16b");
|
||||
TEST_SINGLE(udot(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.8h, v29.16b, v28.16b");
|
||||
TEST_SINGLE(udot(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.4s, v29.16b, v28.16b");
|
||||
TEST_SINGLE(udot(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28), "udot v30.2d, v29.16b, v28.16b");
|
||||
TEST_SINGLE(udot(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.8b, v29.8b, v28.8b");
|
||||
TEST_SINGLE(udot(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.4h, v29.8b, v28.8b");
|
||||
TEST_SINGLE(udot(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.2s, v29.8b, v28.8b");
|
||||
//TEST_SINGLE(udot(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28), "udot v30.1d, v29.8b, v28.8b");
|
||||
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.16b, v29.16b, v28.16b, #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.8h, v29.8h, v28.8h, #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.4s, v29.4s, v28.4s, #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_0), "fcmla v30.2d, v29.2d, v28.2d, #0");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.8b, v29.8b, v28.8b, #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.4h, v29.4h, v28.4h, #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.2s, v29.2s, v28.2s, #0");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_0), "fcmla v30.1d, v29.1d, v28.1d, #0");
|
||||
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.16b, v29.16b, v28.16b, #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.8h, v29.8h, v28.8h, #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.4s, v29.4s, v28.4s, #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcmla v30.2d, v29.2d, v28.2d, #90");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.8b, v29.8b, v28.8b, #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.4h, v29.4h, v28.4h, #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.2s, v29.2s, v28.2s, #90");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcmla v30.1d, v29.1d, v28.1d, #90");
|
||||
|
||||
// Vixl disassembler has a bug that claims 8-bit fcmla exists
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.16b, v29.16b, v28.16b, #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.8h, v29.8h, v28.8h, #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.4s, v29.4s, v28.4s, #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_180), "fcmla v30.2d, v29.2d, v28.2d, #180");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.8b, v29.8b, v28.8b, #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.4h, v29.4h, v28.4h, #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.2s, v29.2s, v28.2s, #180");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_180), "fcmla v30.1d, v29.1d, v28.1d, #180");
|
||||
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.16b, v29.16b, v28.16b, #270");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.8h, v29.8h, v28.8h, #270");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.4s, v29.4s, v28.4s, #270");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcmla v30.2d, v29.2d, v28.2d, #270");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.8b, v29.8b, v28.8b, #270");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.4h, v29.4h, v28.4h, #270");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.2s, v29.2s, v28.2s, #270");
|
||||
//TEST_SINGLE(fcmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcmla v30.1d, v29.1d, v28.1d, #270");
|
||||
|
||||
// Vixl disassembler has a bug that claims 8-bit fcadd exists
|
||||
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.16b, v29.16b, v28.16b, #90");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.8h, v29.8h, v28.8h, #90");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.4s, v29.4s, v28.4s, #90");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_90), "fcadd v30.2d, v29.2d, v28.2d, #90");
|
||||
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.8b, v29.8b, v28.8b, #90");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.4h, v29.4h, v28.4h, #90");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.2s, v29.2s, v28.2s, #90");
|
||||
//TEST_SINGLE(fcadd(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_90), "fcadd v30.1d, v29.1d, v28.1d, #90");
|
||||
|
||||
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.16b, v29.16b, v28.16b, #270");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.8h, v29.8h, v28.8h, #270");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.4s, v29.4s, v28.4s, #270");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q28, Rotation::ROTATE_270), "fcadd v30.2d, v29.2d, v28.2d, #270");
|
||||
//TEST_SINGLE(fcadd(SubRegSize::i8Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.8b, v29.8b, v28.8b, #270");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.4h, v29.4h, v28.4h, #270");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.2s, v29.2s, v28.2s, #270");
|
||||
//TEST_SINGLE(fcadd(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d28, Rotation::ROTATE_270), "fcadd v30.1d, v29.1d, v28.1d, #270");
|
||||
|
||||
// TODO: Enable once vixl disassembler supports these instructions
|
||||
// TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q28), "bfdot v30.4s, v29.8h, v28.8h");
|
||||
// TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d28), "bfdot v30.2s, v29.4h, v28.4h");
|
||||
// TEST_SINGLE(bfmlalb(VReg::v30, VReg::v29, VReg::v28), "bfmlalb v30.4s, v29.8h, v28.8h");
|
||||
// TEST_SINGLE(bfmlalt(VReg::v30, VReg::v29, VReg::v28), "bfmlalt v30.4s, v29.8h, v28.8h");
|
||||
|
||||
TEST_SINGLE(smmla(VReg::v30, VReg::v29, VReg::v28), "smmla v30.4s, v29.16b, v28.16b");
|
||||
TEST_SINGLE(usmmla(VReg::v30, VReg::v29, VReg::v28), "usmmla v30.4s, v29.16b, v28.16b");
|
||||
// TODO: Enable once vixl disassembler supports these instructions
|
||||
//TEST_SINGLE(bfmmla(VReg::v30, VReg::v29, VReg::v28), "bfmmla v30.4s, v29.8h, v28.8h");
|
||||
TEST_SINGLE(ummla(VReg::v30, VReg::v29, VReg::v28), "ummla v30.4s, v29.16b, v28.16b");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD two-register miscellaneous") {
|
||||
// Commented out lines showcase unallocated encodings.
|
||||
@@ -2421,41 +2265,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
TEST_SINGLE(sxtl2(SubRegSize::i32Bit, DReg::d30, DReg::d29), "sxtl2 v30.4s, v29.8h");
|
||||
TEST_SINGLE(sxtl2(SubRegSize::i64Bit, DReg::d30, DReg::d29), "sxtl2 v30.2d, v29.4s");
|
||||
|
||||
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "scvtf v30.16b, v29.16b, #1");
|
||||
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "scvtf v30.16b, v29.16b, #7");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "scvtf v30.8h, v29.8h, #1");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "scvtf v30.8h, v29.8h, #15");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "scvtf v30.4s, v29.4s, #1");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "scvtf v30.4s, v29.4s, #31");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "scvtf v30.2d, v29.2d, #1");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "scvtf v30.2d, v29.2d, #63");
|
||||
|
||||
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "scvtf v30.8b, v29.8b, #1");
|
||||
//TEST_SINGLE(scvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "scvtf v30.8b, v29.8b, #7");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "scvtf v30.4h, v29.4h, #1");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "scvtf v30.4h, v29.4h, #15");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "scvtf v30.2s, v29.2s, #1");
|
||||
TEST_SINGLE(scvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "scvtf v30.2s, v29.2s, #31");
|
||||
//TEST_SINGLE(scvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "scvtf v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(scvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "scvtf v30.1d, v29.1d, #63");
|
||||
|
||||
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.16b, v29.16b, #1");
|
||||
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "fcvtzs v30.16b, v29.16b, #7");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.8h, v29.8h, #1");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "fcvtzs v30.8h, v29.8h, #15");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.4s, v29.4s, #1");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "fcvtzs v30.4s, v29.4s, #31");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "fcvtzs v30.2d, v29.2d, #1");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "fcvtzs v30.2d, v29.2d, #63");
|
||||
|
||||
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.8b, v29.8b, #1");
|
||||
//TEST_SINGLE(fcvtzs(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "fcvtzs v30.8b, v29.8b, #7");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.4h, v29.4h, #1");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "fcvtzs v30.4h, v29.4h, #15");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.2s, v29.2s, #1");
|
||||
TEST_SINGLE(fcvtzs(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "fcvtzs v30.2s, v29.2s, #31");
|
||||
//TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "fcvtzs v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(fcvtzs(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "fcvtzs v30.1d, v29.1d, #63");
|
||||
//// TODO: SCVTF, FCVTZS
|
||||
|
||||
TEST_SINGLE(ushr(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "ushr v30.16b, v29.16b, #1");
|
||||
TEST_SINGLE(ushr(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "ushr v30.16b, v29.16b, #7");
|
||||
@@ -2801,509 +2611,11 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD shift by immed
|
||||
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");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "ucvtf v30.8h, v29.8h, #15");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.4s, v29.4s, #1");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "ucvtf v30.4s, v29.4s, #31");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "ucvtf v30.2d, v29.2d, #1");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "ucvtf v30.2d, v29.2d, #63");
|
||||
|
||||
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.8b, v29.8b, #1");
|
||||
//TEST_SINGLE(ucvtf(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "ucvtf v30.8b, v29.8b, #7");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.4h, v29.4h, #1");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "ucvtf v30.4h, v29.4h, #15");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.2s, v29.2s, #1");
|
||||
TEST_SINGLE(ucvtf(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "ucvtf v30.2s, v29.2s, #31");
|
||||
//TEST_SINGLE(ucvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "ucvtf v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(ucvtf(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "ucvtf v30.1d, v29.1d, #63");
|
||||
|
||||
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.16b, v29.16b, #1");
|
||||
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, QReg::q30, QReg::q29, 7), "fcvtzu v30.16b, v29.16b, #7");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.8h, v29.8h, #1");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, QReg::q30, QReg::q29, 15), "fcvtzu v30.8h, v29.8h, #15");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.4s, v29.4s, #1");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, QReg::q30, QReg::q29, 31), "fcvtzu v30.4s, v29.4s, #31");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, QReg::q30, QReg::q29, 1), "fcvtzu v30.2d, v29.2d, #1");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, QReg::q30, QReg::q29, 63), "fcvtzu v30.2d, v29.2d, #63");
|
||||
|
||||
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.8b, v29.8b, #1");
|
||||
//TEST_SINGLE(fcvtzu(SubRegSize::i8Bit, DReg::d30, DReg::d29, 7), "fcvtzu v30.8b, v29.8b, #7");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.4h, v29.4h, #1");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i16Bit, DReg::d30, DReg::d29, 15), "fcvtzu v30.4h, v29.4h, #15");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.2s, v29.2s, #1");
|
||||
TEST_SINGLE(fcvtzu(SubRegSize::i32Bit, DReg::d30, DReg::d29, 31), "fcvtzu v30.2s, v29.2s, #31");
|
||||
//TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, DReg::d30, DReg::d29, 1), "fcvtzu v30.1d, v29.1d, #1");
|
||||
//TEST_SINGLE(fcvtzu(SubRegSize::i64Bit, DReg::d30, DReg::d29, 63), "fcvtzu v30.1d, v29.1d, #63");
|
||||
//// XXX: UCVTF/FCVTZU
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Advanced SIMD vector x indexed element") {
|
||||
TEST_SINGLE(smlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(smlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlal v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlal v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlal v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(smlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlal v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(smlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(smlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlal2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlal2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlal2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlal2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(smlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlal2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlal v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlal v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlal v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlal v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlal2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlal2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlal2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlal2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlal2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(smlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(smlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlsl v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlsl v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlsl v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(smlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlsl v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(smlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(smlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smlsl2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smlsl2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smlsl2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smlsl2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(smlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smlsl2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlsl v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlsl v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlsl v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlsl v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmlsl2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmlsl2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmlsl2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmlsl2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmlsl2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(mul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mul v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(mul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "mul v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "mul v30.4s, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "mul v30.4s, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mul v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(mul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "mul v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(mul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mul v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(mul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "mul v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "mul v30.2s, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "mul v30.2s, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mul v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(mul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "mul v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(smull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(smull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smull v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smull v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smull v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(smull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smull v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(smull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(smull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "smull2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "smull2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "smull2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "smull2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(smull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "smull2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmull v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmull v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmull v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmull v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "sqdmull2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "sqdmull2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "sqdmull2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "sqdmull2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "sqdmull2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqdmulh v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqdmulh v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqdmulh v30.4s, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqdmulh v30.4s, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqdmulh v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqdmulh v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqdmulh v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqdmulh v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqdmulh v30.2s, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqdmulh v30.2s, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqdmulh v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(sqdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqdmulh v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmulh v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqrdmulh v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqrdmulh v30.4s, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqrdmulh v30.4s, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmulh v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqrdmulh v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmulh v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqrdmulh v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqrdmulh v30.2s, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqrdmulh v30.2s, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmulh v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(sqrdmulh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqrdmulh v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q28, 0), "sdot v30.4s, v29.16b, v28.4b[0]");
|
||||
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q28, 3), "sdot v30.4s, v29.16b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q15, 0), "sdot v30.4s, v29.16b, v15.4b[0]");
|
||||
TEST_SINGLE(sdot(QReg::q30, QReg::q29, QReg::q15, 3), "sdot v30.4s, v29.16b, v15.4b[3]");
|
||||
|
||||
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d28, 0), "sdot v30.2s, v29.8b, v28.4b[0]");
|
||||
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d28, 3), "sdot v30.2s, v29.8b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d15, 0), "sdot v30.2s, v29.8b, v15.4b[0]");
|
||||
TEST_SINGLE(sdot(DReg::d30, DReg::d29, DReg::d15, 3), "sdot v30.2s, v29.8b, v15.4b[3]");
|
||||
|
||||
TEST_SINGLE(fmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmla v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(fmla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "fmla v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmla v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(fmla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "fmla v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmls v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(fmls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "fmls v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmls v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(fmls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "fmls v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmul v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(fmul(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "fmul v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmul v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(fmul(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "fmul v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q28, 0), "sudot v30.4s, v29.16b, v28.4b[0]");
|
||||
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q28, 3), "sudot v30.4s, v29.16b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q15, 0), "sudot v30.4s, v29.16b, v15.4b[0]");
|
||||
TEST_SINGLE(sudot(QReg::q30, QReg::q29, QReg::q15, 3), "sudot v30.4s, v29.16b, v15.4b[3]");
|
||||
|
||||
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d28, 0), "sudot v30.2s, v29.8b, v28.4b[0]");
|
||||
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d28, 3), "sudot v30.2s, v29.8b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d15, 0), "sudot v30.2s, v29.8b, v15.4b[0]");
|
||||
TEST_SINGLE(sudot(DReg::d30, DReg::d29, DReg::d15, 3), "sudot v30.2s, v29.8b, v15.4b[3]");
|
||||
|
||||
// Unimplemented in vixl disassembler
|
||||
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q28, 0), "bfdot v30.4s, v29.8h, v28.2h[0]");
|
||||
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q28, 3), "bfdot v30.4s, v29.8h, v28.2h[3]");
|
||||
|
||||
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q15, 0), "bfdot v30.4s, v29.8h, v15.2h[0]");
|
||||
//TEST_SINGLE(bfdot(QReg::q30, QReg::q29, QReg::q15, 3), "bfdot v30.4s, v29.8h, v15.2h[3]");
|
||||
|
||||
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d28, 0), "bfdot v30.2s, v29.4h, v28.2h[0]");
|
||||
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d28, 3), "bfdot v30.2s, v29.4h, v28.2h[3]");
|
||||
|
||||
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d15, 0), "bfdot v30.2s, v29.4h, v15.2h[0]");
|
||||
//TEST_SINGLE(bfdot(DReg::d30, DReg::d29, DReg::d15, 3), "bfdot v30.2s, v29.4h, v15.2h[3]");
|
||||
|
||||
TEST_SINGLE(fmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmla v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(fmla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "fmla v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(fmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmla v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(fmla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "fmla v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(fmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmla v30.2d, v29.2d, v15.d[0]");
|
||||
TEST_SINGLE(fmla(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 1), "fmla v30.2d, v29.2d, v15.d[1]");
|
||||
|
||||
//TEST_SINGLE(fmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmla v30.1d, v29.1d, v15.d[0]");
|
||||
//TEST_SINGLE(fmla(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 1), "fmla v30.1d, v29.1d, v15.d[1]");
|
||||
|
||||
TEST_SINGLE(fmls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmls v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(fmls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "fmls v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(fmls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmls v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(fmls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "fmls v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(fmls(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmls v30.2d, v29.2d, v15.d[0]");
|
||||
TEST_SINGLE(fmls(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 1), "fmls v30.2d, v29.2d, v15.d[1]");
|
||||
|
||||
//TEST_SINGLE(fmls(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmls v30.1d, v29.1d, v15.d[0]");
|
||||
//TEST_SINGLE(fmls(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 1), "fmls v30.1d, v29.1d, v15.d[1]");
|
||||
|
||||
TEST_SINGLE(fmul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmul v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(fmul(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "fmul v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(fmul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmul v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(fmul(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "fmul v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(fmul(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 0), "fmul v30.2d, v29.2d, v15.d[0]");
|
||||
TEST_SINGLE(fmul(SubRegSize::i64Bit, QReg::q30, QReg::q29, QReg::q15, 1), "fmul v30.2d, v29.2d, v15.d[1]");
|
||||
|
||||
//TEST_SINGLE(fmul(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 0), "fmul v30.1d, v29.1d, v15.d[0]");
|
||||
//TEST_SINGLE(fmul(SubRegSize::i64Bit, DReg::d30, DReg::d29, DReg::d15, 1), "fmul v30.1d, v29.1d, v15.d[1]");
|
||||
|
||||
TEST_SINGLE(fmlal(QReg::q30, QReg::q29, QReg::q15, 0), "fmlal v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(fmlal(QReg::q30, QReg::q29, QReg::q15, 7), "fmlal v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmlal(DReg::d30, DReg::d29, DReg::d15, 0), "fmlal v30.2s, v29.2h, v15.h[0]");
|
||||
TEST_SINGLE(fmlal(DReg::d30, DReg::d29, DReg::d15, 7), "fmlal v30.2s, v29.2h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmlal2(QReg::q30, QReg::q29, QReg::q15, 0), "fmlal2 v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(fmlal2(QReg::q30, QReg::q29, QReg::q15, 7), "fmlal2 v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmlal2(DReg::d30, DReg::d29, DReg::d15, 0), "fmlal2 v30.2s, v29.2h, v15.h[0]");
|
||||
TEST_SINGLE(fmlal2(DReg::d30, DReg::d29, DReg::d15, 7), "fmlal2 v30.2s, v29.2h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmlsl(QReg::q30, QReg::q29, QReg::q15, 0), "fmlsl v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(fmlsl(QReg::q30, QReg::q29, QReg::q15, 7), "fmlsl v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmlsl(DReg::d30, DReg::d29, DReg::d15, 0), "fmlsl v30.2s, v29.2h, v15.h[0]");
|
||||
TEST_SINGLE(fmlsl(DReg::d30, DReg::d29, DReg::d15, 7), "fmlsl v30.2s, v29.2h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmlsl2(QReg::q30, QReg::q29, QReg::q15, 0), "fmlsl2 v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(fmlsl2(QReg::q30, QReg::q29, QReg::q15, 7), "fmlsl2 v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(fmlsl2(DReg::d30, DReg::d29, DReg::d15, 0), "fmlsl2 v30.2s, v29.2h, v15.h[0]");
|
||||
TEST_SINGLE(fmlsl2(DReg::d30, DReg::d29, DReg::d15, 7), "fmlsl2 v30.2s, v29.2h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q28, 0), "usdot v30.4s, v29.16b, v28.4b[0]");
|
||||
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q28, 3), "usdot v30.4s, v29.16b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q15, 0), "usdot v30.4s, v29.16b, v15.4b[0]");
|
||||
TEST_SINGLE(usdot(QReg::q30, QReg::q29, QReg::q15, 3), "usdot v30.4s, v29.16b, v15.4b[3]");
|
||||
|
||||
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d28, 0), "usdot v30.2s, v29.8b, v28.4b[0]");
|
||||
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d28, 3), "usdot v30.2s, v29.8b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d15, 0), "usdot v30.2s, v29.8b, v15.4b[0]");
|
||||
TEST_SINGLE(usdot(DReg::d30, DReg::d29, DReg::d15, 3), "usdot v30.2s, v29.8b, v15.4b[3]");
|
||||
|
||||
// Unimplemented in vixl disassembler
|
||||
//TEST_SINGLE(bfmlalb(VReg::v30, VReg::v29, VReg::v15, 0), "bfmlalb v30.4s, v29.8h, v15.h[0]");
|
||||
//TEST_SINGLE(bfmlalb(VReg::v30, VReg::v29, VReg::v15, 7), "bfmlalb v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
//TEST_SINGLE(bfmlalt(VReg::v30, VReg::v29, VReg::v15, 0), "bfmlalt v30.4s, v29.8h, v15.h[0]");
|
||||
//TEST_SINGLE(bfmlalt(VReg::v30, VReg::v29, VReg::v15, 7), "bfmlalt v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
TEST_SINGLE(mla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mla v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(mla(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "mla v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "mla v30.4s, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "mla v30.4s, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mla v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(mla(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "mla v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(mla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mla v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(mla(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "mla v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "mla v30.2s, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "mla v30.2s, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mla v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(mla(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "mla v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(umlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(umlal(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlal v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlal v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlal v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(umlal(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlal v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(umlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(umlal2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlal2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlal2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlal2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlal2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(umlal2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlal2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(mls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mls v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(mls(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "mls v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "mls v30.4s, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "mls v30.4s, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "mls v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(mls(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "mls v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(mls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mls v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(mls(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "mls v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "mls v30.2s, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "mls v30.2s, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "mls v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(mls(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "mls v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(umlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(umlsl(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlsl v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlsl v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlsl v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(umlsl(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlsl v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(umlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(umlsl2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umlsl2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umlsl2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umlsl2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umlsl2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(umlsl2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umlsl2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(umull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull v30.4s, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(umull(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umull v30.4s, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umull v30.2d, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umull v30.2d, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull v30.2d, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(umull(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umull v30.2d, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(umull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull2 v30.4s, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(umull2(SubRegSize::i32Bit, VReg::v30, VReg::v29, VReg::v15, 7), "umull2 v30.4s, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 0), "umull2 v30.2d, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v28, 3), "umull2 v30.2d, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 0), "umull2 v30.2d, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(umull2(SubRegSize::i64Bit, VReg::v30, VReg::v29, VReg::v15, 3), "umull2 v30.2d, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlah v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqrdmlah v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqrdmlah v30.4s, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqrdmlah v30.4s, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlah v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqrdmlah v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlah v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqrdmlah v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqrdmlah v30.2s, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqrdmlah v30.2s, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlah v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(sqrdmlah(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqrdmlah v30.2s, v29.2s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q28, 0), "udot v30.4s, v29.16b, v28.4b[0]");
|
||||
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q28, 3), "udot v30.4s, v29.16b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q15, 0), "udot v30.4s, v29.16b, v15.4b[0]");
|
||||
TEST_SINGLE(udot(QReg::q30, QReg::q29, QReg::q15, 3), "udot v30.4s, v29.16b, v15.4b[3]");
|
||||
|
||||
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d28, 0), "udot v30.2s, v29.8b, v28.4b[0]");
|
||||
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d28, 3), "udot v30.2s, v29.8b, v28.4b[3]");
|
||||
|
||||
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d15, 0), "udot v30.2s, v29.8b, v15.4b[0]");
|
||||
TEST_SINGLE(udot(DReg::d30, DReg::d29, DReg::d15, 3), "udot v30.2s, v29.8b, v15.4b[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlsh v30.8h, v29.8h, v15.h[0]");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, QReg::q30, QReg::q29, QReg::q15, 7), "sqrdmlsh v30.8h, v29.8h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 0), "sqrdmlsh v30.4s, v29.4s, v28.s[0]");
|
||||
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q28, 3), "sqrdmlsh v30.4s, v29.4s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 0), "sqrdmlsh v30.4s, v29.4s, v15.s[0]");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, QReg::q30, QReg::q29, QReg::q15, 3), "sqrdmlsh v30.4s, v29.4s, v15.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlsh v30.4h, v29.4h, v15.h[0]");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i16Bit, DReg::d30, DReg::d29, DReg::d15, 7), "sqrdmlsh v30.4h, v29.4h, v15.h[7]");
|
||||
|
||||
// vixl has a disassembler bug where it doesn't decode rm correctly for registers >= 16
|
||||
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 0), "sqrdmlsh v30.2s, v29.2s, v28.s[0]");
|
||||
//TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d28, 3), "sqrdmlsh v30.2s, v29.2s, v28.s[3]");
|
||||
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 0), "sqrdmlsh v30.2s, v29.2s, v15.s[0]");
|
||||
TEST_SINGLE(sqrdmlsh(SubRegSize::i32Bit, DReg::d30, DReg::d29, DReg::d15, 3), "sqrdmlsh v30.2s, v29.2s, v15.s[3]");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Cryptographic three-register, imm2") {
|
||||
// TODO: Implement in emitter.
|
||||
@@ -3318,61 +2630,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Cryptographic two-register S
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Conversion between floating-point and fixed-point") {
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "scvtf h29, w30, #1");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "scvtf h29, w30, #32");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "scvtf s29, w30, #1");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "scvtf s29, w30, #32");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "scvtf d29, w30, #1");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "scvtf d29, w30, #32");
|
||||
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "scvtf h29, x30, #1");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "scvtf h29, x30, #64");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "scvtf s29, x30, #1");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "scvtf s29, x30, #64");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "scvtf d29, x30, #1");
|
||||
TEST_SINGLE(scvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "scvtf d29, x30, #64");
|
||||
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "ucvtf h29, w30, #1");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "ucvtf h29, w30, #32");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "ucvtf s29, w30, #1");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "ucvtf s29, w30, #32");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 1), "ucvtf d29, w30, #1");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i32Bit, Reg::r30, 32), "ucvtf d29, w30, #32");
|
||||
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "ucvtf h29, x30, #1");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i16Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "ucvtf h29, x30, #64");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "ucvtf s29, x30, #1");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i32Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "ucvtf s29, x30, #64");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 1), "ucvtf d29, x30, #1");
|
||||
TEST_SINGLE(ucvtf(ScalarRegSize::i64Bit, VReg::v29, Size::i64Bit, Reg::r30, 64), "ucvtf d29, x30, #64");
|
||||
|
||||
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzs w30, h29, #1");
|
||||
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 32), "fcvtzs w30, h29, #32");
|
||||
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzs w30, s29, #1");
|
||||
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 32), "fcvtzs w30, s29, #32");
|
||||
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzs w30, d29, #1");
|
||||
TEST_SINGLE(fcvtzs(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 32), "fcvtzs w30, d29, #32");
|
||||
|
||||
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzs x30, h29, #1");
|
||||
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 64), "fcvtzs x30, h29, #64");
|
||||
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzs x30, s29, #1");
|
||||
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 64), "fcvtzs x30, s29, #64");
|
||||
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzs x30, d29, #1");
|
||||
TEST_SINGLE(fcvtzs(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 64), "fcvtzs x30, d29, #64");
|
||||
|
||||
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzu w30, h29, #1");
|
||||
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 32), "fcvtzu w30, h29, #32");
|
||||
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzu w30, s29, #1");
|
||||
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 32), "fcvtzu w30, s29, #32");
|
||||
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzu w30, d29, #1");
|
||||
TEST_SINGLE(fcvtzu(Size::i32Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 32), "fcvtzu w30, d29, #32");
|
||||
|
||||
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 1), "fcvtzu x30, h29, #1");
|
||||
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i16Bit, VReg::v29, 64), "fcvtzu x30, h29, #64");
|
||||
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 1), "fcvtzu x30, s29, #1");
|
||||
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i32Bit, VReg::v29, 64), "fcvtzu x30, s29, #64");
|
||||
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 1), "fcvtzu x30, d29, #1");
|
||||
TEST_SINGLE(fcvtzu(Size::i64Bit, Reg::r30, ScalarRegSize::i64Bit, VReg::v29, 64), "fcvtzu x30, d29, #64");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: ASIMD: Conversion between floating-point and integer") {
|
||||
TEST_SINGLE(fcvtns(Size::i32Bit, Reg::r29, HReg::h30), "fcvtns w29, h30");
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
if (COMPILE_VIXL_DISASSEMBLER)
|
||||
if (ENABLE_VIXL_DISASSEMBLER)
|
||||
file(GLOB_RECURSE TESTS CONFIGURE_DEPENDS *.cpp)
|
||||
|
||||
set (LIBS fmt::fmt vixl Catch2::Catch2WithMain FEXCore_Base)
|
||||
|
||||
+9
-109
@@ -39,6 +39,10 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: Base Encodings") {
|
||||
TEST_SINGLE(mov(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29), "mov z30.s, p6/m, z29.s");
|
||||
TEST_SINGLE(mov(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29), "mov z30.d, p6/m, z29.d");
|
||||
//TEST_SINGLE(mov(SubRegSize::i128Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29), "mov z30.q, p6/m, z29.q");
|
||||
|
||||
// TODO: HISTCNT
|
||||
// TODO: FCMLA
|
||||
// TODO: FCADD
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer add/subtract vectors (unpredicated)") {
|
||||
TEST_SINGLE(add(SubRegSize::i8Bit, ZReg::z30, ZReg::z29, ZReg::z28), "add z30.b, z29.b, z28.b");
|
||||
@@ -241,15 +245,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer clamp") {
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 character match") {
|
||||
TEST_SINGLE(match(SubRegSize::i8Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
|
||||
"match p8.b, p6/z, z30.b, z29.b");
|
||||
TEST_SINGLE(match(SubRegSize::i16Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
|
||||
"match p8.h, p6/z, z30.h, z29.h");
|
||||
|
||||
TEST_SINGLE(nmatch(SubRegSize::i8Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
|
||||
"nmatch p8.b, p6/z, z30.b, z29.b");
|
||||
TEST_SINGLE(nmatch(SubRegSize::i16Bit, PReg::p8, PReg::p6.Zeroing(), ZReg::z30, ZReg::z29),
|
||||
"nmatch p8.h, p6/z, z30.h, z29.h");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point convert precision odd elements") {
|
||||
TEST_SINGLE(fcvtxnt(ZReg::z30, PReg::p6.Merging(), ZReg::z29), "fcvtxnt z30.s, p6/m, z29.d");
|
||||
@@ -301,72 +297,11 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 floating-point pairwise o
|
||||
TEST_SINGLE(fmin(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28), "fmin z30.d, p6/m, z30.d, z28.d");
|
||||
//TEST_SINGLE(fmin(SubRegSize::i128Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28), "fmin z30.q, p6/m, z30.q, z28.q");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point complex add") {
|
||||
TEST_SINGLE(fcadd(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_90),
|
||||
"fcadd z30.h, p6/m, z30.h, z28.h, #90");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_90),
|
||||
"fcadd z30.s, p6/m, z30.s, z28.s, #90");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_90),
|
||||
"fcadd z30.d, p6/m, z30.d, z28.d, #90");
|
||||
|
||||
TEST_SINGLE(fcadd(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_270),
|
||||
"fcadd z30.h, p6/m, z30.h, z28.h, #270");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_270),
|
||||
"fcadd z30.s, p6/m, z30.s, z28.s, #270");
|
||||
TEST_SINGLE(fcadd(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z30, ZReg::z28, Rotation::ROTATE_270),
|
||||
"fcadd z30.d, p6/m, z30.d, z28.d, #270");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point multiply-add (vector)") {
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_0),
|
||||
"fcmla z30.h, p6/m, z10.h, z28.h, #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_0),
|
||||
"fcmla z30.s, p6/m, z10.s, z28.s, #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_0),
|
||||
"fcmla z30.d, p6/m, z10.d, z28.d, #0");
|
||||
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_90),
|
||||
"fcmla z30.h, p6/m, z10.h, z28.h, #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_90),
|
||||
"fcmla z30.s, p6/m, z10.s, z28.s, #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_90),
|
||||
"fcmla z30.d, p6/m, z10.d, z28.d, #90");
|
||||
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_180),
|
||||
"fcmla z30.h, p6/m, z10.h, z28.h, #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_180),
|
||||
"fcmla z30.s, p6/m, z10.s, z28.s, #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_180),
|
||||
"fcmla z30.d, p6/m, z10.d, z28.d, #180");
|
||||
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_270),
|
||||
"fcmla z30.h, p6/m, z10.h, z28.h, #270");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_270),
|
||||
"fcmla z30.s, p6/m, z10.s, z28.s, #270");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z10, ZReg::z28, Rotation::ROTATE_270),
|
||||
"fcmla z30.d, p6/m, z10.d, z28.d, #270");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point multiply-add (indexed)") {
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point complex multiply-add (indexed)") {
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 0, Rotation::ROTATE_0),
|
||||
"fcmla z30.h, z10.h, z7.h[0], #0");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 0, Rotation::ROTATE_0),
|
||||
"fcmla z30.s, z10.s, z15.s[0], #0");
|
||||
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 1, Rotation::ROTATE_90),
|
||||
"fcmla z30.h, z10.h, z7.h[1], #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 1, Rotation::ROTATE_90),
|
||||
"fcmla z30.s, z10.s, z15.s[1], #90");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 1, Rotation::ROTATE_180),
|
||||
"fcmla z30.s, z10.s, z15.s[1], #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i32Bit, ZReg::z30, ZReg::z10, ZReg::z15, 1, Rotation::ROTATE_270),
|
||||
"fcmla z30.s, z10.s, z15.s[1], #270");
|
||||
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 2, Rotation::ROTATE_180),
|
||||
"fcmla z30.h, z10.h, z7.h[2], #180");
|
||||
TEST_SINGLE(fcmla(SubRegSize::i16Bit, ZReg::z30, ZReg::z10, ZReg::z7, 3, Rotation::ROTATE_270),
|
||||
"fcmla z30.h, z10.h, z7.h[3], #270");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point multiply (indexed)") {
|
||||
// TODO: Implement in emitter.
|
||||
@@ -433,25 +368,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point arithmetic
|
||||
//TEST_SINGLE(frsqrts(SubRegSize::i128Bit, ZReg::z30, ZReg::z29, ZReg::z28), "frsqrts z30.q, z29.q, z28.q");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE floating-point recursive reduction") {
|
||||
TEST_SINGLE(faddv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "faddv h30, p7, z28.h");
|
||||
TEST_SINGLE(faddv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "faddv s30, p7, z28.s");
|
||||
TEST_SINGLE(faddv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "faddv d30, p7, z28.d");
|
||||
|
||||
TEST_SINGLE(fmaxnmv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxnmv h30, p7, z28.h");
|
||||
TEST_SINGLE(fmaxnmv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxnmv s30, p7, z28.s");
|
||||
TEST_SINGLE(fmaxnmv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxnmv d30, p7, z28.d");
|
||||
|
||||
TEST_SINGLE(fminnmv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fminnmv h30, p7, z28.h");
|
||||
TEST_SINGLE(fminnmv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fminnmv s30, p7, z28.s");
|
||||
TEST_SINGLE(fminnmv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fminnmv d30, p7, z28.d");
|
||||
|
||||
TEST_SINGLE(fmaxv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxv h30, p7, z28.h");
|
||||
TEST_SINGLE(fmaxv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxv s30, p7, z28.s");
|
||||
TEST_SINGLE(fmaxv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fmaxv d30, p7, z28.d");
|
||||
|
||||
TEST_SINGLE(fminv(SubRegSize::i16Bit, VReg::v30, PReg::p7, ZReg::z28), "fminv h30, p7, z28.h");
|
||||
TEST_SINGLE(fminv(SubRegSize::i32Bit, VReg::v30, PReg::p7, ZReg::z28), "fminv s30, p7, z28.s");
|
||||
TEST_SINGLE(fminv(SubRegSize::i64Bit, VReg::v30, PReg::p7, ZReg::z28), "fminv d30, p7, z28.d");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer multiply-accumulate writing addend (predicated)") {
|
||||
@@ -463,20 +380,7 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer multiply-add writi
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer add/subtract vectors (predicated)") {
|
||||
TEST_SINGLE(add(SubRegSize::i8Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.b, p7/m, z30.b, z28.b");
|
||||
TEST_SINGLE(add(SubRegSize::i16Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.h, p7/m, z30.h, z28.h");
|
||||
TEST_SINGLE(add(SubRegSize::i32Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.s, p7/m, z30.s, z28.s");
|
||||
TEST_SINGLE(add(SubRegSize::i64Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "add z30.d, p7/m, z30.d, z28.d");
|
||||
|
||||
TEST_SINGLE(sub(SubRegSize::i8Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.b, p7/m, z30.b, z28.b");
|
||||
TEST_SINGLE(sub(SubRegSize::i16Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.h, p7/m, z30.h, z28.h");
|
||||
TEST_SINGLE(sub(SubRegSize::i32Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.s, p7/m, z30.s, z28.s");
|
||||
TEST_SINGLE(sub(SubRegSize::i64Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "sub z30.d, p7/m, z30.d, z28.d");
|
||||
|
||||
TEST_SINGLE(subr(SubRegSize::i8Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.b, p7/m, z30.b, z28.b");
|
||||
TEST_SINGLE(subr(SubRegSize::i16Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.h, p7/m, z30.h, z28.h");
|
||||
TEST_SINGLE(subr(SubRegSize::i32Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.s, p7/m, z30.s, z28.s");
|
||||
TEST_SINGLE(subr(SubRegSize::i64Bit, ZReg::z30, PReg::p7, ZReg::z30, ZReg::z28), "subr z30.d, p7/m, z30.d, z28.d");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE integer min/max/difference (predicated)") {
|
||||
@@ -1779,12 +1683,8 @@ TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 bitwise shift right narro
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 integer add/subtract narrow high part") {
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 Histogram Computation") {
|
||||
TEST_SINGLE(histcnt(SubRegSize::i32Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29, ZReg::z28), "histcnt z30.s, p6/z, z29.s, z28.s");
|
||||
TEST_SINGLE(histcnt(SubRegSize::i64Bit, ZReg::z30, PReg::p6.Merging(), ZReg::z29, ZReg::z28), "histcnt z30.d, p6/z, z29.d, z28.d");
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 Histogram Computation - Segment") {
|
||||
TEST_SINGLE(histseg(ZReg::z30, ZReg::z29, ZReg::z28), "histseg z30.b, z29.b, z28.b");
|
||||
// TODO: Implement in emitter.
|
||||
}
|
||||
TEST_CASE_METHOD(TestDisassembler, "Emitter: SVE: SVE2 crypto unary operations") {
|
||||
// TODO: Implement in emitter.
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ public:
|
||||
TestDisassembler() {
|
||||
fp = tmpfile();
|
||||
Disasm = std::make_unique<vixl::aarch64::PrintDisassembler>(fp);
|
||||
// 2MB code size.
|
||||
const size_t CodeSize = 2 * 1024 * 1024;
|
||||
SetBuffer(reinterpret_cast<uint8_t*>(mmap(nullptr, CodeSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), CodeSize);
|
||||
SetBuffer(reinterpret_cast<uint8_t*>(mmap(nullptr, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0)), 4096);
|
||||
BufferBegin = GetCursorAddress<const vixl::aarch64::Instruction*>();
|
||||
}
|
||||
~TestDisassembler() {
|
||||
|
||||
Vendored
+1
-1
Submodule External/jemalloc updated: ac8870dd6b...3b08599cfa.
Vendored
+1
-1
Submodule External/vixl updated: c9e5307fe9...bff2bd9ef2.
Vendored
+1
-1
Submodule External/xbyak updated: b0f0c7805a...ea21d6e295.
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
#include <sys/stat.h>
|
||||
#include <span>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace FHU::Symlinks {
|
||||
// Checks to see if a filepath is a symlink.
|
||||
inline bool IsSymlink(const std::string &Filename) {
|
||||
struct stat Buffer{};
|
||||
int Result = lstat(Filename.c_str(), &Buffer);
|
||||
return Result == 0 && S_ISLNK(Buffer.st_mode);
|
||||
}
|
||||
|
||||
// Resolves a symlink path.
|
||||
// Doesn't handle recursive symlinks.
|
||||
// Doesn't append null terminator character.
|
||||
// Returns a string_view of the resolved path, or an empty view on error.
|
||||
inline std::string_view ResolveSymlink(const std::string &Filename, std::span<char> ResultBuffer) {
|
||||
ssize_t Result = readlink(Filename.c_str(), ResultBuffer.data(), ResultBuffer.size());
|
||||
if (Result == -1) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return std::string_view(ResultBuffer.data(), Result);
|
||||
}
|
||||
}
|
||||
@@ -61,7 +61,7 @@ inline int32_t gettid() {
|
||||
}
|
||||
|
||||
inline int32_t tgkill(pid_t tgid, pid_t tid, int sig) {
|
||||
#if defined(HAS_SYSCALL_TGKILL) && HAS_SYSCALL_TGKILL
|
||||
#if defined(HAS_SYSCALL_GETTID) && HAS_SYSCALL_GETTID
|
||||
return ::tgkill(tgid, tid, sig);
|
||||
#else
|
||||
return ::syscall(SYS_tgkill, tgid, tid, sig);
|
||||
|
||||
@@ -307,12 +307,12 @@ def GetRootFSPath():
|
||||
def CheckRootFSInstallStatus():
|
||||
# Matches what is available on https://rootfs.fex-emu.com/file/fex-rootfs/RootFS_links.json
|
||||
UbuntuVersionToRootFS = {
|
||||
"20.04": "Ubuntu_20_04.sqsh",
|
||||
"20.04": "Ubuntu_20_04.ero",
|
||||
"20.04": "Ubuntu_21_04.sqsh",
|
||||
"21.04": "Ubuntu_21_04.sqsh",
|
||||
"21.10": "Ubuntu_21_10.sqsh",
|
||||
"21.10": "Ubuntu_21_10.ero",
|
||||
"22.04": "Ubuntu_22_04.sqsh",
|
||||
"22.04": "Ubuntu_22_04.ero",
|
||||
"22.10": "Ubuntu_22_10.sqsh",
|
||||
"22.10": "Ubuntu_22_10.ero",
|
||||
}
|
||||
|
||||
return os.path.exists(GetRootFSPath() + UbuntuVersionToRootFS[GetDistro()[1]])
|
||||
|
||||
@@ -72,7 +72,6 @@ class HostFeatures(Flag) :
|
||||
FEATURE_CLZERO = (1 << 5)
|
||||
FEATURE_BMI1 = (1 << 6)
|
||||
FEATURE_BMI2 = (1 << 7)
|
||||
FEATURE_CLWB = (1 << 8)
|
||||
|
||||
RegStringLookup = {
|
||||
"NONE": Regs.REG_NONE,
|
||||
@@ -144,7 +143,6 @@ HostFeaturesLookup = {
|
||||
"CLZERO" : HostFeatures.FEATURE_CLZERO,
|
||||
"BMI1" : HostFeatures.FEATURE_BMI1,
|
||||
"BMI2" : HostFeatures.FEATURE_BMI2,
|
||||
"CLWB" : HostFeatures.FEATURE_CLWB,
|
||||
}
|
||||
|
||||
def parse_hexstring(s):
|
||||
|
||||
@@ -2,13 +2,11 @@
|
||||
#include "Common/Config.h"
|
||||
|
||||
#include <FEXCore/Config/Config.h>
|
||||
#include <FEXHeaderUtils/SymlinkChecks.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <linux/limits.h>
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
@@ -42,75 +40,12 @@ namespace FEX::Config {
|
||||
}
|
||||
}
|
||||
|
||||
std::string RecoverGuestProgramFilename(std::string Program, bool ExecFDInterp, const std::string_view ProgramFDFromEnv) {
|
||||
// If executed with a FEX FD then the Program argument might be empty.
|
||||
// In this case we need to scan the FD node to recover the application binary that exists on disk.
|
||||
// Only do this if the Program argument is empty, since we would prefer the application's expectation
|
||||
// of application name.
|
||||
if (!ProgramFDFromEnv.empty() && Program.empty()) {
|
||||
// Get the `dev` node of the execveat fd string.
|
||||
Program = "/dev/fd/";
|
||||
Program += ProgramFDFromEnv;
|
||||
}
|
||||
|
||||
// If we were provided a relative path then we need to canonicalize it to become absolute.
|
||||
// If the program name isn't resolved to an absolute path then glibc breaks inside it's `_dl_get_origin` function.
|
||||
// This is because we rewrite `/proc/self/exe` to the absolute program path calculated in here.
|
||||
if (!Program.starts_with('/')) {
|
||||
Program = std::filesystem::canonical(std::move(Program)).string();
|
||||
}
|
||||
|
||||
// If FEX was invoked through an FD path (either binfmt_misc or execveat) then we need to check the
|
||||
// Program to see if it is a symlink to find the real path.
|
||||
//
|
||||
// binfmt_misc: Arg[0] is actually the execve `pathname` argument or `/dev/fd/<FD>` path
|
||||
// - `pathname` with execve (See Side Note)
|
||||
// - FD path with execveat and FD doesn't have an existing file on the disk
|
||||
//
|
||||
// ProgramFDFromEnv: Arg[0] is Application provided data or `/dev/fd/<FD>` from above fix-up.
|
||||
// - execveat was either passed no arguments (argv=NULL) or the first argument is an empty string (argv[0]="").
|
||||
// - FD path with execveat and FD doesn't have an existing file on the disk
|
||||
//
|
||||
// Side Note:
|
||||
// The `execve` syscall doesn't take an FD but binfmt_misc will give FEX an FD to execute still.
|
||||
// Arg[0] will always contain the `pathname` argument provided to execve.
|
||||
// It does not resolve symlinks, and it does not convert the path to absolute.
|
||||
//
|
||||
// Examples:
|
||||
// - Regular execve. Application must exist on disk.
|
||||
// execve binfmt_misc args layout: `FEXInterpreter <Path provided to execve pathname> <user provided argv[0]> <user provided argv[n]>...`
|
||||
// - Regular execveat with FD. FD is backed by application on disk.
|
||||
// execveat binfmt_misc args layout: `FEXInterpreter <Path provided to execve pathname> <user provided argv[0]> <user provided argv[n]>...`
|
||||
// - Regular execveat with FD. FD points to file on disk that has been deleted.
|
||||
// execveat binfmt_misc args layout: `FEXInterpreter /dev/fd/<FD> <user provided argv[0]> <user provided argv[n]>...`
|
||||
if (ExecFDInterp || !ProgramFDFromEnv.empty()) {
|
||||
// Only in the case that FEX is executing an FD will the program argument potentially be a symlink.
|
||||
// This symlink will be in the style of `/dev/fd/<FD>`.
|
||||
//
|
||||
// If the argument /is/ a symlink then resolve its path to get the original application name.
|
||||
if (FHU::Symlinks::IsSymlink(Program)) {
|
||||
char Filename[PATH_MAX];
|
||||
auto SymlinkPath = FHU::Symlinks::ResolveSymlink(Program, Filename);
|
||||
if (SymlinkPath.starts_with('/')) {
|
||||
// This file was executed through an FD.
|
||||
// Remove the ` (deleted)` text if the file was deleted after the fact.
|
||||
// Otherwise just get the symlink without the deleted text.
|
||||
return std::string{SymlinkPath.substr(0, SymlinkPath.rfind(" (deleted)"))};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Program;
|
||||
}
|
||||
|
||||
ApplicationNames LoadConfig(
|
||||
std::pair<std::string, std::string> LoadConfig(
|
||||
bool NoFEXArguments,
|
||||
bool LoadProgramConfig,
|
||||
int argc,
|
||||
char **argv,
|
||||
char **const envp,
|
||||
bool ExecFDInterp,
|
||||
const std::string_view ProgramFDFromEnv) {
|
||||
char **const envp) {
|
||||
FEXCore::Config::Initialize();
|
||||
FEXCore::Config::AddLayer(FEXCore::Config::CreateGlobalMainLayer());
|
||||
FEXCore::Config::AddLayer(FEXCore::Config::CreateMainLayer());
|
||||
@@ -133,8 +68,7 @@ namespace FEX::Config {
|
||||
return {};
|
||||
}
|
||||
|
||||
Args[0] = RecoverGuestProgramFilename(std::move(Args[0]), ExecFDInterp, ProgramFDFromEnv);
|
||||
std::string& Program = Args[0];
|
||||
std::string Program = Args[0];
|
||||
|
||||
bool Wine = false;
|
||||
std::filesystem::path ProgramName;
|
||||
@@ -187,7 +121,7 @@ namespace FEX::Config {
|
||||
FEXCore::Config::AddLayer(FEXCore::Config::CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_LOCAL_STEAM_APP));
|
||||
}
|
||||
|
||||
return ApplicationNames{std::move(Program), std::move(ProgramName)};
|
||||
return std::make_pair(Program, ProgramName);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
+2
-24
@@ -19,33 +19,11 @@ namespace FEX::Config {
|
||||
|
||||
void SaveLayerToJSON(const std::string& Filename, FEXCore::Config::Layer *const Layer);
|
||||
|
||||
struct ApplicationNames {
|
||||
// This is the full path to the program (if it exists).
|
||||
std::string ProgramPath;
|
||||
// This is the program executable name (if it exists).
|
||||
std::string ProgramName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Loads the FEX and application configurations for the application that is getting ready to run.
|
||||
*
|
||||
* @param NoFEXArguments Do we want to parse FEXLoader arguments, Or is this FEXInterpreter?
|
||||
* @param LoadProgramConfig Do we want to load application specific configurations?
|
||||
* @param argc The `argc` passed to main(...)
|
||||
* @param argv The `argv` passed to main(...)
|
||||
* @param envp The `envp` passed to main(...)
|
||||
* @param ExecFDInterp If FEX was executed with binfmt_misc FD argument
|
||||
* @param ProgramFDFromEnv The execveat FD argument passed through FEX
|
||||
*
|
||||
* @return The application name and path structure
|
||||
*/
|
||||
ApplicationNames LoadConfig(
|
||||
std::pair<std::string, std::string> LoadConfig(
|
||||
bool NoFEXArguments,
|
||||
bool LoadProgramConfig,
|
||||
int argc,
|
||||
char **argv,
|
||||
char **const envp,
|
||||
bool ExecFDInterp,
|
||||
const std::string_view ProgramFDFromEnv
|
||||
char **const envp
|
||||
);
|
||||
}
|
||||
@@ -12,83 +12,65 @@ $end_info$
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <system_error>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <vector>
|
||||
|
||||
namespace ELFLoader {
|
||||
ELFContainer::ELFType ELFContainer::GetELFType(std::string const &Filename) {
|
||||
std::fstream ELFFile(Filename, std::fstream::in | std::fstream::binary);
|
||||
|
||||
static ELFContainer::ELFType CheckELFType(uint8_t* Data) {
|
||||
if (Data[EI_MAG0] != ELFMAG0 ||
|
||||
Data[EI_MAG1] != ELFMAG1 ||
|
||||
Data[EI_MAG2] != ELFMAG2 ||
|
||||
Data[EI_MAG3] != ELFMAG3) {
|
||||
return ELFContainer::ELFType::TYPE_NONE;
|
||||
if (!ELFFile.is_open()) {
|
||||
return ELFType::TYPE_NONE;
|
||||
}
|
||||
|
||||
ELFFile.seekg(0, ELFFile.end);
|
||||
size_t FileSize = ELFFile.tellg();
|
||||
ELFFile.seekg(0, ELFFile.beg);
|
||||
|
||||
size_t ELFHeaderSize = std::max(sizeof(Elf32_Ehdr), sizeof(Elf64_Ehdr));
|
||||
if (FileSize < ELFHeaderSize) {
|
||||
return ELFType::TYPE_NONE;
|
||||
}
|
||||
|
||||
FileSize = ELFHeaderSize;
|
||||
|
||||
std::vector<char> RawFile(FileSize);
|
||||
ELFFile.read(RawFile.data(), FileSize);
|
||||
ELFFile.close();
|
||||
|
||||
uint8_t *Ident = reinterpret_cast<uint8_t*>(&RawFile.at(0));
|
||||
|
||||
if (Ident[EI_MAG0] != ELFMAG0 ||
|
||||
Ident[EI_MAG1] != ELFMAG1 ||
|
||||
Ident[EI_MAG2] != ELFMAG2 ||
|
||||
Ident[EI_MAG3] != ELFMAG3) {
|
||||
return ELFType::TYPE_NONE;
|
||||
}
|
||||
|
||||
union {
|
||||
Elf32_Ehdr _32;
|
||||
Elf64_Ehdr _64;
|
||||
} Header;
|
||||
|
||||
if (Ident[EI_CLASS] == ELFCLASS32) {
|
||||
memcpy(&Header, reinterpret_cast<Elf32_Ehdr *>(&RawFile.at(0)),
|
||||
sizeof(Elf32_Ehdr));
|
||||
if (Header._32.e_machine == EM_386) {
|
||||
return ELFType::TYPE_X86_32;
|
||||
}
|
||||
|
||||
if (Data[EI_CLASS] == ELFCLASS32) {
|
||||
Elf32_Ehdr *Header = reinterpret_cast<Elf32_Ehdr *>(Data);
|
||||
if (Header->e_machine == EM_386) {
|
||||
return ELFContainer::ELFType::TYPE_X86_32;
|
||||
}
|
||||
}
|
||||
else if (Ident[EI_CLASS] == ELFCLASS64) {
|
||||
memcpy(&Header, reinterpret_cast<Elf64_Ehdr *>(&RawFile.at(0)),
|
||||
sizeof(Elf64_Ehdr));
|
||||
if (Header._64.e_machine == EM_X86_64) {
|
||||
return ELFType::TYPE_X86_64;
|
||||
}
|
||||
else if (Data[EI_CLASS] == ELFCLASS64) {
|
||||
Elf64_Ehdr *Header = reinterpret_cast<Elf64_Ehdr *>(Data);
|
||||
if (Header->e_machine == EM_X86_64) {
|
||||
return ELFContainer::ELFType::TYPE_X86_64;
|
||||
}
|
||||
}
|
||||
|
||||
return ELFContainer::ELFType::TYPE_OTHER_ELF;
|
||||
}
|
||||
|
||||
ELFContainer::ELFType ELFContainer::GetELFType(std::string const &Filename) {
|
||||
// Open the Filename to determine if it is a shebang file.
|
||||
int FD = open(Filename.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (FD == -1) {
|
||||
return ELFType::TYPE_NONE;
|
||||
}
|
||||
|
||||
auto ELFType = GetELFType(FD);
|
||||
close(FD);
|
||||
return ELFType;
|
||||
}
|
||||
|
||||
ELFContainer::ELFType ELFContainer::GetELFType(int FD) {
|
||||
// We don't know the state of the FD coming in since this might be a guest tracked FD.
|
||||
// Need to be extra careful here not to adjust file offsets and status flags.
|
||||
//
|
||||
// We can't use dup since that makes the FD have the same underlying state backing both FDs.
|
||||
|
||||
// We need to first determine the file size through fstat.
|
||||
struct stat buf{};
|
||||
if (fstat(FD, &buf) == -1) {
|
||||
// Couldn't get size.
|
||||
return ELFType::TYPE_NONE;
|
||||
}
|
||||
|
||||
constexpr size_t ELFHeaderSize = std::max(sizeof(Elf32_Ehdr), sizeof(Elf64_Ehdr));
|
||||
if (buf.st_size < ELFHeaderSize) {
|
||||
// Is not a valid ELF.
|
||||
return ELFType::TYPE_NONE;
|
||||
}
|
||||
|
||||
std::array<char, ELFHeaderSize> RawFile;
|
||||
|
||||
// Read the header so we can tell if it is a supported ELF file.
|
||||
// Can't adjust file offset, so use pread.
|
||||
if (pread(FD, &RawFile.at(0), RawFile.size(), 0) != RawFile.size()) {
|
||||
// Couldn't read
|
||||
LogMan::Msg::EFmt("Couldn't read potential ELF FD");
|
||||
return ELFType::TYPE_NONE;
|
||||
}
|
||||
|
||||
return CheckELFType(reinterpret_cast<uint8_t*>(&RawFile.at(0)));
|
||||
return ELFType::TYPE_OTHER_ELF;
|
||||
}
|
||||
|
||||
ELFContainer::ELFContainer(std::string const &Filename, std::string const &RootFS, bool CustomInterpreter) {
|
||||
|
||||
@@ -117,7 +117,6 @@ public:
|
||||
TYPE_OTHER_ELF,
|
||||
};
|
||||
static ELFType GetELFType(std::string const &Filename);
|
||||
static ELFType GetELFType(int FD);
|
||||
static bool IsSupportedELF(std::string const &Filename) {
|
||||
ELFType Type = GetELFType(Filename);
|
||||
return Type == TYPE_X86_64 || Type == TYPE_X86_32;
|
||||
|
||||
+66
-35
@@ -7,47 +7,78 @@ if (TERMUX_BUILD)
|
||||
list(APPEND LIBS android-shmem)
|
||||
endif()
|
||||
|
||||
function(GenerateInterpreter NAME AsInterpreter)
|
||||
add_executable(${NAME}
|
||||
FEXLoader.cpp
|
||||
VDSO_Emulation.cpp
|
||||
AOT/AOTGenerator.cpp)
|
||||
add_executable(FEXLoader
|
||||
FEXLoader.cpp
|
||||
VDSO_Emulation.cpp
|
||||
AOT/AOTGenerator.cpp)
|
||||
|
||||
# Enable FEX APIs to be used by targets that use target_link_libraries on FEXLoader
|
||||
set_target_properties(${NAME} PROPERTIES ENABLE_EXPORTS 1)
|
||||
set_target_properties(FEXLoader PROPERTIES ENABLE_EXPORTS 1)
|
||||
|
||||
target_include_directories(${NAME}
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
target_link_libraries(${NAME}
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
LinuxEmulation
|
||||
${PTHREAD_LIB}
|
||||
fmt::fmt
|
||||
)
|
||||
target_compile_definitions(${NAME} PRIVATE -DFEXLOADER_AS_INTERPRETER=${AsInterpreter})
|
||||
target_include_directories(FEXLoader
|
||||
PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Source/
|
||||
${CMAKE_BINARY_DIR}/generated
|
||||
)
|
||||
target_link_libraries(FEXLoader
|
||||
PRIVATE
|
||||
${LIBS}
|
||||
LinuxEmulation
|
||||
${PTHREAD_LIB}
|
||||
fmt::fmt
|
||||
)
|
||||
|
||||
if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
|
||||
target_link_options(${NAME}
|
||||
PRIVATE
|
||||
"LINKER:--gc-sections"
|
||||
"LINKER:--strip-all"
|
||||
"LINKER:--as-needed"
|
||||
if (CMAKE_BUILD_TYPE MATCHES "RELEASE")
|
||||
target_link_options(FEXLoader
|
||||
PRIVATE
|
||||
"LINKER:--gc-sections"
|
||||
"LINKER:--strip-all"
|
||||
"LINKER:--as-needed"
|
||||
)
|
||||
endif()
|
||||
|
||||
install(TARGETS FEXLoader
|
||||
RUNTIME
|
||||
DESTINATION bin
|
||||
COMPONENT runtime
|
||||
)
|
||||
|
||||
if(TERMUX_BUILD)
|
||||
# Termux doesn't support hard links, just copy FEXLoader
|
||||
add_custom_target(FEXInterpreter ALL
|
||||
COMMAND "cp" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXLoader" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXInterpreter"
|
||||
DEPENDS FEXLoader
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND cp FEXLoader FEXInterpreter
|
||||
WORKING_DIRECTORY $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/
|
||||
)"
|
||||
)
|
||||
else()
|
||||
add_custom_target(FEXInterpreter ALL
|
||||
COMMAND "ln" "-f" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXLoader" "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/FEXInterpreter"
|
||||
DEPENDS FEXLoader
|
||||
)
|
||||
|
||||
install(
|
||||
CODE "MESSAGE(\"-- Installing: $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter\")"
|
||||
CODE "
|
||||
EXECUTE_PROCESS(COMMAND ln -f FEXLoader FEXInterpreter
|
||||
WORKING_DIRECTORY $ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/
|
||||
)"
|
||||
)
|
||||
|
||||
if(TARGET uninstall)
|
||||
add_custom_target(uninstall_FEXInterpreter
|
||||
COMMAND "rm" "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/FEXInterpreter"
|
||||
)
|
||||
|
||||
add_dependencies(uninstall uninstall_FEXInterpreter)
|
||||
endif()
|
||||
|
||||
install(TARGETS ${NAME}
|
||||
RUNTIME
|
||||
DESTINATION bin
|
||||
COMPONENT runtime
|
||||
)
|
||||
endfunction()
|
||||
|
||||
GenerateInterpreter(FEXLoader 0)
|
||||
GenerateInterpreter(FEXInterpreter 1)
|
||||
endif()
|
||||
|
||||
install(PROGRAMS "${PROJECT_SOURCE_DIR}/Scripts/FEXUpdateAOTIRCache.sh" DESTINATION bin RENAME FEXUpdateAOTIRCache)
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <FEXCore/Utils/LogManager.h>
|
||||
#include <FEXHeaderUtils/Syscalls.h>
|
||||
#include <FEXHeaderUtils/TypeDefines.h>
|
||||
#include <FEXHeaderUtils/SymlinkChecks.h>
|
||||
|
||||
#include <elf.h>
|
||||
#include <fcntl.h>
|
||||
@@ -35,7 +34,6 @@
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/personality.h>
|
||||
#include <sys/random.h>
|
||||
|
||||
#define PAGE_START(x) ((x) & ~(uintptr_t)(4095))
|
||||
#define PAGE_OFFSET(x) ((x) & 4095)
|
||||
@@ -178,17 +176,6 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
return LoadBase;
|
||||
}
|
||||
|
||||
static bool GetRandom(void *Data, size_t DataSize) {
|
||||
ssize_t Result{};
|
||||
do {
|
||||
// This is guaranteed to not be interrupted by a signal,
|
||||
// since fewer than 256 bytes of RNG data are requested
|
||||
Result = getrandom(Data, DataSize, 0);
|
||||
} while (Result != -1 && Result != DataSize);
|
||||
|
||||
return Result != -1;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static std::string ResolveRootfsFile(std::string const &File, std::string RootFS) {
|
||||
@@ -204,10 +191,10 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
// Do some special handling if the RootFS's linker is a symlink
|
||||
// Ubuntu's rootFS by default provides an absolute location symlink to the linker
|
||||
// Resolve this around back to the rootfs
|
||||
auto SymlinkPath = FHU::Symlinks::ResolveSymlink(RootFSLink, Filename);
|
||||
if (SymlinkPath.starts_with('/')) {
|
||||
auto SymlinkSize = FEX::HLE::GetSymlink(RootFSLink, Filename, PATH_MAX - 1);
|
||||
if (SymlinkSize > 0 && Filename[0] == '/') {
|
||||
RootFSLink = RootFS;
|
||||
RootFSLink += SymlinkPath;
|
||||
RootFSLink += std::string_view(Filename, SymlinkSize);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
@@ -228,24 +215,12 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
|
||||
std::vector<LoadedSection> Sections;
|
||||
|
||||
ELFCodeLoader2(std::string const &Filename, const std::string_view FEXFDString, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *AdditionalEnvp = nullptr) :
|
||||
ELFCodeLoader2(std::string const &Filename, std::string const &RootFS, [[maybe_unused]] std::vector<std::string> const &args, std::vector<std::string> const &ParsedArgs, char **const envp = nullptr, FEXCore::Config::Value<std::string> *AdditionalEnvp = nullptr) :
|
||||
Args {args} {
|
||||
|
||||
bool LoadedWithFD = false;
|
||||
int FD = getauxval(AT_EXECFD);
|
||||
|
||||
if (!FEXFDString.empty()) {
|
||||
// If we passed the execve FD to us then use that.
|
||||
const char *StartPtr = FEXFDString.data();
|
||||
char *EndPtr{};
|
||||
FD = ::strtol(StartPtr, &EndPtr, 10);
|
||||
if (EndPtr == StartPtr) {
|
||||
LogMan::Msg::AFmt("FEXInterpreter passed invalid FD to exececute: {}", FEXFDString);
|
||||
return;
|
||||
}
|
||||
unsetenv("FEX_EXECVEFD");
|
||||
}
|
||||
|
||||
// If we are provided an EXECFD then attempt to execute that first
|
||||
// This happens in the case of binfmt_misc usage
|
||||
if (FD != 0) {
|
||||
@@ -497,12 +472,9 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
if (!NoRandomize) {
|
||||
constexpr uint64_t ASLR_BITS_64 = 28;
|
||||
constexpr uint64_t ASLR_BITS_32 = 8;
|
||||
uint64_t ASLR_Offset{};
|
||||
if (!GetRandom(&ASLR_Offset, sizeof(ASLR_Offset))) {
|
||||
// getrandom failed for some reason.
|
||||
ASLR_Offset = 0;
|
||||
LogMan::Msg::EFmt("RNG failed. ASLR will not work.");
|
||||
}
|
||||
std::random_device rd;
|
||||
std::uniform_int_distribution<uint64_t> d(0);
|
||||
uint64_t ASLR_Offset = d(rd);
|
||||
|
||||
if (Is64BitMode()) {
|
||||
ASLR_Offset &= (1ULL << ASLR_BITS_64) - 1;
|
||||
@@ -748,12 +720,11 @@ class ELFCodeLoader2 final : public FEXCore::CodeLoader {
|
||||
}
|
||||
else {
|
||||
// Nothing provided from the kernel, generate our own random values.
|
||||
if (!GetRandom(&RandomLoc[0], sizeof(uint64_t) * 2)) {
|
||||
// getrandom failed for some reason.
|
||||
RandomLoc[0] = 0;
|
||||
RandomLoc[1] = 0;
|
||||
LogMan::Msg::EFmt("RNG failed. AT_RANDOM will not be random.");
|
||||
}
|
||||
std::random_device rd;
|
||||
std::uniform_int_distribution<uint64_t> d(0);
|
||||
|
||||
RandomLoc[0] = d(rd);
|
||||
RandomLoc[1] = d(rd);
|
||||
}
|
||||
|
||||
// Stack setup
|
||||
|
||||
+12
-27
@@ -184,7 +184,7 @@ void RootFSRedirect(std::string *Filename, std::string const &RootFS) {
|
||||
}
|
||||
|
||||
bool RanAsInterpreter(const char *Program) {
|
||||
return ExecutedWithFD || FEXLOADER_AS_INTERPRETER;
|
||||
return ExecutedWithFD || strstr(Program, "FEXInterpreter") != nullptr;
|
||||
}
|
||||
|
||||
bool IsInterpreterInstalled() {
|
||||
@@ -200,8 +200,6 @@ int main(int argc, char **argv, char **const envp) {
|
||||
const bool IsInterpreter = RanAsInterpreter(argv[0]);
|
||||
|
||||
ExecutedWithFD = getauxval(AT_EXECFD) != 0;
|
||||
const char* FEXFD = getenv("FEX_EXECVEFD");
|
||||
const std::string_view FEXFDView = FEXFD ? std::string_view{FEXFD} : std::string_view{};
|
||||
|
||||
LogMan::Throw::InstallHandler(AssertHandler);
|
||||
LogMan::Msg::InstallHandler(MsgHandler);
|
||||
@@ -209,11 +207,10 @@ int main(int argc, char **argv, char **const envp) {
|
||||
auto Program = FEX::Config::LoadConfig(
|
||||
IsInterpreter,
|
||||
true,
|
||||
argc, argv, envp,
|
||||
ExecutedWithFD,
|
||||
FEXFDView);
|
||||
argc, argv, envp
|
||||
);
|
||||
|
||||
if (Program.ProgramPath.empty() && !FEXFD) {
|
||||
if (Program.first.empty()) {
|
||||
// Early exit if we weren't passed an argument
|
||||
return 0;
|
||||
}
|
||||
@@ -289,14 +286,14 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEXCore::Profiler::Init();
|
||||
FEXCore::Telemetry::Initialize();
|
||||
|
||||
RootFSRedirect(&Program.ProgramPath, LDPath());
|
||||
InterpreterHandler(&Program.ProgramPath, LDPath(), &Args);
|
||||
RootFSRedirect(&Program.first, LDPath());
|
||||
InterpreterHandler(&Program.first, LDPath(), &Args);
|
||||
|
||||
std::error_code ec{};
|
||||
if (!ExecutedWithFD && !FEXFD && !std::filesystem::exists(Program.ProgramPath, ec)) {
|
||||
if (!std::filesystem::exists(Program.first, ec)) {
|
||||
// Early exit if the program passed in doesn't exist
|
||||
// Will prevent a crash later
|
||||
fmt::print(stderr, "{}: command not found\n", Program.ProgramPath);
|
||||
fmt::print(stderr, "{}: command not found\n", Program.first);
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
@@ -313,7 +310,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
putenv(HostEnv.data());
|
||||
}
|
||||
|
||||
ELFCodeLoader2 Loader{Program.ProgramPath, FEXFDView, LDPath(), Args, ParsedArgs, envp, &Environment};
|
||||
ELFCodeLoader2 Loader{Program.first, LDPath(), Args, ParsedArgs, envp, &Environment};
|
||||
//FEX::HarnessHelper::ELFCodeLoader Loader{Program.first, LDPath(), Args, ParsedArgs, envp, &Environment};
|
||||
|
||||
if (!Loader.ELFWasLoaded()) {
|
||||
@@ -332,20 +329,8 @@ int main(int argc, char **argv, char **const envp) {
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
if (ExecutedWithFD) {
|
||||
// Don't need to canonicalize Program.ProgramPath, Config loader will have resolved this already.
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, Program.ProgramPath);
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.ProgramName);
|
||||
}
|
||||
else if (FEXFD) {
|
||||
// Anonymous program.
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, "<Anonymous>");
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, "<Anonymous>");
|
||||
}
|
||||
else {
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.ProgramPath).string());
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.ProgramName);
|
||||
}
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_FILENAME, std::filesystem::canonical(Program.first).string());
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_APP_CONFIG_NAME, Program.second);
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_IS64BIT_MODE, Loader.Is64BitMode() ? "1" : "0");
|
||||
|
||||
std::unique_ptr<FEX::HLE::MemAllocator> Allocator;
|
||||
@@ -521,7 +506,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEXCore::Allocator::ClearHooks();
|
||||
FEXCore::Allocator::ReclaimMemoryRegion(Base48Bit);
|
||||
// Allocator is now original system allocator
|
||||
FEXCore::Telemetry::Shutdown(Program.ProgramName);
|
||||
FEXCore::Telemetry::Shutdown(Program.second);
|
||||
FEXCore::Profiler::Shutdown();
|
||||
if (ShutdownReason == FEXCore::Context::ExitReason::EXIT_SHUTDOWN) {
|
||||
return ProgramStatus;
|
||||
|
||||
@@ -201,22 +201,22 @@ namespace FEX::HarnessHelper {
|
||||
if (BaseConfig.OptionRegDataCount > 0) {
|
||||
static constexpr std::array<uint64_t, 45> OffsetArrayAVX = {{
|
||||
offsetof(FEXCore::Core::CPUState, rip),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RAX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RCX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSI]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDI]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBP]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSP]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R8]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R9]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R10]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R11]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R12]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R13]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R14]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R15]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[0]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[1]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[2]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[3]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[4]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[5]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[6]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[7]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[8]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[9]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[10]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[11]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[12]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[13]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[14]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[15]),
|
||||
offsetof(FEXCore::Core::CPUState, xmm.avx.data[0][0]),
|
||||
offsetof(FEXCore::Core::CPUState, xmm.avx.data[1][0]),
|
||||
offsetof(FEXCore::Core::CPUState, xmm.avx.data[2][0]),
|
||||
@@ -248,22 +248,22 @@ namespace FEX::HarnessHelper {
|
||||
}};
|
||||
static constexpr std::array<uint64_t, 45> OffsetArraySSE = {{
|
||||
offsetof(FEXCore::Core::CPUState, rip),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RAX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RCX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDX]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSI]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RDI]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RBP]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_RSP]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R8]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R9]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R10]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R11]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R12]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R13]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R14]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[FEXCore::X86State::REG_R15]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[0]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[1]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[2]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[3]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[4]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[5]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[6]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[7]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[8]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[9]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[10]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[11]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[12]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[13]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[14]),
|
||||
offsetof(FEXCore::Core::CPUState, gregs[15]),
|
||||
offsetof(FEXCore::Core::CPUState, xmm.sse.data[0][0]),
|
||||
offsetof(FEXCore::Core::CPUState, xmm.sse.data[1][0]),
|
||||
offsetof(FEXCore::Core::CPUState, xmm.sse.data[2][0]),
|
||||
@@ -385,7 +385,6 @@ namespace FEX::HarnessHelper {
|
||||
FEATURE_CLZERO = (1 << 5),
|
||||
FEATURE_BMI1 = (1 << 6),
|
||||
FEATURE_BMI2 = (1 << 7),
|
||||
FEATURE_CLWB = (1 << 8),
|
||||
};
|
||||
|
||||
bool Requires3DNow() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_3DNOW; }
|
||||
@@ -396,7 +395,6 @@ namespace FEX::HarnessHelper {
|
||||
bool RequiresCLZERO() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_CLZERO; }
|
||||
bool RequiresBMI1() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI1; }
|
||||
bool RequiresBMI2() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_BMI2; }
|
||||
bool RequiresCLWB() const { return BaseConfig.OptionHostFeatures & HostFeatures::FEATURE_CLWB; }
|
||||
|
||||
private:
|
||||
FEX_CONFIG_OPT(ConfigDumpGPRs, DUMPGPRS);
|
||||
@@ -536,7 +534,6 @@ namespace FEX::HarnessHelper {
|
||||
bool RequiresCLZERO() const { return Config.RequiresCLZERO(); }
|
||||
bool RequiresBMI1() const { return Config.RequiresBMI1(); }
|
||||
bool RequiresBMI2() const { return Config.RequiresBMI2(); }
|
||||
bool RequiresCLWB() const { return Config.RequiresCLWB(); }
|
||||
|
||||
private:
|
||||
constexpr static uint64_t STACK_SIZE = FHU::FEX_PAGE_SIZE;
|
||||
|
||||
@@ -42,7 +42,6 @@ $end_info$
|
||||
#include <memory>
|
||||
#include <regex>
|
||||
#include <sched.h>
|
||||
#include <span>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
@@ -167,19 +166,33 @@ uint64_t GetDentsEmulation<false>(int, FEX::HLE::x64::linux_dirent*, uint32_t);
|
||||
template
|
||||
uint64_t GetDentsEmulation<true>(int, FEX::HLE::x32::linux_dirent_32*, uint32_t);
|
||||
|
||||
static bool IsShebangFile(std::span<char> Data) {
|
||||
// File isn't large enough to even contain a shebang.
|
||||
if (Data.size() <= 2) {
|
||||
return false;
|
||||
static bool IsSupportedByInterpreter(std::string const &Filename) {
|
||||
// If it is a supported ELF then we can
|
||||
if (ELFLoader::ELFContainer::IsSupportedELF(Filename.c_str())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle shebang files.
|
||||
if (Data[0] == '#' &&
|
||||
Data[1] == '!') {
|
||||
std::string InterpreterLine {
|
||||
Data.begin() + 2, // strip off "#!" prefix
|
||||
std::find(Data.begin(), Data.end(), '\n')
|
||||
};
|
||||
// If it is a shebang then we also can
|
||||
std::fstream File;
|
||||
size_t FileSize{0};
|
||||
File.open(Filename, std::fstream::in | std::fstream::binary);
|
||||
|
||||
if (!File.is_open())
|
||||
return false;
|
||||
|
||||
File.seekg(0, File.end);
|
||||
FileSize = File.tellg();
|
||||
File.seekg(0, File.beg);
|
||||
|
||||
// Is the file large enough for shebang
|
||||
if (FileSize <= 2)
|
||||
return false;
|
||||
|
||||
// Handle shebang files
|
||||
if (File.get() == '#' &&
|
||||
File.get() == '!') {
|
||||
std::string InterpreterLine;
|
||||
std::getline(File, InterpreterLine);
|
||||
std::vector<std::string> ShebangArguments{};
|
||||
|
||||
// Shebang line can have a single argument
|
||||
@@ -189,7 +202,7 @@ static bool IsShebangFile(std::span<char> Data) {
|
||||
if (Argument.empty()) {
|
||||
continue;
|
||||
}
|
||||
ShebangArguments.push_back(std::move(Argument));
|
||||
ShebangArguments.emplace_back(Argument);
|
||||
}
|
||||
|
||||
// Executable argument
|
||||
@@ -198,107 +211,57 @@ static bool IsShebangFile(std::span<char> Data) {
|
||||
// If the filename is absolute then prepend the rootfs
|
||||
// If it is relative then don't append the rootfs
|
||||
if (ShebangProgram[0] == '/') {
|
||||
ShebangProgram = FEX::HLE::_SyscallHandler->RootFSPath() + ShebangProgram;
|
||||
std::string RootFS = FEX::HLE::_SyscallHandler->RootFSPath();
|
||||
ShebangProgram = RootFS + ShebangProgram;
|
||||
}
|
||||
|
||||
std::error_code ec;
|
||||
bool exists = std::filesystem::exists(ShebangProgram, ec);
|
||||
return !ec && exists;
|
||||
if (ec || !exists) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool IsShebangFD(int FD) {
|
||||
// We don't know the state of the FD coming in since this might be a guest tracked FD.
|
||||
// Need to be extra careful here not to adjust file offsets and status flags.
|
||||
//
|
||||
// Can't use dup since that makes the FD have the same file description backing both FDs.
|
||||
|
||||
// The maximum length of the shebang line is `#!` + 255 chars
|
||||
std::array<char, 257> Header;
|
||||
const auto ChunkSize = 257l;
|
||||
const auto ReadSize = pread(FD, &Header.at(0), ChunkSize, 0);
|
||||
|
||||
return IsShebangFile(std::span<char>(Header.data(), ReadSize));
|
||||
}
|
||||
|
||||
static bool IsShebangFilename(std::string const &Filename) {
|
||||
// Open the Filename to determine if it is a shebang file.
|
||||
int FD = open(Filename.c_str(), O_RDONLY | O_CLOEXEC);
|
||||
if (FD == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsShebang = IsShebangFD(FD);
|
||||
close(FD);
|
||||
return IsShebang;
|
||||
}
|
||||
|
||||
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs Args) {
|
||||
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs *Args) {
|
||||
std::string Filename{};
|
||||
|
||||
std::error_code ec;
|
||||
std::string RootFS = FEX::HLE::_SyscallHandler->RootFSPath();
|
||||
ELFLoader::ELFContainer::ELFType Type{};
|
||||
|
||||
// AT_EMPTY_PATH is only used if the pathname is empty.
|
||||
const bool IsFDExec = (Args.flags & AT_EMPTY_PATH) && strlen(pathname) == 0;
|
||||
std::string FDExecEnv;
|
||||
|
||||
bool IsShebang{};
|
||||
|
||||
if (IsFDExec) {
|
||||
Type = ELFLoader::ELFContainer::GetELFType(Args.dirfd);
|
||||
|
||||
IsShebang = IsShebangFD(Args.dirfd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// For absolute paths, check the rootfs first (if available)
|
||||
if (pathname[0] == '/') {
|
||||
auto Path = FEX::HLE::_SyscallHandler->FM.GetEmulatedPath(pathname, true);
|
||||
if (!Path.empty() && std::filesystem::exists(Path, ec)) {
|
||||
Filename = Path;
|
||||
}
|
||||
else {
|
||||
Filename = pathname;
|
||||
}
|
||||
// Check the rootfs if it is available first
|
||||
if (pathname[0] == '/') {
|
||||
auto Path = FEX::HLE::_SyscallHandler->FM.GetEmulatedPath(pathname, true);
|
||||
if (!Path.empty() && std::filesystem::exists(Path, ec)) {
|
||||
Filename = Path;
|
||||
}
|
||||
else {
|
||||
Filename = pathname;
|
||||
}
|
||||
|
||||
bool exists = std::filesystem::exists(Filename, ec);
|
||||
if (ec || !exists) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int pid = getpid();
|
||||
|
||||
char PidSelfPath[50];
|
||||
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
|
||||
|
||||
if (strcmp(pathname, "/proc/self/exe") == 0 ||
|
||||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
|
||||
strcmp(pathname, PidSelfPath) == 0) {
|
||||
// If the application is trying to execve `/proc/self/exe` or its variants,
|
||||
// then we need to redirect this path to the true application path.
|
||||
// This is because this path is a symlink to the executing application, which is always `FEXInterpreter` or `FEXLoader`.
|
||||
// ex: JRE and shapez.io do this self-execution.
|
||||
Filename = FEX::HLE::_SyscallHandler->Filename();
|
||||
}
|
||||
|
||||
Type = ELFLoader::ELFContainer::GetELFType(Filename);
|
||||
|
||||
IsShebang = IsShebangFilename(Filename);
|
||||
}
|
||||
else {
|
||||
Filename = pathname;
|
||||
}
|
||||
|
||||
if (!IsShebang && Type == ELFLoader::ELFContainer::ELFType::TYPE_NONE) {
|
||||
// If our interpeter doesn't support this file format AND ELF format is NONE then ENOEXEC
|
||||
// binfmt_misc could end up handling this case but we can't know that without parsing binfmt_misc ourselves
|
||||
// Return -ENOEXEC until proven otherwise
|
||||
return -ENOEXEC;
|
||||
bool exists = std::filesystem::exists(Filename, ec);
|
||||
if (ec || !exists) {
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
int pid = getpid();
|
||||
|
||||
char PidSelfPath[50];
|
||||
snprintf(PidSelfPath, 50, "/proc/%i/exe", pid);
|
||||
|
||||
if (strcmp(pathname, "/proc/self/exe") == 0 ||
|
||||
strcmp(pathname, "/proc/thread-self/exe") == 0 ||
|
||||
strcmp(pathname, PidSelfPath) == 0) {
|
||||
// If pointing to self then redirect to the application
|
||||
// JRE and shapez.io does this
|
||||
Filename = FEX::HLE::_SyscallHandler->Filename();
|
||||
}
|
||||
|
||||
// If we don't have the interpreter installed we need to be extra careful for ENOEXEC
|
||||
@@ -306,6 +269,7 @@ uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* env
|
||||
// Kernel does its own checks for file format support for this
|
||||
// We can only call execve directly if we both have an interpreter installed AND were ran with the interpreter
|
||||
// If the user ran FEX through FEXLoader then we must go down the emulated path
|
||||
ELFLoader::ELFContainer::ELFType Type = ELFLoader::ELFContainer::GetELFType(Filename);
|
||||
uint64_t Result{};
|
||||
if (FEX::HLE::_SyscallHandler->IsInterpreterInstalled() &&
|
||||
FEX::HLE::_SyscallHandler->IsInterpreter() &&
|
||||
@@ -313,24 +277,38 @@ uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* env
|
||||
Type == ELFLoader::ELFContainer::ELFType::TYPE_X86_64)) {
|
||||
// If the FEX interpreter is installed then just execve the ELF file
|
||||
// This will stay inside of our emulated environment since binfmt_misc will capture it
|
||||
Result = ::syscall(SYS_execveat, Args.dirfd, Filename.c_str(), argv, envp, Args.flags);
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), argv, envp, Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve(Filename.c_str(), argv, envp);
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
if (!IsSupportedByInterpreter(Filename) && Type == ELFLoader::ELFContainer::ELFType::TYPE_NONE) {
|
||||
// If our interpeter doesn't support this file format AND ELF format is NONE then ENOEXEC
|
||||
// binfmt_misc could end up handling this case but we can't know that without parsing binfmt_misc ourselves
|
||||
// Return -ENOEXEC until proven otherwise
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
if (Type == ELFLoader::ELFContainer::ELFType::TYPE_OTHER_ELF) {
|
||||
// We are trying to execute an ELF of a different architecture
|
||||
// We can't know if we can support this without architecture specific checks and binfmt_misc parsing
|
||||
// Just execve it and let the kernel handle the process
|
||||
Result = ::syscall(SYS_execveat, Args.dirfd, Filename.c_str(), argv, envp, Args.flags);
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, Filename.c_str(), argv, envp, Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve(Filename.c_str(), argv, envp);
|
||||
}
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
// We don't have an interpreter installed or we are executing a non-ELF executable
|
||||
// We now need to munge the arguments
|
||||
std::vector<const char *> ExecveArgs{};
|
||||
std::vector<const char *> EnvpArgs{};
|
||||
char *const *EnvpPtr = envp;
|
||||
const char NullString[] = "";
|
||||
FEX::HLE::_SyscallHandler->GetCodeLoader()->GetExecveArguments(&ExecveArgs);
|
||||
if (!FEX::HLE::_SyscallHandler->IsInterpreter()) {
|
||||
// If we were launched from FEXLoader then we need to make sure to split arguments from FEXLoader and guest
|
||||
@@ -343,61 +321,25 @@ uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* env
|
||||
|
||||
auto OldArgv = argv;
|
||||
|
||||
// It is valid to provide nullptr first argument.
|
||||
if (*OldArgv) {
|
||||
// Skip filename argument
|
||||
// Skip filename argument
|
||||
++OldArgv;
|
||||
while (*OldArgv) {
|
||||
// Append the arguments together
|
||||
ExecveArgs.emplace_back(*OldArgv);
|
||||
++OldArgv;
|
||||
while (*OldArgv) {
|
||||
// Append the arguments together
|
||||
ExecveArgs.emplace_back(*OldArgv);
|
||||
++OldArgv;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Linux kernel will stick an empty argument in to the argv list if none are provided.
|
||||
ExecveArgs.emplace_back(NullString);
|
||||
}
|
||||
|
||||
// Emplace nullptr at the end to stop
|
||||
ExecveArgs.emplace_back(nullptr);
|
||||
}
|
||||
|
||||
if (IsFDExec) {
|
||||
if (envp) {
|
||||
auto OldEnvp = envp;
|
||||
while (*OldEnvp) {
|
||||
EnvpArgs.emplace_back(*OldEnvp);
|
||||
++OldEnvp;
|
||||
}
|
||||
}
|
||||
|
||||
int Flags = fcntl(Args.dirfd, F_GETFD);
|
||||
if (Flags & FD_CLOEXEC) {
|
||||
// FEX needs the FD to live past execve when binfmt_misc isn't used,
|
||||
// so duplicate the FD if FD_CLOEXEC is set
|
||||
Args.dirfd = dup(Args.dirfd);
|
||||
}
|
||||
|
||||
// Remove AT_EMPTY_PATH flag now.
|
||||
// We need to emulate this flag with `FEX_EXECVEFD` environment variable.
|
||||
// If we passed this flag through to the real `execveat` then the target FD wouldn't get emulated by FEX.
|
||||
Args.flags &= ~AT_EMPTY_PATH;
|
||||
|
||||
// Create the environment variable to pass the FD to our FEX.
|
||||
// Needs to stick around until execveat completes.
|
||||
FDExecEnv = "FEX_EXECVEFD=" + std::to_string(Args.dirfd);
|
||||
|
||||
// Insert the FD for FEX to track.
|
||||
EnvpArgs.emplace_back(FDExecEnv.data());
|
||||
|
||||
// Emplace nullptr at the end to stop
|
||||
EnvpArgs.emplace_back(nullptr);
|
||||
|
||||
EnvpPtr = const_cast<char *const *>(EnvpArgs.data());
|
||||
if (Args) {
|
||||
Result = ::syscall(SYS_execveat, Args->dirfd, "/proc/self/exe",
|
||||
const_cast<char *const *>(ExecveArgs.data()), envp, Args->flags);
|
||||
}
|
||||
else {
|
||||
Result = execve("/proc/self/exe", const_cast<char *const *>(ExecveArgs.data()), envp);
|
||||
}
|
||||
|
||||
Result = ::syscall(SYS_execveat, Args.dirfd, "/proc/self/exe",
|
||||
const_cast<char *const *>(ExecveArgs.data()), EnvpPtr, Args.flags);
|
||||
|
||||
SYSCALL_ERRNO();
|
||||
}
|
||||
|
||||
@@ -20,7 +20,6 @@ $end_info$
|
||||
#include <shared_mutex>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdint.h>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
@@ -81,15 +80,9 @@ uint64_t UnimplementedSyscallSafe(FEXCore::Core::CpuStateFrame *Frame, uint64_t
|
||||
struct ExecveAtArgs {
|
||||
int dirfd;
|
||||
int flags;
|
||||
static ExecveAtArgs Empty() {
|
||||
return ExecveAtArgs {
|
||||
.dirfd = AT_FDCWD,
|
||||
.flags = 0,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs Args);
|
||||
uint64_t ExecveHandler(const char *pathname, char* const* argv, char* const* envp, ExecveAtArgs *Args);
|
||||
|
||||
class SyscallHandler : public FEXCore::HLE::SyscallHandler, FEXCore::HLE::SourcecodeResolver {
|
||||
public:
|
||||
|
||||
@@ -330,10 +330,7 @@ namespace FEX::HLE::x32 {
|
||||
|
||||
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
|
||||
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
|
||||
|
||||
FEX::HLE::ExecveAtArgs AtArgs = FEX::HLE::ExecveAtArgs::Empty();
|
||||
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, nullptr);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(execveat, ([](FEXCore::Core::CpuStateFrame *Frame, int dirfd, const char *pathname, uint32_t *argv, uint32_t *envp, int flags) -> uint64_t {
|
||||
@@ -362,7 +359,7 @@ namespace FEX::HLE::x32 {
|
||||
|
||||
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
|
||||
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, &AtArgs);
|
||||
}));
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X32(wait4, [](FEXCore::Core::CpuStateFrame *Frame, pid_t pid, int *wstatus, int options, struct rusage_32 *rusage) -> uint64_t {
|
||||
|
||||
@@ -119,10 +119,7 @@ namespace FEX::HLE::x64 {
|
||||
|
||||
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
|
||||
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
|
||||
|
||||
FEX::HLE::ExecveAtArgs AtArgs = FEX::HLE::ExecveAtArgs::Empty();
|
||||
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, nullptr);
|
||||
});
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_FLAGS(execveat, SyscallFlags::DEFAULT,
|
||||
@@ -153,7 +150,7 @@ namespace FEX::HLE::x64 {
|
||||
|
||||
auto* const* ArgsPtr = argv ? const_cast<char* const*>(Args.data()) : nullptr;
|
||||
auto* const* EnvpPtr = envp ? const_cast<char* const*>(Envp.data()) : nullptr;
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, AtArgs);
|
||||
return FEX::HLE::ExecveHandler(pathname, ArgsPtr, EnvpPtr, &AtArgs);
|
||||
}));
|
||||
|
||||
REGISTER_SYSCALL_IMPL_X64_PASS_FLAGS(wait4, SyscallFlags::OPTIMIZETHROUGH | SyscallFlags::NOSYNCSTATEONENTRY,
|
||||
|
||||
@@ -178,8 +178,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
(!HostFeatures.SupportsSHA && Loader.RequiresSHA()) ||
|
||||
(!HostFeatures.SupportsCLZERO && Loader.RequiresCLZERO()) ||
|
||||
(!HostFeatures.SupportsBMI1 && Loader.RequiresBMI1()) ||
|
||||
(!HostFeatures.SupportsBMI2 && Loader.RequiresBMI2()) ||
|
||||
(!HostFeatures.SupportsCLWB && Loader.RequiresCLWB());
|
||||
(!HostFeatures.SupportsBMI2 && Loader.RequiresBMI2());
|
||||
|
||||
if (TestUnsupported) {
|
||||
FEXCore::Context::DestroyContext(CTX);
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
Label Gate{};
|
||||
// Patch gate entry point
|
||||
// mov(dword[rip + Gate], edi)
|
||||
jmp(qword [rip + Gate], LabelType::T_FAR);
|
||||
jmpf(ptr[rip + Gate]);
|
||||
|
||||
L(Gate);
|
||||
dd(0x1'0000); // This is a 32-bit offset from the start of the gate. We start at 0x1'0000 + 0
|
||||
|
||||
@@ -19,20 +19,13 @@
|
||||
|
||||
namespace ArgOptions {
|
||||
bool AssumeYes = false;
|
||||
enum class CompressedImageOption {
|
||||
enum CompressedImageOption {
|
||||
OPTION_ASK,
|
||||
OPTION_EXTRACT,
|
||||
OPTION_ASIS,
|
||||
};
|
||||
|
||||
CompressedImageOption CompressedUsageOption {CompressedImageOption::OPTION_ASK};
|
||||
|
||||
enum class ListQueryOption {
|
||||
OPTION_ASK,
|
||||
OPTION_FIRST,
|
||||
};
|
||||
|
||||
ListQueryOption DistroListOption {ListQueryOption::OPTION_ASK};
|
||||
CompressedImageOption CompressedUsageOption {OPTION_ASK};
|
||||
|
||||
std::vector<std::string> RemainingArgs;
|
||||
|
||||
@@ -62,9 +55,6 @@ namespace ArgOptions {
|
||||
Parser.add_option("--distro-version")
|
||||
.help("Which distro version to select");
|
||||
|
||||
Parser.add_option("--distro-list-first")
|
||||
.action("store_true")
|
||||
.help("When presented the distro-list option, automatically select the first distro if there isn't an exact match.");
|
||||
|
||||
optparse::Values Options = Parser.parse_args(argc, argv);
|
||||
|
||||
@@ -73,15 +63,11 @@ namespace ArgOptions {
|
||||
}
|
||||
|
||||
if (Options.is_set_by_user("extract")) {
|
||||
CompressedUsageOption = CompressedImageOption::OPTION_EXTRACT;
|
||||
CompressedUsageOption = OPTION_EXTRACT;
|
||||
}
|
||||
|
||||
if (Options.is_set_by_user("as_is")) {
|
||||
CompressedUsageOption = CompressedImageOption::OPTION_ASIS;
|
||||
}
|
||||
|
||||
if (Options.is_set_by_user("distro_list_first")) {
|
||||
DistroListOption = ListQueryOption::OPTION_FIRST;
|
||||
CompressedUsageOption = OPTION_ASIS;
|
||||
}
|
||||
|
||||
if (Options.is_set_by_user("distro_name")) {
|
||||
@@ -717,11 +703,6 @@ namespace Zenity {
|
||||
return DistroIndex;
|
||||
}
|
||||
|
||||
if (ArgOptions::DistroListOption == ArgOptions::ListQueryOption::OPTION_FIRST) {
|
||||
// Return the first option if not an exact match.
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> Args;
|
||||
|
||||
Args.emplace_back("--column=Index");
|
||||
@@ -895,11 +876,6 @@ namespace TTY {
|
||||
return DistroIndex;
|
||||
}
|
||||
|
||||
if (ArgOptions::DistroListOption == ArgOptions::ListQueryOption::OPTION_FIRST) {
|
||||
// Return the first option if not an exact match.
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::vector<std::string> Args;
|
||||
for (size_t i = 0; i < Targets.size(); ++i) {
|
||||
const auto &Target = Targets[i];
|
||||
@@ -1104,8 +1080,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEX::Config::LoadConfig(
|
||||
true,
|
||||
false,
|
||||
argc, argv, envp,
|
||||
false, {}
|
||||
argc, argv, envp
|
||||
);
|
||||
|
||||
// Reload the meta layer
|
||||
|
||||
@@ -146,8 +146,7 @@ int main(int argc, char **argv, char **const envp) {
|
||||
FEX::Config::LoadConfig(
|
||||
true,
|
||||
false,
|
||||
argc, argv, envp,
|
||||
false, {}
|
||||
argc, argv, envp
|
||||
);
|
||||
|
||||
// Reload the meta layer
|
||||
|
||||
@@ -474,7 +474,7 @@ namespace ProcessPipe {
|
||||
// Add the new client to the temporary array
|
||||
NewPollFDs.emplace_back(pollfd {
|
||||
.fd = NewFD,
|
||||
.events = POLLIN | POLLPRI | POLLRDHUP,
|
||||
.events = POLLIN | POLLPRI | POLLRDHUP | POLLREMOVE,
|
||||
.revents = 0,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace SquashFS {
|
||||
|
||||
void ShutdownImagePID() {
|
||||
if (FuseMountPID) {
|
||||
FHU::Syscalls::tgkill(FuseMountPID, FuseMountPID, SIGINT);
|
||||
tgkill(FuseMountPID, FuseMountPID, SIGINT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -442,7 +442,7 @@ void GenerateThunkLibsAction::ParseInterface(clang::ASTContext& context) {
|
||||
}
|
||||
|
||||
thunked_api.push_back(ThunkedAPIFunction { (const FunctionParams&)data, data.function_name, data.return_type,
|
||||
namespace_info.host_loader.empty() ? "dlsym_default" : namespace_info.host_loader,
|
||||
namespace_info.host_loader.empty() ? "dlsym" : namespace_info.host_loader,
|
||||
data.is_variadic || annotations.custom_guest_entrypoint,
|
||||
data.is_variadic,
|
||||
std::nullopt });
|
||||
@@ -761,11 +761,7 @@ void GenerateThunkLibsAction::EmitOutput() {
|
||||
version_suffix = '.' + std::to_string(*lib_version);
|
||||
}
|
||||
const std::string library_filename = libfilename + ".so" + version_suffix;
|
||||
|
||||
// Load the host library in the global symbol namespace.
|
||||
// This follows how these libraries get loaded in a non-emulated environment,
|
||||
// Either by directly linking to the library or a loader (In OpenGL or Vulkan) putting everything in the global namespace.
|
||||
file << " fexldr_ptr_" << libname << "_so = dlopen(\"" << library_filename << "\", RTLD_GLOBAL | RTLD_LAZY);\n";
|
||||
file << " fexldr_ptr_" << libname << "_so = dlopen(\"" << library_filename << "\", RTLD_LOCAL | RTLD_LAZY);\n";
|
||||
|
||||
file << " if (!fexldr_ptr_" << libname << "_so) { return false; }\n\n";
|
||||
for (auto& import : thunked_api) {
|
||||
|
||||
@@ -8,7 +8,6 @@ $end_info$
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <dlfcn.h>
|
||||
|
||||
#include "PackedArguments.h"
|
||||
|
||||
@@ -193,14 +192,3 @@ void FinalizeHostTrampolineForGuestFunction(F* PreallocatedTrampolineForGuestFun
|
||||
(FEXCore::HostToGuestTrampolinePtr*)PreallocatedTrampolineForGuestFunction,
|
||||
(void*)&CallbackUnpack<F>::CallGuestPtr);
|
||||
}
|
||||
|
||||
// In the case of the thunk host_loader being the default, FEX need to use dlsym with RTLD_DEFAULT.
|
||||
// If FEX queried the symbol object directly then it wouldn't follow symbol overriding rules.
|
||||
//
|
||||
// Common usecase is LD_PRELOAD with a library that defines some symbols.
|
||||
// And then programs and libraries will pick up the preloaded symbols.
|
||||
// ex: MangoHud overrides GLX and EGL symbols.
|
||||
inline
|
||||
void *dlsym_default(void* handle, const char* symbol) {
|
||||
return dlsym(RTLD_DEFAULT, symbol);
|
||||
}
|
||||
Loaded 100 of 164 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user