Use a bit of relaxed constexpr to make FeatureBitset costant intializable

This requires std::intializer_list to be a literal type, which it is
starting with C++14. The downside is that std::bitset is still not
constexpr-friendly so this change contains a re-implementation of most
of it.

Shrinks clang by ~60k.

llvm-svn: 369847
This commit is contained in:
Benjamin Kramer 2019-08-24 15:02:44 +00:00
parent 19651b68d9
commit 16b322914a
9 changed files with 129 additions and 43 deletions

View File

@ -18,6 +18,7 @@
#define LLVM_MC_SUBTARGETFEATURE_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/MathExtras.h"
#include <array>
#include <bitset>
#include <initializer_list>
@ -33,20 +34,117 @@ const unsigned MAX_SUBTARGET_WORDS = 3;
const unsigned MAX_SUBTARGET_FEATURES = MAX_SUBTARGET_WORDS * 64;
/// Container class for subtarget features.
/// This is convenient because std::bitset does not have a constructor
/// with an initializer list of set bits.
class FeatureBitset : public std::bitset<MAX_SUBTARGET_FEATURES> {
/// This is a constexpr reimplementation of a subset of std::bitset. It would be
/// nice to use std::bitset directly, but it doesn't support constant
/// initialization.
class FeatureBitset {
static_assert((MAX_SUBTARGET_FEATURES % 64) == 0,
"Should be a multiple of 64!");
// This cannot be a std::array, operator[] is not constexpr until C++17.
uint64_t Bits[MAX_SUBTARGET_WORDS] = {};
protected:
constexpr FeatureBitset(const std::array<uint64_t, MAX_SUBTARGET_WORDS> &B) {
for (unsigned I = 0; I != B.size(); ++I)
Bits[I] = B[I];
}
public:
// Cannot inherit constructors because it's not supported by VC++..
FeatureBitset() = default;
FeatureBitset(const bitset<MAX_SUBTARGET_FEATURES>& B) : bitset(B) {}
FeatureBitset(std::initializer_list<unsigned> Init) {
constexpr FeatureBitset(std::initializer_list<unsigned> Init) {
for (auto I : Init)
set(I);
}
FeatureBitset &set() {
std::fill(std::begin(Bits), std::end(Bits), -1ULL);
return *this;
}
constexpr FeatureBitset &set(unsigned I) {
Bits[I / 64] |= uint64_t(1) << (I % 64);
return *this;
}
constexpr FeatureBitset &reset(unsigned I) {
Bits[I / 64] &= ~(uint64_t(1) << (I % 64));
return *this;
}
constexpr FeatureBitset &flip(unsigned I) {
Bits[I / 64] ^= uint64_t(1) << (I % 64);
return *this;
}
constexpr bool operator[](unsigned I) const {
uint64_t Mask = uint64_t(1) << (I % 64);
return (Bits[I / 64] & Mask) != 0;
}
constexpr bool test(unsigned I) const { return (*this)[I]; }
constexpr size_t size() const { return MAX_SUBTARGET_FEATURES; }
bool any() const {
return llvm::any_of(Bits, [](uint64_t I) { return I != 0; });
}
bool none() const { return !any(); }
size_t count() const {
size_t Count = 0;
for (auto B : Bits)
Count += countPopulation(B);
return Count;
}
constexpr FeatureBitset &operator^=(const FeatureBitset &RHS) {
for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) {
Bits[I] ^= RHS.Bits[I];
}
return *this;
}
constexpr FeatureBitset operator^(const FeatureBitset &RHS) const {
FeatureBitset Result = *this;
Result ^= RHS;
return Result;
}
constexpr FeatureBitset &operator&=(const FeatureBitset &RHS) {
for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) {
Bits[I] &= RHS.Bits[I];
}
return *this;
}
constexpr FeatureBitset operator&(const FeatureBitset &RHS) const {
FeatureBitset Result = *this;
Result &= RHS;
return Result;
}
constexpr FeatureBitset &operator|=(const FeatureBitset &RHS) {
for (unsigned I = 0, E = array_lengthof(Bits); I != E; ++I) {
Bits[I] |= RHS.Bits[I];
}
return *this;
}
constexpr FeatureBitset operator|(const FeatureBitset &RHS) const {
FeatureBitset Result = *this;
Result |= RHS;
return Result;
}
constexpr FeatureBitset operator~() const {
FeatureBitset Result = *this;
for (auto &B : Result.Bits)
B = ~B;
return Result;
}
bool operator==(const FeatureBitset &RHS) const {
return std::equal(std::begin(Bits), std::end(Bits), std::begin(RHS.Bits));
}
bool operator!=(const FeatureBitset &RHS) const { return !(*this == RHS); }
bool operator < (const FeatureBitset &Other) const {
for (unsigned I = 0, E = size(); I != E; ++I) {
bool LHS = test(I), RHS = Other.test(I);
@ -58,23 +156,12 @@ public:
};
/// Class used to store the subtarget bits in the tables created by tablegen.
/// The std::initializer_list constructor of FeatureBitset can't be done at
/// compile time and requires a static constructor to run at startup.
class FeatureBitArray {
std::array<uint64_t, MAX_SUBTARGET_WORDS> Bits;
class FeatureBitArray : public FeatureBitset {
public:
constexpr FeatureBitArray(const std::array<uint64_t, MAX_SUBTARGET_WORDS> &B)
: Bits(B) {}
: FeatureBitset(B) {}
FeatureBitset getAsBitset() const {
FeatureBitset Result;
for (unsigned i = 0, e = Bits.size(); i != e; ++i)
Result |= FeatureBitset(Bits[i]) << (64 * i);
return Result;
}
const FeatureBitset &getAsBitset() const { return *this; }
};
//===----------------------------------------------------------------------===//

View File

@ -313,9 +313,9 @@ struct SysAlias {
uint16_t Encoding;
FeatureBitset FeaturesRequired;
SysAlias (const char *N, uint16_t E) : Name(N), Encoding(E) {};
SysAlias (const char *N, uint16_t E, FeatureBitset F) :
Name(N), Encoding(E), FeaturesRequired(F) {};
constexpr SysAlias(const char *N, uint16_t E) : Name(N), Encoding(E) {}
constexpr SysAlias(const char *N, uint16_t E, FeatureBitset F)
: Name(N), Encoding(E), FeaturesRequired(F) {}
bool haveFeatures(FeatureBitset ActiveFeatures) const {
return (FeaturesRequired & ActiveFeatures) == FeaturesRequired;
@ -326,9 +326,10 @@ struct SysAlias {
struct SysAliasReg : SysAlias {
bool NeedsReg;
SysAliasReg(const char *N, uint16_t E, bool R) : SysAlias(N, E), NeedsReg(R) {};
SysAliasReg(const char *N, uint16_t E, bool R, FeatureBitset F) : SysAlias(N, E, F),
NeedsReg(R) {};
constexpr SysAliasReg(const char *N, uint16_t E, bool R)
: SysAlias(N, E), NeedsReg(R) {}
constexpr SysAliasReg(const char *N, uint16_t E, bool R, FeatureBitset F)
: SysAlias(N, E, F), NeedsReg(R) {}
};
namespace AArch64AT{

View File

@ -119,7 +119,7 @@ HexagonSubtarget::initializeSubtargetDependencies(StringRef CPU, StringRef FS) {
FeatureBitset Features = getFeatureBits();
if (HexagonDisableDuplex)
setFeatureBits(Features.set(Hexagon::FeatureDuplex, false));
setFeatureBits(Features.reset(Hexagon::FeatureDuplex));
setFeatureBits(Hexagon_MC::completeHVXFeatures(Features));
return *this;

View File

@ -264,14 +264,12 @@ createHexagonObjectTargetStreamer(MCStreamer &S, const MCSubtargetInfo &STI) {
}
static void LLVM_ATTRIBUTE_UNUSED clearFeature(MCSubtargetInfo* STI, uint64_t F) {
uint64_t FB = STI->getFeatureBits().to_ullong();
if (FB & (1ULL << F))
if (STI->getFeatureBits()[F])
STI->ToggleFeature(F);
}
static bool LLVM_ATTRIBUTE_UNUSED checkFeature(MCSubtargetInfo* STI, uint64_t F) {
uint64_t FB = STI->getFeatureBits().to_ullong();
return (FB & (1ULL << F)) != 0;
return STI->getFeatureBits()[F];
}
namespace {
@ -398,7 +396,7 @@ MCSubtargetInfo *Hexagon_MC::createHexagonMCSubtargetInfo(const Triple &TT,
MCSubtargetInfo *X = createHexagonMCSubtargetInfoImpl(TT, CPUName, ArchFS);
if (HexagonDisableDuplex) {
llvm::FeatureBitset Features = X->getFeatureBits();
X->setFeatureBits(Features.set(Hexagon::FeatureDuplex, false));
X->setFeatureBits(Features.reset(Hexagon::FeatureDuplex));
}
X->setFeatureBits(completeHVXFeatures(X->getFeatureBits()));

View File

@ -96,9 +96,9 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-NEXT: computeAvailableModuleFeatures(const MyTargetSubtarget *Subtarget) const {
// CHECK-NEXT: PredicateBitset Features;
// CHECK-NEXT: if (Subtarget->hasA())
// CHECK-NEXT: Features[Feature_HasABit] = 1;
// CHECK-NEXT: Features.set(Feature_HasABit);
// CHECK-NEXT: if (Subtarget->hasB())
// CHECK-NEXT: Features[Feature_HasBBit] = 1;
// CHECK-NEXT: Features.set(Feature_HasBBit);
// CHECK-NEXT: return Features;
// CHECK-NEXT: }
@ -106,7 +106,7 @@ def HasC : Predicate<"Subtarget->hasC()"> { let RecomputePerFunction = 1; }
// CHECK-NEXT: computeAvailableFunctionFeatures(const MyTargetSubtarget *Subtarget, const MachineFunction *MF) const {
// CHECK-NEXT: PredicateBitset Features;
// CHECK-NEXT: if (Subtarget->hasC())
// CHECK-NEXT: Features[Feature_HasCBit] = 1;
// CHECK-NEXT: Features.set(Feature_HasCBit);
// CHECK-NEXT: return Features;
// CHECK-NEXT: }

View File

@ -3366,7 +3366,7 @@ void AsmMatcherEmitter::run(raw_ostream &OS) {
OS << " " << getNameForFeatureBitset(FeatureBitset) << ",\n";
}
OS << "};\n\n"
<< "const static FeatureBitset FeatureBitsets[] {\n"
<< "static constexpr FeatureBitset FeatureBitsets[] = {\n"
<< " {}, // AMFBS_None\n";
for (const auto &FeatureBitset : FeatureBitsets) {
if (FeatureBitset.empty())

View File

@ -385,8 +385,8 @@ void CodeEmitterGen::run(raw_ostream &o) {
o << " " << getNameForFeatureBitset(FeatureBitset) << ",\n";
}
o << "};\n\n"
<< "const static FeatureBitset FeatureBitsets[] {\n"
<< " {}, // CEFBS_None\n";
<< "static constexpr FeatureBitset FeatureBitsets[] = {\n"
<< " {}, // CEFBS_None\n";
for (const auto &FeatureBitset : FeatureBitsets) {
if (FeatureBitset.empty())
continue;

View File

@ -496,7 +496,7 @@ void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_IMPL").str(), OS);
// The primary data table contains all the fields defined for this map.
OS << "const " << Table.CppTypeName << " " << Table.Name << "[] = {\n";
OS << "constexpr " << Table.CppTypeName << " " << Table.Name << "[] = {\n";
for (unsigned i = 0; i < Table.Entries.size(); ++i) {
Record *Entry = Table.Entries[i];
OS << " { ";

View File

@ -103,7 +103,7 @@ void SubtargetFeatureInfo::emitComputeAvailableFeatures(
assert(!CondStr.empty() && "true predicate should have been filtered");
OS << " if (" << CondStr << ")\n";
OS << " Features[" << SFI.getEnumBitName() << "] = 1;\n";
OS << " Features.set(" << SFI.getEnumBitName() << ");\n";
}
OS << " return Features;\n";
OS << "}\n\n";
@ -148,7 +148,7 @@ void SubtargetFeatureInfo::emitComputeAssemblerAvailableFeatures(
} while (true);
OS << ")\n";
OS << " Features[" << SFI.getEnumBitName() << "] = 1;\n";
OS << " Features.set(" << SFI.getEnumBitName() << ");\n";
}
OS << " return Features;\n";
OS << "}\n\n";