Compare commits

..
7 Commits
Author SHA1 Message Date
Ryan Houdek 4f9c1f1e36 Docs: Update for release FEX-2611.1 2026-11-11 11:13:58 -08:00
Ryan Houdek 5e18ae0dd4 FEXRootFSFetcher: Fixes Config setting
A couple paths should get generated early so later routines have paths
properly made. Fixes an issue that a fresh install to XDG paths wouldn't
have some paths set.

This has been an issue every since the XDG paths were introduced, I just
never looked in to why.
2026-11-11 11:13:02 -08:00
Ryan Houdek 0ec72ac1a7 ImageTracker: Actually wire up config option for Volatile Metadata
I forgot to wire this up, so you couldn't turn off an application's
volatile metadata for testing.
2026-11-11 11:12:55 -08:00
Ryan Houdek 16233b3b6a Windows: Fixes forced assert
This wasn't ever wired up for Windows.
2026-11-11 11:12:50 -08:00
Ryan Houdek 01bcd23c82 CPUID: Fixes crash in CPUID
When affinity changes are in play this can cause a crash because core
index is larger than the number of cores known about.

Happens on Frame.
2026-11-11 11:12:45 -08:00
Ryan Houdek 8f8c19f009 InstcountCI: Update 2026-11-11 11:12:38 -08:00
Ryan Houdek 6c0dcff0c7 DiskCache: Moves SVE/AVX from HostFeatureHash to ProcessHash
Annoyingly these are technically host features but they end up being
process feature hash bits because they change between Linux, wow64, and
arm64ec.

This now makes the HostFeaturesHash match between the three which is
what we want. While the process feature bits change between them.

Adds an info log to the DiskCache startup for confirmation.
2026-11-11 11:12:31 -08:00
163 changed files with 456 additions and 1425 deletions

No files matched your search

