llvm-project/llvm/utils/TableGen/SearchableTableEmitter.cpp
Jason Eckhardt 271b3383d7
[TableGen][NFC] Factor early-out range check. (#123645)
Combine the EarlyOut and IsContiguous range check.
Also avoid "comparison is always false" warnings in emitted code when
the lower-bound check is against 0.
2025-01-20 19:13:31 -06:00

909 lines
32 KiB
C++

//===- SearchableTableEmitter.cpp - Generate efficiently searchable tables -==//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This tablegen backend emits a generic array initialized by specified fields,
// together with companion index tables and lookup functions. The lookup
// function generated is either a direct lookup (when a single primary key field
// is integral and densely numbered) or a binary search otherwise.
//
//===----------------------------------------------------------------------===//
#include "Basic/CodeGenIntrinsics.h"
#include "Common/CodeGenTarget.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include "llvm/TableGen/TableGenBackend.h"
#include <algorithm>
#include <set>
#include <string>
#include <vector>
using namespace llvm;
#define DEBUG_TYPE "searchable-table-emitter"
static int64_t getAsInt(const Init *B) {
if (const BitsInit *BI = dyn_cast<BitsInit>(B))
return *BI->convertInitializerToInt();
if (const IntInit *II = dyn_cast<IntInit>(B))
return II->getValue();
llvm_unreachable("Unexpected initializer");
}
static int64_t getInt(const Record *R, StringRef Field) {
return getAsInt(R->getValueInit(Field));
}
namespace {
struct GenericEnum {
using Entry = std::pair<StringRef, int64_t>;
std::string Name;
const Record *Class = nullptr;
std::string PreprocessorGuard;
std::vector<std::unique_ptr<Entry>> Entries;
DenseMap<const Record *, Entry *> EntryMap;
};
struct GenericField {
std::string Name;
const RecTy *RecType = nullptr;
bool IsCode = false;
bool IsIntrinsic = false;
bool IsInstruction = false;
GenericEnum *Enum = nullptr;
GenericField(StringRef Name) : Name(std::string(Name)) {}
};
struct SearchIndex {
std::string Name;
SMLoc Loc; // Source location of PrimaryKey or Key field definition.
SmallVector<GenericField, 1> Fields;
bool EarlyOut = false;
bool ReturnRange = false;
};
struct GenericTable {
std::string Name;
ArrayRef<SMLoc> Locs; // Source locations from the Record instance.
std::string PreprocessorGuard;
std::string CppTypeName;
SmallVector<GenericField, 2> Fields;
std::vector<const Record *> Entries;
std::unique_ptr<SearchIndex> PrimaryKey;
SmallVector<std::unique_ptr<SearchIndex>, 2> Indices;
const GenericField *getFieldByName(StringRef Name) const {
for (const auto &Field : Fields) {
if (Name == Field.Name)
return &Field;
}
return nullptr;
}
};
class SearchableTableEmitter {
const RecordKeeper &Records;
std::unique_ptr<CodeGenTarget> Target;
std::vector<std::unique_ptr<GenericEnum>> Enums;
DenseMap<const Record *, GenericEnum *> EnumMap;
std::set<std::string> PreprocessorGuards;
public:
explicit SearchableTableEmitter(const RecordKeeper &R) : Records(R) {}
void run(raw_ostream &OS);
private:
typedef std::pair<const Init *, int> SearchTableEntry;
enum TypeContext {
TypeInStaticStruct,
TypeInTempStruct,
TypeInArgument,
};
std::string primaryRepresentation(SMLoc Loc, const GenericField &Field,
const Init *I) {
if (const StringInit *SI = dyn_cast<StringInit>(I)) {
if (Field.IsCode || SI->hasCodeFormat())
return std::string(SI->getValue());
else
return SI->getAsString();
} else if (const BitsInit *BI = dyn_cast<BitsInit>(I))
return "0x" + utohexstr(getAsInt(BI));
else if (const BitInit *BI = dyn_cast<BitInit>(I))
return BI->getValue() ? "true" : "false";
else if (Field.IsIntrinsic)
return "Intrinsic::" + getIntrinsic(I).EnumName.str();
else if (Field.IsInstruction)
return I->getAsString();
else if (Field.Enum) {
auto *Entry = Field.Enum->EntryMap[cast<DefInit>(I)->getDef()];
if (!Entry)
PrintFatalError(Loc,
Twine("Entry for field '") + Field.Name + "' is null");
return std::string(Entry->first);
}
PrintFatalError(Loc, Twine("invalid field type for field '") + Field.Name +
"'; expected: bit, bits, string, or code");
}
bool isIntrinsic(const Init *I) {
if (const DefInit *DI = dyn_cast<DefInit>(I))
return DI->getDef()->isSubClassOf("Intrinsic");
return false;
}
const CodeGenIntrinsic &getIntrinsic(const Init *I) {
const Record *Def = cast<DefInit>(I)->getDef();
return Target->getIntrinsic(Def);
}
bool compareBy(const Record *LHS, const Record *RHS,
const SearchIndex &Index);
std::string searchableFieldType(const GenericTable &Table,
const SearchIndex &Index,
const GenericField &Field, TypeContext Ctx) {
if (isa<StringRecTy>(Field.RecType)) {
if (Ctx == TypeInStaticStruct)
return "const char *";
if (Ctx == TypeInTempStruct)
return "std::string";
return "StringRef";
} else if (const BitsRecTy *BI = dyn_cast<BitsRecTy>(Field.RecType)) {
unsigned NumBits = BI->getNumBits();
if (NumBits <= 8)
return "uint8_t";
if (NumBits <= 16)
return "uint16_t";
if (NumBits <= 32)
return "uint32_t";
if (NumBits <= 64)
return "uint64_t";
PrintFatalError(Index.Loc, Twine("In table '") + Table.Name +
"' lookup method '" + Index.Name +
"', key field '" + Field.Name +
"' of type bits is too large");
} else if (isa<BitRecTy>(Field.RecType)) {
return "bool";
} else if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction)
return "unsigned";
PrintFatalError(Index.Loc,
Twine("In table '") + Table.Name + "' lookup method '" +
Index.Name + "', key field '" + Field.Name +
"' has invalid type: " + Field.RecType->getAsString());
}
void emitGenericTable(const GenericTable &Table, raw_ostream &OS);
void emitGenericEnum(const GenericEnum &Enum, raw_ostream &OS);
void emitLookupDeclaration(const GenericTable &Table,
const SearchIndex &Index, raw_ostream &OS);
void emitLookupFunction(const GenericTable &Table, const SearchIndex &Index,
bool IsPrimary, raw_ostream &OS);
void emitIfdef(StringRef Guard, raw_ostream &OS);
bool parseFieldType(GenericField &Field, const Init *II);
std::unique_ptr<SearchIndex>
parseSearchIndex(GenericTable &Table, const RecordVal *RecVal, StringRef Name,
ArrayRef<StringRef> Key, bool EarlyOut, bool ReturnRange);
void collectEnumEntries(GenericEnum &Enum, StringRef NameField,
StringRef ValueField, ArrayRef<const Record *> Items);
void collectTableEntries(GenericTable &Table, ArrayRef<const Record *> Items);
int64_t getNumericKey(const SearchIndex &Index, const Record *Rec);
};
} // End anonymous namespace.
// For search indices that consists of a single field whose numeric value is
// known, return that numeric value.
int64_t SearchableTableEmitter::getNumericKey(const SearchIndex &Index,
const Record *Rec) {
assert(Index.Fields.size() == 1);
const GenericField &Field = Index.Fields[0];
// To be consistent with compareBy and primaryRepresentation elsewhere,
// we check for IsInstruction before Enum-- these fields are not exclusive.
if (Field.IsInstruction) {
const Record *TheDef = Rec->getValueAsDef(Field.Name);
return Target->getInstrIntValue(TheDef);
}
if (Field.Enum) {
const Record *EnumEntry = Rec->getValueAsDef(Field.Name);
return Field.Enum->EntryMap[EnumEntry]->second;
}
assert(isa<BitsRecTy>(Field.RecType) && "unexpected field type");
return getInt(Rec, Field.Name);
}
/// Less-than style comparison between \p LHS and \p RHS according to the
/// key of \p Index.
bool SearchableTableEmitter::compareBy(const Record *LHS, const Record *RHS,
const SearchIndex &Index) {
for (const auto &Field : Index.Fields) {
const Init *LHSI = LHS->getValueInit(Field.Name);
const Init *RHSI = RHS->getValueInit(Field.Name);
if (isa<BitsRecTy>(Field.RecType) || isa<IntRecTy>(Field.RecType)) {
int64_t LHSi = getAsInt(LHSI);
int64_t RHSi = getAsInt(RHSI);
if (LHSi < RHSi)
return true;
if (LHSi > RHSi)
return false;
} else if (Field.IsIntrinsic) {
const CodeGenIntrinsic &LHSi = getIntrinsic(LHSI);
const CodeGenIntrinsic &RHSi = getIntrinsic(RHSI);
if (std::tie(LHSi.TargetPrefix, LHSi.Name) <
std::tie(RHSi.TargetPrefix, RHSi.Name))
return true;
if (std::tie(LHSi.TargetPrefix, LHSi.Name) >
std::tie(RHSi.TargetPrefix, RHSi.Name))
return false;
} else if (Field.IsInstruction) {
// This does not correctly compare the predefined instructions!
const Record *LHSr = cast<DefInit>(LHSI)->getDef();
const Record *RHSr = cast<DefInit>(RHSI)->getDef();
bool LHSpseudo = LHSr->getValueAsBit("isPseudo");
bool RHSpseudo = RHSr->getValueAsBit("isPseudo");
if (LHSpseudo && !RHSpseudo)
return true;
if (!LHSpseudo && RHSpseudo)
return false;
int comp = LHSr->getName().compare(RHSr->getName());
if (comp < 0)
return true;
if (comp > 0)
return false;
} else if (Field.Enum) {
auto LHSr = cast<DefInit>(LHSI)->getDef();
auto RHSr = cast<DefInit>(RHSI)->getDef();
int64_t LHSv = Field.Enum->EntryMap[LHSr]->second;
int64_t RHSv = Field.Enum->EntryMap[RHSr]->second;
if (LHSv < RHSv)
return true;
if (LHSv > RHSv)
return false;
} else {
std::string LHSs = primaryRepresentation(Index.Loc, Field, LHSI);
std::string RHSs = primaryRepresentation(Index.Loc, Field, RHSI);
if (isa<StringRecTy>(Field.RecType)) {
LHSs = StringRef(LHSs).upper();
RHSs = StringRef(RHSs).upper();
}
int comp = LHSs.compare(RHSs);
if (comp < 0)
return true;
if (comp > 0)
return false;
}
}
return false;
}
void SearchableTableEmitter::emitIfdef(StringRef Guard, raw_ostream &OS) {
OS << "#ifdef " << Guard << "\n";
PreprocessorGuards.insert(std::string(Guard));
}
/// Emit a generic enum.
void SearchableTableEmitter::emitGenericEnum(const GenericEnum &Enum,
raw_ostream &OS) {
emitIfdef((Twine("GET_") + Enum.PreprocessorGuard + "_DECL").str(), OS);
OS << "enum " << Enum.Name << " {\n";
for (const auto &Entry : Enum.Entries)
OS << " " << Entry->first << " = " << Entry->second << ",\n";
OS << "};\n";
OS << "#endif\n\n";
}
void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
const SearchIndex &Index,
bool IsPrimary,
raw_ostream &OS) {
OS << "\n";
emitLookupDeclaration(Table, Index, OS);
OS << " {\n";
std::vector<const Record *> IndexRowsStorage;
ArrayRef<const Record *> IndexRows;
StringRef IndexTypeName;
StringRef IndexName;
if (IsPrimary) {
IndexTypeName = Table.CppTypeName;
IndexName = Table.Name;
IndexRows = Table.Entries;
} else {
OS << " struct IndexType {\n";
for (const auto &Field : Index.Fields) {
OS << " "
<< searchableFieldType(Table, Index, Field, TypeInStaticStruct) << " "
<< Field.Name << ";\n";
}
OS << " unsigned _index;\n";
OS << " };\n";
OS << " static const struct IndexType Index[] = {\n";
std::vector<std::pair<const Record *, unsigned>> Entries;
Entries.reserve(Table.Entries.size());
for (unsigned i = 0; i < Table.Entries.size(); ++i)
Entries.emplace_back(Table.Entries[i], i);
llvm::stable_sort(Entries,
[&](const std::pair<const Record *, unsigned> &LHS,
const std::pair<const Record *, unsigned> &RHS) {
return compareBy(LHS.first, RHS.first, Index);
});
IndexRowsStorage.reserve(Entries.size());
for (const auto &Entry : Entries) {
IndexRowsStorage.push_back(Entry.first);
OS << " { ";
ListSeparator LS;
for (const auto &Field : Index.Fields) {
std::string Repr = primaryRepresentation(
Index.Loc, Field, Entry.first->getValueInit(Field.Name));
if (isa<StringRecTy>(Field.RecType))
Repr = StringRef(Repr).upper();
OS << LS << Repr;
}
OS << ", " << Entry.second << " },\n";
}
OS << " };\n\n";
IndexTypeName = "IndexType";
IndexName = "Index";
IndexRows = IndexRowsStorage;
}
bool IsContiguous = false;
if (Index.Fields.size() == 1 &&
(Index.Fields[0].Enum || isa<BitsRecTy>(Index.Fields[0].RecType) ||
Index.Fields[0].IsInstruction)) {
int64_t FirstKeyVal = getNumericKey(Index, IndexRows[0]);
IsContiguous = true;
for (unsigned i = 0; i < IndexRows.size(); ++i) {
if (getNumericKey(Index, IndexRows[i]) != (FirstKeyVal + i)) {
IsContiguous = false;
break;
}
}
}
if (Index.EarlyOut || IsContiguous) {
const GenericField &Field = Index.Fields[0];
std::string FirstRepr = primaryRepresentation(
Index.Loc, Field, IndexRows[0]->getValueInit(Field.Name));
std::string LastRepr = primaryRepresentation(
Index.Loc, Field, IndexRows.back()->getValueInit(Field.Name));
std::string TS =
'(' + searchableFieldType(Table, Index, Field, TypeInStaticStruct) +
')';
OS << " if (" << TS << Field.Name << " != std::clamp(" << TS << Field.Name
<< ", " << TS << FirstRepr << ", " << TS << LastRepr << "))\n";
OS << " return nullptr;\n\n";
if (IsContiguous && !Index.EarlyOut) {
OS << " auto Table = ArrayRef(" << IndexName << ");\n";
OS << " size_t Idx = " << Field.Name << " - " << FirstRepr << ";\n";
OS << " return ";
if (IsPrimary)
OS << "&Table[Idx]";
else
OS << "&" << Table.Name << "[Table[Idx]._index]";
OS << ";\n";
OS << "}\n";
return;
}
}
OS << " struct KeyType {\n";
for (const auto &Field : Index.Fields) {
OS << " " << searchableFieldType(Table, Index, Field, TypeInTempStruct)
<< " " << Field.Name << ";\n";
}
OS << " };\n";
OS << " KeyType Key = {";
ListSeparator LS;
for (const auto &Field : Index.Fields) {
OS << LS << Field.Name;
if (isa<StringRecTy>(Field.RecType)) {
OS << ".upper()";
if (IsPrimary)
PrintFatalError(Index.Loc,
Twine("In table '") + Table.Name +
"', use a secondary lookup method for "
"case-insensitive comparison of field '" +
Field.Name + "'");
}
}
OS << "};\n";
OS << " struct Comp {\n";
OS << " bool operator()(const " << IndexTypeName
<< " &LHS, const KeyType &RHS) const {\n";
auto emitComparator = [&]() {
for (const auto &Field : Index.Fields) {
if (isa<StringRecTy>(Field.RecType)) {
OS << " int Cmp" << Field.Name << " = StringRef(LHS." << Field.Name
<< ").compare(RHS." << Field.Name << ");\n";
OS << " if (Cmp" << Field.Name << " < 0) return true;\n";
OS << " if (Cmp" << Field.Name << " > 0) return false;\n";
} else if (Field.Enum) {
// Explicitly cast to unsigned, because the signedness of enums is
// compiler-dependent.
OS << " if ((unsigned)LHS." << Field.Name << " < (unsigned)RHS."
<< Field.Name << ")\n";
OS << " return true;\n";
OS << " if ((unsigned)LHS." << Field.Name << " > (unsigned)RHS."
<< Field.Name << ")\n";
OS << " return false;\n";
} else {
OS << " if (LHS." << Field.Name << " < RHS." << Field.Name
<< ")\n";
OS << " return true;\n";
OS << " if (LHS." << Field.Name << " > RHS." << Field.Name
<< ")\n";
OS << " return false;\n";
}
}
OS << " return false;\n";
OS << " }\n";
};
emitComparator();
bool ShouldReturnRange = Index.ReturnRange;
if (ShouldReturnRange) {
OS << " bool operator()(const KeyType &LHS, const " << IndexTypeName
<< " &RHS) const {\n";
emitComparator();
}
OS << " };\n";
OS << " auto Table = ArrayRef(" << IndexName << ");\n";
if (ShouldReturnRange)
OS << " auto It = std::equal_range(Table.begin(), Table.end(), Key, ";
else
OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key, ";
OS << "Comp());\n";
if (!ShouldReturnRange) {
OS << " if (Idx == Table.end()";
for (const auto &Field : Index.Fields)
OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name;
}
if (ShouldReturnRange)
OS << " return llvm::make_range(It.first, It.second);\n";
else if (IsPrimary) {
OS << ")\n return nullptr;\n\n";
OS << " return &*Idx;\n";
} else {
OS << ")\n return nullptr;\n\n";
OS << " return &" << Table.Name << "[Idx->_index];\n";
}
OS << "}\n";
}
void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table,
const SearchIndex &Index,
raw_ostream &OS) {
if (Index.ReturnRange)
OS << "llvm::iterator_range<const " << Table.CppTypeName << " *> ";
else
OS << "const " << Table.CppTypeName << " *";
OS << Index.Name << "(";
ListSeparator LS;
for (const auto &Field : Index.Fields)
OS << LS << searchableFieldType(Table, Index, Field, TypeInArgument) << " "
<< Field.Name;
OS << ")";
}
void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
raw_ostream &OS) {
emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_DECL").str(), OS);
// Emit the declarations for the functions that will perform lookup.
if (Table.PrimaryKey) {
emitLookupDeclaration(Table, *Table.PrimaryKey, OS);
OS << ";\n";
}
for (const auto &Index : Table.Indices) {
emitLookupDeclaration(Table, *Index, OS);
OS << ";\n";
}
OS << "#endif\n\n";
emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_IMPL").str(), OS);
// The primary data table contains all the fields defined for this map.
OS << "constexpr " << Table.CppTypeName << " " << Table.Name << "[] = {\n";
for (unsigned i = 0; i < Table.Entries.size(); ++i) {
const Record *Entry = Table.Entries[i];
OS << " { ";
ListSeparator LS;
for (const auto &Field : Table.Fields)
OS << LS
<< primaryRepresentation(Table.Locs[0], Field,
Entry->getValueInit(Field.Name));
OS << " }, // " << i << "\n";
}
OS << " };\n";
// Indexes are sorted "{ Thing, PrimaryIdx }" arrays, so that a binary
// search can be performed by "Thing".
if (Table.PrimaryKey)
emitLookupFunction(Table, *Table.PrimaryKey, /*IsPrimary=*/true, OS);
for (const auto &Index : Table.Indices)
emitLookupFunction(Table, *Index, /*IsPrimary=*/false, OS);
OS << "#endif\n\n";
}
bool SearchableTableEmitter::parseFieldType(GenericField &Field,
const Init *TypeOf) {
auto Type = dyn_cast<StringInit>(TypeOf);
if (!Type)
return false;
StringRef TypeStr = Type->getValue();
if (TypeStr == "code") {
Field.IsCode = true;
return true;
}
if (const Record *TypeRec = Records.getDef(TypeStr)) {
if (TypeRec->isSubClassOf("GenericEnum")) {
Field.Enum = EnumMap[TypeRec];
Field.RecType = RecordRecTy::get(Field.Enum->Class);
return true;
}
}
return false;
}
std::unique_ptr<SearchIndex> SearchableTableEmitter::parseSearchIndex(
GenericTable &Table, const RecordVal *KeyRecVal, StringRef Name,
ArrayRef<StringRef> Key, bool EarlyOut, bool ReturnRange) {
auto Index = std::make_unique<SearchIndex>();
Index->Name = std::string(Name);
Index->Loc = KeyRecVal->getLoc();
Index->EarlyOut = EarlyOut;
Index->ReturnRange = ReturnRange;
for (const auto &FieldName : Key) {
const GenericField *Field = Table.getFieldByName(FieldName);
if (!Field)
PrintFatalError(
KeyRecVal,
Twine("In table '") + Table.Name +
"', 'PrimaryKey' or 'Key' refers to nonexistent field '" +
FieldName + "'");
Index->Fields.push_back(*Field);
}
if (EarlyOut && isa<StringRecTy>(Index->Fields[0].RecType)) {
PrintFatalError(
KeyRecVal, Twine("In lookup method '") + Name + "', early-out is not " +
"supported for a first key field of type string");
}
return Index;
}
void SearchableTableEmitter::collectEnumEntries(
GenericEnum &Enum, StringRef NameField, StringRef ValueField,
ArrayRef<const Record *> Items) {
for (const Record *EntryRec : Items) {
StringRef Name;
if (NameField.empty())
Name = EntryRec->getName();
else
Name = EntryRec->getValueAsString(NameField);
int64_t Value = 0;
if (!ValueField.empty())
Value = getInt(EntryRec, ValueField);
Enum.Entries.push_back(std::make_unique<GenericEnum::Entry>(Name, Value));
Enum.EntryMap.try_emplace(EntryRec, Enum.Entries.back().get());
}
if (ValueField.empty()) {
llvm::stable_sort(Enum.Entries,
[](const std::unique_ptr<GenericEnum::Entry> &LHS,
const std::unique_ptr<GenericEnum::Entry> &RHS) {
return LHS->first < RHS->first;
});
for (size_t i = 0; i < Enum.Entries.size(); ++i)
Enum.Entries[i]->second = i;
}
}
void SearchableTableEmitter::collectTableEntries(
GenericTable &Table, ArrayRef<const Record *> Items) {
if (Items.empty())
PrintFatalError(Table.Locs,
Twine("Table '") + Table.Name + "' has no entries");
for (auto *EntryRec : Items) {
for (auto &Field : Table.Fields) {
auto TI = dyn_cast<TypedInit>(EntryRec->getValueInit(Field.Name));
if (!TI || !TI->isComplete()) {
PrintFatalError(EntryRec, Twine("Record '") + EntryRec->getName() +
"' for table '" + Table.Name +
"' is missing field '" + Field.Name +
"'");
}
if (!Field.RecType) {
Field.RecType = TI->getType();
} else {
const RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
if (!Ty)
PrintFatalError(EntryRec->getValue(Field.Name),
Twine("Field '") + Field.Name + "' of table '" +
Table.Name + "' entry has incompatible type: " +
TI->getType()->getAsString() + " vs. " +
Field.RecType->getAsString());
Field.RecType = Ty;
}
}
Table.Entries.push_back(EntryRec); // Add record to table's record list.
}
const Record *IntrinsicClass = Records.getClass("Intrinsic");
const Record *InstructionClass = Records.getClass("Instruction");
for (auto &Field : Table.Fields) {
if (!Field.RecType)
PrintFatalError(Twine("Cannot determine type of field '") + Field.Name +
"' in table '" + Table.Name + "'. Maybe it is not used?");
if (auto RecordTy = dyn_cast<RecordRecTy>(Field.RecType)) {
if (IntrinsicClass && RecordTy->isSubClassOf(IntrinsicClass))
Field.IsIntrinsic = true;
else if (InstructionClass && RecordTy->isSubClassOf(InstructionClass))
Field.IsInstruction = true;
}
}
SearchIndex Idx;
std::copy(Table.Fields.begin(), Table.Fields.end(),
std::back_inserter(Idx.Fields));
llvm::sort(Table.Entries, [&](const Record *LHS, const Record *RHS) {
return compareBy(LHS, RHS, Idx);
});
}
void SearchableTableEmitter::run(raw_ostream &OS) {
// Emit tables in a deterministic order to avoid needless rebuilds.
SmallVector<std::unique_ptr<GenericTable>, 4> Tables;
DenseMap<const Record *, GenericTable *> TableMap;
bool NeedsTarget =
!Records.getAllDerivedDefinitionsIfDefined("Instruction").empty() ||
!Records.getAllDerivedDefinitionsIfDefined("Intrinsic").empty();
if (NeedsTarget)
Target = std::make_unique<CodeGenTarget>(Records);
// Collect all definitions first.
for (const auto *EnumRec : Records.getAllDerivedDefinitions("GenericEnum")) {
StringRef NameField;
if (!EnumRec->isValueUnset("NameField"))
NameField = EnumRec->getValueAsString("NameField");
StringRef ValueField;
if (!EnumRec->isValueUnset("ValueField"))
ValueField = EnumRec->getValueAsString("ValueField");
auto Enum = std::make_unique<GenericEnum>();
Enum->Name = std::string(EnumRec->getName());
Enum->PreprocessorGuard = std::string(EnumRec->getName());
StringRef FilterClass = EnumRec->getValueAsString("FilterClass");
Enum->Class = Records.getClass(FilterClass);
if (!Enum->Class)
PrintFatalError(EnumRec->getValue("FilterClass"),
Twine("Enum FilterClass '") + FilterClass +
"' does not exist");
collectEnumEntries(*Enum, NameField, ValueField,
Records.getAllDerivedDefinitions(FilterClass));
EnumMap.try_emplace(EnumRec, Enum.get());
Enums.emplace_back(std::move(Enum));
}
for (const auto *TableRec :
Records.getAllDerivedDefinitions("GenericTable")) {
auto Table = std::make_unique<GenericTable>();
Table->Name = std::string(TableRec->getName());
Table->Locs = TableRec->getLoc();
Table->PreprocessorGuard = std::string(TableRec->getName());
Table->CppTypeName = std::string(TableRec->getValueAsString("CppTypeName"));
std::vector<StringRef> Fields = TableRec->getValueAsListOfStrings("Fields");
for (const auto &FieldName : Fields) {
Table->Fields.emplace_back(FieldName); // Construct a GenericField.
if (auto TypeOfRecordVal =
TableRec->getValue(("TypeOf_" + FieldName).str())) {
if (!parseFieldType(Table->Fields.back(),
TypeOfRecordVal->getValue())) {
PrintError(TypeOfRecordVal,
Twine("Table '") + Table->Name + "' has invalid 'TypeOf_" +
FieldName +
"': " + TypeOfRecordVal->getValue()->getAsString());
PrintFatalNote("The 'TypeOf_xxx' field must be a string naming a "
"GenericEnum record, or \"code\"");
}
}
}
StringRef FilterClass = TableRec->getValueAsString("FilterClass");
if (!Records.getClass(FilterClass))
PrintFatalError(TableRec->getValue("FilterClass"),
Twine("Table FilterClass '") + FilterClass +
"' does not exist");
const RecordVal *FilterClassFieldVal =
TableRec->getValue("FilterClassField");
std::vector<const Record *> Definitions =
Records.getAllDerivedDefinitions(FilterClass);
if (auto *FilterClassFieldInit =
dyn_cast<StringInit>(FilterClassFieldVal->getValue())) {
StringRef FilterClassField = FilterClassFieldInit->getValue();
llvm::erase_if(Definitions, [&](const Record *R) {
const RecordVal *Filter = R->getValue(FilterClassField);
if (auto *BitV = dyn_cast<BitInit>(Filter->getValue()))
return !BitV->getValue();
PrintFatalError(Filter, Twine("FilterClassField '") + FilterClass +
"' should be a bit value");
return true;
});
}
collectTableEntries(*Table, Definitions);
if (!TableRec->isValueUnset("PrimaryKey")) {
Table->PrimaryKey =
parseSearchIndex(*Table, TableRec->getValue("PrimaryKey"),
TableRec->getValueAsString("PrimaryKeyName"),
TableRec->getValueAsListOfStrings("PrimaryKey"),
TableRec->getValueAsBit("PrimaryKeyEarlyOut"),
TableRec->getValueAsBit("PrimaryKeyReturnRange"));
llvm::stable_sort(Table->Entries,
[&](const Record *LHS, const Record *RHS) {
return compareBy(LHS, RHS, *Table->PrimaryKey);
});
}
TableMap.try_emplace(TableRec, Table.get());
Tables.emplace_back(std::move(Table));
}
for (const Record *IndexRec :
Records.getAllDerivedDefinitions("SearchIndex")) {
const Record *TableRec = IndexRec->getValueAsDef("Table");
auto It = TableMap.find(TableRec);
if (It == TableMap.end())
PrintFatalError(IndexRec->getValue("Table"),
Twine("SearchIndex '") + IndexRec->getName() +
"' refers to nonexistent table '" +
TableRec->getName());
GenericTable &Table = *It->second;
Table.Indices.push_back(parseSearchIndex(
Table, IndexRec->getValue("Key"), IndexRec->getName(),
IndexRec->getValueAsListOfStrings("Key"),
IndexRec->getValueAsBit("EarlyOut"), /*ReturnRange*/ false));
}
// Translate legacy tables.
const Record *SearchableTable = Records.getClass("SearchableTable");
for (auto &NameRec : Records.getClasses()) {
const Record *Class = NameRec.second.get();
if (Class->getSuperClasses().size() != 1 ||
!Class->isSubClassOf(SearchableTable))
continue;
StringRef TableName = Class->getName();
ArrayRef<const Record *> Items =
Records.getAllDerivedDefinitions(TableName);
if (!Class->isValueUnset("EnumNameField")) {
StringRef NameField = Class->getValueAsString("EnumNameField");
StringRef ValueField;
if (!Class->isValueUnset("EnumValueField"))
ValueField = Class->getValueAsString("EnumValueField");
auto Enum = std::make_unique<GenericEnum>();
Enum->Name = (Twine(Class->getName()) + "Values").str();
Enum->PreprocessorGuard = Class->getName().upper();
Enum->Class = Class;
collectEnumEntries(*Enum, NameField, ValueField, Items);
Enums.emplace_back(std::move(Enum));
}
auto Table = std::make_unique<GenericTable>();
Table->Name = (Twine(Class->getName()) + "sList").str();
Table->Locs = Class->getLoc();
Table->PreprocessorGuard = Class->getName().upper();
Table->CppTypeName = std::string(Class->getName());
for (const RecordVal &Field : Class->getValues()) {
std::string FieldName = std::string(Field.getName());
// Skip uninteresting fields: either special to us, or injected
// template parameters (if they contain a ':').
if (FieldName.find(':') != std::string::npos ||
FieldName == "SearchableFields" || FieldName == "EnumNameField" ||
FieldName == "EnumValueField")
continue;
Table->Fields.emplace_back(FieldName);
}
collectTableEntries(*Table, Items);
for (const auto &Field :
Class->getValueAsListOfStrings("SearchableFields")) {
std::string Name =
(Twine("lookup") + Table->CppTypeName + "By" + Field).str();
Table->Indices.push_back(
parseSearchIndex(*Table, Class->getValue(Field), Name, {Field},
/*EarlyOut*/ false, /*ReturnRange*/ false));
}
Tables.emplace_back(std::move(Table));
}
// Emit everything.
for (const auto &Enum : Enums)
emitGenericEnum(*Enum, OS);
for (const auto &Table : Tables)
emitGenericTable(*Table, OS);
// Put all #undefs last, to allow multiple sections guarded by the same
// define.
for (const auto &Guard : PreprocessorGuards)
OS << "#undef " << Guard << "\n";
}
static TableGen::Emitter::OptClass<SearchableTableEmitter>
X("gen-searchable-tables", "Generate generic binary-searchable table");