Compare commits

...
45 Commits
Author SHA1 Message Date
LC ab135df091 Merge pull request #6126 from Sonicadvance1/320
FEXCore: Ensure BSF/BSR works with repne prefix
2026-11-11 20:20:16 -05:00
Ryan Houdek 059a623b41 FEXCore: Ensure BSF/BSR works with repne prefix
Turns out this is used in the wild by SecuROM. Helps get #6124 further,
but doesn't quite fix the game. Just has SecuROM error 8016 now.

Might be related to the DEP disablement stuff?
2026-11-11 16:05:45 -08:00
LC 9143ccaf2b Merge pull request #6125 from Sonicadvance1/319
FEXMingwTests: Adds a synchronous fault test
2026-11-11 18:55:07 -05:00
Ryan Houdek afee4dcc9e FEXMingwTests: Adds a synchronous fault test
This supplements the partial_decode_failure test with explicit
instructions that generate synchronous faults in FEX.

Today both of these tests pass in arm64ec and wow64, but wow64 currently
doesn't do fault-free synchronous faults. When tinkering with converting
wow64 over, it was picking up implementation failures.

Can't yet test these in CI directly but good to have.
2026-11-11 14:02:05 -08:00
Ryan Houdek 1800ddad5d Merge pull request #6123 from cjacek/ymmstate
ARM64EC: Remove no longer needed CONTEXT_ARM64_FEX_YMMSTATE flag
2026-11-11 11:02:53 -08:00
Jacek Caban 15f30601b7 ARM64EC: Remove no longer needed CONTEXT_ARM64_FEX_YMMSTATE flag
Wine now supports Windows-compatible context flags for storing YMM state
in the ARM64 context. That includes CONTEXT_ARM64_FLOATING_POINT_LOW/HIGH,
which can be used to address the FP registers representing SSE and AVX
registers separately. CONTEXT_ARM64_FLOATING_POINT is equivalent to
setting both of those flags, which is enough for how FEX currently uses
the context.

CONTEXT_ARM64_FEX_YMMSTATE has the same value as CONTEXT_ARM64_FLOATING_POINT_LOW,
which is not what it's meant to express, but it's harmless and redundant.
Just remove the flag to avoid confusion.
2026-11-11 16:16:56 +01:00
LC e3b30dda25 Merge pull request #6122 from Sonicadvance1/318
FEXInterpreter: select is banned
2026-11-11 00:00:14 -05:00
Ryan Houdek 95b7bf0f5f FEXInterpreter: select is banned
Practice what we preach, we are the party ppoll now.

This is the only remaining select in the project (aside from actual
syscalls).
2026-11-10 20:41:39 -08:00
LC 2a62720470 Merge pull request #6121 from Sonicadvance1/317
JIT: Ensure IR ops that jump out of the JIT reset the stack
2026-11-10 23:34:20 -05:00
Ryan Houdek dbbda7b2b3 JIT: Ensure IR ops that jump out of the JIT reset the stack
Noticed this while I was looking at the CPUID bits and my stack was
growing. Hits the various ops that leave the JIT and don't come back.
2026-11-10 20:15:56 -08:00
LC 5c0cfdbd2a Merge pull request #6120 from Sonicadvance1/316
CPUID: Fixes for CPU count
2026-11-10 23:01:03 -05:00
Ryan Houdek c8e9fc10e9 unittests/FEXLinuxTests: Fixes test
This test was wrongfully assuming that the HTT bit means hyperthreading.
Resolve it.
2026-11-10 19:35:35 -08:00
Ryan Houdek f00813de7b CPUID: Disable CPUID function 4h with emulating AMD 2026-11-10 19:35:34 -08:00
Ryan Houdek 65da98d642 CPUID: Fixes ApicIdSize.
This was supposed to be ilog2
2026-11-10 19:35:34 -08:00
Ryan Houdek eb7a9f2339 CPUID: Fixes for CPU count
Fixes Dragon Age: Origins - Ultimate Edition thinking we only have 1
core. Although the game does get angry when you have 32 or more cores
still.

The main change here is enabling the HTT CPUID bit. While the bit name
is called HTT it's actually been retconned over time. [1][2]

Initial SDM did infact say that the bit was used for hyperthreading, but
when the first multi-core system launched, it was also used to just
describe how many cores are in the logical processor. The real way to
determine if there is hyperthreading is to use the extended topology
information to determine how many logical processors are in each core.

Plus a minor fix for reporting CPU count to Win32 APIs.

