From 631b8c3f587d86fac5d270b9b55f237466df18d5 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Mon, 24 Aug 2026 20:33:41 -0700 Subject: [PATCH] FEXCore/HostFeatures: Adds HostFeatures hashing support As long as the hash is smaller than 64-bits we can just return the bits encoded directly. Codegen slightly changes with this packed representation, but doesn't really matter. Also removes ICacheLineSize as that doesn't actually affect codegen for us. Once we add 27 more HostFeatures we can switch the hash over to XXH3. --- FEXCore/Source/Interface/Core/CPUID.cpp | 2 +- FEXCore/Source/Interface/Core/JIT/JIT.cpp | 8 +- .../Source/Interface/Core/JIT/MemoryOps.cpp | 12 +-- .../Interface/Core/OpcodeDispatcher.cpp | 2 +- FEXCore/include/FEXCore/Core/HostFeatures.h | 100 ++++++++++-------- Source/Common/HostFeatures.cpp | 7 +- 6 files changed, 71 insertions(+), 60 deletions(-) diff --git a/FEXCore/Source/Interface/Core/CPUID.cpp b/FEXCore/Source/Interface/Core/CPUID.cpp index 365ebfc55..3c42d3043 100644 --- a/FEXCore/Source/Interface/Core/CPUID.cpp +++ b/FEXCore/Source/Interface/Core/CPUID.cpp @@ -1347,7 +1347,7 @@ FEXCore::CPUID::XCRResults CPUIDEmu::XCRFunction_0h() const { CPUIDEmu::CPUIDEmu(const FEXCore::Context::ContextImpl* ctx) : CTX {ctx} - , SupportsCPUIndexInTPIDRRO {CTX->HostFeatures.SupportsCPUIndexInTPIDRRO} + , SupportsCPUIndexInTPIDRRO {CTX->HostFeatures.SupportsCPUIndexInTPIDRRO != 0} , GetCPUID {GetCPUID_Syscall} { Cores = CTX->HostFeatures.CPUMIDRs.size(); diff --git a/FEXCore/Source/Interface/Core/JIT/JIT.cpp b/FEXCore/Source/Interface/Core/JIT/JIT.cpp index 46cc17902..1b3d90981 100644 --- a/FEXCore/Source/Interface/Core/JIT/JIT.cpp +++ b/FEXCore/Source/Interface/Core/JIT/JIT.cpp @@ -616,11 +616,11 @@ 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} - , HostSupportsSVE256 {ctx->HostFeatures.SupportsSVE256} + , HostSupportsSVE128 {ctx->HostFeatures.SupportsSVE128 != 0} + , HostSupportsSVE256 {ctx->HostFeatures.SupportsSVE256 != 0} , HostSupportsAVX256 {ctx->HostFeatures.SupportsAVX && ctx->HostFeatures.SupportsSVE256} - , HostSupportsRPRES {ctx->HostFeatures.SupportsRPRES} - , HostSupportsAFP {ctx->HostFeatures.SupportsAFP} + , HostSupportsRPRES {ctx->HostFeatures.SupportsRPRES != 0} + , HostSupportsAFP {ctx->HostFeatures.SupportsAFP != 0} , CTX {ctx} , TempCodeBufferAllocator(ctx->CPUBackendAllocator, 0) { diff --git a/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp b/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp index 849df0f2c..805a7312a 100644 --- a/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp +++ b/FEXCore/Source/Interface/Core/JIT/MemoryOps.cpp @@ -2401,13 +2401,13 @@ DEF_OP(CacheLineClear) { // Clear dcache only // icache doesn't matter here since the guest application shouldn't be calling clflush on JIT code. // check host cacheline size again x86_64 size to ensure at least 64 bytes are cleaned - if (CTX->HostFeatures.DCacheLineSize >= 64U) { + if (CTX->HostFeatures.DCacheSize() >= 64U) { dc(ARMEmitter::DataCacheOperation::CIVAC, MemReg); } else { auto CurrentWorkingReg = MemReg.X(); - for (size_t i = 0; i < std::max(1U, 64U / CTX->HostFeatures.DCacheLineSize); ++i) { + for (size_t i = 0; i < std::max(1U, 64U / CTX->HostFeatures.DCacheSize()); ++i) { dc(ARMEmitter::DataCacheOperation::CIVAC, CurrentWorkingReg); - add(ARMEmitter::Size::i64Bit, TMP1, CurrentWorkingReg, CTX->HostFeatures.DCacheLineSize); + add(ARMEmitter::Size::i64Bit, TMP1, CurrentWorkingReg, CTX->HostFeatures.DCacheSize()); CurrentWorkingReg = TMP1; } } @@ -2430,13 +2430,13 @@ DEF_OP(CacheLineClean) { // Clean dcache only // check host cacheline size again x86_64 size to ensure at least 64 bytes are cleaned - if (CTX->HostFeatures.DCacheLineSize >= 64U) { + if (CTX->HostFeatures.DCacheSize() >= 64U) { dc(ARMEmitter::DataCacheOperation::CVAC, MemReg); } else { auto CurrentWorkingReg = MemReg.X(); - for (size_t i = 0; i < std::max(1U, 64U / CTX->HostFeatures.DCacheLineSize); ++i) { + for (size_t i = 0; i < std::max(1U, 64U / CTX->HostFeatures.DCacheSize()); ++i) { dc(ARMEmitter::DataCacheOperation::CVAC, CurrentWorkingReg); - add(ARMEmitter::Size::i64Bit, TMP1, CurrentWorkingReg, CTX->HostFeatures.DCacheLineSize); + add(ARMEmitter::Size::i64Bit, TMP1, CurrentWorkingReg, CTX->HostFeatures.DCacheSize()); CurrentWorkingReg = TMP1; } } diff --git a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp index 01d43168f..51b305b65 100644 --- a/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp +++ b/FEXCore/Source/Interface/Core/OpcodeDispatcher.cpp @@ -4617,7 +4617,7 @@ void OpDispatchBuilder::StoreResult(RegClass Class, X86Tables::DecodedOp Op, Ref } OpDispatchBuilder::OpDispatchBuilder(FEXCore::Context::ContextImpl* ctx, FEXCore::Core::InternalThreadState* Thread) - : IREmitter {ctx->OpDispatcherAllocator, ctx->HostFeatures.SupportsTSOImm9} + : IREmitter {ctx->OpDispatcherAllocator, ctx->HostFeatures.SupportsTSOImm9 != 0} , CTX {ctx} , Thread {Thread} { if (CTX->HostFeatures.SupportsAVX && CTX->HostFeatures.SupportsSVE256) { diff --git a/FEXCore/include/FEXCore/Core/HostFeatures.h b/FEXCore/include/FEXCore/Core/HostFeatures.h index c927df66b..077e4de67 100644 --- a/FEXCore/include/FEXCore/Core/HostFeatures.h +++ b/FEXCore/include/FEXCore/Core/HostFeatures.h @@ -13,48 +13,6 @@ namespace FEXCore { * Not the x86->IR process */ struct HostFeatures { - // Whether or not the host supports any kind of SVE implementation. - [[nodiscard]] - bool SupportsSVE() const { - return SupportsSVE128 || SupportsSVE256; - } - - uint32_t DCacheLineSize {}; - uint32_t ICacheLineSize {}; - bool SupportsCacheMaintenanceOps {}; - bool SupportsAES {}; - bool SupportsCRC {}; - bool SupportsCLZERO {}; - bool SupportsAtomics {}; - bool SupportsRCPC {}; - bool SupportsTSOImm9 {}; - bool SupportsRAND {}; - bool SupportsAVX {}; - bool SupportsSVE128 {}; - bool SupportsSVE256 {}; - bool SupportsSHA {}; - bool SupportsPMULL_128Bit {}; - bool SupportsCSSC {}; - bool SupportsFCMA {}; - bool SupportsFlagM {}; - bool SupportsFlagM2 {}; - bool SupportsRPRES {}; - bool SupportsPreserveAllABI {}; - bool SupportsAES256 {}; - bool SupportsSVEBitPerm {}; - bool SupportsCPUIndexInTPIDRRO {}; - bool SupportsFRINTTS {}; - bool SupportsECV {}; - bool SupportsWFXT {}; - bool Supports3DNow {}; - bool SupportsSSE4a {}; - bool SupportsMOPS {}; - bool PreferZVAForVZero {}; - - // Float exception behaviour - bool SupportsAFP {}; - bool SupportsFloatExceptions {}; - // Changes code generation slightly. enum class HostTypeEnum { Unknown, @@ -62,10 +20,64 @@ struct HostFeatures { Wow64, Arm64ec, }; - HostTypeEnum HostType {}; + // Whether or not the host supports any kind of SVE implementation. + [[nodiscard]] + bool SupportsSVE() const { + return SupportsSVE128 || SupportsSVE256; + } + + [[nodiscard]] + uint32_t DCacheSize() const { + return 4 << DCacheLineLog2; + } + + [[nodiscard]] + uint64_t HashForCaching() const { + // As long as the number of options is 64-bit or below, we can just return it. + // Skip CPUMIDRs as it doesn't affect codegen. + static_assert(offsetof(HostFeatures, CPUMIDRs) == 8); + uint64_t Result {}; + memcpy(&Result, this, sizeof(Result)); + return Result; + } + + uint32_t DCacheLineLog2 : 4 {}; + uint32_t SupportsCacheMaintenanceOps : 1 {}; + uint32_t SupportsAES : 1 {}; + uint32_t SupportsCRC : 1 {}; + uint32_t SupportsCLZERO : 1 {}; + uint32_t SupportsAtomics : 1 {}; + uint32_t SupportsRCPC : 1 {}; + uint32_t SupportsTSOImm9 : 1 {}; + uint32_t SupportsRAND : 1 {}; + uint32_t SupportsAVX : 1 {}; + uint32_t SupportsSVE128 : 1 {}; + uint32_t SupportsSVE256 : 1 {}; + uint32_t SupportsSHA : 1 {}; + uint32_t SupportsPMULL_128Bit : 1 {}; + uint32_t SupportsCSSC : 1 {}; + uint32_t SupportsFCMA : 1 {}; + uint32_t SupportsFlagM : 1 {}; + uint32_t SupportsFlagM2 : 1 {}; + uint32_t SupportsRPRES : 1 {}; + uint32_t SupportsPreserveAllABI : 1 {}; + uint32_t SupportsAES256 : 1 {}; + uint32_t SupportsSVEBitPerm : 1 {}; + uint32_t SupportsCPUIndexInTPIDRRO : 1 {}; + uint32_t SupportsFRINTTS : 1 {}; + uint32_t SupportsECV : 1 {}; + uint32_t SupportsWFXT : 1 {}; + uint32_t Supports3DNow : 1 {}; + uint32_t SupportsSSE4a : 1 {}; + uint32_t SupportsMOPS : 1 {}; + uint32_t PreferZVAForVZero : 1 {}; + uint32_t SupportsAFP : 1 {}; + uint32_t SupportsFloatExceptions : 1 {}; // Flag if this is InstCountCI - bool IsInstCountCI {}; + uint32_t IsInstCountCI : 1 {}; + HostTypeEnum HostType : 2 {}; + uint32_t pad : 26 {}; // MIDR information // Also used for determining number of CPU cores for CPUID diff --git a/Source/Common/HostFeatures.cpp b/Source/Common/HostFeatures.cpp index 00abcaa5b..ddc2cf4cd 100644 --- a/Source/Common/HostFeatures.cpp +++ b/Source/Common/HostFeatures.cpp @@ -543,11 +543,10 @@ void FetchHostFeatures(FEX::CPUFeatures& Features, FEXCore::HostFeatures& HostFe HostFeatures.PreferZVAForVZero = false; if (CTR) { - HostFeatures.DCacheLineSize = 4 << ((CTR >> 16) & 0xF); - HostFeatures.ICacheLineSize = 4 << (CTR & 0xF); + HostFeatures.DCacheLineLog2 = (CTR >> 16) & 0xF; } else { - HostFeatures.DCacheLineSize = 64; - HostFeatures.ICacheLineSize = 64; + // 64-bytes + HostFeatures.DCacheLineLog2 = 4; } if (!HostFeatures.SupportsAtomics) {