diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -262,15 +262,6 @@ void getTargetDefines(const LangOptions &Opts, MacroBuilder &Builder) const override; - static void setSSELevel(llvm::StringMap &Features, X86SSEEnum Level, - bool Enabled); - - static void setMMXLevel(llvm::StringMap &Features, MMX3DNowEnum Level, - bool Enabled); - - static void setXOPLevel(llvm::StringMap &Features, XOPEnum Level, - bool Enabled); - void setFeatureEnabled(llvm::StringMap &Features, StringRef Name, bool Enabled) const override { setFeatureEnabledImpl(Features, Name, Enabled); diff --git a/clang/lib/Basic/Targets/X86.cpp b/clang/lib/Basic/Targets/X86.cpp --- a/clang/lib/Basic/Targets/X86.cpp +++ b/clang/lib/Basic/Targets/X86.cpp @@ -145,155 +145,6 @@ return true; } -void X86TargetInfo::setSSELevel(llvm::StringMap &Features, - X86SSEEnum Level, bool Enabled) { - if (Enabled) { - switch (Level) { - case AVX512F: - Features["avx512f"] = true; - Features["fma"] = true; - Features["f16c"] = true; - LLVM_FALLTHROUGH; - case AVX2: - Features["avx2"] = true; - LLVM_FALLTHROUGH; - case AVX: - Features["avx"] = true; - LLVM_FALLTHROUGH; - case SSE42: - Features["sse4.2"] = true; - LLVM_FALLTHROUGH; - case SSE41: - Features["sse4.1"] = true; - LLVM_FALLTHROUGH; - case SSSE3: - Features["ssse3"] = true; - LLVM_FALLTHROUGH; - case SSE3: - Features["sse3"] = true; - LLVM_FALLTHROUGH; - case SSE2: - Features["sse2"] = true; - LLVM_FALLTHROUGH; - case SSE1: - Features["sse"] = true; - LLVM_FALLTHROUGH; - case NoSSE: - break; - } - return; - } - - switch (Level) { - case NoSSE: - case SSE1: - Features["sse"] = false; - LLVM_FALLTHROUGH; - case SSE2: - Features["sse2"] = Features["pclmul"] = Features["aes"] = false; - Features["sha"] = Features["gfni"] = false; - LLVM_FALLTHROUGH; - case SSE3: - Features["sse3"] = false; - setXOPLevel(Features, NoXOP, false); - LLVM_FALLTHROUGH; - case SSSE3: - Features["ssse3"] = false; - LLVM_FALLTHROUGH; - case SSE41: - Features["sse4.1"] = false; - LLVM_FALLTHROUGH; - case SSE42: - Features["sse4.2"] = false; - LLVM_FALLTHROUGH; - case AVX: - Features["fma"] = Features["avx"] = Features["f16c"] = false; - Features["vaes"] = Features["vpclmulqdq"] = false; - setXOPLevel(Features, FMA4, false); - LLVM_FALLTHROUGH; - case AVX2: - Features["avx2"] = false; - LLVM_FALLTHROUGH; - case AVX512F: - Features["avx512f"] = Features["avx512cd"] = Features["avx512er"] = false; - Features["avx512pf"] = Features["avx512dq"] = Features["avx512bw"] = false; - Features["avx512vl"] = Features["avx512vbmi"] = false; - Features["avx512ifma"] = Features["avx512vpopcntdq"] = false; - Features["avx512bitalg"] = Features["avx512vnni"] = false; - Features["avx512vbmi2"] = Features["avx512bf16"] = false; - Features["avx512vp2intersect"] = false; - break; - } -} - -void X86TargetInfo::setMMXLevel(llvm::StringMap &Features, - MMX3DNowEnum Level, bool Enabled) { - if (Enabled) { - switch (Level) { - case AMD3DNowAthlon: - Features["3dnowa"] = true; - LLVM_FALLTHROUGH; - case AMD3DNow: - Features["3dnow"] = true; - LLVM_FALLTHROUGH; - case MMX: - Features["mmx"] = true; - LLVM_FALLTHROUGH; - case NoMMX3DNow: - break; - } - return; - } - - switch (Level) { - case NoMMX3DNow: - case MMX: - Features["mmx"] = false; - LLVM_FALLTHROUGH; - case AMD3DNow: - Features["3dnow"] = false; - LLVM_FALLTHROUGH; - case AMD3DNowAthlon: - Features["3dnowa"] = false; - break; - } -} - -void X86TargetInfo::setXOPLevel(llvm::StringMap &Features, XOPEnum Level, - bool Enabled) { - if (Enabled) { - switch (Level) { - case XOP: - Features["xop"] = true; - LLVM_FALLTHROUGH; - case FMA4: - Features["fma4"] = true; - setSSELevel(Features, AVX, true); - LLVM_FALLTHROUGH; - case SSE4A: - Features["sse4a"] = true; - setSSELevel(Features, SSE3, true); - LLVM_FALLTHROUGH; - case NoXOP: - break; - } - return; - } - - switch (Level) { - case NoXOP: - case SSE4A: - Features["sse4a"] = false; - LLVM_FALLTHROUGH; - case FMA4: - Features["fma4"] = false; - LLVM_FALLTHROUGH; - case XOP: - Features["xop"] = false; - break; - } -} - void X86TargetInfo::setFeatureEnabledImpl(llvm::StringMap &Features, StringRef Name, bool Enabled) { if (Name == "sse4") { @@ -309,96 +160,10 @@ Features[Name] = Enabled; - if (Name == "mmx") { - setMMXLevel(Features, MMX, Enabled); - } else if (Name == "sse") { - setSSELevel(Features, SSE1, Enabled); - } else if (Name == "sse2") { - setSSELevel(Features, SSE2, Enabled); - } else if (Name == "sse3") { - setSSELevel(Features, SSE3, Enabled); - } else if (Name == "ssse3") { - setSSELevel(Features, SSSE3, Enabled); - } else if (Name == "sse4.2") { - setSSELevel(Features, SSE42, Enabled); - } else if (Name == "sse4.1") { - setSSELevel(Features, SSE41, Enabled); - } else if (Name == "3dnow") { - setMMXLevel(Features, AMD3DNow, Enabled); - } else if (Name == "3dnowa") { - setMMXLevel(Features, AMD3DNowAthlon, Enabled); - } else if (Name == "aes") { - if (Enabled) - setSSELevel(Features, SSE2, Enabled); - else - Features["vaes"] = false; - } else if (Name == "vaes") { - if (Enabled) { - setSSELevel(Features, AVX, Enabled); - Features["aes"] = true; - } - } else if (Name == "pclmul") { - if (Enabled) - setSSELevel(Features, SSE2, Enabled); - else - Features["vpclmulqdq"] = false; - } else if (Name == "vpclmulqdq") { - if (Enabled) { - setSSELevel(Features, AVX, Enabled); - Features["pclmul"] = true; - } - } else if (Name == "gfni") { - if (Enabled) - setSSELevel(Features, SSE2, Enabled); - } else if (Name == "avx") { - setSSELevel(Features, AVX, Enabled); - } else if (Name == "avx2") { - setSSELevel(Features, AVX2, Enabled); - } else if (Name == "avx512f") { - setSSELevel(Features, AVX512F, Enabled); - } else if (Name.startswith("avx512")) { - if (Enabled) - setSSELevel(Features, AVX512F, Enabled); - // Enable BWI instruction if certain features are being enabled. - if ((Name == "avx512vbmi" || Name == "avx512vbmi2" || - Name == "avx512bitalg" || Name == "avx512bf16") && Enabled) - Features["avx512bw"] = true; - // Also disable some features if BWI is being disabled. - if (Name == "avx512bw" && !Enabled) { - Features["avx512vbmi"] = false; - Features["avx512vbmi2"] = false; - Features["avx512bitalg"] = false; - Features["avx512bf16"] = false; - } - } else if (Name == "fma") { - if (Enabled) - setSSELevel(Features, AVX, Enabled); - else - setSSELevel(Features, AVX512F, Enabled); - } else if (Name == "fma4") { - setXOPLevel(Features, FMA4, Enabled); - } else if (Name == "xop") { - setXOPLevel(Features, XOP, Enabled); - } else if (Name == "sse4a") { - setXOPLevel(Features, SSE4A, Enabled); - } else if (Name == "f16c") { - if (Enabled) - setSSELevel(Features, AVX, Enabled); - else - setSSELevel(Features, AVX512F, Enabled); - } else if (Name == "sha") { - if (Enabled) - setSSELevel(Features, SSE2, Enabled); - } else if (Name == "xsave") { - if (!Enabled) - Features["xsaveopt"] = Features["xsavec"] = Features["xsaves"] = false; - } else if (Name == "xsaveopt" || Name == "xsavec" || Name == "xsaves") { - if (Enabled) - Features["xsave"] = true; - } else if (Name == "amx-tile" && !Enabled) { - Features["amx-bf16"] = Features["amx-int8"] = false; - } else if ((Name == "amx-bf16" || Name == "amx-int8") && Enabled) - Features["amx-tile"] = true; + SmallVector ImpliedFeatures; + llvm::X86::getImpliedFeatures(Name, Enabled, ImpliedFeatures); + for (const auto &F : ImpliedFeatures) + Features[F] = Enabled; } /// handleTargetFeatures - Perform initialization based on the user diff --git a/llvm/include/llvm/Support/X86TargetParser.h b/llvm/include/llvm/Support/X86TargetParser.h --- a/llvm/include/llvm/Support/X86TargetParser.h +++ b/llvm/include/llvm/Support/X86TargetParser.h @@ -137,6 +137,11 @@ /// Fill in the features that \p CPU supports into \p Features. void getFeaturesForCPU(StringRef CPU, SmallVectorImpl &Features); +/// Fill \p Features with the features that are implied to be enabled/disabled +/// by the provided \p Feature. +void getImpliedFeatures(StringRef Feature, bool Enabled, + SmallVectorImpl &Features); + } // namespace X86 } // namespace llvm diff --git a/llvm/include/llvm/Support/X86TargetParser.def b/llvm/include/llvm/Support/X86TargetParser.def --- a/llvm/include/llvm/Support/X86TargetParser.def +++ b/llvm/include/llvm/Support/X86TargetParser.def @@ -174,13 +174,16 @@ X86_FEATURE (3DNOW, "3dnow") X86_FEATURE (3DNOWA, "3dnowa") X86_FEATURE (ADX, "adx") +X86_FEATURE (AMX_BF16, "amx-bf16") +X86_FEATURE (AMX_INT8, "amx-int8") +X86_FEATURE (AMX_TILE, "amx-tile") X86_FEATURE (CLDEMOTE, "cldemote") X86_FEATURE (CLFLUSHOPT, "clflushopt") X86_FEATURE (CLWB, "clwb") X86_FEATURE (CLZERO, "clzero") X86_FEATURE (CMPXCHG16B, "cx16") X86_FEATURE (CMPXCHG8B, "cx8") -X86_FEATURE (EM64T, nullptr) +X86_FEATURE (EM64T, "") X86_FEATURE (ENQCMD, "enqcmd") X86_FEATURE (F16C, "f16c") X86_FEATURE (FSGSBASE, "fsgsbase") @@ -209,6 +212,7 @@ X86_FEATURE (TBM, "tbm") X86_FEATURE (TSXLDTRK, "tsxldtrk") X86_FEATURE (VAES, "vaes") +X86_FEATURE (VZEROUPPER, "vzeroupper") X86_FEATURE (WAITPKG, "waitpkg") X86_FEATURE (WBNOINVD, "wbnoinvd") X86_FEATURE (X87, "x87") @@ -216,5 +220,10 @@ X86_FEATURE (XSAVEC, "xsavec") X86_FEATURE (XSAVEOPT, "xsaveopt") X86_FEATURE (XSAVES, "xsaves") +// These features aren't really CPU features, but the frontend can set them. +X86_FEATURE (RETPOLINE_INDIRECT_BRANCHES, "retpoline-indirect-branches") +X86_FEATURE (RETPOLINE_INDIRECT_CALLS, "retpoline-indirect-calls") +X86_FEATURE (LVI_CFI, "lvi-cfi") +X86_FEATURE (LVI_LOAD_HARDENING, "lvi-load-hardening") #undef X86_FEATURE_COMPAT #undef X86_FEATURE diff --git a/llvm/lib/Support/X86TargetParser.cpp b/llvm/lib/Support/X86TargetParser.cpp --- a/llvm/lib/Support/X86TargetParser.cpp +++ b/llvm/lib/Support/X86TargetParser.cpp @@ -48,6 +48,14 @@ return (Bits[I / 32] & Mask) != 0; } + constexpr FeatureBitset &operator|=(const FeatureBitset &RHS) { + for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) { + uint32_t NewBits = Bits[I] | RHS.Bits[I]; + Bits[I] = NewBits; + } + return *this; + } + constexpr FeatureBitset operator&(const FeatureBitset &RHS) const { FeatureBitset Result; for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) @@ -77,6 +85,11 @@ FeatureBitset Features; }; +struct FeatureInfo { + StringLiteral Name; + FeatureBitset ImpliedFeatures; +}; + } // end anonymous namespace #define X86_FEATURE(ENUM, STRING) \ @@ -376,19 +389,187 @@ llvm_unreachable("Unable to find CPU kind!"); } -static const char *FeatureStrings[X86::CPU_FEATURE_MAX] = { -#define X86_FEATURE(ENUM, STR) STR, +// Features with no dependencies. +static constexpr FeatureBitset ImpliedFeaturesADX = {}; +static constexpr FeatureBitset ImpliedFeaturesBMI = {}; +static constexpr FeatureBitset ImpliedFeaturesBMI2 = {}; +static constexpr FeatureBitset ImpliedFeaturesCLDEMOTE = {}; +static constexpr FeatureBitset ImpliedFeaturesCLFLUSHOPT = {}; +static constexpr FeatureBitset ImpliedFeaturesCLWB = {}; +static constexpr FeatureBitset ImpliedFeaturesCLZERO = {}; +static constexpr FeatureBitset ImpliedFeaturesCMOV = {}; +static constexpr FeatureBitset ImpliedFeaturesCMPXCHG16B = {}; +static constexpr FeatureBitset ImpliedFeaturesCMPXCHG8B = {}; +static constexpr FeatureBitset ImpliedFeaturesEM64T = {}; +static constexpr FeatureBitset ImpliedFeaturesENQCMD = {}; +static constexpr FeatureBitset ImpliedFeaturesFSGSBASE = {}; +static constexpr FeatureBitset ImpliedFeaturesFXSR = {}; +static constexpr FeatureBitset ImpliedFeaturesINVPCID = {}; +static constexpr FeatureBitset ImpliedFeaturesLWP = {}; +static constexpr FeatureBitset ImpliedFeaturesLZCNT = {}; +static constexpr FeatureBitset ImpliedFeaturesMWAITX = {}; +static constexpr FeatureBitset ImpliedFeaturesMOVBE = {}; +static constexpr FeatureBitset ImpliedFeaturesMOVDIR64B = {}; +static constexpr FeatureBitset ImpliedFeaturesMOVDIRI = {}; +static constexpr FeatureBitset ImpliedFeaturesPCONFIG = {}; +static constexpr FeatureBitset ImpliedFeaturesPOPCNT = {}; +static constexpr FeatureBitset ImpliedFeaturesPKU = {}; +static constexpr FeatureBitset ImpliedFeaturesPREFETCHWT1 = {}; +static constexpr FeatureBitset ImpliedFeaturesPRFCHW = {}; +static constexpr FeatureBitset ImpliedFeaturesPTWRITE = {}; +static constexpr FeatureBitset ImpliedFeaturesRDPID = {}; +static constexpr FeatureBitset ImpliedFeaturesRDRND = {}; +static constexpr FeatureBitset ImpliedFeaturesRDSEED = {}; +static constexpr FeatureBitset ImpliedFeaturesRTM = {}; +static constexpr FeatureBitset ImpliedFeaturesSAHF = {}; +static constexpr FeatureBitset ImpliedFeaturesSERIALIZE = {}; +static constexpr FeatureBitset ImpliedFeaturesSGX = {}; +static constexpr FeatureBitset ImpliedFeaturesSHSTK = {}; +static constexpr FeatureBitset ImpliedFeaturesTBM = {}; +static constexpr FeatureBitset ImpliedFeaturesTSXLDTRK = {}; +static constexpr FeatureBitset ImpliedFeaturesWAITPKG = {}; +static constexpr FeatureBitset ImpliedFeaturesWBNOINVD = {}; +static constexpr FeatureBitset ImpliedFeaturesVZEROUPPER = {}; +static constexpr FeatureBitset ImpliedFeaturesX87 = {}; +static constexpr FeatureBitset ImpliedFeaturesXSAVE = {}; + +// Not really CPU features, but need to be in the table because clang uses +// target features to communicate them to the backend. +static constexpr FeatureBitset ImpliedFeaturesRETPOLINE_INDIRECT_BRANCHES = {}; +static constexpr FeatureBitset ImpliedFeaturesRETPOLINE_INDIRECT_CALLS = {}; +static constexpr FeatureBitset ImpliedFeaturesLVI_CFI = {}; +static constexpr FeatureBitset ImpliedFeaturesLVI_LOAD_HARDENING = {}; + +// XSAVE features are dependent on basic XSAVE. +static constexpr FeatureBitset ImpliedFeaturesXSAVEC = FeatureXSAVE; +static constexpr FeatureBitset ImpliedFeaturesXSAVEOPT = FeatureXSAVE; +static constexpr FeatureBitset ImpliedFeaturesXSAVES = FeatureXSAVE; + +// MMX->3DNOW->3DNOWA chain. +static constexpr FeatureBitset ImpliedFeaturesMMX = {}; +static constexpr FeatureBitset ImpliedFeatures3DNOW = FeatureMMX; +static constexpr FeatureBitset ImpliedFeatures3DNOWA = Feature3DNOW; + +// SSE/AVX/AVX512F chain. +static constexpr FeatureBitset ImpliedFeaturesSSE = {}; +static constexpr FeatureBitset ImpliedFeaturesSSE2 = FeatureSSE; +static constexpr FeatureBitset ImpliedFeaturesSSE3 = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesSSSE3 = FeatureSSE3; +static constexpr FeatureBitset ImpliedFeaturesSSE4_1 = FeatureSSSE3; +static constexpr FeatureBitset ImpliedFeaturesSSE4_2 = FeatureSSE4_1; +static constexpr FeatureBitset ImpliedFeaturesAVX = FeatureSSE4_2; +static constexpr FeatureBitset ImpliedFeaturesAVX2 = FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesAVX512F = + FeatureAVX2 | FeatureF16C | FeatureFMA; + +// Vector extensions that build on SSE or AVX. +static constexpr FeatureBitset ImpliedFeaturesAES = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesF16C = FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesFMA = FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesGFNI = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesPCLMUL = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesSHA = FeatureSSE2; +static constexpr FeatureBitset ImpliedFeaturesVAES = FeatureAES | FeatureAVX; +static constexpr FeatureBitset ImpliedFeaturesVPCLMULQDQ = + FeatureAVX | FeaturePCLMUL; + +// AVX512 features. +static constexpr FeatureBitset ImpliedFeaturesAVX512CD = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512BW = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512DQ = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512ER = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512PF = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VL = FeatureAVX512F; + +static constexpr FeatureBitset ImpliedFeaturesAVX512BF16 = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512BITALG = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512IFMA = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VNNI = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VPOPCNTDQ = FeatureAVX512F; +static constexpr FeatureBitset ImpliedFeaturesAVX512VBMI = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512VBMI2 = FeatureAVX512BW; +static constexpr FeatureBitset ImpliedFeaturesAVX512VP2INTERSECT = + FeatureAVX512F; + +// FIXME: These two aren't really implemented and just exist in the feature +// list for __builtin_cpu_supports. So omit their dependencies. +static constexpr FeatureBitset ImpliedFeaturesAVX5124FMAPS = {}; +static constexpr FeatureBitset ImpliedFeaturesAVX5124VNNIW = {}; + +// SSE4_A->FMA4->XOP chain. +static constexpr FeatureBitset ImpliedFeaturesSSE4_A = FeatureSSSE3; +static constexpr FeatureBitset ImpliedFeaturesFMA4 = FeatureAVX | FeatureSSE4_A; +static constexpr FeatureBitset ImpliedFeaturesXOP = FeatureFMA4; + +// AMX Features +static constexpr FeatureBitset ImpliedFeaturesAMX_TILE = {}; +static constexpr FeatureBitset ImpliedFeaturesAMX_BF16 = FeatureAMX_TILE; +static constexpr FeatureBitset ImpliedFeaturesAMX_INT8 = FeatureAMX_TILE; + +static constexpr FeatureInfo FeatureInfos[X86::CPU_FEATURE_MAX] = { +#define X86_FEATURE(ENUM, STR) {{STR}, ImpliedFeatures##ENUM}, #include "llvm/Support/X86TargetParser.def" }; +// Convert the set bits in FeatureBitset to a list of strings. +static void getFeatureBitsAsStrings(const FeatureBitset &Bits, + SmallVectorImpl &Features) { + for (unsigned i = 0; i != CPU_FEATURE_MAX; ++i) + if (Bits[i] && !FeatureInfos[i].Name.empty()) + Features.push_back(FeatureInfos[i].Name); +} + void llvm::X86::getFeaturesForCPU(StringRef CPU, - SmallVectorImpl &Features) { + SmallVectorImpl &EnabledFeatures) { auto I = llvm::find_if(Processors, [&](const ProcInfo &P) { return P.Name == CPU; }); assert(I != std::end(Processors) && "Processor not found!"); // Add the string version of all set bits. - for (unsigned i = 0; i != CPU_FEATURE_MAX; ++i) - if (FeatureStrings[i] && I->Features[i]) - Features.push_back(FeatureStrings[i]); + getFeatureBitsAsStrings(I->Features, EnabledFeatures); +} + +// For each feature that is (transitively) implied by this feature, set it. +static void getImpliedEnabledFeatures(FeatureBitset &Bits, + const FeatureBitset &Implies) { + Bits |= Implies; + for (unsigned i = 0; i != CPU_FEATURE_MAX; ++i) { + if (Implies[i]) + getImpliedEnabledFeatures(Bits, FeatureInfos[i].ImpliedFeatures); + } +} + +/// Create bit vector of features that are implied disabled if the feature +/// passed in Value is disabled. +static void getImpliedDisabledFeatures(FeatureBitset &Bits, unsigned Value) { + // Check all features looking for any dependent on this feature. If we find + // one, mark it and recursively find any feature that depend on it. + for (unsigned i = 0; i != CPU_FEATURE_MAX; ++i) { + if (FeatureInfos[i].ImpliedFeatures[Value]) { + Bits.set(i); + getImpliedDisabledFeatures(Bits, i); + } + } +} + +void llvm::X86::getImpliedFeatures( + StringRef Feature, bool Enabled, + SmallVectorImpl &ImpliedFeatures) { + auto I = llvm::find_if( + FeatureInfos, [&](const FeatureInfo &FI) { return FI.Name == Feature; }); + if (I == std::end(FeatureInfos)) { + // This shouldn't happen, but handle it gracefully for release builds. + assert(false && "Feature not in table!"); + return; + } + + FeatureBitset ImpliedBits; + if (Enabled) + getImpliedEnabledFeatures(ImpliedBits, I->ImpliedFeatures); + else + getImpliedDisabledFeatures(ImpliedBits, + std::distance(std::begin(FeatureInfos), I)); + + // Convert all the found bits into strings. + getFeatureBitsAsStrings(ImpliedBits, ImpliedFeatures); }