[1] Latest SDM: `EDX bit 28, if set, indicates that bits 23:16 of CPUID.(EAX=1):EBX are valid. If this bit is not set, then the CPU package contains only 1 logical processor.`
[2] [Deep dive](https://www.os2museum.com/wp/htt-means-hyper-threading-right/)
2026-11-10 18:37:57 -08:00
LC 0fe0648349 Merge pull request #6117 from Sonicadvance1/315
Config: Hide details of EnumParser in the cpp
2026-11-10 17:23:58 -05:00
Ryan Houdek e760431de4 Config: Hide details of EnumParser in the cpp
This was causing some heavily compiler instantion overhead being in the
header. Also turns out quite a few random things were relying on fmt
from Config.h, so need to add some headers in a few locations.
2026-11-10 13:12:02 -08:00
LC 7fdac22483 Merge pull request #6116 from Sonicadvance1/314
CPUID: Claim support for FRED
2026-11-10 06:57:33 -05:00
Ryan Houdek 9e860a852b CPUID: Claim support for FRED
FRED doesn't have any userside facing behaviour changes except for
apparently TF and syscall behaviour. Since we behaviour like a FRED
device, expose the flag.
2026-11-09 18:09:18 -08:00
Ryan Houdek 90c9595cf4 Merge pull request #6112 from k33bs/upstream-tf-l1
Dispatcher: honour TF on the L1 fast path after SYSCALL and CPUID
2026-11-09 17:13:10 -08:00
k33bs 74e9029d53 FEXLinuxTests: single-step over CPUID and SYSCALL
With TF set, both instructions leave the block (the CPUID stub, the
syscall handler) and return through the L1 lookup. The lookup only hits
when the code behind the instruction is already compiled, so each probe
runs that part once with TF clear. The stepped run then sets TF in a
lead-in of its own, jumps onto the instruction and checks the RIP of
every trap up to the final RET, where the handler clears TF. Without
the TF check in the L1 lookup the traps after CPUID/SYSCALL are lost.

After SYSCALL the test accepts the trap before the next instruction
(FEX, FRED kernels) and after it (kernels returning through IRET).
2026-11-10 00:05:33 +01:00
k33bs 26d8ec55fc Dispatcher: honour TF on the L1 fast path after SYSCALL and CPUID
The L1 lookup that ends the syscall handler (502a1900e) and the CPUID
stub (05293166c) jumps straight into the cached block on a hit. That
block was compiled without the per-instruction single-step trap, so a
single-stepped SYSCALL or CPUID raises its trap only at the next
dispatcher entry, instructions later.

Check TF before the lookup and take the loop top when it is set, which
compiles single-step blocks. The trap then lands on the instruction
after CPUID, as on hardware, and on the instruction after SYSCALL, as
before 502a1900e (a FRED kernel does the same; a kernel returning
through IRET traps one instruction later).
2026-11-10 00:04:35 +01:00
LC b4ebbb5dc9 Merge pull request #6115 from Sonicadvance1/313
Hostrunner: reenable host runner FEX Linux tests
2026-11-09 17:45:15 -05:00
Ryan Houdek 08f35b3330 Hostrunner: reenable host runner FEX Linux tests
Not sure why we had disabled this.
2026-11-09 14:32:23 -08:00
Ryan Houdek fe931e3aa5 unittests: Fix FEX Linux tests 2026-11-09 14:31:58 -08:00
LC 8646d5b07f Merge pull request #6114 from Sonicadvance1/312
Ensure serialize isn't covering too much of the instruction space
2026-11-09 16:45:16 -05:00
Ryan Houdek 44dd02ac2f InstcountCI: Update 2026-11-09 12:42:48 -08:00
Ryan Houdek bfad5d6925 Ensure serialize isn't covering too much of the instruction space
Makes sure to jump to the second indirect table so it doesn't cover the
whole row. This time with unittests to ensure it is correct.

Supercedes #6113.
2026-11-09 12:42:46 -08:00
LC 16c100025b Merge pull request #6111 from Sonicadvance1/311
FEXRootFSFetcher: Fixes Config setting
2026-11-08 15:23:24 -05:00
Ryan Houdek 9cf556baa3 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-08 10:27:53 -08:00
LC 388fd9cf40 Merge pull request #6109 from crueter/ci-checkout-update
CI: Update Actions deps
2026-11-07 15:32:42 -05:00
crueter ec3d5ace98 CI: Update Actions deps
checkout -> v7, code format was on v4 which doesn't use nodejs 24
upload-artifact -> v7, supports uploading unzipped files so might add
that in later.
setup-python -> v7, more node 24 sauce

Signed-off-by: crueter <[email protected]>
2026-11-07 14:35:19 -05:00
LC ae495b971b Merge pull request #6106 from Sonicadvance1/310
Minor fixes
2026-11-06 09:52:49 -05:00
Ryan Houdek feee41a768 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-06 00:12:26 -08:00
Ryan Houdek cf2f831ad9 Windows: Fixes forced assert
This wasn't ever wired up for Windows.
2026-11-05 20:26:44 -08:00
Ryan Houdek b9fc587ac8 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-05 20:25:03 -08:00
LC c985199ed5 Merge pull request #6104 from Sonicadvance1/308
FEX: Implement support for CPUID faulting
2026-11-05 19:49:40 -05:00
LC e67bbd3ecf Merge pull request #6105 from Sonicadvance1/309
DiskCache: Moves SVE/AVX from HostFeatureHash to ProcessHash
2026-11-05 18:55:39 -05:00
Ryan Houdek 02e932d9f4 InstcountCI: Remove CPUID with constant. This no longer optimizes 2026-11-05 15:04:25 -08:00
Ryan Houdek a3f5700c33 InstcountCI: Update 2026-11-05 14:48:10 -08:00
Ryan Houdek a2b15c8e0f FEXLinuxTests: Adds a unittest for CPUID fault testing
Tests various instruction lengths to ensure it works robustly.
2026-11-05 14:48:08 -08:00
Ryan Houdek 05293166ce FEX: Implement support for CPUID faulting 2026-11-05 14:31:03 -08:00
Ryan Houdek ef586f03a8 Arm64Emitter: Add a way to query SRA x86 registers to their arm64 mapping 2026-11-05 14:31:03 -08:00
Ryan Houdek 6a3d85a26f InstcountCI: Update 2026-11-05 13:45:15 -08:00
Ryan Houdek 36aec0d54d 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-05 13:45:13 -08:00
174 changed files with 1566 additions and 549 deletions

No files matched your search

+2 -2
View File
@@ -24,7 +24,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -154,7 +154,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
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@v6
- uses: actions/checkout@v7
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -115,7 +115,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
timeout-minutes: 1
with:
name: Results-${{ env.runner_name }}-${{ env.runner_label }}
+10 -3
View File
@@ -24,7 +24,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
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
-DENABLE_ASSERTIONS=True -DENABLE_X86_HOST_DEBUG=True -DBUILD_FEX_LINUX_TESTS=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,9 +57,16 @@ 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@v6
uses: actions/upload-artifact@v7
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@v6
- uses: actions/checkout@v7
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -70,7 +70,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
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@v6
- uses: actions/checkout@v7
with:
fetch-depth: '0'
fetch-tags: 'true'
+2 -2
View File
@@ -14,7 +14,7 @@ jobs:
steps:
- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@v7
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@v4
uses: actions/setup-python@v7
with:
python-version: 3.11
cache: pip
+2 -2
View File
@@ -23,7 +23,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
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@v6
uses: actions/upload-artifact@v7
timeout-minutes: 1
with:
overwrite: true
+2 -2
View File
@@ -25,7 +25,7 @@ jobs:
fail-fast: false
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v7
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -75,7 +75,7 @@ jobs:
- name: Upload results
if: ${{ always() }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
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@v6
- uses: actions/checkout@v7
with:
fetch-depth: '0'
fetch-tags: 'true'
@@ -45,7 +45,7 @@ jobs:
target: arm64ec
- name: Upload libraries
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
timeout-minutes: 1
with:
overwrite: true
+5 -6
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::{}ConfigPair>(FEXCore::Config::{}_EnumPairs, Value_View);\n".format(op_key, op_key))
output_argloader.write("\tValue = FEXCore::Config::EnumParser(FEXCore::Config::{}_EnumPairs, Value_View);\n".format(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::{}ConfigPair>(FEXCore::Config::{}_EnumPairs, Value_View));\n".format(op_key, op_key))
output_argloader.write("\tSet(KeyOption, FEXCore::Config::EnumParser(FEXCore::Config::{}_EnumPairs, Value_View));\n".format(op_key))
output_argloader.write("}\n")
elif (value_type == "strarray"):
output_argloader.write("else if (KeyName == \"{0}\") {{\n".format(op_key))
@@ -394,13 +394,12 @@ def print_parse_enum_options(options):
if (op_vals["Type"] == "strenum"):
Enums = op_vals["Enums"]
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))
output_argloader.write("constexpr static std::array<StringEnumPairType, {}> {}_EnumPairs = {{{{\n".format(len(Enums) + 1, op_key))
i = 0
# Always have an OFF.
output_argloader.write("\t{{ \"off\", FEXCore::Config::{}::OFF }},\n".format(op_key))
output_argloader.write("\t{{ \"off\", FEXCore::ToUnderlying(FEXCore::Config::{}::OFF) }},\n".format(op_key))
for enum_op_key, enum_op_vals in Enums.items():
output_argloader.write("\t{{ \"{}\", FEXCore::Config::{}::{} }},\n".format(enum_op_vals, op_key, enum_op_key.upper()))
output_argloader.write("\t{{ \"{}\", FEXCore::ToUnderlying(FEXCore::Config::{}::{}) }},\n".format(enum_op_vals, op_key, enum_op_key.upper()))
i += 1
output_argloader.write("}};\n")
@@ -18,6 +18,7 @@
#include <FEXHeaderUtils/Filesystem.h>
#include <array>
#include <charconv>
#include <cstdlib>
#include <optional>
#include <stddef.h>
@@ -558,4 +559,36 @@ 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
@@ -665,11 +665,11 @@ void Arm64Emitter::FillSpecialRegs(ARMEmitter::Register TmpReg, ARMEmitter::Regi
// We don't bother spilling these in SpillStaticRegs,
// since all that matters is we restore them on a fill.
// It's not a concern if they get trounced by something else.
if (EmitterCTX->HostFeatures.SupportsSVE256) {
if (EmitterCTX->HostFeatures.SupportsSVE256()) {
ptrue(ARMEmitter::SubRegSize::i8Bit, PRED_TMP_32B, ARMEmitter::PredicatePattern::SVE_VL32);
}
if (EmitterCTX->HostFeatures.SupportsSVE128) {
if (EmitterCTX->HostFeatures.SupportsSVE128()) {
ptrue(ARMEmitter::SubRegSize::i8Bit, PRED_TMP_16B, ARMEmitter::PredicatePattern::SVE_VL16);
}
@@ -739,7 +739,7 @@ void Arm64Emitter::SpillStaticRegs(ARMEmitter::Register TmpReg, SpillStaticRegOp
}
if (Options.FPRs) {
if (EmitterCTX->HostFeatures.SupportsAVX && EmitterCTX->HostFeatures.SupportsSVE256) {
if (EmitterCTX->HostFeatures.SupportsAVX() && EmitterCTX->HostFeatures.SupportsSVE256()) {
for (size_t i = 0; i < StaticFPRegisters.size(); i++) {
const auto Reg = StaticFPRegisters[i];
@@ -826,7 +826,7 @@ void Arm64Emitter::FillStaticRegs(FillStaticRegOptions Options) {
FillSpecialRegs(TmpReg, TmpReg2, {.SetFIZ = true, .SetPredRegs = Options.FPRs});
if (Options.FPRs) {
if (EmitterCTX->HostFeatures.SupportsAVX && EmitterCTX->HostFeatures.SupportsSVE256) {
if (EmitterCTX->HostFeatures.SupportsAVX() && EmitterCTX->HostFeatures.SupportsSVE256()) {
for (size_t i = 0; i < StaticFPRegisters.size(); i++) {
const auto Reg = StaticFPRegisters[i];
if (((1U << Reg.Idx()) & Options.FPRFillMask) != 0) {
@@ -990,7 +990,7 @@ void Arm64Emitter::PopGeneralRegisters(std::span<const ARMEmitter::Register> Reg
}
size_t Arm64Emitter::PushDynamicRegs(ARMEmitter::Register TmpReg) {
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256();
const auto GPRSize = GeneralRegistersNotPreserved.size() * Core::CPUState::GPR_REG_SIZE;
const auto FPRRegSize = CanUseSVE256 ? 32 : 16;
const auto FPRSize = GeneralFPRegisters.size() * FPRRegSize;
@@ -1013,7 +1013,7 @@ size_t Arm64Emitter::PushDynamicRegs(ARMEmitter::Register TmpReg) {
}
void Arm64Emitter::PopDynamicRegs() {
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256();
// Pop vectors first
PopVectorRegisters(CanUseSVE256, GeneralFPRegisters);
@@ -1023,7 +1023,7 @@ void Arm64Emitter::PopDynamicRegs() {
}
size_t Arm64Emitter::SpillForPreserveAllABICall(ARMEmitter::Register TmpReg, bool FPRs) {
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256();
const auto FPRRegSize = CanUseSVE256 ? 32 : 16;
std::span<const ARMEmitter::Register> DynamicGPRs {};
@@ -1078,7 +1078,7 @@ size_t Arm64Emitter::SpillForPreserveAllABICall(ARMEmitter::Register TmpReg, boo
}
void Arm64Emitter::FillForPreserveAllABICall(bool FPRs) {
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256;
const auto CanUseSVE256 = EmitterCTX->HostFeatures.SupportsSVE256();
std::span<const ARMEmitter::Register> DynamicGPRs {};
std::span<const ARMEmitter::VRegister> DynamicFPRs {};
@@ -1129,4 +1129,9 @@ 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,6 +235,8 @@ protected:
void Align16B();
ARMEmitter::Register GetX86GPRMapping(FEXCore::X86State::X86Reg Reg);
#ifdef VIXL_SIMULATOR
// Generates a vixl simulator runtime call.
//
+13 -9
View File
@@ -443,7 +443,7 @@ void CPUIDEmu::SetupHostHybridFlag() {}
void CPUIDEmu::SetupFeatures() {
if (CTX->HostFeatures.SupportsAVX) {
if (CTX->HostFeatures.SupportsAVX()) {
XCR0 |= XCR0_AVX;
}
@@ -545,7 +545,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_01h(uint32_t Leaf) const {
(1 << 25) | // SSE
(1 << 26) | // SSE2
(0 << 27) | // Self Snoop
(0 << 28) | // (HTT) Max APIC IDs reserved field is valid
(1 << 28) | // (HTT) Max APIC IDs reserved field is valid
(1 << 29) | // Thermal monitor
(0 << 30) | // Reserved
(0 << 31); // Pending break enable
@@ -572,6 +572,10 @@ 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;
@@ -823,7 +827,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_07h(uint32_t Leaf) const {
(0U << 14) | // Reserved
(0U << 15) | // Reserved
(0U << 16) | // Reserved
(0U << 17) | // FRED (Flexible Return and Event Delivery)
(1U << 17) | // FRED (Flexible Return and Event Delivery)
(0U << 18) | // LKGS (Load into Kernel GS Base)
(0U << 19) | // WRMSRNS
(0U << 20) | // NMI_SRC
@@ -1162,14 +1166,14 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0004h(uint32_t Leaf) con
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0002h(uint32_t Leaf, uint32_t CPU) const {
FEXCore::CPUID::FunctionResults Res {};
auto& Data = PerCPUData[CPU];
auto& Data = PerCPUData[std::min<uint32_t>(CPU, PerCPUData.size() - 1)];
memcpy(&Res, Data.ProductName, std::min(strlen(Data.ProductName), sizeof(FEXCore::CPUID::FunctionResults)));
return Res;
}
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0003h(uint32_t Leaf, uint32_t CPU) const {
FEXCore::CPUID::FunctionResults Res {};
auto& Data = PerCPUData[CPU];
auto& Data = PerCPUData[std::min<uint32_t>(CPU, PerCPUData.size() - 1)];
const auto RemainingStringSize = std::max<ssize_t>(0, strlen(Data.ProductName) - 16);
memcpy(&Res, Data.ProductName + 16, std::min<size_t>(RemainingStringSize, sizeof(FEXCore::CPUID::FunctionResults)));
return Res;
@@ -1177,7 +1181,7 @@ FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0003h(uint32_t Leaf, uin
FEXCore::CPUID::FunctionResults CPUIDEmu::Function_8000_0004h(uint32_t Leaf, uint32_t CPU) const {
FEXCore::CPUID::FunctionResults Res {};
auto& Data = PerCPUData[CPU];
auto& Data = PerCPUData[std::min<uint32_t>(CPU, PerCPUData.size() - 1)];
const auto RemainingStringSize = std::max<ssize_t>(0, strlen(Data.ProductName) - 32);
memcpy(&Res, Data.ProductName + 32, std::min<size_t>(RemainingStringSize, sizeof(FEXCore::CPUID::FunctionResults)));
return Res;
@@ -1264,9 +1268,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
(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
(FEXCore::ilog2(std::bit_ceil(Cores)) << 12) | // ApicIdSize: Number of bits in ApicID
(CoreCount << 0); // Count count subtract one
return Res;
}
+4 -4
View File
@@ -264,8 +264,8 @@ uint32_t ContextImpl::ReconstructCompactedEFLAGS(FEXCore::Core::InternalThreadSt
void ContextImpl::ReconstructXMMRegisters(const FEXCore::Core::InternalThreadState* Thread, __uint128_t* XMM_Low, __uint128_t* YMM_High) {
const size_t MaximumRegisters = Config.Is64BitMode ? FEXCore::Core::CPUState::NUM_XMMS : 8;
if (YMM_High != nullptr && HostFeatures.SupportsAVX) {
const bool SupportsConvergedRegisters = HostFeatures.SupportsSVE256;
if (YMM_High != nullptr && HostFeatures.SupportsAVX()) {
const bool SupportsConvergedRegisters = HostFeatures.SupportsSVE256();
if (SupportsConvergedRegisters) {
///< Output wants to de-interleave
@@ -288,8 +288,8 @@ void ContextImpl::ReconstructXMMRegisters(const FEXCore::Core::InternalThreadSta
void ContextImpl::SetXMMRegistersFromState(FEXCore::Core::InternalThreadState* Thread, const __uint128_t* XMM_Low, const __uint128_t* YMM_High) {
const size_t MaximumRegisters = Config.Is64BitMode ? FEXCore::Core::CPUState::NUM_XMMS : 8;
if (YMM_High != nullptr && HostFeatures.SupportsAVX) {
const bool SupportsConvergedRegisters = HostFeatures.SupportsSVE256;
if (YMM_High != nullptr && HostFeatures.SupportsAVX()) {
const bool SupportsConvergedRegisters = HostFeatures.SupportsSVE256();
if (SupportsConvergedRegisters) {
///< Output wants to de-interleave
@@ -409,6 +409,12 @@ namespace DiskCache {
uint64_t HostFeaturesHash;
} MachineBucketData = {FormatVersion, HostFeatureHash.HostFeaturesHash};
uint8_t ProcessFeaturesHash {};
memcpy(&ProcessFeaturesHash, &HostFeatureHash.ProcessFeaturesHash, sizeof(ProcessFeaturesHash));
LogMan::Msg::IFmt("Disk Cache features:");
LogMan::Msg::IFmt("HostFeatures Hash: 0x{:016x}", HostFeatureHash.HostFeaturesHash);
LogMan::Msg::IFmt("ProcessFeaturesHash: 0x{:02x}", ProcessFeaturesHash);
fextl::vector<uint8_t> BucketBytes(sizeof(MachineBucketData) + sizeof(HostFeatureHash.ProcessFeaturesHash) + SerializedConfig.size());
memcpy(BucketBytes.data(), &MachineBucketData, sizeof(MachineBucketData));
+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 = 42;
static constexpr uint16_t FormatVersion = 44;
static constexpr uint32_t LOOKUP_KEY_MAX_BUCKET_DEPTH = 500;
@@ -12,6 +12,7 @@
#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>
@@ -417,6 +418,7 @@ void Dispatcher::EmitDispatcher() {
brk(0);
}
ARMEmitter::BackwardLabel GuestSIGSEGVLabelPostSpill;
{
// Guest Overflow handler
// Needs to be distinct from the SignalHandlerReturnAddress
@@ -424,6 +426,8 @@ void Dispatcher::EmitDispatcher() {
SpillStaticRegs(TMP1);
(void)Bind(&GuestSIGSEGVLabelPostSpill);
// hlt/udf = SIGILL
// brk = SIGTRAP
// ??? = SIGSEGV
@@ -534,6 +538,35 @@ 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.
@@ -566,23 +599,90 @@ void Dispatcher::EmitDispatcher() {
// We can safely claim we are no longer in a syscall
str(ARMEmitter::XReg::zr, STATE, offsetof(FEXCore::Core::CpuStateFrame, InSyscallInfo));
{
// 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));
EmitL1Lookup();
}
// 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);
{
// 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>();
ldp<ARMEmitter::IndexType::OFFSET>(TMP2, TMP1, TMP1, 0);
// CPUID behaves like an ISB.
isb();
sub(TMP1, TMP1, TMP3);
(void)cbnz(ARMEmitter::Size::i64Bit, TMP1, &LoopTop);
br(TMP2);
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);
}
// 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);
}
{
@@ -2683,6 +2783,7 @@ 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,6 +82,7 @@ private:
uint64_t ThreadPauseHandlerAddress {};
uint64_t ThreadPauseHandlerAddressSpillSRA {};
uint64_t ThreadDispatchSyscallHandler {};
uint64_t ThreadDispatchCPUIDHandler {};
uint64_t ThreadDispatchRemoveCodeEntry {};
uint64_t ExitFunctionLinkerAddress {};
uint64_t SignalHandlerReturnAddress {};
+14 -3
View File
@@ -79,10 +79,10 @@ Decoder::Decoder(FEXCore::Core::InternalThreadState* Thread)
X87Table = &FEXCore::X86Tables::X87F80Ops;
}
if (CTX->HostFeatures.SupportsAVX && CTX->HostFeatures.SupportsSVE256) {
if (CTX->HostFeatures.SupportsAVX() && CTX->HostFeatures.SupportsSVE256()) {
VEXTable = &FEXCore::X86Tables::VEXTableOps;
VEXTableGroup = &FEXCore::X86Tables::VEXTableGroupOps;
} else if (CTX->HostFeatures.SupportsAVX) {
} else if (CTX->HostFeatures.SupportsAVX()) {
VEXTable = &FEXCore::X86Tables::VEXTableOps_AVX128;
VEXTableGroup = &FEXCore::X86Tables::VEXTableGroupOps_AVX128;
}
@@ -390,6 +390,10 @@ 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;
@@ -699,6 +703,10 @@ 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.
@@ -749,7 +757,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, 255, 255, 3,
255, 0, 1, 2, 255, 3, 255, 4,
};
uint8_t Field = RegToField[ModRM.reg];
if (Field == 255) {
@@ -758,6 +766,9 @@ 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);
}
+13 -37
View File
@@ -285,6 +285,9 @@ 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);
@@ -376,6 +379,9 @@ 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));
@@ -395,45 +401,15 @@ DEF_OP(ThreadRemoveCodeEntry) {
DEF_OP(CPUID) {
auto Op = IROp->C<IR::IROp_CPUID>();
isb();
mov(ARMEmitter::Size::i64Bit, TMP2, GetReg(Op->Function));
mov(ARMEmitter::Size::i64Bit, TMP3, GetReg(Op->Leaf));
SpillStaticRegs(TMP1);
PushDynamicRegs(TMP4);
SpillStaticRegs(TMP4);
// Not coming back.
ResetStack();
// 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);
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);
}
DEF_OP(XGetBV) {
+3 -3
View File
@@ -625,9 +625,9 @@ void Arm64JITCore::Op_NoOp(const IR::IROp_Header* IROp, IR::Ref Node) {}
Arm64JITCore::Arm64JITCore(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::InternalThreadState* Thread)
: CPUBackend(*ctx, Thread)
, Arm64Emitter(ctx)
, HostSupportsSVE128 {ctx->HostFeatures.SupportsSVE128 != 0}
, HostSupportsSVE256 {ctx->HostFeatures.SupportsSVE256 != 0}
, HostSupportsAVX256 {ctx->HostFeatures.SupportsAVX && ctx->HostFeatures.SupportsSVE256}
, HostSupportsSVE128 {ctx->HostFeatures.SupportsSVE128()}
, HostSupportsSVE256 {ctx->HostFeatures.SupportsSVE256()}
, HostSupportsAVX256 {ctx->HostFeatures.SupportsAVX() && ctx->HostFeatures.SupportsSVE256()}
, HostSupportsRPRES {ctx->HostFeatures.SupportsRPRES != 0}
, HostSupportsAFP {ctx->HostFeatures.SupportsAFP != 0}
, CTX {ctx}
@@ -1370,20 +1370,19 @@ void OpDispatchBuilder::MOVOffsetOp(OpcodeArgs) {
}
void OpDispatchBuilder::CPUIDOp(OpcodeArgs) {
Ref Src = LoadGPRRegister(X86State::REG_RAX);
Ref Leaf = LoadGPRRegister(X86State::REG_RCX);
BlockSetRIP = true;
Ref RAX = _AllocateGPR(false);
Ref RBX = _AllocateGPR(false);
Ref RCX = _AllocateGPR(false);
Ref RDX = _AllocateGPR(false);
// Calculate flags early.
CalculateDeferredFlags();
_CPUID(Src, Leaf, RAX, RBX, RCX, RDX);
// 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));
StoreGPRRegister(X86State::REG_RAX, RAX);
StoreGPRRegister(X86State::REG_RBX, RBX);
StoreGPRRegister(X86State::REG_RCX, RCX);
StoreGPRRegister(X86State::REG_RDX, RDX);
FlushRegisterCache();
_CPUID(Op->InstSize);
}
uint32_t OpDispatchBuilder::GetConstantShift(X86Tables::DecodedOp Op, bool Is1Bit) {
@@ -4752,11 +4751,11 @@ OpDispatchBuilder::OpDispatchBuilder(FEXCore::Context::ContextImpl* ctx, FEXCore
: IREmitter {ctx->OpDispatcherAllocator, ctx->HostFeatures.SupportsTSOImm9 != 0}
, CTX {ctx}
, Thread {Thread} {
if (CTX->HostFeatures.SupportsAVX && CTX->HostFeatures.SupportsSVE256) {
if (CTX->HostFeatures.SupportsAVX() && CTX->HostFeatures.SupportsSVE256()) {
SaveAVXStateFunc = &OpDispatchBuilder::SaveAVXState;
RestoreAVXStateFunc = &OpDispatchBuilder::RestoreAVXState;
DefaultAVXStateFunc = &OpDispatchBuilder::DefaultAVXState;
} else if (CTX->HostFeatures.SupportsAVX) {
} else if (CTX->HostFeatures.SupportsAVX()) {
SaveAVXStateFunc = &OpDispatchBuilder::AVX128_SaveAVXState;
RestoreAVXStateFunc = &OpDispatchBuilder::AVX128_RestoreAVXState;
DefaultAVXStateFunc = &OpDispatchBuilder::AVX128_DefaultAVXState;
@@ -1330,7 +1330,7 @@ protected:
/* On ASIMD platforms, the emulation happens to preserve NZCV, unlike the
* more optimal SVE implementation that clobbers.
*/
if (!CTX->HostFeatures.SupportsSVE128 && !CTX->HostFeatures.SupportsSVE256) {
if (!CTX->HostFeatures.SupportsSVE()) {
return;
}
@@ -1677,7 +1677,7 @@ private:
}
constexpr OpSize GetGuestVectorLength() const {
return (CTX->HostFeatures.SupportsSVE256 && CTX->HostFeatures.SupportsAVX) ? OpSize::i256Bit : OpSize::i128Bit;
return (CTX->HostFeatures.SupportsSVE256() && CTX->HostFeatures.SupportsAVX()) ? OpSize::i256Bit : OpSize::i128Bit;
}
[[nodiscard]]
@@ -2067,7 +2067,7 @@ OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherImpl(OpcodeArgs, Op
BaseAddr = Invalid();
}
if (CTX->HostFeatures.SupportsSVE128) {
if (CTX->HostFeatures.SupportsSVE128()) {
if (ElementLoadSize == OpSize::i64Bit && AddrElementSize == OpSize::i32Bit) {
// In the case that FEX is loading double the amount of data than the number of address bits then we can optimize this case.
// For 256-bits of data we need to sign extend all four 32-bit address elements to be 64-bit.
@@ -2170,7 +2170,7 @@ OpDispatchBuilder::RefPair OpDispatchBuilder::AVX128_VPGatherQPSImpl(OpcodeArgs,
bool NeedsSVEScale = (VSIB.Scale == 2 || VSIB.Scale == 8) || (BaseAddr == Invalid() && VSIB.Scale != 1);
if (CTX->HostFeatures.SupportsSVE128 && NeedsSVEScale) {
if (CTX->HostFeatures.SupportsSVE128() && NeedsSVEScale) {
// SVE gather instructions don't support scaling their vector elements by anything other than 1 or the address element size.
// Pre-scale 64-bit addresses in the case that scale doesn't match in-order to hit SVE code paths more frequently.
// Only hit this path if the host supports SVE. Otherwise it's a degradation for the ASIMD codepath.
@@ -2236,7 +2236,7 @@ void OpDispatchBuilder::AVX128_VPGATHER(OpcodeArgs, OpSize AddrElementSize) {
bool NeedsSVEScale = (VSIB.Scale == 2 || VSIB.Scale == 8) || (VSIB.BaseAddr == Invalid() && VSIB.Scale != 1);
const bool NeedsExplicitSVEPath =
CTX->HostFeatures.SupportsSVE128 && AddrElementSize == OpSize::i32Bit && ElementLoadSize == OpSize::i32Bit && NeedsSVEScale;
CTX->HostFeatures.SupportsSVE128() && AddrElementSize == OpSize::i32Bit && ElementLoadSize == OpSize::i32Bit && NeedsSVEScale;
RefPair Result {};
if (NeedsExplicitSVEPath) {
@@ -27,7 +27,7 @@ void OpDispatchBuilder::SHA1NEXTEOp(OpcodeArgs) {
Ref Src = LoadSourceFPR(Op, Op->Src[0], Op->Flags);
Ref Result {};
if (CTX->HostFeatures.SupportsSVE128) {
if (CTX->HostFeatures.SupportsSVE128()) {
auto ZeroVec = LoadZeroVector(OpSize::i128Bit);
auto Tmp = _VInsElement(OpSize::i128Bit, OpSize::i32Bit, 3, 3, ZeroVec, Dest);
auto Xar = _VXar(OpSize::i128Bit, OpSize::i32Bit, ZeroVec, Tmp, 2);
@@ -41,8 +41,6 @@ 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,9 +15,12 @@ constexpr DispatchTableEntry OpDispatch_SecondaryModRMTables[] = {
// REG /3
{((2 << 3) | 7), 1, &OpDispatchBuilder::PermissionRestrictedOp},
// REG /5
{((3 << 3) | 0), 1, &OpDispatchBuilder::SerializeOp},
// REG /7
{((3 << 3) | 1), 1, &OpDispatchBuilder::RDTSCPOp},
{((3 << 3) | 4), 1, &OpDispatchBuilder::CLZeroOp},
{((4 << 3) | 1), 1, &OpDispatchBuilder::RDTSCPOp},
{((4 << 3) | 4), 1, &OpDispatchBuilder::CLZeroOp},
};
} // namespace FEXCore::IR
@@ -201,6 +201,8 @@ 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>},
@@ -3025,7 +3025,7 @@ void OpDispatchBuilder::XSaveOpImpl(OpcodeArgs) {
StoreIfFlagSet(1, [this, Op] { SaveSSEState(XSaveBase(Op)); });
}
// AVX
if (CTX->HostFeatures.SupportsAVX) {
if (CTX->HostFeatures.SupportsAVX()) {
StoreIfFlagSet(2, [this, Op] { std::invoke(SaveAVXStateFunc, this, XSaveBase(Op)); });
}
@@ -3232,7 +3232,7 @@ void OpDispatchBuilder::XRstorOpImpl(OpcodeArgs) {
RestoreIfFlagSetOrDefault(1, [this, Op] { RestoreSSEState(XSaveBase(Op)); }, [this] { DefaultSSEState(); });
}
// AVX
if (CTX->HostFeatures.SupportsAVX) {
if (CTX->HostFeatures.SupportsAVX()) {
RestoreIfFlagSetOrDefault(
2, [this, Op] { std::invoke(RestoreAVXStateFunc, this, XSaveBase(Op)); }, [this] { std::invoke(DefaultAVXStateFunc, this); });
}
@@ -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{"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, 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, 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_INVALID, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F3, 5), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, 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_INVALID, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_66, 5), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, 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_INVALID, FLAGS_NONE, 0}},
{OPD(TYPE_GROUP_7, PF_F2, 5), 1, X86InstInfo{"", TYPE_SECOND_GROUP_MODRM, 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_3_0,
ENTRY_7_0,
ENTRY_MAX,
};
constexpr std::array<X86InstInfo[2], ENTRY_MAX> ModRM_ArchSelect_LUT = {{
// ENTRY_3_0
// ENTRY_7_0
{
{"SWAPGS", TYPE_INST, FLAGS_NONE, 0, { .OpDispatch = &IR::OpDispatchBuilder::UnimplementedOp }},
{"SWAPGS", TYPE_INST, FLAGS_NONE, 0, { .OpDispatch = &IR::OpDispatchBuilder::PermissionRestrictedOp }},
@@ -58,15 +58,25 @@ 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
{((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}},
{((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}},
};
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, 0}},
{0xA2, 1, X86InstInfo{"CPUID", TYPE_INST, FLAGS_NO_OVERLAY | FLAGS_BLOCK_END, 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,10 +454,13 @@ 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, 5, X86InstInfo{"", TYPE_INVALID, 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}},
{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,6 +272,7 @@ 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.
@@ -521,7 +522,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 = 32;
constexpr size_t MAX_SECOND_MODRM_TABLE_SIZE = 40;
// (3 bit prefixes) | 8 bit opcode
constexpr size_t MAX_0F_38_TABLE_SIZE = (1 << 11);
// 1 REX | 1 prefixes | 8 bit opcode
+1 -2
View File
@@ -337,9 +337,8 @@
"HasSideEffects": true
},
"GPR:$EAX, GPR:$EBX, GPR:$ECX, GPR:$EDX = CPUID GPR:$Function, GPR:$Leaf": {
"CPUID i8:$InstSize": {
"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,6 +21,7 @@ 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,6 +12,7 @@ $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,27 +527,6 @@ 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.
+5 -39
View File
@@ -4,18 +4,15 @@
#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>
@@ -39,6 +36,9 @@ 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,41 +61,6 @@ 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;
@@ -124,6 +89,7 @@ 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 >;
+10 -1
View File
@@ -71,6 +71,12 @@ 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.
@@ -107,7 +113,9 @@ struct alignas(64) CPUState {
uint64_t L1Pointer {};
uint64_t L1Mask {};
uint64_t callret_sp {};
uint64_t _pad1 {};
// CPUID is default enabled.
CoreFlagsEnum CoreFlags {CoreFlagsEnum::CPUID_ENABLED};
// Cacheline: 1,2,3,4
// The high 128-bits of AVX registers when not being emulated by SVE256.
@@ -406,6 +414,7 @@ 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 {};
+47 -29
View File
@@ -24,7 +24,22 @@ struct HostFeatures {
// Whether or not the host supports any kind of SVE implementation.
[[nodiscard]]
bool SupportsSVE() const {
return SupportsSVE128 || SupportsSVE256;
return ProcessFeatures.SupportsSVE128 || ProcessFeatures.SupportsSVE256;
}
[[nodiscard]]
bool SupportsSVE128() const {
return ProcessFeatures.SupportsSVE128;
}
[[nodiscard]]
bool SupportsSVE256() const {
return ProcessFeatures.SupportsSVE256;
}
[[nodiscard]]
bool SupportsAVX() const {
return ProcessFeatures.SupportsAVX;
}
[[nodiscard]]
@@ -42,7 +57,13 @@ struct HostFeatures {
// This only determines the starting mode a process started in.
// The process can technically long jump to-from the other modes.
uint8_t Is64BitMode : 1 {};
uint8_t pad : 4 {};
// These are technically HostFeatures, but they differ between Linux, wow64, arm64.
uint8_t SupportsAVX : 1 {};
uint8_t SupportsSVE128 : 1 {};
uint8_t SupportsSVE256 : 1 {};
uint8_t pad : 1 {};
};
static_assert(sizeof(ProcessFeaturesHash) == 1);
@@ -76,35 +97,32 @@ struct HostFeatures {
uint32_t SupportsRCPC : 1 {}; // [9]
uint32_t SupportsTSOImm9 : 1 {}; // [10]
uint32_t SupportsRAND : 1 {}; // [11]
uint32_t SupportsAVX : 1 {}; // [12] // Not enabled on wine?
uint32_t SupportsSVE128 : 1 {}; // [13]
uint32_t SupportsSVE256 : 1 {}; // [14]
uint32_t SupportsSHA : 1 {}; // [15]
uint32_t SupportsPMULL_128Bit : 1 {}; // [16]
uint32_t SupportsCSSC : 1 {}; // [17]
uint32_t SupportsFCMA : 1 {}; // [18]
uint32_t SupportsFlagM : 1 {}; // [19]
uint32_t SupportsFlagM2 : 1 {}; // [20]
uint32_t SupportsRPRES : 1 {}; // [21]
uint32_t SupportsPreserveAllABI : 1 {}; // [22] // Not enabled on wine
uint32_t SupportsAES256 : 1 {}; // [23]
uint32_t SupportsSVEBitPerm : 1 {}; // [24]
uint32_t SupportsCPUIndexInTPIDRRO : 1 {}; // [25]
uint32_t SupportsFRINTTS : 1 {}; // [26]
uint32_t SupportsECV : 1 {}; // [27]
uint32_t SupportsWFXT : 1 {}; // [28]
uint32_t Supports3DNow : 1 {}; // [29]
uint32_t SupportsSSE4a : 1 {}; // [30]
uint32_t SupportsMOPS : 1 {}; // [31]
uint32_t SupportsI8MM : 1 {}; // [32]
uint32_t SupportsDotProd : 1 {}; // [33]
uint32_t PreferZVAForVZero : 1 {}; // [34]
uint32_t SupportsAFP : 1 {}; // [35]
uint32_t SupportsFloatExceptions : 1 {}; // [36]
uint32_t SupportsBF16 : 1 {}; // [37]
uint32_t SupportsSHA : 1 {}; // [12]
uint32_t SupportsPMULL_128Bit : 1 {}; // [13]
uint32_t SupportsCSSC : 1 {}; // [14]
uint32_t SupportsFCMA : 1 {}; // [15]
uint32_t SupportsFlagM : 1 {}; // [16]
uint32_t SupportsFlagM2 : 1 {}; // [17]
uint32_t SupportsRPRES : 1 {}; // [18]
uint32_t SupportsPreserveAllABI : 1 {}; // [19]
uint32_t SupportsAES256 : 1 {}; // [20]
uint32_t SupportsSVEBitPerm : 1 {}; // [21]
uint32_t SupportsCPUIndexInTPIDRRO : 1 {}; // [22]
uint32_t SupportsFRINTTS : 1 {}; // [23]
uint32_t SupportsECV : 1 {}; // [24]
uint32_t SupportsWFXT : 1 {}; // [25]
uint32_t Supports3DNow : 1 {}; // [26]
uint32_t SupportsSSE4a : 1 {}; // [27]
uint32_t SupportsMOPS : 1 {}; // [28]
uint32_t SupportsI8MM : 1 {}; // [29]
uint32_t SupportsDotProd : 1 {}; // [30]
uint32_t PreferZVAForVZero : 1 {}; // [31]
uint32_t SupportsAFP : 1 {}; // [32]
uint32_t SupportsFloatExceptions : 1 {}; // [33]
uint32_t SupportsBF16 : 1 {}; // [34]
// Flag if this is InstCountCI
uint32_t IsInstCountCI : 1 {}; // [38]
uint32_t pad : 25 {}; // [63:39]
uint32_t pad : 28 {}; // [63:35]
// This affects codegen, but it isn't machine state
ProcessFeaturesHash ProcessFeatures {};
+2
View File
@@ -13,6 +13,8 @@
#include <FEXCore/Utils/WildcardMatcher.h>
#include <FEXHeaderUtils/Filesystem.h>
#include <FEXHeaderUtils/SymlinkChecks.h>
#include <charconv>
#include <cstring>
#include <fmt/format.h>
#include <functional>
+13 -11
View File
@@ -7,10 +7,12 @@
#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>
@@ -354,8 +356,8 @@ static void OverrideFeatures(FEXCore::HostFeatures* Features, uint64_t ForceSVEW
const bool Enable##name = (HostFeatures() & FEXCore::Config::HostFeatures::ENABLE##enum_name) != 0; \
LogMan::Throw::AFmt(!(Disable##name && Enable##name), "Disabling and Enabling CPU feature (" #name ") is mutually exclusive");
ENABLE_DISABLE_OPTION(SupportsAVX, AVX, AVX);
ENABLE_DISABLE_OPTION(SupportsSVE128, SVE, SVE);
ENABLE_DISABLE_OPTION(ProcessFeatures.SupportsAVX, AVX, AVX);
ENABLE_DISABLE_OPTION(ProcessFeatures.SupportsSVE128, SVE, SVE);
ENABLE_DISABLE_OPTION(SupportsAFP, AFP, AFP);
ENABLE_DISABLE_OPTION(SupportsRCPC, LRCPC, LRCPC);
ENABLE_DISABLE_OPTION(SupportsTSOImm9, LRCPC2, LRCPC2);
@@ -399,7 +401,7 @@ static void OverrideFeatures(FEXCore::HostFeatures* Features, uint64_t ForceSVEW
}
///< Only force enable SVE256 if SVE is already enabled and ForceSVEWidth is set to >= 256.
Features->SupportsSVE256 = ForceSVEWidth && ForceSVEWidth >= 256;
Features->ProcessFeatures.SupportsSVE256 = ForceSVEWidth && ForceSVEWidth >= 256;
}
static void HandleErrata(FEXCore::HostFeatures* HostFeatures, uint64_t MIDR) {
@@ -515,15 +517,15 @@ void FetchHostFeatures(FEX::CPUFeatures& Features, FEXCore::HostFeatures& HostFe
#ifdef VIXL_SIMULATOR
// Hardcode enable SVE with 256-bit wide registers.
HostFeatures.SupportsSVE128 = ForceSVEWidth() ? ForceSVEWidth() >= 128 : true;
HostFeatures.SupportsSVE256 = ForceSVEWidth() ? ForceSVEWidth() >= 256 : true;
HostFeatures.ProcessFeatures.SupportsSVE128 = ForceSVEWidth() ? ForceSVEWidth() >= 128 : true;
HostFeatures.ProcessFeatures.SupportsSVE256 = ForceSVEWidth() ? ForceSVEWidth() >= 256 : true;
HostFeatures.SupportsMOPS = true;
// Simulator has a hardcoded ZVA size of 64-bytes.
HostFeatures.SupportsCLZERO = true;
HostFeatures.SupportsAES = true;
HostFeatures.SupportsCRC = true;
HostFeatures.SupportsAVX = true;
HostFeatures.ProcessFeatures.SupportsAVX = true;
HostFeatures.SupportsSHA = true;
HostFeatures.SupportsPMULL_128Bit = true;
HostFeatures.SupportsAES256 = true;
@@ -532,8 +534,8 @@ void FetchHostFeatures(FEX::CPUFeatures& Features, FEXCore::HostFeatures& HostFe
HostFeatures.SupportsRPRES = false;
HostFeatures.SupportsAFP = false;
#else
HostFeatures.SupportsSVE128 = Features.Supports(CPUFeatures::Feature::SVE2);
HostFeatures.SupportsSVE256 = Features.Supports(CPUFeatures::Feature::SVE2) && Features.GetSVEVectorLengthInBits() >= 256;
HostFeatures.ProcessFeatures.SupportsSVE128 = Features.Supports(CPUFeatures::Feature::SVE2);
HostFeatures.ProcessFeatures.SupportsSVE256 = Features.Supports(CPUFeatures::Feature::SVE2) && Features.GetSVEVectorLengthInBits() >= 256;
HostFeatures.SupportsMOPS = Features.Supports(CPUFeatures::Feature::MOPS);
// Check if we can support cacheline clears
@@ -546,8 +548,8 @@ void FetchHostFeatures(FEX::CPUFeatures& Features, FEXCore::HostFeatures& HostFe
#endif
HostFeatures.Supports3DNow = true;
HostFeatures.SupportsAVX = true;
HostFeatures.SupportsAES256 = HostFeatures.SupportsAVX && HostFeatures.SupportsAES;
HostFeatures.ProcessFeatures.SupportsAVX = true;
HostFeatures.SupportsAES256 = HostFeatures.SupportsAVX() && HostFeatures.SupportsAES;
HostFeatures.SupportsPreserveAllABI = FEX_HAS_PRESERVE_ALL_ATTR;
HostFeatures.PreferZVAForVZero = false;
@@ -589,7 +591,7 @@ void FetchHostFeatures(FEX::CPUFeatures& Features, FEXCore::HostFeatures& HostFe
HostFeatures.SupportsRAND = Feature.Feat_rand;
HostFeatures.SupportsRCPC = true;
HostFeatures.SupportsTSOImm9 = true;
HostFeatures.SupportsAVX = Feature.Feat_avx;
HostFeatures.ProcessFeatures.SupportsAVX = Feature.Feat_avx;
HostFeatures.SupportsSHA = Feature.Feat_sha;
HostFeatures.SupportsPMULL_128Bit = Feature.Feat_pclmulqdq;
HostFeatures.SupportsAES256 = Feature.Feat_aes;
+1
View File
@@ -1,6 +1,7 @@
// 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,6 +12,7 @@
#include <FEXCore/Utils/LogManager.h>
#include <FEXCore/Utils/SignalScopeGuards.h>
#include <charconv>
#include <sys/stat.h>
namespace CodeSize {
+4 -1
View File
@@ -1,9 +1,12 @@
// 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,7 +58,6 @@ $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>
@@ -434,7 +433,7 @@ int main(int argc, char** argv, char** const envp) {
if (StallProcess) {
while (1) {
// Stall this process out forever
select(0, nullptr, nullptr, nullptr, nullptr);
ppoll(nullptr, 0, nullptr, nullptr);
}
}
@@ -552,8 +551,8 @@ int main(int argc, char** argv, char** const envp) {
{
auto HostFeatures = FEX::FetchHostFeatures(Loader.Is64BitMode());
CTX = FEXCore::Context::Context::CreateNewContext(HostFeatures);
SupportsAVX = HostFeatures.SupportsAVX;
SupportsSVE256 = HostFeatures.SupportsSVE256;
SupportsAVX = HostFeatures.SupportsAVX();
SupportsSVE256 = HostFeatures.SupportsSVE256();
}
FEX::Kernel::Init(Loader.Is64BitMode(), CTX.get());
+14 -3
View File
@@ -2,6 +2,7 @@
#include <FEXCore/fextl/fmt.h>
#include <FEXCore/fextl/string.h>
#include <FEXCore/fextl/vector.h>
#include <FEXHeaderUtils/Filesystem.h>
#include "Common/cpp-optparse/OptionParser.h"
#include "Common/JSONPool.h"
@@ -1050,6 +1051,11 @@ bool ValidateDownloadSelection(const WebFileFetcher::FileTargets& Target) {
namespace ConfigSetter {
void SetRootFSAsDefault(const fextl::string& RootFS) {
// Create config diectory.
if (!FHU::Filesystem::CreateDirectories(FEXCore::Config::GetConfigDirectory(false))) {
ExecWithInfo(fextl::fmt::format("Failed to create config directory: {}", FEXCore::Config::GetConfigDirectory(false)));
}
fextl::string Filename = FEXCore::Config::GetConfigFileLocation();
auto LoadedConfig = FEX::Config::CreateMainLayer(&Filename);
LoadedConfig->Load();
@@ -1146,6 +1152,12 @@ int main(int argc, char** argv, char** const envp) {
}
FEX_CONFIG_OPT(LDPath, ROOTFS);
const fextl::string RootFSPath = FEXCore::Config::GetDataDirectory() + "RootFS/";
// Create the rootfs path as early as possible.
if (!FHU::Filesystem::CreateDirectories(RootFSPath)) {
ExecWithInfo(fextl::fmt::format("Failed to create RootFS directory: {}", RootFSPath));
}
std::error_code ec;
fextl::string Question {};
@@ -1172,9 +1184,8 @@ int main(int argc, char** argv, char** const envp) {
int32_t DistroIndex = AskForDistroSelection(Targets);
if (DistroIndex != -1) {
const auto& Target = Targets[DistroIndex];
fextl::string RootFS = FEXCore::Config::GetDataDirectory() + "RootFS/";
auto filename = Target.URL.substr(Target.URL.find_last_of('/') + 1);
auto PathName = RootFS + filename;
auto PathName = RootFSPath + filename;
if (!ValidateCheckExists(Target)) {
// Keep going
@@ -1295,7 +1306,7 @@ int main(int argc, char** argv, char** const envp) {
if (UseImageAs == ArgOptions::CompressedImageOption::OPTION_EXTRACT) {
auto FolderName = filename.substr(0, filename.find_last_of('.'));
if (ExtractHelper(RootFS, PathName, FolderName)) {
if (ExtractHelper(RootFSPath, PathName, FolderName)) {
// Remove the image file suffix since we extracted to that.
filename = std::move(FolderName);
}
@@ -362,6 +362,7 @@ 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,6 +9,7 @@ $end_info$
#include "CodeLoader.h"
#include "GdbServer/Info.h"
#include <charconv>
#include <cstdlib>
#include <cstdio>
#include <iomanip>
@@ -7,6 +7,8 @@ $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,11 +546,16 @@ void RegisterThread(FEX::HLE::SyscallHandler* Handler) {
Result = -EINVAL; // We don't support CET, return EINVAL
break;
case 0x1011: // ARCH_GET_CPUID
return 1;
break;
case 0x1012: // ARCH_SET_CPUID
return -ENODEV; // Claim we don't support faulting on CPUID
break;
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;
default:
LogMan::Msg::EFmt("Unknown prctl: 0x{:x}", code);
Result = -EINVAL;
@@ -247,7 +247,7 @@ int main(int argc, char** argv, char** const envp) {
auto CTX = FEXCore::Context::Context::CreateNewContext(HostFeatures);
#ifndef _WIN32
auto SignalDelegation = FEX::HLE::CreateSignalDelegator(CTX.get(), {}, HostFeatures.SupportsAVX, HostFeatures.SupportsSVE256);
auto SignalDelegation = FEX::HLE::CreateSignalDelegator(CTX.get(), {}, HostFeatures.SupportsAVX(), HostFeatures.SupportsSVE256());
#else
// Enable exit on HLT while Wine's longjump is broken.
//
@@ -257,7 +257,7 @@ int main(int argc, char** argv, char** const envp) {
#endif
// Skip any tests that the host doesn't support features for
SupportsAVX = HostFeatures.SupportsAVX;
SupportsAVX = HostFeatures.SupportsAVX();
bool TestUnsupported = (!SupportsAVX && Loader.RequiresAVX()) || (!HostFeatures.SupportsRAND && Loader.RequiresRAND()) ||
(!HostFeatures.SupportsSHA && Loader.RequiresSHA()) || (!HostFeatures.SupportsCLZERO && Loader.RequiresCLZERO()) ||
+5 -5
View File
@@ -549,11 +549,6 @@ 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;
@@ -903,6 +898,11 @@ bool ResetToConsistentStateImpl(const ThreadCPUArea CPUArea, EXCEPTION_RECORD* E
return true;
}
if (NativeContext->Pc == reinterpret_cast<uint64_t>(&FEXCore::Assert::ForcedAssert)) {
LogMan::Msg::DFmt("FEX: Forced assert");
NtTerminateProcess(NtCurrentProcess(), 0x42);
}
if (!IsJIT && !IsDispatcherAddress(NativeContext->Pc)) {
LogMan::Msg::DFmt("Passing through exception");
return false;
+6 -3
View File
@@ -79,15 +79,15 @@ FEXCore::HostFeatures CPUFeatures::FetchHostFeatures(bool IsWine, FEXCore::HostF
FEX::FetchHostFeatures(Features, HostFeatures, IsWine, CTR, MIDR);
// Force-disable SVE until wine/windows gain support for SVE context save/restore
HostFeatures.SupportsSVE128 = false;
HostFeatures.SupportsSVE256 = false;
HostFeatures.ProcessFeatures.SupportsSVE128 = false;
HostFeatures.ProcessFeatures.SupportsSVE256 = false;
HostFeatures.SupportsCPUIndexInTPIDRRO = !IsWine || FEX::Windows::UnixLib::SupportsTPIDRRO_CPU();
HostFeatures.ProcessPID = ProcessPID;
// AVX is unsupported for WOW64
HostFeatures.SupportsAVX = HostType == FEXCore::HostFeatures::HostTypeEnum::Arm64ec;
HostFeatures.ProcessFeatures.SupportsAVX = HostType == FEXCore::HostFeatures::HostTypeEnum::Arm64ec;
HostFeatures.ProcessFeatures.HostType = HostType;
HostFeatures.ProcessFeatures.Is64BitMode = HostType == FEXCore::HostFeatures::HostTypeEnum::Arm64ec;
@@ -134,6 +134,8 @@ 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) {
@@ -166,6 +168,7 @@ 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;
+16 -13
View File
@@ -182,20 +182,23 @@ FEXCore::ExecutableFileSectionInfo ImageTracker::HandleImageMap(std::string_view
}
}
uint64_t EndAddress = Address + Nt->OptionalHeader.SizeOfImage;
fextl::set<uint64_t> VolatileInstructions {};
FEXCore::IntervalList<uint64_t> VolatileValidRanges {};
LoadImageVolatileMetadata(VolatileInstructions, VolatileValidRanges, Module, Nt, Address, EndAddress);
if (auto It = ExtendedMetaData.find(ID); It != ExtendedMetaData.end()) {
FEX::VolatileMetadata::ApplyFEXExtendedVolatileMetadata(It->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
}
if (auto It = ExtendedMetaData.find(fextl::string {ModuleName}); It != ExtendedMetaData.end()) {
FEX::VolatileMetadata::ApplyFEXExtendedVolatileMetadata(It->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
}
if (!VolatileInstructions.empty() || !VolatileValidRanges.Empty()) {
LogMan::Msg::DFmt("Loaded volatile metadata for {:X}: {} entries", Address, VolatileInstructions.size());
CTX.AddForceTSOInformation(VolatileValidRanges, std::move(VolatileInstructions));
if (FEXCore::Config::Get_VOLATILEMETADATA()) {
uint64_t EndAddress = Address + Nt->OptionalHeader.SizeOfImage;
fextl::set<uint64_t> VolatileInstructions {};
FEXCore::IntervalList<uint64_t> VolatileValidRanges {};
LoadImageVolatileMetadata(VolatileInstructions, VolatileValidRanges, Module, Nt, Address, EndAddress);
if (auto It = ExtendedMetaData.find(ID); It != ExtendedMetaData.end()) {
FEX::VolatileMetadata::ApplyFEXExtendedVolatileMetadata(It->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
}
if (auto It = ExtendedMetaData.find(fextl::string {ModuleName}); It != ExtendedMetaData.end()) {
FEX::VolatileMetadata::ApplyFEXExtendedVolatileMetadata(It->second, VolatileInstructions, VolatileValidRanges, Address, EndAddress);
}
if (!VolatileInstructions.empty() || !VolatileValidRanges.Empty()) {
LogMan::Msg::DFmt("Loaded volatile metadata for {:X}: {} entries", Address, VolatileInstructions.size());
CTX.AddForceTSOInformation(VolatileValidRanges, std::move(VolatileInstructions));
}
}
return ImageInfo->SectionInfo;
+5
View File
@@ -833,6 +833,11 @@ bool BTCpuResetToConsistentStateImpl(EXCEPTION_POINTERS* Ptrs) {
}
}
if (Context->Pc == reinterpret_cast<uint64_t>(&FEXCore::Assert::ForcedAssert)) {
LogMan::Msg::DFmt("FEX: Forced assert");
NtTerminateProcess(NtCurrentProcess(), 0x42);
}
if (!Thread || !IsAddressInJit(Context->Pc)) {
return false;
}
-3
View File
@@ -209,9 +209,6 @@ 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
@@ -0,0 +1,33 @@
%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
@@ -0,0 +1,32 @@
%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 HTT;
bool SMT;
};
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;
info.HTT = (cpuid_fn_1.edx >> 28) & 1;
bool HTT = (cpuid_fn_1.edx >> 28) & 1;
const auto cpuid_fn_8000_0000 = get_cpuid(0x8000'0000U);
@@ -50,8 +50,10 @@ 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 {};
@@ -67,18 +69,24 @@ std::optional<core_info> cpuid_calculate_core_info() {
info.max_addressible_ids = MNLP;
const auto cpuid_fn_4 = get_cpuid(4);
if (cpuid_fn_4.eax & 0xF) {
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) {
// 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 (info.HTT) {
if (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 (info.HTT) {
} else if (HTT) {
info.cores = (cpuid_fn_1.ebx >> 16) & 0xFF;
info.threads = info.cores * 2;
} else {
@@ -96,7 +104,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->HTT) {
if (core_info->SMT) {
// May not be entirely correct on systems that mix HTT and non-HTT cpu cores.
CHECK((core_info->cores * 2) == core_info->threads);
} else {
@@ -0,0 +1,309 @@
#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);
}
}
@@ -0,0 +1,102 @@
#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;
act.sa_flags = SA_SIGINFO | SA_NODEFER;
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;
act.sa_flags = SA_SIGINFO | SA_NODEFER;
sigaction(SIGSEGV, &act, nullptr);
auto PageSize = sysconf(_SC_PAGESIZE);
@@ -0,0 +1,167 @@
#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);
}
}
@@ -0,0 +1,220 @@
#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": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"roundss xmm0, xmm1, 00000000b": {
@@ -7,7 +7,7 @@
"AFP"
],
"DisabledHostFeatures": [],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"cvtpi2ps xmm0, [rax]": {
@@ -9,7 +9,7 @@
"DisabledHostFeatures": [
"RPRES"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"cvtsi2ss xmm0, eax": {
@@ -7,7 +7,7 @@
"AFP"
],
"DisabledHostFeatures": [],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"cvtsi2sd xmm0, eax": {
@@ -8,7 +8,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"cvtpi2ps xmm0, [rax]": {
@@ -9,7 +9,7 @@
"SVE256",
"RPRES"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"cvtsi2ss xmm0, eax": {
@@ -8,7 +8,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"cvtsi2sd xmm0, eax": {
@@ -9,7 +9,7 @@
"DisabledHostFeatures": [
"RPRES"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vsqrtss xmm0, xmm1, xmm2": {
@@ -7,7 +7,7 @@
"AFP"
],
"DisabledHostFeatures": [],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vroundss xmm0, xmm1, 00000000b": {
+31 -31
View File
@@ -7,7 +7,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vfmaddsubps xmm0, xmm1, xmm2, xmm3": {
@@ -22,7 +22,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -38,7 +38,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -54,7 +54,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -70,7 +70,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -86,7 +86,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -102,7 +102,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -118,7 +118,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -134,7 +134,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -150,7 +150,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -166,7 +166,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -182,7 +182,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -198,7 +198,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -214,7 +214,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -230,7 +230,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -246,7 +246,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -262,7 +262,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -278,7 +278,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -294,7 +294,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -310,7 +310,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -326,7 +326,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -342,7 +342,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -358,7 +358,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -374,7 +374,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -390,7 +390,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -406,7 +406,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -422,7 +422,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -438,7 +438,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -454,7 +454,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -470,7 +470,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -486,7 +486,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
}
@@ -13,7 +13,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vmovups xmm0, xmm0": {
@@ -9,7 +9,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vaddsubpd xmm0, xmm1, xmm2": {
@@ -10,7 +10,7 @@
"FLAGM2",
"SVE256"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vmovntps [rax], xmm0": {
@@ -10,7 +10,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vucomiss xmm0, xmm1": {
@@ -13,7 +13,7 @@
"I8MM",
"DOTPROD"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vpshufb xmm0, xmm1, xmm2": {
@@ -2224,7 +2224,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2241,7 +2241,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2258,7 +2258,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2275,7 +2275,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2292,7 +2292,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2309,7 +2309,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2326,7 +2326,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2343,7 +2343,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2360,7 +2360,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2377,7 +2377,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2394,7 +2394,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -2411,7 +2411,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6268,7 +6268,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6284,7 +6284,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6300,7 +6300,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6316,7 +6316,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6457,7 +6457,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6474,7 +6474,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6491,7 +6491,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6508,7 +6508,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6525,7 +6525,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6542,7 +6542,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6559,7 +6559,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6576,7 +6576,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6593,7 +6593,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6610,7 +6610,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6627,7 +6627,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -6644,7 +6644,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -8,7 +8,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vfmadd132ss xmm0, xmm1, xmm2": {
@@ -12,7 +12,7 @@
"SVE256",
"I8MM"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vpdpbusd xmm0, xmm1, xmm2": {
@@ -11,7 +11,7 @@
"SVE128",
"SVE256"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vpdpbusd xmm0, xmm1, xmm2": {
@@ -10,7 +10,7 @@
"FLAGM2",
"SVE256"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vmovntdqa xmm0, [rax]": {
@@ -11,7 +11,7 @@
"SVE256",
"SVEBITPERM"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vtestps xmm0, xmm1": {
@@ -7,7 +7,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vpermq ymm0, ymm1, 1": {
@@ -8,7 +8,7 @@
"AFP",
"SVE256"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vblendvps xmm0, xmm1, xmm2, xmm3": {
@@ -9,7 +9,7 @@
"SVE256",
"SVE128"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"vpsrlw xmm0, xmm1, 0": {
+1 -1
View File
@@ -9,7 +9,7 @@
"FLAGM",
"FLAGM2"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"lock add byte [rax], cl": {
@@ -9,7 +9,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"sha1nexte xmm0, xmm1": {
@@ -91,7 +91,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -107,7 +107,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -123,7 +123,7 @@
"mov w0, #0x401",
"str x0, [x28, #1296]",
"str x20, [x28, #1304]",
"ldr x0, [x28, #2800]",
"ldr x0, [x28, #2808]",
"br x0"
]
},
@@ -9,7 +9,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"sha1nexte xmm0, xmm1": {
@@ -9,7 +9,7 @@
"SVE256",
"AFP"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Instructions": {
"pclmulqdq xmm0, xmm1, 00000b": {
+1 -1
View File
@@ -10,7 +10,7 @@
"AFP",
"RPRES"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Comment": [
"These 3DNow! instructions are optimal assuming that FEX doesn't SRA MMX registers",
@@ -8,7 +8,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Comment": [
"Instructions that explicitly push against the limits of ARM's loadstore instructions"
@@ -8,7 +8,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Comment": [
"Instructions that explicitly push against the limits of ARM's loadstore instructions"
@@ -13,36 +13,12 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"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": 42
"BinaryCacheVersion": 44
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
@@ -9,7 +9,7 @@
"SVE256",
"RPRES"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
@@ -14,7 +14,7 @@
"RPRES",
"AFP"
],
"BinaryCacheVersion": 42
"BinaryCacheVersion": 44
},
"Comment": [
"These are instruction combinations that could be more optimal if FEX optimized for them"
Loaded 100 of 174 files, more files were not shown because too many files have changed in this diff. Show more