+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -154,7 +154,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}-${{ env.runner_label }}
+2 -2
View File
@@ -31,7 +31,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -115,7 +115,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}-${{ env.runner_label }}
+3 -10
View File
@@ -24,7 +24,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -40,7 +40,7 @@ jobs:
- name: Configure CMake
run: |
cmake -S . -B build -DCMAKE_BUILD_TYPE=$BUILD_TYPE -G Ninja -DENABLE_LTO=False \
-DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_FEX_LINUX_TESTS=True
-DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True
# These steps make a lot of noise but rarely fail.
# Put them in a separate step to make normal build logs easier to parse
@@ -57,16 +57,9 @@ jobs:
with:
target: asm_tests
# host fex tests tests
- name: Host Tests
if: ${{ always() }}
uses: ./.github/workflows/test
with:
target: fex_linux_tests_host
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}-${{ env.runner_label }}
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -70,7 +70,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}-${{ env.runner_label }}
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
+2 -2
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v7
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
@@ -39,7 +39,7 @@ jobs:
run: which git-clang-format-19
- name: Setup Python env
uses: actions/setup-python@v7
uses: actions/setup-python@v4
with:
python-version: 3.11
cache: pip
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -62,7 +62,7 @@ jobs:
run: DESTDIR="$PWD"/install distrobox enter --name steamrt4 -- cmake --build build -t install
- name: Upload libraries
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
timeout-minutes: 1
with:
overwrite: true
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -75,7 +75,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}-${{ env.runner_label }}
+2 -2
View File
@@ -17,7 +17,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v7
- uses: actions/checkout@v6
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -45,7 +45,7 @@ jobs:
target: arm64ec
- name: Upload libraries
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@v6
timeout-minutes: 1
with:
overwrite: true
+6 -5
View File
@@ -337,7 +337,7 @@ def print_parse_envloader_options(options):
value_type = op_vals["Type"]
if (value_type == "strenum"):
output_argloader.write("else if (Key == \"FEX_{0}\") {{\n".format(op_key.upper()))
output_argloader.write("\tValue = FEXCore::Config::EnumParser(FEXCore::Config::{}_EnumPairs, Value_View);\n".format(op_key))
output_argloader.write("\tValue = FEXCore::Config::EnumParser<FEXCore::Config::{}ConfigPair>(FEXCore::Config::{}_EnumPairs, Value_View);\n".format(op_key, op_key))
output_argloader.write("}\n")
if ("ArgumentHandler" in op_vals):
@@ -357,7 +357,7 @@ def print_parse_jsonloader_options(options):
value_type = op_vals["Type"]
if (value_type == "strenum"):
output_argloader.write("else if (KeyName == \"{0}\") {{\n".format(op_key))
output_argloader.write("\tSet(KeyOption, FEXCore::Config::EnumParser(FEXCore::Config::{}_EnumPairs, Value_View));\n".format(op_key))
output_argloader.write("\tSet(KeyOption, FEXCore::Config::EnumParser<FEXCore::Config::{}ConfigPair>(FEXCore::Config::{}_EnumPairs, Value_View));\n".format(op_key, op_key))
output_argloader.write("}\n")
elif (value_type == "strarray"):
output_argloader.write("else if (KeyName == \"{0}\") {{\n".format(op_key))
@@ -394,12 +394,13 @@ def print_parse_enum_options(options):
if (op_vals["Type"] == "strenum"):
Enums = op_vals["Enums"]
output_argloader.write("constexpr static std::array<StringEnumPairType, {}> {}_EnumPairs = {{{{\n".format(len(Enums) + 1, op_key))
output_argloader.write("using {}ConfigPair = std::pair<std::string_view, FEXCore::Config::{}>;\n".format(op_key, op_key))
output_argloader.write("constexpr static std::array<{}ConfigPair, {}> {}_EnumPairs = {{{{\n".format(op_key, len(Enums) + 1, op_key))
i = 0
# Always have an OFF.
output_argloader.write("\t{{ \"off\", FEXCore::ToUnderlying(FEXCore::Config::{}::OFF) }},\n".format(op_key))
output_argloader.write("\t{{ \"off\", FEXCore::Config::{}::OFF }},\n".format(op_key))
for enum_op_key, enum_op_vals in Enums.items():
output_argloader.write("\t{{ \"{}\", FEXCore::ToUnderlying(FEXCore::Config::{}::{}) }},\n".format(enum_op_vals, op_key, enum_op_key.upper()))
output_argloader.write("\t{{ \"{}\", FEXCore::Config::{}::{} }},\n".format(enum_op_vals, op_key, enum_op_key.upper()))
i += 1
output_argloader.write("}};\n")
@@ -18,7 +18,6 @@
#include <FEXHeaderUtils/Filesystem.h>
#include <array>
#include <charconv>
#include <cstdlib>
#include <optional>
#include <stddef.h>
@@ -559,36 +558,4 @@ FEX_DEFAULT_VISIBILITY bool CheckConfigMatches(std::string_view Config) {
return SerializeForCache() == Config;
}
std::optional<fextl::string> EnumParser(std::span<const StringEnumPairType> EnumPairs, const std::string_view View) {
uint64_t EnumMask {};
auto Results = std::from_chars(View.data(), View.data() + View.size(), EnumMask);
if (Results.ec == std::errc()) {
// If the data is a valid number, just pass it through.
return std::nullopt;
}
auto Begin = 0;
auto End = View.find_first_of(',');
std::string_view Option = View.substr(Begin, End);
while (Option.size() != 0) {
auto EnumValue =
std::find_if(EnumPairs.begin(), EnumPairs.end(), [Option](const StringEnumPairType& Value) -> bool { return Value.first == Option; });
if (EnumValue == EnumPairs.end()) {
LogMan::Msg::IFmt("Skipping Unknown option: {}", Option);
} else {
EnumMask |= EnumValue->second;
}
if (End == std::string::npos) {
break;
}
Begin = End + 1;
End = View.find_first_of(',', Begin);
Option = View.substr(Begin, End - Begin);
}
return fextl::fmt::format("{}", EnumMask);
}
} // namespace FEXCore::Config
@@ -1129,9 +1129,4 @@ void Arm64Emitter::Align16B() {
}
}
ARMEmitter::Register Arm64Emitter::GetX86GPRMapping(FEXCore::X86State::X86Reg Reg) {
LOGMAN_THROW_A_FMT(Reg <= FEXCore::X86State::X86Reg::REG_R15, "Incorrect register");
return StaticRegisters[Reg];
}
} // namespace FEXCore::CPU
@@ -235,8 +235,6 @@ protected:
void Align16B();
ARMEmitter::Register GetX86GPRMapping(FEXCore::X86State::X86Reg Reg);
#ifdef VIXL_SIMULATOR
// Generates a vixl simulator runtime call.
//
+5 -9
View File
@@ -545,7 +545,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) const {
(1 << 25) | // SSE
(1 << 26) | // SSE2
(0 << 27) | // Self Snoop
(1 << 28) | // (HTT) Max APIC IDs reserved field is valid
(0 << 28) | // (HTT) Max APIC IDs reserved field is valid
(1 << 29) | // Thermal monitor
(0 << 30) | // Reserved
(0 << 31); // Pending break enable
@@ -572,10 +572,6 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_02h(uint32_t Leaf) const {
// 4: Deterministic cache parameters for each level
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_04h(uint32_t Leaf) const {
FEXCore::CPUID::FunctionResults Res {};
#ifdef CPUID_AMD
// CPUID function 4h is Intel exclusive.
return Res;
#endif
constexpr uint32_t CacheType_Data = 1;
constexpr uint32_t CacheType_Instruction = 2;
constexpr uint32_t CacheType_Unified = 3;
@@ -827,7 +823,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const {
(0U << 14) | // Reserved
(0U << 15) | // Reserved
(0U << 16) | // Reserved
(1U << 17) | // FRED (Flexible Return and Event Delivery)
(0U << 17) | // FRED (Flexible Return and Event Delivery)
(0U << 18) | // LKGS (Load into Kernel GS Base)
(0U << 19) | // WRMSRNS
(0U << 20) | // NMI_SRC
@@ -1268,9 +1264,9 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0008h(uint32_t Leaf) con
(CTX->HostFeatures.SupportsCLZERO << 0); // CLZERO support
uint32_t CoreCount = Cores - 1;
Res.ecx = (0 << 16) | // PerfTscSize: Performance timestamp count size
(FEXCore::ilog2(std::bit_ceil(Cores)) << 12) | // ApicIdSize: Number of bits in ApicID
(CoreCount << 0); // Count count subtract one
Res.ecx = (0 << 16) | // PerfTscSize: Performance timestamp count size
(std::bit_ceil(Cores) << 12) | // ApicIdSize: Number of bits in ApicID
(CoreCount << 0); // Count count subtract one
return Res;
}
+1 -1
View File
@@ -249,7 +249,7 @@ namespace DiskCache {
// The current version of the diskcache.
// This must be changed any time codegen changes occur!
// Be aware of the impact of changing this frequently!
static constexpr uint16_t FormatVersion = 44;
static constexpr uint16_t FormatVersion = 43;
static constexpr uint32_t LOOKUP_KEY_MAX_BUCKET_DEPTH = 500;
@@ -12,7 +12,6 @@
#include <FEXCore/Core/SignalDelegator.h>
#include <FEXCore/Core/X86Enums.h>
#include <FEXCore/Debug/InternalThreadState.h>
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/HLE/SyscallHandler.h>
#include <FEXCore/Utils/Event.h>
#include <FEXCore/Utils/LogManager.h>
@@ -418,7 +417,6 @@ void Dispatcher::EmitDispatcher() {
brk(0);
}
ARMEmitter::BackwardLabel GuestSIGSEGVLabelPostSpill;
{
// Guest Overflow handler
// Needs to be distinct from the SignalHandlerReturnAddress
@@ -426,8 +424,6 @@ void Dispatcher::EmitDispatcher() {
SpillStaticRegs(TMP1);
(void)Bind(&GuestSIGSEGVLabelPostSpill);
// hlt/udf = SIGILL
// brk = SIGTRAP
// ??? = SIGSEGV
@@ -538,35 +534,6 @@ void Dispatcher::EmitDispatcher() {
(void)b(&LoopTop);
}
auto EmitL1Lookup = [&](int rip_offset = 0) {
// L1 lookup after syscall. Mirrors the implementation in `ExitFunction`.
// Most syscall instructions just return to the following RIP, so it is highly likely to be in the L1 in tight loops.
ldr(TMP3, STATE_PTR(CpuStateFrame, State.rip));
// TF may be set: a single-stepped SYSCALL or CPUID. An L1 hit would jump into a block compiled without the
// per-instruction single-step trap, and the trap would only be raised at the next dispatcher entry, instructions
// later. The loop top checks TF and compiles single-step blocks.
ldrb(TMP1, STATE_PTR(CpuStateFrame, State.flags[X86State::RFLAG_TF_RAW_LOC]));
(void)cbnz(ARMEmitter::Size::i32Bit, TMP1, &LoopTop);
ldp<ARMEmitter::IndexType::OFFSET>(TMP1, TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, State.L1Pointer));
if (rip_offset) {
add(ARMEmitter::Size::i64Bit, TMP3, TMP3, rip_offset);
}
// Calculate (tmp1 + ((ripreg & L1_ENTRIES_MASK) << 4)) for the address
// L1Mask is pre-shifted.
and_(ARMEmitter::Size::i64Bit, TMP2, TMP2, TMP3, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(sizeof(LookupCache::LookupCacheEntry)));
add(TMP1, TMP1, TMP2);
ldp<ARMEmitter::IndexType::OFFSET>(TMP2, TMP1, TMP1, 0);
sub(TMP1, TMP1, TMP3);
(void)cbnz(ARMEmitter::Size::i64Bit, TMP1, &LoopTop);
br(TMP2);
};
{
// All dynamic and static registers are spilled coming in to this handler.
// It's also the end of block and RIP might have changed, so we jump directly to the top of the loop.
@@ -599,90 +566,23 @@ void Dispatcher::EmitDispatcher() {
// We can safely claim we are no longer in a syscall
str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
EmitL1Lookup();
}
{
// L1 lookup after syscall. Mirrors the implementation in `ExitFunction`.
// Most syscall instructions just return to the following RIP, so it is highly likely to be in the L1 in tight loops.
ldr(TMP3, STATE_PTR(CpuStateFrame, State.rip));
ldp<ARMEmitter::IndexType::OFFSET>(TMP1, TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, State.L1Pointer));
{
// All dynamic and static registers are spilled coming in to this handler.
// It's also the end of block and RIP might have changed, so we jump directly to the top of the loop.
ThreadDispatchCPUIDHandler = GetCursorAddress<uint64_t>();
// Calculate (tmp1 + ((ripreg & L1_ENTRIES_MASK) << 4)) for the address
// L1Mask is pre-shifted.
and_(ARMEmitter::Size::i64Bit, TMP2, TMP2, TMP3, ARMEmitter::ShiftType::LSL, FEXCore::ilog2(sizeof(LookupCache::LookupCacheEntry)));
add(TMP1, TMP1, TMP2);
// CPUID behaves like an ISB.
isb();
ldp<ARMEmitter::IndexType::OFFSET>(TMP2, TMP1, TMP1, 0);
auto RAXReg = GetX86GPRMapping(FEXCore::X86State::REG_RAX);
auto RBXReg = GetX86GPRMapping(FEXCore::X86State::REG_RBX);
auto RCXReg = GetX86GPRMapping(FEXCore::X86State::REG_RCX);
auto RDXReg = GetX86GPRMapping(FEXCore::X86State::REG_RDX);
ARMEmitter::ForwardLabel SetupCPUIDFault {};
ldr(TMP2, STATE, offsetof(FEXCore::Core::CPUState, CoreFlags));
(void)tbz(TMP2, FEXCore::ilog2(FEXCore::ToUnderlying(FEXCore::Core::CoreFlagsEnum::CPUID_ENABLED)), &SetupCPUIDFault);
// TMP1 passes the instruction size. Usually only 2-bytes but prefixes can make it 15.
ldr(TMP2, STATE_PTR(CpuStateFrame, State.rip));
add(TMP2, TMP2, TMP1);
str(TMP2, STATE_PTR(CpuStateFrame, State.rip));
mov(ARMEmitter::XReg::x1, RAXReg.X());
mov(ARMEmitter::XReg::x2, RCXReg.X());
// x0 = CPUID Handler
// x1 = CPUID Function (RAX)
// x2 = CPUID Leaf (RCX)
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.CPUIDObj));
ldr(ARMEmitter::XReg::x3, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.CPUIDFunction));
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
GenerateIndirectRuntimeCall<__uint128_t, void*, uint64_t, uint64_t>(ARMEmitter::Reg::r3);
} else {
blr(ARMEmitter::Reg::r3);
sub(TMP1, TMP1, TMP3);
(void)cbnz(ARMEmitter::Size::i64Bit, TMP1, &LoopTop);
br(TMP2);
}
// Results are in x0, x1
auto Res0 = ARMEmitter::Reg::r0;
auto Res1 = ARMEmitter::Reg::r1;
if (!TMP_ABIARGS) {
// SRA Overlaps, need to move.
mov(ARMEmitter::Size::i64Bit, TMP1, Res0);
mov(ARMEmitter::Size::i64Bit, TMP2, Res1);
Res0 = TMP1;
Res1 = TMP2;
}
FillStaticRegs();
// Results need to be moved to 32-bit EAX, ECX, EBX, EDX.
mov(ARMEmitter::Size::i32Bit, RAXReg, Res0);
mov(ARMEmitter::Size::i32Bit, RCXReg, Res1);
ubfx(ARMEmitter::Size::i64Bit, RBXReg, Res0, 32, 32);
ubfx(ARMEmitter::Size::i64Bit, RDXReg, Res1, 32, 32);
EmitL1Lookup();
// CPUID fault handling
(void)Bind(&SetupCPUIDFault);
constexpr Core::CpuStateFrame::SynchronousFaultDataStruct State = {
.FaultToTopAndGeneratedException = 1,
.Signal = Core::FAULT_SIGSEGV,
.TrapNo = X86State::X86_TRAPNO_GP,
.si_code = 0x80,
.err_code = 0,
.FaultAddress = 0, // Comes from register.
};
uint64_t Constant {};
memcpy(&Constant, &State, sizeof(uint64_t));
LoadConstant(ARMEmitter::Size::i64Bit, TMP1, static_cast<uint64_t>(Constant));
str(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData));
ldr(TMP2, STATE_PTR(CpuStateFrame, State.rip));
str(TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, SynchronousFaultData) + 8);
(void)b(&GuestSIGSEGVLabelPostSpill);
}
{
@@ -2783,7 +2683,6 @@ void Dispatcher::InitThreadPointers(FEXCore::Core::InternalThreadState* Thread)
Ptrs.ThreadStopHandlerSpillSRA = ThreadStopHandlerAddressSpillSRA;
Ptrs.ThreadPauseHandlerSpillSRA = ThreadPauseHandlerAddressSpillSRA;
Ptrs.ThreadDispatchSyscallHandler = ThreadDispatchSyscallHandler;
Ptrs.ThreadDispatchCPUIDHandler = ThreadDispatchCPUIDHandler;
Ptrs.ThreadDispatchRemoveCodeEntry = ThreadDispatchRemoveCodeEntry;
Ptrs.GuestSignal_SIGILL = GuestSignal_SIGILL;
Ptrs.GuestSignal_SIGTRAP = GuestSignal_SIGTRAP;
@@ -82,7 +82,6 @@ private:
uint64_t ThreadPauseHandlerAddress {};
uint64_t ThreadPauseHandlerAddressSpillSRA {};
uint64_t ThreadDispatchSyscallHandler {};
uint64_t ThreadDispatchCPUIDHandler {};
uint64_t ThreadDispatchRemoveCodeEntry {};
uint64_t ExitFunctionLinkerAddress {};
uint64_t SignalHandlerReturnAddress {};
+1 -12
View File
@@ -390,10 +390,6 @@ Decoder::DecodedBlockStatus Decoder::NormalOp(const FEXCore::X86Tables::X86InstI
return DecodedBlockStatus::INVALID_INST;
}
if (Info->Type == FEXCore::X86Tables::TYPE_UNIMPLEMENTED) {
return DecodedBlockStatus::UNIMPLEMENTED_INST;
}
if (!(Info->Flags & FEXCore::X86Tables::InstFlags::FLAGS_SUPPORTS_LOCK) && (DecodeInst->Flags & DecodeFlags::FLAG_LOCK)) {
// Instruction has lock prefix but doesn't support lock.
return DecodedBlockStatus::UNIMPLEMENTED_INST;
@@ -703,10 +699,6 @@ Decoder::DecodedBlockStatus Decoder::NormalOpHeader(const FEXCore::X86Tables::X8
return DecodedBlockStatus::INVALID_INST;
}
if (Info->Type == FEXCore::X86Tables::TYPE_UNIMPLEMENTED) {
return DecodedBlockStatus::UNIMPLEMENTED_INST;
}
LOGMAN_THROW_A_FMT(Info->Type != FEXCore::X86Tables::TYPE_REX_PREFIX, "REX PREFIX should have been decoded before this!");
// A normal instruction is the most likely.
@@ -757,7 +749,7 @@ Decoder::DecodedBlockStatus Decoder::NormalOpHeader(const FEXCore::X86Tables::X8
if (LocalInfo->Type == FEXCore::X86Tables::TYPE_SECOND_GROUP_MODRM && ModRM.mod == 0b11) {
// Everything in this group is privileged instructions aside from XGETBV
constexpr std::array<uint8_t, 8> RegToField = {
255, 0, 1, 2, 255, 3, 255, 4,
255, 0, 1, 2, 255, 255, 255, 3,
};
uint8_t Field = RegToField[ModRM.reg];
if (Field == 255) {
@@ -766,9 +758,6 @@ Decoder::DecodedBlockStatus Decoder::NormalOpHeader(const FEXCore::X86Tables::X8
LocalOp = (Field << 3) | ModRM.rm;
return NormalOp(&SecondModRMTableOps[LocalOp], LocalOp);
} else if (LocalInfo->Type == FEXCore::X86Tables::TYPE_SECOND_GROUP_MODRM && ModRM.reg == 5) {
// Special case 0x0F 0x01 /5 group as they are currently all reserved.
return DecodedBlockStatus::UNIMPLEMENTED_INST;
} else {
return NormalOp(&SecondInstGroupOps[LocalOp], LocalOp);
}
+37 -13
View File
@@ -285,9 +285,6 @@ DEF_OP(CondJump) {
DEF_OP(Syscall) {
SpillStaticRegs(TMP1);
// Not coming back.
ResetStack();
// Jump to the syscall dispatch handler. We won't return after this.
ldr(TMP1, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.ThreadDispatchSyscallHandler));
br(TMP1);
@@ -379,9 +376,6 @@ DEF_OP(ThreadRemoveCodeEntry) {
SpillStaticRegs(TMP1);
// Not coming back.
ResetStack();
// Store the new RIP to go to.
str(GetReg(Op->NewRIP).X(), STATE, offsetof(FEXCore::Core::CpuStateFrame, State.rip));
@@ -401,15 +395,45 @@ DEF_OP(ThreadRemoveCodeEntry) {
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
SpillStaticRegs(TMP1);
isb();
mov(ARMEmitter::Size::i64Bit, TMP2, GetReg(Op->Function));
mov(ARMEmitter::Size::i64Bit, TMP3, GetReg(Op->Leaf));
// Not coming back.
ResetStack();
PushDynamicRegs(TMP4);
SpillStaticRegs(TMP4);
LoadConstant(ARMEmitter::Size::i32Bit, TMP1, Op->InstSize);
// Jump to the syscall dispatch handler. We won't return after this.
ldr(TMP2, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.ThreadDispatchCPUIDHandler));
br(TMP2);
// x0 = CPUID Handler
// x1 = CPUID Function
// x2 = CPUID Leaf
ldr(ARMEmitter::XReg::x0, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.CPUIDObj));
ldr(ARMEmitter::XReg::x3, STATE, offsetof(FEXCore::Core::CpuStateFrame, Pointers.CPUIDFunction));
if (!TMP_ABIARGS) {
mov(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r1, TMP2);
mov(ARMEmitter::Size::i64Bit, ARMEmitter::Reg::r2, TMP3);
}
if (!CTX->Config.DisableVixlIndirectCalls) [[unlikely]] {
GenerateIndirectRuntimeCall<__uint128_t, void*, uint64_t, uint64_t>(ARMEmitter::Reg::r3);
} else {
blr(ARMEmitter::Reg::r3);
}
if (!TMP_ABIARGS) {
mov(ARMEmitter::Size::i64Bit, TMP1, ARMEmitter::Reg::r0);
mov(ARMEmitter::Size::i64Bit, TMP2, ARMEmitter::Reg::r1);
}
FillStaticRegs();
PopDynamicRegs();
// Results are in x0, x1
// Results want to be 4xi32 scalars
mov(ARMEmitter::Size::i32Bit, GetReg(Op->OutEAX), TMP1);
mov(ARMEmitter::Size::i32Bit, GetReg(Op->OutECX), TMP2);
ubfx(ARMEmitter::Size::i64Bit, GetReg(Op->OutEBX), TMP1, 32, 32);
ubfx(ARMEmitter::Size::i64Bit, GetReg(Op->OutEDX), TMP2, 32, 32);
}
DEF_OP(XGetBV) {
@@ -1370,19 +1370,20 @@ void OpDispatchBuilder::MOVOffsetOp(OpcodeArgs) {
}
void OpDispatchBuilder::CPUIDOp(OpcodeArgs) {
BlockSetRIP = true;
Ref Src = LoadGPRRegister(X86State::REG_RAX);
Ref Leaf = LoadGPRRegister(X86State::REG_RCX);
// Calculate flags early.
CalculateDeferredFlags();
Ref RAX = _AllocateGPR(false);
Ref RBX = _AllocateGPR(false);
Ref RCX = _AllocateGPR(false);
Ref RDX = _AllocateGPR(false);
// Similar to Syscall. Store the RIP to the start of the instruction.
// CPUID operation will modify the RIP itself.
const auto GPRSize = GetGPROpSize();
auto NewRIP = GetRelocatedPC(Op, -Op->InstSize);
_StoreContextGPR(GPRSize, NewRIP, offsetof(FEXCore::Core::CPUState, rip));
_CPUID(Src, Leaf, RAX, RBX, RCX, RDX);
FlushRegisterCache();
_CPUID(Op->InstSize);
StoreGPRRegister(X86State::REG_RAX, RAX);
StoreGPRRegister(X86State::REG_RBX, RBX);
StoreGPRRegister(X86State::REG_RCX, RCX);
StoreGPRRegister(X86State::REG_RDX, RDX);
}
uint32_t OpDispatchBuilder::GetConstantShift(X86Tables::DecodedOp Op, bool Is1Bit) {
@@ -41,6 +41,8 @@ constexpr DispatchTableEntry OpDispatch_SecondaryGroupTables[] = {
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_66, 4), 1, &OpDispatchBuilder::SMSWOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F2, 4), 1, &OpDispatchBuilder::SMSWOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_NONE, 5), 1, &OpDispatchBuilder::SerializeOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_NONE, 6), 1, &OpDispatchBuilder::PermissionRestrictedOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_F3, 6), 1, &OpDispatchBuilder::PermissionRestrictedOp},
{OPD(FEXCore::X86Tables::TYPE_GROUP_7, PF_66, 6), 1, &OpDispatchBuilder::PermissionRestrictedOp},
@@ -15,12 +15,9 @@ constexpr DispatchTableEntry OpDispatch_SecondaryModRMTables[] = {
// REG /3
{((2 << 3) | 7), 1, &OpDispatchBuilder::PermissionRestrictedOp},
// REG /5
{((3 << 3) | 0), 1, &OpDispatchBuilder::SerializeOp},
// REG /7
{((4 << 3) | 1), 1, &OpDispatchBuilder::RDTSCPOp},
{((4 << 3) | 4), 1, &OpDispatchBuilder::CLZeroOp},
{((3 << 3) | 1), 1, &OpDispatchBuilder::RDTSCPOp},
{((3 << 3) | 4), 1, &OpDispatchBuilder::CLZeroOp},
};
} // namespace FEXCore::IR
@@ -201,8 +201,6 @@ constexpr DispatchTableEntry OpDispatch_SecondaryRepNEModTables[] = {
{0x79, 1, &OpDispatchBuilder::Insertq},
{0x7C, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::VectorALUOp, IR::OP_VFADDP, OpSize::i32Bit>},
{0x7D, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::HSUBP, OpSize::i32Bit>},
{0xBC, 1, &OpDispatchBuilder::BSFOp},
{0xBD, 1, &OpDispatchBuilder::BSROp},
{0xD0, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::ADDSUBPOp, OpSize::i32Bit>},
{0xD6, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::MOVQ2DQ, false>},
{0xC2, 1, &OpDispatchBuilder::Bind<&OpDispatchBuilder::InsertScalarFCMPOp, OpSize::i64Bit>},
@@ -98,13 +98,13 @@ constexpr std::array<X86InstInfo, MAX_INST_SECOND_GROUP_TABLE_SIZE> SecondInstGr
{OPD(TYPE_GROUP_6, PF_F2, 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
// GROUP 7
{OPD(TYPE_GROUP_7, PF_NONE, 0), 1, X86InstInfo{"SGDT", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 1), 1, X86InstInfo{"SIDT", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 2), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 3), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 4), 1, X86InstInfo{"SMSW", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 5), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 6), 1, X86InstInfo{"LMSW", TYPE_INST, FLAGS_MODRM, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 0), 1, X86InstInfo{"SGDT", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 1), 1, X86InstInfo{"SIDT", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 2), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 3), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 4), 1, X86InstInfo{"SMSW", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 5), 1, X86InstInfo{"SERIALIZE", TYPE_INST, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 6), 1, X86InstInfo{"LMSW", TYPE_INST, FLAGS_MODRM, 0}},
{OPD(TYPE_GROUP_7, PF_NONE, 7), 1, X86InstInfo{"INVLPG", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 0), 1, X86InstInfo{"SGDT", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
@@ -112,7 +112,7 @@ constexpr std::array<X86InstInfo, MAX_INST_SECOND_GROUP_TABLE_SIZE> SecondInstGr
{OPD(TYPE_GROUP_7, PF_F3, 2), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 3), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 4), 1, X86InstInfo{"SMSW", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 5), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 6), 1, X86InstInfo{"LMSW", TYPE_INST, FLAGS_MODRM, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 7), 1, X86InstInfo{"INVLPG", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
@@ -121,7 +121,7 @@ constexpr std::array<X86InstInfo, MAX_INST_SECOND_GROUP_TABLE_SIZE> SecondInstGr
{OPD(TYPE_GROUP_7, PF_66, 2), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_66, 3), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_66, 4), 1, X86InstInfo{"SMSW", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_66, 5), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_66, 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_66, 6), 1, X86InstInfo{"LMSW", TYPE_INST, FLAGS_MODRM, 0}},
{OPD(TYPE_GROUP_7, PF_66, 7), 1, X86InstInfo{"INVLPG", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
@@ -130,7 +130,7 @@ constexpr std::array<X86InstInfo, MAX_INST_SECOND_GROUP_TABLE_SIZE> SecondInstGr
{OPD(TYPE_GROUP_7, PF_F2, 2), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F2, 3), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F2, 4), 1, X86InstInfo{"SMSW", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
{OPD(TYPE_GROUP_7, PF_F2, 5), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F2, 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F2, 6), 1, X86InstInfo{"LMSW", TYPE_INST, FLAGS_MODRM, 0}},
{OPD(TYPE_GROUP_7, PF_F2, 7), 1, X86InstInfo{"INVLPG", TYPE_SECOND_GROUP_MODRM, FLAGS_MODRM | FLAGS_SF_MOD_DST, 0}},
@@ -13,12 +13,12 @@ $end_info$
namespace FEXCore::X86Tables {
using namespace InstFlags;
enum ModRM_LUT {
ENTRY_7_0,
ENTRY_3_0,
ENTRY_MAX,
};
constexpr std::array<X86InstInfo[2], ENTRY_MAX> ModRM_ArchSelect_LUT = {{
// ENTRY_7_0
// ENTRY_3_0
{
{"SWAPGS", TYPE_INST, FLAGS_NONE, 0, { .OpDispatch = &IR::OpDispatchBuilder::UnimplementedOp }},
{"SWAPGS", TYPE_INST, FLAGS_NONE, 0, { .OpDispatch = &IR::OpDispatchBuilder::PermissionRestrictedOp }},
@@ -58,25 +58,15 @@ constexpr std::array<X86InstInfo, MAX_SECOND_MODRM_TABLE_SIZE> SecondModRMTableO
{((2 << 3) | 6), 1, X86InstInfo{"SKINIT", TYPE_PRIV, FLAGS_NONE, 0}},
{((2 << 3) | 7), 1, X86InstInfo{"INVLPGA", TYPE_INST, FLAGS_NONE, 0}},
// REG /5
{((3 << 3) | 0), 1, X86InstInfo{"SERIALIZE", TYPE_INST, FLAGS_NONE, 0}},
{((3 << 3) | 1), 1, X86InstInfo{"", TYPE_UNIMPLEMENTED, FLAGS_NONE, 0}},
{((3 << 3) | 2), 1, X86InstInfo{"", TYPE_UNIMPLEMENTED, FLAGS_NONE, 0}},
{((3 << 3) | 3), 1, X86InstInfo{"", TYPE_UNIMPLEMENTED, FLAGS_NONE, 0}},
{((3 << 3) | 4), 1, X86InstInfo{"", TYPE_UNIMPLEMENTED, FLAGS_NONE, 0}},
{((3 << 3) | 5), 1, X86InstInfo{"", TYPE_UNIMPLEMENTED, FLAGS_NONE, 0}},
{((3 << 3) | 6), 1, X86InstInfo{"RDPKRU", TYPE_UNIMPLEMENTED, FLAGS_NONE, 0}},
{((3 << 3) | 7), 1, X86InstInfo{"WRPKRU", TYPE_UNIMPLEMENTED, FLAGS_NONE, 0}},
// REG /7
{((4 << 3) | 0), 1, X86InstInfo{"", TYPE_ARCH_DISPATCHER, FLAGS_NONE, 0, { .Indirect = ModRM_ArchSelect_LUT[ENTRY_7_0] }}},
{((4 << 3) | 1), 1, X86InstInfo{"RDTSCP", TYPE_INST, FLAGS_NONE, 0}},
{((4 << 3) | 2), 1, X86InstInfo{"MONITORX", TYPE_PRIV, FLAGS_NONE, 0}},
{((4 << 3) | 3), 1, X86InstInfo{"MWAITX", TYPE_PRIV, FLAGS_NONE, 0}},
{((4 << 3) | 4), 1, X86InstInfo{"CLZERO", TYPE_INST, GenFlagsSameSize(SIZE_64BITDEF) | FLAGS_DEBUG_MEM_ACCESS, 0}},
{((4 << 3) | 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{((4 << 3) | 6), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{((4 << 3) | 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{((3 << 3) | 0), 1, X86InstInfo{"", TYPE_ARCH_DISPATCHER, FLAGS_NONE, 0, { .Indirect = ModRM_ArchSelect_LUT[ENTRY_3_0] }}},
{((3 << 3) | 1), 1, X86InstInfo{"RDTSCP", TYPE_INST, FLAGS_NONE, 0}},
{((3 << 3) | 2), 1, X86InstInfo{"MONITORX", TYPE_PRIV, FLAGS_NONE, 0}},
{((3 << 3) | 3), 1, X86InstInfo{"MWAITX", TYPE_PRIV, FLAGS_NONE, 0}},
{((3 << 3) | 4), 1, X86InstInfo{"CLZERO", TYPE_INST, GenFlagsSameSize(SIZE_64BITDEF) | FLAGS_DEBUG_MEM_ACCESS, 0}},
{((3 << 3) | 5), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{((3 << 3) | 6), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{((3 << 3) | 7), 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
};
GenerateTable(Table.data(), SecondaryModRMExtensionOpTable, std::size(SecondaryModRMExtensionOpTable));
@@ -204,7 +204,7 @@ constexpr std::array<X86InstInfo, MAX_SECOND_TABLE_SIZE> SecondBaseOps = []() co
{0xA0, 1, X86InstInfo{"", TYPE_ARCH_DISPATCHER, FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .Indirect = Secondary_ArchSelect_LUT[ENTRY_A0] }}},
{0xA1, 1, X86InstInfo{"", TYPE_ARCH_DISPATCHER, FLAGS_DEBUG_MEM_ACCESS | FLAGS_NO_OVERLAY, 0, { .Indirect = Secondary_ArchSelect_LUT[ENTRY_A1] }}},
{0xA2, 1, X86InstInfo{"CPUID", TYPE_INST, FLAGS_NO_OVERLAY | FLAGS_BLOCK_END, 0}},
{0xA2, 1, X86InstInfo{"CPUID", TYPE_INST, FLAGS_NO_OVERLAY, 0}},
{0xA3, 1, X86InstInfo{"BT", TYPE_INST, FLAGS_DEBUG_MEM_ACCESS | FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_NO_OVERLAY | FLAGS_SUPPORTS_LOCK, 0}},
{0xA4, 1, X86InstInfo{"SHLD", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_NO_OVERLAY, 1}},
{0xA5, 1, X86InstInfo{"SHLD", TYPE_INST, FLAGS_MODRM | FLAGS_SF_MOD_DST | FLAGS_NO_OVERLAY, 0}},
@@ -454,13 +454,10 @@ constexpr std::array<X86InstInfo, MAX_REPNE_MOD_TABLE_SIZE> RepNEModOps = []() c
{0x90, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0}},
{0xA0, 16, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0}},
{0xB0, 8, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0}},
{0xB8, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{0xB8, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{0xB9, 1, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0}},
{0xBA, 1, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0}},
{0xBB, 1, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{0xBC, 1, X86InstInfo{"BSF", TYPE_INST, FLAGS_MODRM | FLAGS_NO_PREFIX_ORDER | FLAGS_NO_OVERLAY66, 0}},
{0xBD, 1, X86InstInfo{"BSR", TYPE_INST, FLAGS_MODRM | FLAGS_NO_PREFIX_ORDER | FLAGS_NO_OVERLAY66, 0}},
{0xBE, 2, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{0xBB, 5, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
{0xC0, 2, X86InstInfo{"", TYPE_COPY_OTHER, FLAGS_NONE, 0}},
{0xC2, 1, X86InstInfo{"CMPSD", TYPE_INST, GenFlagsSizes(SIZE_128BIT, SIZE_64BIT) | FLAGS_MODRM | FLAGS_XMM_FLAGS, 1}},
{0xC3, 5, X86InstInfo{"", TYPE_INVALID, FLAGS_NONE, 0}},
@@ -272,7 +272,6 @@ enum InstType {
TYPE_INST,
TYPE_X87 = TYPE_INST,
TYPE_INVALID,
TYPE_UNIMPLEMENTED,
TYPE_COPY_OTHER,
// Changes `X86InstInfo::OpcodeDispatcher` member to use the `Indirect` version.
// Points to a 2 member array of X86InstInfo to choose instruction description based on executing bitness.
@@ -522,7 +521,7 @@ constexpr size_t MAX_INST_GROUP_TABLE_SIZE = 512;
// 12 (groups) | 3(max indexes) | 8 ops = 0b1111'11'111 = 9 bits
constexpr size_t MAX_INST_SECOND_GROUP_TABLE_SIZE = 512;
constexpr size_t MAX_X87_TABLE_SIZE = 1 << 11;
constexpr size_t MAX_SECOND_MODRM_TABLE_SIZE = 40;
constexpr size_t MAX_SECOND_MODRM_TABLE_SIZE = 32;
// (3 bit prefixes) | 8 bit opcode
constexpr size_t MAX_0F_38_TABLE_SIZE = (1 << 11);
// 1 REX | 1 prefixes | 8 bit opcode
+2 -1
View File
@@ -337,8 +337,9 @@
"HasSideEffects": true
},
"CPUID i8:$InstSize": {
"GPR:$EAX, GPR:$EBX, GPR:$ECX, GPR:$EDX = CPUID GPR:$Function, GPR:$Leaf": {
"Desc": ["Calls in to the CPUID handler function to return emulated CPUID"],
"DestSize": "OpSize::i32Bit",
"HasSideEffects": true
},
"GPR:$EAX, GPR:$EDX = XGetBV GPR:$Function": {
@@ -21,7 +21,6 @@ static bool IsFragmentExit(FEXCore::IR::IROps Op) {
switch (Op) {
case OP_THREADREMOVECODEENTRY:
case OP_SYSCALL:
case OP_CPUID:
case OP_EXITFUNCTION:
case OP_BREAK: return true;
default: return false;
@@ -12,7 +12,6 @@ $end_info$
#include "Interface/IR/Passes/RegisterAllocationPass.h"
#include "Interface/Core/OpcodeDispatcher.h"
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/IR/IR.h>
namespace FEXCore::IR::Debug {
@@ -527,6 +527,27 @@ bool ConstrainedRAPass::TryPostRAMerge(Ref LastNode, Ref CodeNode, IROp_Header*
IREmit->RemovePostRA(CodeNode);
return false;
}
} else if (IROp->Op == OP_CPUID && PhysicalRegister(IROp->Args[0]) == PhysicalRegister(LastNode) && LastOp->Op == OP_CONSTANT) {
// Try to constant fold. As a limitation of merging only 2 instructions, we
// can only handle constant functions, not constant leafs. This could be
// lifted if we generalized at a (significant) complexity cost.
uint64_t ConstantFunction = LastOp->C<IROp_Constant>()->Constant;
auto Op = IROp->CW<IR::IROp_CPUID>();
const auto SupportsConstant = CPUID->DoesFunctionReportConstantData(ConstantFunction);
if (SupportsConstant.SupportsConstantFunction == CPUIDEmu::SupportsConstant::CONSTANT &&
SupportsConstant.NeedsLeaf != CPUIDEmu::NeedsLeafConstant::NEEDSLEAFCONSTANT) {
const auto Result = CPUID->RunFunction(ConstantFunction, 0 /* leaf */);
IREmit->SetWriteCursorBefore(CodeNode);
IREmit->_Fence(IR::FenceType::Inst);
IREmit->_Constant(Result.eax).Node->Reg = PhysicalRegister(Op->OutEAX).Raw;
IREmit->_Constant(Result.ebx).Node->Reg = PhysicalRegister(Op->OutEBX).Raw;
IREmit->_Constant(Result.ecx).Node->Reg = PhysicalRegister(Op->OutECX).Raw;
IREmit->_Constant(Result.edx).Node->Reg = PhysicalRegister(Op->OutEDX).Raw;
IREmit->RemovePostRA(CodeNode);
return false;
}
}
// Merge moves that are immediately consumed.
+39 -5
View File
@@ -4,15 +4,18 @@
#include <FEXCore/Utils/CompilerDefs.h>
#include <FEXCore/Utils/EnumOperators.h>
#include <FEXCore/Utils/EnumUtils.h>
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/memory.h>
#include <FEXCore/fextl/list.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/unordered_map.h>
#include <algorithm>
#include <array>
#include <charconv>
#include <cstdint>
#include <optional>
#include <span>
#include <type_traits>
#include <variant>
@@ -36,9 +39,6 @@ enum ConfigOption {
CONFIG_MAX,
};
using StringArrayType = fextl::list<fextl::string>;
using StringEnumPairType = std::pair<std::string_view, uint64_t>;
#define ENUMDEFINES
#include <FEXCore/Config/ConfigOptions.inl>
@@ -61,6 +61,41 @@ enum class LayerType {
LAYER_TOP,
};
template<typename PairTypes, typename ArrayPairType>
static inline std::optional<fextl::string> EnumParser(const ArrayPairType& EnumPairs, const std::string_view View) {
uint64_t EnumMask {};
auto Results = std::from_chars(View.data(), View.data() + View.size(), EnumMask);
if (Results.ec == std::errc()) {
// If the data is a valid number, just pass it through.
return std::nullopt;
}
auto Begin = 0;
auto End = View.find_first_of(',');
std::string_view Option = View.substr(Begin, End);
while (Option.size() != 0) {
auto EnumValue =
std::find_if(EnumPairs.begin(), EnumPairs.end(), [Option](const PairTypes& Value) -> bool { return Value.first == Option; });
if (EnumValue == EnumPairs.end()) {
LogMan::Msg::IFmt("Skipping Unknown option: {}", Option);
} else {
EnumMask |= FEXCore::ToUnderlying(EnumValue->second);
}
if (End == std::string::npos) {
break;
}
Begin = End + 1;
End = View.find_first_of(',', Begin);
Option = View.substr(Begin, End - Begin);
}
return fextl::fmt::format("{}", EnumMask);
}
using StringArrayType = fextl::list<fextl::string>;
namespace detail {
template<ConfigOption Option>
struct ConfigOptionInfo;
@@ -89,7 +124,6 @@ FEX_DEFAULT_VISIBILITY const fextl::string& GetConfigDirectory(bool Global);
FEX_DEFAULT_VISIBILITY const fextl::string& GetConfigFileLocation(bool Global = false);
FEX_DEFAULT_VISIBILITY fextl::string GetApplicationConfig(const std::string_view Program, bool Global);
FEX_DEFAULT_VISIBILITY const fextl::string& GetCacheDirectory();
FEX_DEFAULT_VISIBILITY std::optional<fextl::string> EnumParser(std::span<const StringEnumPairType> EnumPairs, const std::string_view View);
using LayerValue = std::variant< fextl::string, StringArrayType, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t, bool >;
+1 -10
View File
@@ -71,12 +71,6 @@ static_assert(std::is_standard_layout_v<NonAtomicRefCounter<uint64_t>>, "Needs t
static_assert(std::is_trivially_copyable_v<NonAtomicRefCounter<uint64_t>>, "needs to be trivially copyable");
static_assert(sizeof(NonAtomicRefCounter<uint64_t>) == sizeof(uint64_t), "Needs to be correct size");
// Flags about the core state that the JIT can query.
enum class CoreFlagsEnum : uint64_t {
CPUID_ENABLED = (1ULL << 0),
};
FEX_DEF_NUM_OPS(CoreFlagsEnum)
struct alignas(64) CPUState {
// Allows more efficient handling of the register
// file in the event AVX is not supported.
@@ -113,9 +107,7 @@ struct alignas(64) CPUState {
uint64_t L1Pointer {};
uint64_t L1Mask {};
uint64_t callret_sp {};
// CPUID is default enabled.
CoreFlagsEnum CoreFlags {CoreFlagsEnum::CPUID_ENABLED};
uint64_t _pad1 {};
// Cacheline: 1,2,3,4
// The high 128-bits of AVX registers when not being emulated by SVE256.
@@ -414,7 +406,6 @@ struct JITPointers {
uint64_t ThreadStopHandlerSpillSRA {};
uint64_t ThreadPauseHandlerSpillSRA {};
uint64_t ThreadDispatchSyscallHandler {};
uint64_t ThreadDispatchCPUIDHandler {};
uint64_t ThreadDispatchRemoveCodeEntry {};
uint64_t GuestSignal_SIGILL {};
uint64_t GuestSignal_SIGTRAP {};
-2
View File
@@ -13,8 +13,6 @@
#include <FEXCore/Utils/WildcardMatcher.h>
#include <FEXHeaderUtils/Filesystem.h>
#include <FEXHeaderUtils/SymlinkChecks.h>
#include <charconv>
#include <cstring>
#include <fmt/format.h>
#include <functional>
-2
View File
@@ -7,12 +7,10 @@
#include <FEXCore/Config/Config.h>
#include <FEXCore/Core/HostFeatures.h>
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/Utils/FileLoading.h>
#include <FEXCore/Utils/PrctlUtils.h>
#include <FEXCore/Utils/StringUtils.h>
#include <charconv>
#include <range/v3/view/split.hpp>
#include <range/v3/view/transform.hpp>
-1
View File
@@ -1,7 +1,6 @@
// SPDX-License-Identifier: MIT
#include "PortabilityInfo.h"
#include "Common/FEXServerClient.h"
#include <FEXCore/fextl/fmt.h>
#include <cstdio>
#include <errno.h>
-1
View File
@@ -12,7 +12,6 @@
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/SignalScopeGuards.h>
#include <charconv>
#include <sys/stat.h>
namespace CodeSize {
+1 -4
View File
@@ -1,12 +1,9 @@
// SPDX-License-Identifier: MIT
#pragma once
#include <limits.h>
#include "Common/Config.h"
#include <charconv>
#include <limits.h>
#include <unistd.h>
namespace FEX {
static inline std::optional<fextl::string> GetSelfPath() {
// Read the FEX path from `/proc/self/exe` which is always a symlink to the absolute path of the executable running.
@@ -58,6 +58,7 @@ $end_info$
#include <sys/auxv.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/select.h>
#include <system_error>
#include <thread>
#include <unistd.h>
@@ -433,7 +434,7 @@ int main(int argc, char** argv, char** const envp) {
if (StallProcess) {
while (1) {
// Stall this process out forever
ppoll(nullptr, 0, nullptr, nullptr);
select(0, nullptr, nullptr, nullptr, nullptr);
}
}
@@ -362,7 +362,6 @@ static fextl::string GenerateCPUInfo(FEXCore::Context::Context* ctx, uint32_t CP
add_flag_if(res_7_1.eax & (1 << 0), "sha512");
add_flag_if(res_7_1.eax & (1 << 4), "avx_vnni");
add_flag_if(res_7_1.eax & (1 << 5), "avx512_bf16");
add_flag_if(res_7_1.eax & (1 << 17), "fred");
add_flag_if(res_7_1.eax & (1 << 23), "avx_ifma");
add_flag_if(res_7_1.edx & (1 << 4), "avx_vnni_int8");
add_flag_if(res_7_1.edx & (1 << 5), "avx_ne_convert");
@@ -9,7 +9,6 @@ $end_info$
#include "CodeLoader.h"
#include "GdbServer/Info.h"
#include <charconv>
#include <cstdlib>
#include <cstdio>
#include <iomanip>
@@ -7,8 +7,6 @@ $end_info$
#include "LinuxSyscalls/Seccomp/SeccompEmulator.h"
#include <FEXCore/fextl/fmt.h>
#include <linux/bpf_common.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
@@ -546,16 +546,11 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
Result = -EINVAL; // We don't support CET, return EINVAL
break;
case 0x1011: // ARCH_GET_CPUID
return (Frame->State.CoreFlags & FEXCore::Core::CoreFlagsEnum::CPUID_ENABLED) == FEXCore::Core::CoreFlagsEnum::CPUID_ENABLED ? 1 : 0;
case 0x1012: // ARCH_SET_CPUID
if (addr == 0) {
// Disable CPUID (thus enabling CPUID faulting)
Frame->State.CoreFlags = Frame->State.CoreFlags & ~FEXCore::Core::CoreFlagsEnum::CPUID_ENABLED;
} else {
// Enable CPUID (thus disabling CPUID faulting)
Frame->State.CoreFlags = Frame->State.CoreFlags | FEXCore::Core::CoreFlagsEnum::CPUID_ENABLED;
}
return 0;
return 1;
break;
case 0x1012: // ARCH_SET_CPUID
return -ENODEV; // Claim we don't support faulting on CPUID
break;
default:
LogMan::Msg::EFmt("Unknown prctl: 0x{:x}", code);
Result = -EINVAL;
+5
View File
@@ -549,6 +549,11 @@ static ARM64_NT_CONTEXT StoreStateToPackedECContext(FEXCore::Core::InternalThrea
ARM64_NT_CONTEXT ECContext {};
ECContext.ContextFlags = CONTEXT_ARM64_FULL;
if (CPUFeatures->IsFeaturePresent(PF_AVX2_INSTRUCTIONS_AVAILABLE)) {
// This is a FEX extension and requires corresponding wine-side patches to be of use, however it is harmless to set
// even if those patches are not used.
ECContext.ContextFlags |= CONTEXT_ARM64_FEX_YMMSTATE;
}
auto& State = Thread->CurrentFrame->State;
-3
View File
@@ -134,8 +134,6 @@ CPUFeatures::CPUFeatures(FEXCore::Context::Context& CTX) {
CpuInfo.ProcessorRevision = (FamilyIdentifier & 0xf0000) >> 4; // Extended Model
CpuInfo.ProcessorRevision |= (FamilyIdentifier & 0xf0) << 4; // Model
CpuInfo.ProcessorRevision |= FamilyIdentifier & 0xf; // Stepping
// 8-bits in CPUID 01.ebx for up to 256 cores.
CpuInfo.MaximumProcessors = ((CPUIDResult01.ebx >> 16) & 0xFF) + 1;
}
bool CPUFeatures::IsFeaturePresent(uint32_t Feature) {
@@ -168,7 +166,6 @@ bool CPUFeatures::IsFeaturePresent(uint32_t Feature) {
}
void CPUFeatures::UpdateInformation(SYSTEM_CPU_INFORMATION* Info) {
Info->MaximumProcessors = CpuInfo.MaximumProcessors;
Info->ProcessorArchitecture = CpuInfo.ProcessorArchitecture;
Info->ProcessorLevel = CpuInfo.ProcessorLevel;
Info->ProcessorRevision = CpuInfo.ProcessorRevision;
+3
View File
@@ -209,6 +209,9 @@ typedef struct _IMAGE_VOLATILE_RANGE_METADATA {
NTSYSAPI DWORD WINAPI RtlRunOnceExecuteOnce(PRTL_RUN_ONCE, PRTL_RUN_ONCE_INIT_FN, PVOID, PVOID*);
// This is a FEX extension, and requires corresponding wine patches
#define CONTEXT_ARM64_FEX_YMMSTATE (CONTEXT_ARM64 | 0x00000040)
#ifdef __cplusplus
}
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
# FEX-2611
# FEX-2611.1
## FEXCore
See [FEXCore/Readme.md](../FEXCore/Readme.md) for more details
@@ -1,33 +0,0 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0",
"RBX": "0x81828384",
"RCX": "0x71727374",
"RDX": "0x51525354",
"RSP": "0x61626364",
"RSI": "0x21222324",
"RDI": "2",
"RBP": "0x1e"
},
"Mode": "32BIT"
}
%endif
; FEX-Emu had a bug where BSR and BSF prefixed with repne didn't exist.
; Documentation says that these only work without a prefix.
; With `rep` prefix these change instructions, but with `repne` they still behave like BSF/BSR.
mov eax, 0
mov ebx, 0x81828384
mov ecx, 0x71727374
mov esp, 0x61626364
mov esi, 0x21222324
mov edi, 0x31323334
mov ebp, 0x41424344
mov edx, 0x51525354
repne bsr ebp, edx
repne bsf edi, edx
setz al
hlt
-32
View File
@@ -1,32 +0,0 @@
%ifdef CONFIG
{
"RegData": {
"RAX": "0",
"RBX": "0x8182838485868788",
"RCX": "0x7172737475767778",
"RDX": "0x5152535455565758",
"RSP": "0x6162636465666768",
"RSI": "0x2122232425262728",
"RDI": "3",
"RBP": "0x1e"
}
}
%endif
; FEX-Emu had a bug where BSR and BSF prefixed with repne didn't exist.
; Documentation says that these only work without a prefix.
; With `rep` prefix these change instructions, but with `repne` they still behave like BSF/BSR.
mov rax, 0
mov rbx, 0x8182838485868788
mov rcx, 0x7172737475767778
mov rsp, 0x6162636465666768
mov rsi, 0x2122232425262728
mov rdi, 0x3132333435363738
mov rbp, 0x4142434445464748
mov rdx, 0x5152535455565758
repne bsr ebp, edx
repne bsf edi, edx
setz al
hlt
@@ -16,7 +16,7 @@ struct core_info {
uint32_t max_addressible_ids;
uint32_t cores;
uint32_t threads;
bool SMT;
bool HTT;
};
struct cpuid_fn {
@@ -42,7 +42,7 @@ std::optional<core_info> cpuid_calculate_core_info() {
const auto cpuid_fn_1 = get_cpuid(1);
info.local_apicid = cpuid_fn_1.ebx >> 24;
bool HTT = (cpuid_fn_1.edx >> 28) & 1;
info.HTT = (cpuid_fn_1.edx >> 28) & 1;
const auto cpuid_fn_8000_0000 = get_cpuid(0x8000'0000U);
@@ -50,10 +50,8 @@ std::optional<core_info> cpuid_calculate_core_info() {
return std::nullopt;
}
const auto cpuid_fn_8000_0001 = get_cpuid(0x8000'0001U);
const auto cpuid_fn_8000_0008 = get_cpuid(0x8000'0008U);
const uint32_t apic_id_size = (cpuid_fn_8000_0008.ecx >> 12) & 0xF;
const bool CmpLegacy = cpuid_fn_8000_0001.ecx & 0b10;
// E.5.2: MNLP (Maximum Number of Logical Processors)
uint32_t MNLP {};
@@ -69,24 +67,18 @@ std::optional<core_info> cpuid_calculate_core_info() {
info.max_addressible_ids = MNLP;
const auto cpuid_fn_4 = get_cpuid(4);
if (CmpLegacy && HTT) {
// For CMPLegacy with HTT, number of "logical" cores is just func_1h_ebx[LogicalProcessorCount]
info.threads = (cpuid_fn_1.ebx >> 16) & 0xFF;
// Physical cores is just func_8000_0008.ecx[NC] + 1
info.cores = (cpuid_fn_8000_0008.ecx & 0xFF) + 1;
info.SMT = info.threads != info.cores;
} else if (cpuid_fn_4.eax & 0xF) {
if (cpuid_fn_4.eax & 0xF) {
// Intel exclusive cpuid function, AMD returns zero as unsupported.
// `Maximum number of addressable IDs for processor cores in the physical package`
info.cores = (cpuid_fn_4.eax >> 26) + 1;
if (HTT) {
if (info.HTT) {
// `A value of 1 for HTT indicates the value in CPUID.1.EBX[23:16] (the Maximum number of addressable IDs for logical processors in
// this package) is valid for the package. `Maximum number of addressable IDs for logical processors in this physical package`
info.threads = (cpuid_fn_1.ebx >> 16) & 0xFF;
} else {
info.threads = info.cores;
}
} else if (HTT) {
} else if (info.HTT) {
info.cores = (cpuid_fn_1.ebx >> 16) & 0xFF;
info.threads = info.cores * 2;
} else {
@@ -104,7 +96,7 @@ TEST_CASE("cpu count - cpuid") {
CHECK(core_info->local_apicid < hw_concurrency);
CHECK(core_info->local_apicid < core_info->max_addressible_ids);
CHECK(core_info->max_addressible_ids >= hw_concurrency);
if (core_info->SMT) {
if (core_info->HTT) {
// May not be entirely correct on systems that mix HTT and non-HTT cpu cores.
CHECK((core_info->cores * 2) == core_info->threads);
} else {
@@ -1,309 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include <sys/prctl.h>
#include <sys/syscall.h>
#include <signal.h>
#include <unistd.h>
#ifndef ARCH_GET_CPUID
#define ARCH_GET_CPUID 0x1011
#endif
#ifndef ARCH_SET_CPUID
#define ARCH_SET_CPUID 0x1012
#endif
struct cpuid_fn {
uint32_t eax, ebx, ecx, edx;
};
static cpuid_fn get_cpuid(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile("cpuid" : "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx) : "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_looooong_cpuid(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x66, 0x66, 0x66, 0x66;"
".byte 0x66, 0x66, 0x66, 0x66;"
".byte 0x66, 0x66, 0x66, 0x66;"
".byte 0x66;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_2(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x26;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_3(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x2e;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_4(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x36;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_5(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x3E;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_6(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x64;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_7(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x65;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_8(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x66;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_9(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x67;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_10(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0xF2;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
static cpuid_fn get_cpuid_11(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0xf3;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
#ifdef REG_RIP
// 0x40 isn't REX prefix on 32-bit.
static cpuid_fn get_cpuid_12(uint32_t func, uint32_t leaf = 0) {
cpuid_fn fn {};
__asm volatile(".byte 0x40;"
"cpuid"
: "=a"(fn.eax), "=b"(fn.ebx), "=c"(fn.ecx), "=d"(fn.edx)
: "a"(func), "c"(leaf));
return fn;
}
#endif
static size_t is_cpuid(uint8_t* data) {
size_t inst_size {};
while (true) {
if (inst_size == 15) {
return 0;
}
uint8_t byte = data[inst_size];
switch (byte) {
case 0x26: // ES
case 0x2e: // CS
case 0x36: // SS
case 0x3E: // DS
case 0x64: // FS
case 0x65: // GS
case 0x66: // Opcode size
case 0x67: // Addr size
case 0xF2: // REPNE
case 0xF3: // REPE
#ifdef REG_RIP
case 0x40 ... 0x4f: // REX
#endif
break;
case 0xF0: return 0; // Lock explicitly isn't supported on CPUID, should fault.
case 0xf: {
++inst_size;
byte = data[inst_size];
if (byte == 0xa2) {
return inst_size + 1;
}
// Nope
return 0;
}
}
++inst_size;
}
return inst_size;
}
static int faults {};
static void SignalHandler(int signal, siginfo_t* siginfo, void* context) {
ucontext_t* _context = (ucontext_t*)context;
#ifdef REG_RIP
#define FEX_IP_REG REG_RIP
#define FEX_RAX_REG REG_RAX
#define FEX_RBX_REG REG_RBX
#define FEX_RCX_REG REG_RCX
#define FEX_RDX_REG REG_RDX
#else
#define FEX_IP_REG REG_EIP
#define FEX_RAX_REG REG_EAX
#define FEX_RBX_REG REG_EBX
#define FEX_RCX_REG REG_ECX
#define FEX_RDX_REG REG_EDX
#endif
if (signal != SIGSEGV) {
FAIL("Incorrect signal");
}
if (_context->uc_mcontext.gregs[REG_TRAPNO] != 13) {
FAIL("Incorrect trapno");
}
if (_context->uc_mcontext.gregs[REG_ERR] != 0) {
FAIL("Incorrect reg_err");
}
if (siginfo->si_code != 0x80) {
FAIL("Incorrect si_code");
}
size_t inst_size = is_cpuid(reinterpret_cast<uint8_t*>(_context->uc_mcontext.gregs[FEX_IP_REG]));
if (inst_size == 0) {
FAIL("Isn't cpuid");
}
// Return bogus data
_context->uc_mcontext.gregs[FEX_RAX_REG] = 0x41424344U + faults;
_context->uc_mcontext.gregs[FEX_RBX_REG] = 0x51525354U + faults;
_context->uc_mcontext.gregs[FEX_RCX_REG] = 0x61626364U + faults;
_context->uc_mcontext.gregs[FEX_RDX_REG] = 0x71727374U + faults;
_context->uc_mcontext.gregs[FEX_IP_REG] += inst_size;
++faults;
}
TEST_CASE("cpuid faulting") {
cpuid_fn fn0 = get_cpuid(0);
if (syscall(SYS_arch_prctl, ARCH_SET_CPUID, (void*)1) != 0) {
// Unsupported, just skip.
return;
}
struct sigaction act {};
act.sa_sigaction = SignalHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
using ptr_type = cpuid_fn (*)(uint32_t, uint32_t);
ptr_type test_funcs[] = {
get_cpuid, get_cpuid_2, get_cpuid_3, get_cpuid_4, get_cpuid_5, get_cpuid_6,
get_cpuid_7, get_cpuid_8, get_cpuid_9, get_cpuid_10, get_cpuid_11,
#ifdef REG_RIP
get_cpuid_12,
#endif
get_looooong_cpuid,
};
// Just attempt regular CPUID for now.
for (auto ptr : test_funcs) {
faults = 0;
cpuid_fn fn_emu = ptr(0, 0);
REQUIRE(faults == 0);
CHECK(fn0.eax == fn_emu.eax);
CHECK(fn0.ebx == fn_emu.ebx);
CHECK(fn0.ecx == fn_emu.ecx);
CHECK(fn0.edx == fn_emu.edx);
}
// Enable faulting.
REQUIRE(syscall(SYS_arch_prctl, ARCH_SET_CPUID, (void*)0) == 0);
for (auto ptr : test_funcs) {
faults = 0;
cpuid_fn fn_emu = ptr(0, 0);
REQUIRE(faults == 1);
CHECK(fn0.eax != fn_emu.eax);
CHECK(fn0.ebx != fn_emu.ebx);
CHECK(fn0.ecx != fn_emu.ecx);
CHECK(fn0.edx != fn_emu.edx);
CHECK(fn_emu.eax == 0x41424344U);
CHECK(fn_emu.ebx == 0x51525354U);
CHECK(fn_emu.ecx == 0x61626364U);
CHECK(fn_emu.edx == 0x71727374U);
}
// Disable faulting.
REQUIRE(syscall(SYS_arch_prctl, ARCH_SET_CPUID, (void*)1) == 0);
// Just attempt regular CPUID for now.
for (auto ptr : test_funcs) {
faults = 0;
cpuid_fn fn_emu = ptr(0, 0);
REQUIRE(faults == 0);
CHECK(fn0.eax == fn_emu.eax);
CHECK(fn0.ebx == fn_emu.ebx);
CHECK(fn0.ecx == fn_emu.ecx);
CHECK(fn0.edx == fn_emu.edx);
}
}
@@ -1,102 +0,0 @@
#include "invalid_util.h"
#include <catch2/catch_test_macros.hpp>
#include <signal.h>
#include <sys/mman.h>
#include <stdint.h>
#ifndef REG_RIP
#define REG_RIP REG_EIP
#endif
enum trapno {
X86_TRAPNO_UD = 6,
X86_TRAPNO_GP = 13,
};
#define CONCAT(x, y) x##y
#define TestSymbols(num) \
extern "C" uint64_t CONCAT(TestBegin_, num); \
extern "C" uint64_t CONCAT(TestEnd_, num);
#define Test(num, asm, trapno, errno, si_code, signal) \
capturing_handler_skip = (unsigned long)&CONCAT(TestEnd_, num) - (unsigned long)&CONCAT(TestBegin_, num); \
const unsigned long EXPECTED_RIP = (unsigned long)&CONCAT(TestBegin_, num); \
const int EXPECTED_TRAPNO = trapno; \
const int EXPECTED_ERR = errno; \
const int EXPECTED_SI_CODE = si_code; \
const int EXPECTED_SIGNAL = signal; \
__asm volatile("TestBegin_" #num ":" asm ";" \
"TestEnd_" #num ":" :: \
: "memory");
#define TEST(num, name, asm, trapno, errno, _si_code, _signal) \
TestSymbols(num); \
TEST_CASE("Signals: " #name) { \
struct sigaction act {}; \
act.sa_sigaction = CapturingHandler; \
act.sa_flags = SA_SIGINFO; \
sigaction(SIGSEGV, &act, nullptr); \
sigaction(SIGTRAP, &act, nullptr); \
sigaction(SIGILL, &act, nullptr); \
\
Test(num, asm, trapno, errno, _si_code, _signal); \
\
REQUIRE(from_handler.has_value()); \
CHECK(from_handler->mctx.gregs[REG_RIP] == EXPECTED_RIP); \
CHECK(from_handler->mctx.gregs[REG_TRAPNO] == EXPECTED_TRAPNO); \
CHECK(from_handler->mctx.gregs[REG_ERR] == EXPECTED_ERR); \
CHECK(from_handler->si_code == EXPECTED_SI_CODE); \
CHECK(from_handler->signal == EXPECTED_SIGNAL); \
from_handler.reset(); \
}
// Invalid group 7 row 5 instructions
// 0xe8 is serialize, so skipped.
TEST(0, "0xe9", ".byte 0x0f, 0x01, 0xe9", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(1, "0xea", ".byte 0x0f, 0x01, 0xea", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(2, "0xeb", ".byte 0x0f, 0x01, 0xeb", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(3, "0xec", ".byte 0x0f, 0x01, 0xec", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(4, "0xed", ".byte 0x0f, 0x01, 0xed", X86_TRAPNO_UD, 0, 2, SIGILL);
// Skipped as these are real instructions.
// TEST(5, "rdpkru", "rdpkru", X86_TRAPNO_UD, 0, 2, SIGILL);
// TEST(6, "wrpkru", "wrpkru", X86_TRAPNO_UD, 0, 2, SIGILL);
// Testing with prefixes as well.
TEST(5, "0x66 0xe9", ".byte 0x66, 0x0f, 0x01, 0xe9", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(6, "0x66 0xea", ".byte 0x66, 0x0f, 0x01, 0xea", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(7, "0x66 0xeb", ".byte 0x66, 0x0f, 0x01, 0xeb", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(8, "0x66 0xec", ".byte 0x66, 0x0f, 0x01, 0xec", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(9, "0x66 0xed", ".byte 0x66, 0x0f, 0x01, 0xed", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(10, "0xf2 0xe9", ".byte 0xf2, 0x0f, 0x01, 0xe9", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(11, "0xf2 0xea", ".byte 0xf2, 0x0f, 0x01, 0xea", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(12, "0xf2 0xeb", ".byte 0xf2, 0x0f, 0x01, 0xeb", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(13, "0xf2 0xec", ".byte 0xf2, 0x0f, 0x01, 0xec", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(14, "0xf2 0xed", ".byte 0xf2, 0x0f, 0x01, 0xed", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(15, "0xf3 0xe9", ".byte 0xf3, 0x0f, 0x01, 0xe9", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(16, "0xf3 0xea", ".byte 0xf3, 0x0f, 0x01, 0xea", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(17, "0xf3 0xeb", ".byte 0xf3, 0x0f, 0x01, 0xeb", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(18, "0xf3 0xec", ".byte 0xf3, 0x0f, 0x01, 0xec", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(19, "0xf3 0xed", ".byte 0xf3, 0x0f, 0x01, 0xed", X86_TRAPNO_UD, 0, 2, SIGILL);
#ifdef __x86_64__
TEST(20, "0x48 0xe9", ".byte 0x48, 0x0f, 0x01, 0xe9", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(21, "0x48 0xea", ".byte 0x48, 0x0f, 0x01, 0xea", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(22, "0x48 0xeb", ".byte 0x48, 0x0f, 0x01, 0xeb", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(23, "0x48 0xec", ".byte 0x48, 0x0f, 0x01, 0xec", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(24, "0x48 0xed", ".byte 0x48, 0x0f, 0x01, 0xed", X86_TRAPNO_UD, 0, 2, SIGILL);
#endif
// Test row 5 with modrm.mod != 11, aka r/m == memory
// These are all reserved as #UD.
TEST(25, "0x28 mod==0", ".byte 0x0f, 0x01, 0x28", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(26, "0x29 mod==0", ".byte 0x0f, 0x01, 0x29", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(27, "0x2a mod==0", ".byte 0x0f, 0x01, 0x2a", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(28, "0x2b mod==0", ".byte 0x0f, 0x01, 0x2b", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(29, "0x2c mod==0", ".byte 0x0f, 0x01, 0x2c", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(30, "0x2d mod==0", ".byte 0x0f, 0x01, 0x2d", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(31, "0x2e mod==0", ".byte 0x0f, 0x01, 0x2e", X86_TRAPNO_UD, 0, 2, SIGILL);
TEST(32, "0x2f mod==0", ".byte 0x0f, 0x01, 0x2f", X86_TRAPNO_UD, 0, 2, SIGILL);
@@ -28,7 +28,7 @@ static void SIGSEGV_Handler(int signal, siginfo_t* siginfo, void* context) {
TEST_CASE("Signals: Test No-Exec") {
struct sigaction act {};
act.sa_sigaction = SIGSEGV_Handler;
act.sa_flags = SA_SIGINFO | SA_NODEFER;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
auto PageSize = sysconf(_SC_PAGESIZE);
@@ -71,7 +71,7 @@ TEST_CASE("Signals: Test No-Exec") {
TEST_CASE("Signals: Partial decode") {
struct sigaction act {};
act.sa_sigaction = SIGSEGV_Handler;
act.sa_flags = SA_SIGINFO | SA_NODEFER;
act.sa_flags = SA_SIGINFO;
sigaction(SIGSEGV, &act, nullptr);
auto PageSize = sysconf(_SC_PAGESIZE);
@@ -1,167 +0,0 @@
#include <catch2/catch_test_macros.hpp>
#include <iterator>
#include <signal.h>
#include <stdint.h>
#include <ucontext.h>
// Single-stepping with TF over CPUID and SYSCALL.
//
// Both instructions leave the JIT block: CPUID runs through a dispatcher stub, SYSCALL through the syscall handler,
// and both return to guest code through an L1 cache lookup. With TF set the trap must still land on the instruction
// after CPUID/SYSCALL and on every instruction that follows.
//
// The lookup only hits when the code behind the instruction is already compiled, so each probe runs from its `Warm`
// label once with TF clear. The stepped run shares nothing else with that first run: it sets TF in a lead-in of its
// own that jumps onto the CPUID/SYSCALL instruction, and the handler clears TF when the trap on the final RET
// arrives. The only compiled block the stepped run can enter is then the one behind CPUID/SYSCALL.
#pragma GCC diagnostic ignored "-Wattributes" // Suppress warning in case control-flow checks aren't enabled
extern "C" void ProbeCPUID();
extern "C" void ProbeCPUIDWarm();
extern "C" void TF_CPUID_1();
extern "C" void TF_CPUID_2();
extern "C" void TF_CPUID_3();
extern "C" void TF_CPUID_4();
extern "C" void TF_CPUID_5();
extern "C" void TF_CPUID_6();
extern "C" void TF_CPUID_7();
// Sets TF. Every instruction after the first one then traps, up to the RET where the handler clears TF.
extern "C" __attribute__((naked, nocf_check)) void ProbeCPUID() {
__asm volatile(R"(
pushfq;
or qword ptr [rsp], 0x100;
popfq;
push rbx;
TF_CPUID_1:
xor eax, eax;
TF_CPUID_2:
jmp TF_CPUID_3;
ProbeCPUIDWarm:
push rbx;
xor eax, eax;
TF_CPUID_3:
cpuid;
TF_CPUID_4:
nop;
TF_CPUID_5:
nop;
TF_CPUID_6:
pop rbx;
TF_CPUID_7:
ret;
)");
}
extern "C" void ProbeSYSCALL();
extern "C" void ProbeSYSCALLWarm();
extern "C" void TF_SYSCALL_1();
extern "C" void TF_SYSCALL_2();
extern "C" void TF_SYSCALL_3();
extern "C" void TF_SYSCALL_4();
extern "C" void TF_SYSCALL_5();
extern "C" __attribute__((naked, nocf_check)) void ProbeSYSCALL() {
__asm volatile(R"(
pushfq;
or qword ptr [rsp], 0x100;
popfq;
mov eax, 39;
TF_SYSCALL_1:
jmp TF_SYSCALL_2;
ProbeSYSCALLWarm:
mov eax, 39;
TF_SYSCALL_2:
syscall;
TF_SYSCALL_3:
nop;
TF_SYSCALL_4:
nop;
TF_SYSCALL_5:
ret;
)");
}
constexpr int MAX_TRAPS = 32;
static uintptr_t trap_rips[MAX_TRAPS];
static int trap_count;
static int trap_bad_si_code;
static uintptr_t trap_last_rip;
static void TrapHandler(int, siginfo_t* siginfo, void* context) {
auto* ctx = static_cast<ucontext_t*>(context);
const uintptr_t rip = ctx->uc_mcontext.gregs[REG_RIP];
if (trap_count < MAX_TRAPS) {
trap_rips[trap_count] = rip;
}
trap_count++;
if (siginfo->si_code != TRAP_TRACE) {
trap_bad_si_code++;
}
// Stop stepping on the probe's RET, or once the traps have run away from the probe.
if (rip == trap_last_rip || trap_count >= MAX_TRAPS) {
ctx->uc_mcontext.gregs[REG_EFL] &= ~0x100;
}
}
static void InstallTrapHandler(void (*last)()) {
struct sigaction act {};
act.sa_sigaction = TrapHandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGTRAP, &act, nullptr);
trap_count = 0;
trap_bad_si_code = 0;
trap_last_rip = reinterpret_cast<uintptr_t>(last);
}
template<size_t N>
static void CheckTraps(const uintptr_t (&expected)[N]) {
REQUIRE(trap_count == N);
CHECK(trap_bad_si_code == 0);
for (size_t i = 0; i < N; ++i) {
INFO("trap " << i);
CHECK(trap_rips[i] == expected[i]);
}
}
TEST_CASE("Signals: Trap Flag across CPUID") {
ProbeCPUIDWarm();
InstallTrapHandler(&TF_CPUID_7);
ProbeCPUID();
const uintptr_t expected[] = {
reinterpret_cast<uintptr_t>(&TF_CPUID_1), reinterpret_cast<uintptr_t>(&TF_CPUID_2), reinterpret_cast<uintptr_t>(&TF_CPUID_3),
reinterpret_cast<uintptr_t>(&TF_CPUID_4), reinterpret_cast<uintptr_t>(&TF_CPUID_5), reinterpret_cast<uintptr_t>(&TF_CPUID_6),
reinterpret_cast<uintptr_t>(&TF_CPUID_7),
};
CheckTraps(expected);
}
TEST_CASE("Signals: Trap Flag across SYSCALL") {
ProbeSYSCALLWarm();
InstallTrapHandler(&TF_SYSCALL_5);
ProbeSYSCALL();
// Where the trap after SYSCALL lands depends on the kernel's return path. FEX and a FRED kernel trap before the
// next instruction runs. A kernel returning through IRET does not trap after the IRET itself, so the first trap
// after the syscall lands after the following instruction.
const uintptr_t trap_after_syscall[] = {
reinterpret_cast<uintptr_t>(&TF_SYSCALL_1), reinterpret_cast<uintptr_t>(&TF_SYSCALL_2), reinterpret_cast<uintptr_t>(&TF_SYSCALL_3),
reinterpret_cast<uintptr_t>(&TF_SYSCALL_4), reinterpret_cast<uintptr_t>(&TF_SYSCALL_5),
};
const uintptr_t trap_after_next[] = {
reinterpret_cast<uintptr_t>(&TF_SYSCALL_1),
reinterpret_cast<uintptr_t>(&TF_SYSCALL_2),
reinterpret_cast<uintptr_t>(&TF_SYSCALL_4),
reinterpret_cast<uintptr_t>(&TF_SYSCALL_5),
};
if (trap_count == std::size(trap_after_next)) {
CheckTraps(trap_after_next);
} else {
CheckTraps(trap_after_syscall);
}
}
@@ -1,220 +0,0 @@
#include <catch2/catch_all.hpp>
#include <catch2/catch_test_macros.hpp>
#include <array>
#include <unistd.h>
#include <cstdint>
#include <optional>
#include <windows.h>
// Checks various paths that FEX generates for synchronous exceptions to ensure the data is correct.
#ifdef __x86_64__
#define PREFIX(x) _##x
#else
#define PREFIX(x) x
#endif
extern "C" {
__attribute__((naked)) void Ret() {
__asm volatile(R"(
.global _RetInstruction
_RetInstruction:
ret;
)");
}
__attribute__((naked)) void Do_UD2() {
__asm volatile(R"(
.global _Inst_UD2
_Inst_UD2:
ud2;
ret;
)");
}
__attribute__((naked)) void Do_INT1() {
__asm volatile(R"(
.global _Inst_INT1
_Inst_INT1:
.byte 0xF1; # int1;
ret;
)");
}
__attribute__((naked)) void Do_Long_INT1() {
__asm volatile(R"(
.global _Inst_Long_INT1
_Inst_Long_INT1:
.byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66;
.byte 0xF1; # int1;
ret;
)");
}
__attribute__((naked)) void Do_INT3() {
__asm volatile(R"(
.global _Inst_INT3
_Inst_INT3:
int3;
ret;
)");
}
__attribute__((naked)) void Do_Long_INT3() {
__asm volatile(R"(
.global _Inst_Long_INT3
_Inst_Long_INT3:
.byte 0x66, 0x66, 0x66, 0x66, 0x66, 0x66;
int3;
ret;
)");
}
__attribute__((naked)) void Do_HLT() {
__asm volatile(R"(
.global _Inst_HLT
_Inst_HLT:
hlt;
ret;
)");
}
// Technically an invalid encoding in the 3DNow! space.
__attribute__((naked)) void Do_Invalid_D6() {
__asm volatile(R"(
.global _Inst_Invalid_D6
_Inst_Invalid_D6:
.byte 0x0f, 0x0f, 0xd6;
ret;
)");
}
__attribute__((naked)) void Do_MOVCS() {
__asm volatile(R"(
.global _Inst_MOVCS
_Inst_MOVCS:
mov cs, ax;
ret;
)");
}
extern void* PREFIX(RetInstruction);
// Test instructions.
extern void* PREFIX(Inst_UD2);
extern void* PREFIX(Inst_INT1);
extern void* PREFIX(Inst_Long_INT1);
extern void* PREFIX(Inst_INT3);
extern void* PREFIX(Inst_Long_INT3);
extern void* PREFIX(Inst_HLT);
extern void* PREFIX(Inst_Invalid_D6);
extern void* PREFIX(Inst_MOVCS);
}
struct ReturnDataStruct {
DWORD Signal {};
size_t FaultRIP {};
size_t FaultAddr {};
};
std::optional<ReturnDataStruct> SignalReturnData {};
__attribute__((stdcall)) static long CapturingHandler(struct _EXCEPTION_POINTERS* ExceptionInfo) {
#ifdef __x86_64__
auto& RIP = ExceptionInfo->ContextRecord->Rip;
#else
auto& RIP = ExceptionInfo->ContextRecord->Eip;
#endif
ReturnDataStruct ReturnData {
.Signal = ExceptionInfo->ExceptionRecord->ExceptionCode,
.FaultRIP = RIP,
.FaultAddr = {},
};
if (ExceptionInfo->ExceptionRecord->NumberParameters == 2) {
ReturnData.FaultAddr = ExceptionInfo->ExceptionRecord->ExceptionInformation[1];
}
SignalReturnData = ReturnData;
RIP = reinterpret_cast<size_t>(&PREFIX(RetInstruction));
return EXCEPTION_CONTINUE_EXECUTION;
}
TEST_CASE("Synchronous faults") {
AddVectoredExceptionHandler(true, CapturingHandler);
using PtrType = void (*)();
struct ResultsStruct {
PtrType Code {};
ReturnDataStruct Result;
};
ResultsStruct TestResults[] = {
{Do_UD2,
ReturnDataStruct {
.Signal = STATUS_ILLEGAL_INSTRUCTION,
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_UD2)),
.FaultAddr = 0, // No fault addr here.
}},
{Do_INT1,
ReturnDataStruct {
.Signal = STATUS_SINGLE_STEP,
// Fault RIP is RIP+Size
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_INT1)) + 1,
.FaultAddr = 0, // No fault addr here.
}},
{Do_Long_INT1,
ReturnDataStruct {
.Signal = STATUS_SINGLE_STEP,
// Fault RIP is RIP+Size
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_Long_INT1)) + 7,
.FaultAddr = 0, // No fault addr here.
}},
{Do_INT3,
ReturnDataStruct {
.Signal = STATUS_BREAKPOINT,
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_INT3)),
.FaultAddr = 0, // No fault addr here.
}},
{Do_Long_INT3,
ReturnDataStruct {
.Signal = STATUS_BREAKPOINT,
// Fault RIP is at the fault instruction after the prefixes?
// This might be a WINE bug.
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_Long_INT3)) + 6,
.FaultAddr = 0, // No fault addr here.
}},
{Do_HLT,
ReturnDataStruct {
.Signal = STATUS_PRIVILEGED_INSTRUCTION,
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_HLT)),
.FaultAddr = 0, // No fault addr here.
}},
{Do_Invalid_D6,
ReturnDataStruct {
.Signal = STATUS_ILLEGAL_INSTRUCTION,
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_Invalid_D6)),
.FaultAddr = 0, // No fault addr here.
}},
{Do_MOVCS,
ReturnDataStruct {
.Signal = STATUS_ILLEGAL_INSTRUCTION,
.FaultRIP = reinterpret_cast<size_t>(&PREFIX(Inst_MOVCS)),
.FaultAddr = 0, // No fault addr here.
}},
};
for (auto& TestResult : TestResults) {
SignalReturnData.reset();
// Execute it. This will fault and return.
TestResult.Code();
REQUIRE(SignalReturnData.has_value());
auto Result = SignalReturnData;
CHECK(TestResult.Result.Signal == Result->Signal);
CHECK(TestResult.Result.FaultRIP == Result->FaultRIP);
CHECK(TestResult.Result.FaultAddr == Result->FaultAddr);
}
}
+1 -1
View File
@@ -8,7 +8,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"roundss xmm0, xmm1, 00000000b": {
@@ -7,7 +7,7 @@
"AFP"
],
"DisabledHostFeatures": [],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"cvtpi2ps xmm0, [rax]": {
@@ -9,7 +9,7 @@
"DisabledHostFeatures": [
"RPRES"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"cvtsi2ss xmm0, eax": {
@@ -7,7 +7,7 @@
"AFP"
],
"DisabledHostFeatures": [],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"cvtsi2sd xmm0, eax": {
@@ -8,7 +8,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"cvtpi2ps xmm0, [rax]": {
@@ -9,7 +9,7 @@
"SVE256",
"RPRES"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"cvtsi2ss xmm0, eax": {
@@ -8,7 +8,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"cvtsi2sd xmm0, eax": {
@@ -9,7 +9,7 @@
"DisabledHostFeatures": [
"RPRES"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vsqrtss xmm0, xmm1, xmm2": {
@@ -7,7 +7,7 @@
"AFP"
],
"DisabledHostFeatures": [],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vroundss xmm0, xmm1, 00000000b": {
+31 -31
View File
@@ -7,7 +7,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vfmaddsubps xmm0, xmm1, xmm2, xmm3": {
@@ -22,7 +22,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -38,7 +38,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -54,7 +54,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -70,7 +70,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -86,7 +86,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -102,7 +102,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -118,7 +118,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -134,7 +134,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -150,7 +150,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -166,7 +166,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -182,7 +182,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -198,7 +198,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -214,7 +214,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -230,7 +230,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -246,7 +246,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -262,7 +262,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -278,7 +278,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -294,7 +294,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -310,7 +310,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -326,7 +326,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -342,7 +342,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -358,7 +358,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -374,7 +374,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -390,7 +390,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -406,7 +406,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -422,7 +422,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -438,7 +438,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -454,7 +454,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -470,7 +470,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -486,7 +486,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
}
@@ -13,7 +13,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vmovups xmm0, xmm0": {
@@ -9,7 +9,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vaddsubpd xmm0, xmm1, xmm2": {
@@ -10,7 +10,7 @@
"FLAGM2",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vmovntps [rax], xmm0": {
@@ -10,7 +10,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vucomiss xmm0, xmm1": {
@@ -13,7 +13,7 @@
"I8MM",
"DOTPROD"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vpshufb xmm0, xmm1, xmm2": {
@@ -2224,7 +2224,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2241,7 +2241,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2258,7 +2258,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2275,7 +2275,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2292,7 +2292,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2309,7 +2309,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2326,7 +2326,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2343,7 +2343,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2360,7 +2360,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2377,7 +2377,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2394,7 +2394,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -2411,7 +2411,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6268,7 +6268,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6284,7 +6284,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6300,7 +6300,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6316,7 +6316,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6457,7 +6457,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6474,7 +6474,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6491,7 +6491,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6508,7 +6508,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6525,7 +6525,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6542,7 +6542,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6559,7 +6559,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6576,7 +6576,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6593,7 +6593,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6610,7 +6610,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6627,7 +6627,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -6644,7 +6644,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -8,7 +8,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vfmadd132ss xmm0, xmm1, xmm2": {
@@ -12,7 +12,7 @@
"SVE256",
"I8MM"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vpdpbusd xmm0, xmm1, xmm2": {
@@ -11,7 +11,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vpdpbusd xmm0, xmm1, xmm2": {
@@ -10,7 +10,7 @@
"FLAGM2",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vmovntdqa xmm0, [rax]": {
@@ -11,7 +11,7 @@
"SVE256",
"SVEBITPERM"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vtestps xmm0, xmm1": {
@@ -7,7 +7,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vpermq ymm0, ymm1, 1": {
@@ -8,7 +8,7 @@
"AFP",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vblendvps xmm0, xmm1, xmm2, xmm3": {
@@ -9,7 +9,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"vpsrlw xmm0, xmm1, 0": {
+1 -1
View File
@@ -9,7 +9,7 @@
"FLAGM",
"FLAGM2"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"lock add byte [rax], cl": {
@@ -9,7 +9,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"sha1nexte xmm0, xmm1": {
@@ -91,7 +91,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -107,7 +107,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -123,7 +123,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2808]",
"ldr x0, [x28, #2800]",
"br x0"
]
},
@@ -9,7 +9,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"sha1nexte xmm0, xmm1": {
@@ -9,7 +9,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"pclmulqdq xmm0, xmm1, 00000b": {
+1 -1
View File
@@ -10,7 +10,7 @@
"AFP",
"RPRES"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"These 3DNow! instructions are optimal assuming that FEX doesn't SRA MMX registers",
@@ -8,7 +8,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"Instructions that explicitly push against the limits of ARM's loadstore instructions"
@@ -8,7 +8,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"Instructions that explicitly push against the limits of ARM's loadstore instructions"
@@ -13,12 +13,36 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
],
"Instructions": {
"cpuid constant": {
"x86InstructionCount": 2,
"ExpectedInstructionCount": 11,
"Comment": [
"CPUID function call with constant function id"
],
"x86Insts": [
"mov rax, 0",
"cpuid"
],
"ExpectedArm64ASM": [
"mov w4, #0x0",
"isb",
"mov w20, #0x16",
"mov w6, #0x6547",
"movk w6, #0x756e, lsl #16",
"mov w21, #0x746e",
"movk w21, #0x6c65, lsl #16",
"mov w5, #0x6e69",
"movk w5, #0x4965, lsl #16",
"mov x7, x21",
"mov x4, x20"
]
},
"xgetbv constant": {
"x86InstructionCount": 2,
"ExpectedInstructionCount": 3,
@@ -11,7 +11,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
@@ -9,7 +9,7 @@
"SVE256",
"RPRES"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
@@ -14,7 +14,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
@@ -14,7 +14,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
@@ -13,7 +13,7 @@
"I8MM",
"DOTPROD"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"This file tracks the translations for the 'peephole' optimizations in FEX.",
@@ -10,7 +10,7 @@
"FLAGM2",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [
"This file tracks the translations for the 'peephole' optimizations in FEX.",
@@ -12,7 +12,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Comment": [],
"Instructions": {
@@ -10,7 +10,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"lock add byte [rax], cl": {
@@ -9,7 +9,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"Chained add": {
@@ -10,7 +10,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"ptest xmm0, xmm1": {
@@ -10,7 +10,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"The Witcher 3": {
@@ -10,7 +10,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"Sonic Mania movie player": {
@@ -10,7 +10,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"FMOD scalar loop": {
@@ -13,7 +13,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 44
"BinaryCacheVersion": 43
},
"Instructions": {
"The Sims 1 hot block": {
Loaded 100 of 163 files, more files were not shown because too many files have changed in this diff. Show more