// SPDX-License-Identifier: MIT #include "Common/CPUInfo.h" #include "Common/HostFeatures.h" #ifndef _WIN32 #include "Common/Linux/LinuxVersion.h" #endif #include #include #include #include #include #include #ifdef ARCHITECTURE_x86_64 #include "Common/X86Features.h" #endif namespace FEX { void FillMIDRInformationViaLinux(FEXCore::HostFeatures* Features) { auto Cores = FEX::CPUInfo::CalculateNumberOfCPUs(); Features->CPUMIDRs.resize(Cores); #ifdef ARCHITECTURE_arm64 for (size_t i = 0; i < Cores; ++i) { std::error_code ec {}; fextl::string MIDRPath = fextl::fmt::format("/sys/devices/system/cpu/cpu{}/regs/identification/midr_el1", i); std::array Data; // Needs to be a fixed size since depending on kernel it will try to read a full page of data and fail // Only read 18 bytes for a 64bit value prefixed with 0x if (FEXCore::FileLoading::LoadFileToBuffer(MIDRPath, Data) == sizeof(Data)) { uint64_t MIDR {}; auto Results = std::from_chars(Data.data() + 2, Data.data() + sizeof(Data), MIDR, 16); if (Results.ec == std::errc()) { // Truncate to 32-bits, top 32-bits are all reserved in MIDR Features->CPUMIDRs[i] = static_cast(MIDR); } } } #endif } #if defined(ARCHITECTURE_arm64) && !defined(VIXL_SIMULATOR) __attribute__((naked)) static uint64_t ReadSVEVectorLengthInBits() { ///< Can't use rdvl instruction directly because compilers will complain that sve/sme is required. __asm(R"( .word 0x04bf5100 // rdvl x0, #8 ret; )"); } #else [[maybe_unused]] static int ReadSVEVectorLengthInBits() { // Return unsupported return 0; } #endif #ifdef ARCHITECTURE_arm64 #define GetSysReg(name, reg) \ static uint64_t Get_##name() { \ uint64_t Result {}; \ __asm("mrs %[Res], " #reg : [Res] "=r"(Result)); \ return Result; \ } GetSysReg(ISAR0_EL1, ID_AA64ISAR0_EL1); GetSysReg(PFR0_EL1, ID_AA64PFR0_EL1); GetSysReg(PFR1_EL1, ID_AA64PFR1_EL1); GetSysReg(MIDR_EL1, MIDR_EL1); GetSysReg(ISAR1_EL1, ID_AA64ISAR1_EL1); GetSysReg(MMFR0_EL1, ID_AA64MMFR0_EL1); GetSysReg(MMFR2_EL1, ID_AA64MMFR2_EL1); #ifndef _WIN32 GetSysReg(MMFR3_EL1, s3_0_c0_c7_3); // Can't request by name #endif GetSysReg(ZFR0_EL1, s3_0_c0_c4_4); // Can't request by name GetSysReg(MMFR1_EL1, ID_AA64MMFR1_EL1); GetSysReg(ISAR2_EL1, ID_AA64ISAR2_EL1); GetSysReg(DCZID_EL0, DCZID_EL0); class CPUFeaturesFromID final : public FEX::CPUFeatures { public: CPUFeaturesFromID() { ISAR0.SetReg(Get_ISAR0_EL1()); PFR0.SetReg(Get_PFR0_EL1()); PFR1.SetReg(Get_PFR1_EL1()); MIDR.SetReg(Get_MIDR_EL1()); ISAR1.SetReg(Get_ISAR1_EL1()); MMFR0.SetReg(Get_MMFR0_EL1()); MMFR2.SetReg(Get_MMFR2_EL1()); #ifndef _WIN32 if (FEX::LinuxVersion::CalculateHostKernelVersion() >= FEX::LinuxVersion::KernelVersion(6, 5)) { // Only exists in kernel 6.5 and newer. MMFR3.SetReg(Get_MMFR3_EL1()); } #endif MMFR1.SetReg(Get_MMFR1_EL1()); ISAR2.SetReg(Get_ISAR2_EL1()); DCZID.SetReg(Get_DCZID_EL0()); if (PFR0.SupportsSVE()) { // Can only query if SVE is supported. ZFR0.SetReg(Get_ZFR0_EL1()); } FillFeatureFlags(); if (Supports(CPUFeatures::Feature::SVE2)) { SVEVL.SetReg(ReadSVEVectorLengthInBits()); } } }; FEX::CPUFeatures GetCPUFeaturesFromIDRegisters() { return CPUFeaturesFromID {}; } #endif class CPUFeaturesFromConfig final : public FEX::CPUFeatures { public: CPUFeaturesFromConfig(std::string_view Config) { auto to_string_view = [](auto rng) { return std::string_view(&*rng.begin(), ranges::distance(rng)); }; for (auto Option : ranges::views::split(Config, ',') | ranges::views::transform(to_string_view)) { auto OptionData = ranges::views::split(Option, '=') | ranges::views::transform(to_string_view); auto OptionDataBegin = ranges::begin(OptionData); auto OptionDataEnd = ranges::end(OptionData); if (OptionDataBegin == OptionDataEnd) { continue; } auto Key = *OptionDataBegin; if (Key.empty()) { continue; } ++OptionDataBegin; if (OptionDataBegin == OptionDataEnd) { continue; } auto Value = *OptionDataBegin; uint64_t ValueHex {}; char* str_end {}; ValueHex = std::strtoull(Value.data(), &str_end, 16); if (str_end == Value.data()) { LogMan::Msg::EFmt("Couldn't parse '{}={}'\n", Key, Value); continue; } if (Key == "isar0") { ISAR0.SetReg(ValueHex); } else if (Key == "isar1") { ISAR1.SetReg(ValueHex); } else if (Key == "isar2") { ISAR2.SetReg(ValueHex); } else if (Key == "pfr0") { PFR0.SetReg(ValueHex); } else if (Key == "pfr1") { PFR1.SetReg(ValueHex); } else if (Key == "midr") { MIDR.SetReg(ValueHex); } else if (Key == "mmfr0") { MMFR0.SetReg(ValueHex); } else if (Key == "mmfr1") { MMFR1.SetReg(ValueHex); } else if (Key == "mmfr2") { MMFR2.SetReg(ValueHex); } else if (Key == "mmfr3") { MMFR3.SetReg(ValueHex); } else if (Key == "zfr0") { ZFR0.SetReg(ValueHex); } else if (Key == "dczid") { DCZID.SetReg(ValueHex); } else if (Key == "svevl") { SVEVL.SetReg(ValueHex); } else { LogMan::Msg::EFmt("Unknown Key: {}", Key); } } FillFeatureFlags(); } }; FEX::CPUFeatures GetCPUFeaturesFromConfig(std::string_view Config) { return CPUFeaturesFromConfig {Config}; } class CPUFeaturesAll final : public FEX::CPUFeatures { public: CPUFeaturesAll() { // Special case, just set all feature flags for (uint32_t i = 0; i < FEXCore::ToUnderlying(FEX::CPUFeatures::Feature::MAX); ++i) { SetFeature(FEX::CPUFeatures::Feature {i}); } // Report unsupported for DCZVA DCZID.SetReg(0b1'0000); } }; void FEX::CPUFeatures::FillFeatureFlags() { #define ENABLE_FEATURE_IF(Reg, FeatureName) \ if ((Reg).Supports##FeatureName()) { \ SetFeature(Feature::FeatureName); \ } // ISAR0 ENABLE_FEATURE_IF(ISAR0, AES); ENABLE_FEATURE_IF(ISAR0, PMULL); ENABLE_FEATURE_IF(ISAR0, SHA1); ENABLE_FEATURE_IF(ISAR0, SHA2); ENABLE_FEATURE_IF(ISAR0, SHA512); ENABLE_FEATURE_IF(ISAR0, CRC32); ENABLE_FEATURE_IF(ISAR0, LSE); ENABLE_FEATURE_IF(ISAR0, LSE128); ENABLE_FEATURE_IF(ISAR0, TME); ENABLE_FEATURE_IF(ISAR0, RDM); ENABLE_FEATURE_IF(ISAR0, SHA3); ENABLE_FEATURE_IF(ISAR0, SM3); ENABLE_FEATURE_IF(ISAR0, SM4); ENABLE_FEATURE_IF(ISAR0, DotProd); ENABLE_FEATURE_IF(ISAR0, FlagM); ENABLE_FEATURE_IF(ISAR0, FlagM2); ENABLE_FEATURE_IF(ISAR0, RNDR); // ISAR1 ENABLE_FEATURE_IF(ISAR1, DPB); ENABLE_FEATURE_IF(ISAR1, DPB2); ENABLE_FEATURE_IF(ISAR1, JSCVT); ENABLE_FEATURE_IF(ISAR1, FCMA); ENABLE_FEATURE_IF(ISAR1, LRCPC); ENABLE_FEATURE_IF(ISAR1, LRCPC2); ENABLE_FEATURE_IF(ISAR1, LRCPC3); ENABLE_FEATURE_IF(ISAR1, FRINTTS); ENABLE_FEATURE_IF(ISAR1, SB); ENABLE_FEATURE_IF(ISAR1, SPECRES); ENABLE_FEATURE_IF(ISAR1, SPECRES2); ENABLE_FEATURE_IF(ISAR1, BF16); ENABLE_FEATURE_IF(ISAR1, SME_F64F64); ENABLE_FEATURE_IF(ISAR1, I8MM); ENABLE_FEATURE_IF(ISAR1, XS); ENABLE_FEATURE_IF(ISAR1, LS64); ENABLE_FEATURE_IF(ISAR1, LS64_V); ENABLE_FEATURE_IF(ISAR1, LS64_ACCDATA); // ISAR2 ENABLE_FEATURE_IF(ISAR2, WFxt); ENABLE_FEATURE_IF(ISAR2, RPRES); ENABLE_FEATURE_IF(ISAR2, PACQARMA3); ENABLE_FEATURE_IF(ISAR2, MOPS); ENABLE_FEATURE_IF(ISAR2, HBC); ENABLE_FEATURE_IF(ISAR2, CLRBHB); ENABLE_FEATURE_IF(ISAR2, SYSREG128); ENABLE_FEATURE_IF(ISAR2, SYSINSTR128); ENABLE_FEATURE_IF(ISAR2, PRFMSLC); ENABLE_FEATURE_IF(ISAR2, RPRFM); ENABLE_FEATURE_IF(ISAR2, CSSC); // PFR0 ENABLE_FEATURE_IF(PFR0, FP); if (PFR0.SupportsHP()) { SetFeature(Feature::FP16); } if (PFR0.SupportsAdvSIMD()) { SetFeature(Feature::ASIMD); } if (PFR0.SupportsASIMDHP()) { SetFeature(Feature::ASIMD16); } ENABLE_FEATURE_IF(PFR0, RAS); ENABLE_FEATURE_IF(PFR0, SVE); ENABLE_FEATURE_IF(PFR0, DIT); ENABLE_FEATURE_IF(PFR0, CSV2); ENABLE_FEATURE_IF(PFR0, CSV3); // PFR1 ENABLE_FEATURE_IF(PFR1, BTI); ENABLE_FEATURE_IF(PFR1, SSBS); ENABLE_FEATURE_IF(PFR1, SSBS2); ENABLE_FEATURE_IF(PFR1, MTE); ENABLE_FEATURE_IF(PFR1, MTE2); ENABLE_FEATURE_IF(PFR1, MTE3); ENABLE_FEATURE_IF(PFR1, SME); ENABLE_FEATURE_IF(PFR1, SME2); // MMFR0 ENABLE_FEATURE_IF(MMFR0, ECV); // MMFR1 ENABLE_FEATURE_IF(MMFR1, AFP); // MMFR2 ENABLE_FEATURE_IF(MMFR2, LSE2); // ZFR0 if (Supports(Feature::SVE)) { ENABLE_FEATURE_IF(ZFR0, SVE2); ENABLE_FEATURE_IF(ZFR0, SVE2_1); ENABLE_FEATURE_IF(ZFR0, SVE_AES); ENABLE_FEATURE_IF(ZFR0, SVE_PMULL128); ENABLE_FEATURE_IF(ZFR0, SVE_BitPerm); ENABLE_FEATURE_IF(ZFR0, SVE_BF16); ENABLE_FEATURE_IF(ZFR0, SVE_B16B16); ENABLE_FEATURE_IF(ZFR0, SVE_SHA3); ENABLE_FEATURE_IF(ZFR0, SVE_SM4); ENABLE_FEATURE_IF(ZFR0, SVE_I8MM); ENABLE_FEATURE_IF(ZFR0, SVE_F32MM); ENABLE_FEATURE_IF(ZFR0, SVE_F64MM); } #undef ENABLE_FEATURE_IF } #ifdef ARCHITECTURE_arm64 static uint32_t GetFPCR() { uint64_t Result {}; __asm("mrs %[Res], FPCR" : [Res] "=r"(Result)); return Result; } static void SetFPCR(uint64_t Value) { __asm("msr FPCR, %[Value]" ::[Value] "r"(Value)); } #endif static void OverrideFeatures(FEXCore::HostFeatures* Features, uint64_t ForceSVEWidth) { // Override features if the user has specifically called for it. FEX_CONFIG_OPT(HostFeatures, HOSTFEATURES); if (!HostFeatures()) { // Early exit if no features are overriden. return; } #define ENABLE_DISABLE_OPTION(FeatureName, name, enum_name) \ do { \ const bool Disable##name = (HostFeatures() & FEXCore::Config::HostFeatures::DISABLE##enum_name) != 0; \ 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"); \ const bool AlreadyEnabled = Features->FeatureName; \ const bool Result = (AlreadyEnabled | Enable##name) & !Disable##name; \ Features->FeatureName = Result; \ } while (0) #define GET_SINGLE_OPTION(name, enum_name) \ const bool Disable##name = (HostFeatures() & FEXCore::Config::HostFeatures::DISABLE##enum_name) != 0; \ 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(SupportsAFP, AFP, AFP); ENABLE_DISABLE_OPTION(SupportsRCPC, LRCPC, LRCPC); ENABLE_DISABLE_OPTION(SupportsTSOImm9, LRCPC2, LRCPC2); ENABLE_DISABLE_OPTION(SupportsCSSC, CSSC, CSSC); ENABLE_DISABLE_OPTION(SupportsPMULL_128Bit, PMULL128, PMULL128); ENABLE_DISABLE_OPTION(SupportsRAND, RNG, RNG); ENABLE_DISABLE_OPTION(SupportsCLZERO, CLZERO, CLZERO); ENABLE_DISABLE_OPTION(SupportsAtomics, Atomics, ATOMICS); ENABLE_DISABLE_OPTION(SupportsFCMA, FCMA, FCMA); ENABLE_DISABLE_OPTION(SupportsFlagM, FlagM, FLAGM); ENABLE_DISABLE_OPTION(SupportsFlagM2, FlagM2, FLAGM2); ENABLE_DISABLE_OPTION(SupportsFRINTTS, FRINTTS, FRINTTS); ENABLE_DISABLE_OPTION(SupportsRPRES, RPRES, RPRES); ENABLE_DISABLE_OPTION(SupportsSVEBitPerm, SVEBITPERM, SVEBITPERM); ENABLE_DISABLE_OPTION(SupportsPreserveAllABI, PRESERVEALLABI, PRESERVEALLABI); ENABLE_DISABLE_OPTION(SupportsWFXT, WFXT, WFXT); ENABLE_DISABLE_OPTION(Supports3DNow, 3DNOW, 3DNOW); ENABLE_DISABLE_OPTION(SupportsSSE4a, SSE4A, SSE4A); ENABLE_DISABLE_OPTION(SupportsMOPS, MOPS, MOPS); GET_SINGLE_OPTION(Crypto, CRYPTO); #undef ENABLE_DISABLE_OPTION #undef GET_SINGLE_OPTION if (EnableCrypto) { Features->SupportsAES = true; Features->SupportsCRC = true; Features->SupportsSHA = true; Features->SupportsPMULL_128Bit = true; Features->SupportsAES256 = true; } else if (DisableCrypto) { Features->SupportsAES = false; Features->SupportsCRC = false; Features->SupportsSHA = false; Features->SupportsPMULL_128Bit = false; Features->SupportsAES256 = false; } ///< Only force enable SVE256 if SVE is already enabled and ForceSVEWidth is set to >= 256. Features->SupportsSVE256 = ForceSVEWidth && ForceSVEWidth >= 256; } static void HandleErrata(FEXCore::HostFeatures* HostFeatures, uint64_t MIDR) { constexpr uint32_t Implementer_ARM = 0x41; constexpr uint32_t PartNum_V2 = 0xd4f; constexpr uint32_t PartNum_V3 = 0xd84; constexpr uint32_t PartNum_V3AE = 0xd83; constexpr uint32_t PartNum_X3 = 0xd4e; constexpr uint32_t PartNum_X4 = 0xd82; constexpr uint32_t PartNum_X925 = 0xd85; constexpr uint32_t PartNum_C1Ultra = 0xd8c; constexpr uint32_t PartNum_C1Premium = 0xd90; constexpr uint32_t Implementer_QCOM = 0x51; constexpr uint32_t PartNum_Oryon1 = 0x001; constexpr uint32_t PartNum_Oryon3 = 0x002; constexpr uint32_t Implementer_Ampere = 0xc0; auto GetMIDRImplementer = [](uint32_t MIDR) -> uint32_t { return (MIDR >> 24) & 0xFF; }; auto GetMIDRPartNum = [](uint32_t MIDR) -> uint32_t { return (MIDR >> 4) & 0xFFF; }; const uint32_t MIDR_Implementer = GetMIDRImplementer(MIDR); const uint32_t MIDR_PartNum = GetMIDRPartNum(MIDR); #ifdef ARCHITECTURE_arm64 if (MIDR_Implementer == Implementer_QCOM && (MIDR_PartNum == PartNum_Oryon1 || MIDR_PartNum == PartNum_Oryon3)) { // Work around an errata in Qualcomm's Oryon. // While this CPU implements the RAND extension: // - The RNDR register works. // - The RNDRRS register will never read a random number. (Always return failure) // This is contrary to x86 RNG behaviour where it allows spurious failure with RDSEED, but guarantees eventual success. // This manifested itself on Linux when an x86 processor failed to guarantee forward progress and boot of services would infinite // loop. Just disable this extension if this CPU is detected. HostFeatures->SupportsRAND = false; } #endif // The LDAPUR instruction suffers from significant performance issues on many ARM implementations. This is // listed in the official Cortex errata list as follows: // // 3877900 // LDAPUR, LDAPURB, LDAPURH instructions have stricter memory ordering than required // // LDAPUR instructions execute with full Load-Acquire ordering instead of the relaxed ordering described // in the LDAPUR pseudocode. This might cause significant performance degradation in workloads that do // not require this stricter memory ordering. Note that this erratum only affects the unscaled versions of // LDAPUR (LDAPUR, LDAPURB, LDAPURH), and not LDAPR (LDAPR, LDAPRB, LDAPRH). // // The list of cores to disable its use on was taken from the following LLVM PR that accomplishes the same // thing: https://github.com/llvm/llvm-project/pull/124274 for (uint32_t CoreIndex = 0; CoreIndex < HostFeatures->CPUMIDRs.size(); CoreIndex++) { const uint32_t CoreMIDR = HostFeatures->CPUMIDRs[CoreIndex]; const uint32_t Core_MIDR_Implementer = GetMIDRImplementer(CoreMIDR); const uint32_t Core_MIDR_PartNum = GetMIDRPartNum(CoreMIDR); bool IgnoreLRCPC2 = (Core_MIDR_Implementer == Implementer_ARM) && ((Core_MIDR_PartNum == PartNum_V2) || (Core_MIDR_PartNum == PartNum_V3) || (Core_MIDR_PartNum == PartNum_X3) || (Core_MIDR_PartNum == PartNum_X4) || (Core_MIDR_PartNum == PartNum_X925) || (Core_MIDR_PartNum == PartNum_V3AE) || (Core_MIDR_PartNum == PartNum_C1Ultra) || (Core_MIDR_PartNum == PartNum_C1Premium)); if (IgnoreLRCPC2) { HostFeatures->SupportsTSOImm9 = false; break; } } if (MIDR_Implementer == Implementer_Ampere) { // Ampere Computing CPUs that support CLZero should prefer using `dc zva` for vzero{upper,all} as its faster there. // For Cortex CPUs it doesn't matter one way or the other. // For Oryon CPUs, it is dramatically faster to avoid `dc zva` as it has dramatic stalls around barriers and overlapping `dc zva`. // // Because the `dc zva` optimization was implemented for Ampere, only use that path on the hardware. HostFeatures->PreferZVAForVZero = HostFeatures->SupportsCLZERO; } } void FetchHostFeatures(FEX::CPUFeatures& Features, FEXCore::HostFeatures& HostFeatures, bool SupportsCacheMaintenanceOps, uint64_t CTR, uint64_t MIDR) { FEX_CONFIG_OPT(ForceSVEWidth, FORCESVEWIDTH); FEX_CONFIG_OPT(Is64BitMode, IS64BIT_MODE); HostFeatures.SupportsCacheMaintenanceOps = SupportsCacheMaintenanceOps; HostFeatures.SupportsAES = Features.Supports(CPUFeatures::Feature::AES); HostFeatures.SupportsCRC = Features.Supports(CPUFeatures::Feature::CRC32); HostFeatures.SupportsSHA = Features.Supports(CPUFeatures::Feature::SHA1) && Features.Supports(CPUFeatures::Feature::SHA2); HostFeatures.SupportsAtomics = Features.Supports(CPUFeatures::Feature::LSE); HostFeatures.SupportsRAND = Features.Supports(CPUFeatures::Feature::RNDR); // Only supported when FEAT_AFP is supported HostFeatures.SupportsAFP = Features.Supports(CPUFeatures::Feature::AFP); HostFeatures.SupportsRCPC = Features.Supports(CPUFeatures::Feature::LRCPC); HostFeatures.SupportsTSOImm9 = Features.Supports(CPUFeatures::Feature::LRCPC2); HostFeatures.SupportsPMULL_128Bit = Features.Supports(CPUFeatures::Feature::PMULL); HostFeatures.SupportsCSSC = Features.Supports(CPUFeatures::Feature::CSSC); HostFeatures.SupportsFCMA = Features.Supports(CPUFeatures::Feature::FCMA); HostFeatures.SupportsFlagM = Features.Supports(CPUFeatures::Feature::FlagM); HostFeatures.SupportsFlagM2 = Features.Supports(CPUFeatures::Feature::FlagM2); HostFeatures.SupportsFRINTTS = Features.Supports(CPUFeatures::Feature::FRINTTS); HostFeatures.SupportsRPRES = Features.Supports(CPUFeatures::Feature::RPRES); HostFeatures.SupportsSVEBitPerm = Features.Supports(CPUFeatures::Feature::SVE_BitPerm); HostFeatures.SupportsECV = Features.Supports(CPUFeatures::Feature::ECV); HostFeatures.SupportsWFXT = Features.Supports(CPUFeatures::Feature::WFxt); #ifdef VIXL_SIMULATOR // Hardcode enable SVE with 256-bit wide registers. HostFeatures.SupportsSVE128 = ForceSVEWidth() ? ForceSVEWidth() >= 128 : true; HostFeatures.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.SupportsSHA = true; HostFeatures.SupportsPMULL_128Bit = true; HostFeatures.SupportsAES256 = true; // Simulator doesn't support these 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.SupportsMOPS = Features.Supports(CPUFeatures::Feature::MOPS); // Check if we can support cacheline clears if (Features.GetDCZID().SupportsDCZVA()) { // If the DC ZVA size matches the emulated cache line size // This means we can use the instruction constexpr static uint64_t CACHELINE_SIZE = 64; HostFeatures.SupportsCLZERO = Features.GetDCZID().BlockSizeInBytes() == CACHELINE_SIZE; } #endif HostFeatures.Supports3DNow = true; HostFeatures.SupportsAVX = true; HostFeatures.SupportsAES256 = HostFeatures.SupportsAVX && HostFeatures.SupportsAES; HostFeatures.SupportsPreserveAllABI = FEX_HAS_PRESERVE_ALL_ATTR; HostFeatures.PreferZVAForVZero = false; if (CTR) { HostFeatures.DCacheLineLog2 = (CTR >> 16) & 0xF; } else { // 64-bytes HostFeatures.DCacheLineLog2 = 4; } if (!HostFeatures.SupportsAtomics) { WARN_ONCE_FMT("Host CPU doesn't support atomics. Expect bad performance"); } #ifdef ARCHITECTURE_arm64 // Test if this CPU supports float exception trapping by attempting to enable // On unsupported these bits are architecturally defined as RAZ/WI constexpr uint32_t ExceptionEnableTraps = (1U << 8) | // Invalid Operation float exception trap enable (1U << 9) | // Divide by zero float exception trap enable (1U << 10) | // Overflow float exception trap enable (1U << 11) | // Underflow float exception trap enable (1U << 12) | // Inexact float exception trap enable (1U << 15); // Input Denormal float exception trap enable uint32_t OriginalFPCR = GetFPCR(); uint32_t FPCR = OriginalFPCR | ExceptionEnableTraps; SetFPCR(FPCR); FPCR = GetFPCR(); HostFeatures.SupportsFloatExceptions = (FPCR & ExceptionEnableTraps) == ExceptionEnableTraps; // Set FPCR back to original just in case anything changed SetFPCR(OriginalFPCR); #endif #if defined(ARCHITECTURE_x86_64) && !defined(VIXL_SIMULATOR) FEX::X86::Features Feature {}; HostFeatures.SupportsAES = Feature.Feat_aes; HostFeatures.SupportsCRC = Feature.Feat_crc; HostFeatures.SupportsRAND = Feature.Feat_rand; HostFeatures.SupportsRCPC = true; HostFeatures.SupportsTSOImm9 = true; HostFeatures.SupportsAVX = Feature.Feat_avx; HostFeatures.SupportsSHA = Feature.Feat_sha; HostFeatures.SupportsPMULL_128Bit = Feature.Feat_pclmulqdq; HostFeatures.SupportsAES256 = Feature.Feat_aes; HostFeatures.SupportsCLZERO = Feature.Feat_clzero; HostFeatures.SupportsAFP = true; HostFeatures.SupportsFloatExceptions = true; #endif HandleErrata(&HostFeatures, MIDR); OverrideFeatures(&HostFeatures, ForceSVEWidth()); } FEXCore::HostFeatures FetchHostFeatures() { FEX_CONFIG_OPT(CPUFeatureRegisters, CPUFEATUREREGISTERS); CPUFeatures Features {}; if (!CPUFeatureRegisters().empty()) { Features = GetCPUFeaturesFromConfig(CPUFeatureRegisters()); } else { #ifdef ARCHITECTURE_x86_64 Features = CPUFeaturesAll {}; // Vixl simulator doesn't support AFP. Features.RemoveFeature(CPUFeatures::Feature::AFP); // Vixl simulator doesn't support RPRES. Features.RemoveFeature(CPUFeatures::Feature::RPRES); #else Features = GetCPUFeaturesFromIDRegisters(); #endif } uint64_t CTR = 0; uint64_t MIDR = 0; #if defined(ARCHITECTURE_arm64) && !defined(VIXL_SIMULATOR) // We need to get the CPU's cache line size // We expect sane targets that have correct cacheline sizes across clusters __asm volatile("mrs %[ctr], ctr_el0" : [ctr] "=r"(CTR)); __asm volatile("mrs %[midr], midr_el1" : [midr] "=r"(MIDR)); #endif FEXCore::HostFeatures HostFeatures = {}; FillMIDRInformationViaLinux(&HostFeatures); FetchHostFeatures(Features, HostFeatures, true, CTR, MIDR); HostFeatures.SupportsCPUIndexInTPIDRRO = false; HostFeatures.HostType = FEXCore::HostFeatures::HostTypeEnum::Linux; return HostFeatures; } } // namespace FEX