| //===- SearchableTableEmitter.cpp - Generate efficiently searchable tables -==// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This tablegen backend emits a generic array initialized by specified fields, | |
| // together with companion index tables and lookup functions (binary search, | |
| // currently). | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/ADT/DenseMap.h" | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/SourceMgr.h" | |
| #include "llvm/TableGen/Error.h" | |
| #include "llvm/TableGen/Record.h" | |
| #include "CodeGenIntrinsics.h" | |
| #include <algorithm> | |
| #include <set> | |
| #include <string> | |
| #include <vector> | |
| using namespace llvm; | |
| #define DEBUG_TYPE "searchable-table-emitter" | |
| namespace { | |
| struct GenericTable; | |
| int getAsInt(Init *B) { | |
| return cast<IntInit>(B->convertInitializerTo(IntRecTy::get()))->getValue(); | |
| } | |
| int getInt(Record *R, StringRef Field) { | |
| return getAsInt(R->getValueInit(Field)); | |
| } | |
| struct GenericEnum { | |
| using Entry = std::pair<StringRef, int64_t>; | |
| std::string Name; | |
| Record *Class; | |
| std::string PreprocessorGuard; | |
| std::vector<std::unique_ptr<Entry>> Entries; | |
| DenseMap<Record *, Entry *> EntryMap; | |
| }; | |
| struct GenericField { | |
| std::string Name; | |
| RecTy *RecType = nullptr; | |
| bool IsIntrinsic = false; | |
| bool IsInstruction = false; | |
| GenericEnum *Enum = nullptr; | |
| GenericField(StringRef Name) : Name(Name) {} | |
| }; | |
| struct SearchIndex { | |
| std::string Name; | |
| SmallVector<GenericField, 1> Fields; | |
| bool EarlyOut; | |
| }; | |
| struct GenericTable { | |
| std::string Name; | |
| std::string PreprocessorGuard; | |
| std::string CppTypeName; | |
| SmallVector<GenericField, 2> Fields; | |
| std::vector<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 { | |
| RecordKeeper &Records; | |
| DenseMap<Init *, std::unique_ptr<CodeGenIntrinsic>> Intrinsics; | |
| std::vector<std::unique_ptr<GenericEnum>> Enums; | |
| DenseMap<Record *, GenericEnum *> EnumMap; | |
| std::set<std::string> PreprocessorGuards; | |
| public: | |
| SearchableTableEmitter(RecordKeeper &R) : Records(R) {} | |
| void run(raw_ostream &OS); | |
| private: | |
| typedef std::pair<Init *, int> SearchTableEntry; | |
| enum TypeContext { | |
| TypeInStaticStruct, | |
| TypeInTempStruct, | |
| TypeInArgument, | |
| }; | |
| std::string primaryRepresentation(const GenericField &Field, Init *I) { | |
| if (StringInit *SI = dyn_cast<StringInit>(I)) | |
| return SI->getAsString(); | |
| else if (BitsInit *BI = dyn_cast<BitsInit>(I)) | |
| return "0x" + utohexstr(getAsInt(BI)); | |
| else if (BitInit *BI = dyn_cast<BitInit>(I)) | |
| return BI->getValue() ? "true" : "false"; | |
| else if (CodeInit *CI = dyn_cast<CodeInit>(I)) | |
| return CI->getValue(); | |
| else if (Field.IsIntrinsic) | |
| return "Intrinsic::" + getIntrinsic(I).EnumName; | |
| else if (Field.IsInstruction) | |
| return I->getAsString(); | |
| else if (Field.Enum) | |
| return Field.Enum->EntryMap[cast<DefInit>(I)->getDef()]->first; | |
| PrintFatalError(Twine("invalid field type for field '") + Field.Name + | |
| "', expected: string, bits, bit or code"); | |
| } | |
| bool isIntrinsic(Init *I) { | |
| if (DefInit *DI = dyn_cast<DefInit>(I)) | |
| return DI->getDef()->isSubClassOf("Intrinsic"); | |
| return false; | |
| } | |
| CodeGenIntrinsic &getIntrinsic(Init *I) { | |
| std::unique_ptr<CodeGenIntrinsic> &Intr = Intrinsics[I]; | |
| if (!Intr) | |
| Intr = make_unique<CodeGenIntrinsic>(cast<DefInit>(I)->getDef()); | |
| return *Intr; | |
| } | |
| bool compareBy(Record *LHS, Record *RHS, const SearchIndex &Index); | |
| bool isIntegral(Init *I) { | |
| return isa<BitsInit>(I) || isIntrinsic(I); | |
| } | |
| std::string searchableFieldType(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 (BitsRecTy *BI = dyn_cast<BitsRecTy>(Field.RecType)) { | |
| unsigned NumBits = BI->getNumBits(); | |
| if (NumBits <= 8) | |
| NumBits = 8; | |
| else if (NumBits <= 16) | |
| NumBits = 16; | |
| else if (NumBits <= 32) | |
| NumBits = 32; | |
| else if (NumBits <= 64) | |
| NumBits = 64; | |
| else | |
| PrintFatalError(Twine("bitfield '") + Field.Name + | |
| "' too large to search"); | |
| return "uint" + utostr(NumBits) + "_t"; | |
| } else if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction) | |
| return "unsigned"; | |
| PrintFatalError(Twine("Field '") + Field.Name + "' has unknown type '" + | |
| Field.RecType->getAsString() + "' to search by"); | |
| } | |
| 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, Init *II); | |
| std::unique_ptr<SearchIndex> | |
| parseSearchIndex(GenericTable &Table, StringRef Name, | |
| const std::vector<StringRef> &Key, bool EarlyOut); | |
| void collectEnumEntries(GenericEnum &Enum, StringRef NameField, | |
| StringRef ValueField, | |
| const std::vector<Record *> &Items); | |
| void collectTableEntries(GenericTable &Table, | |
| const std::vector<Record *> &Items); | |
| }; | |
| } // End anonymous namespace. | |
| // For search indices that consists of a single field whose numeric value is | |
| // known, return that numeric value. | |
| static int64_t getNumericKey(const SearchIndex &Index, Record *Rec) { | |
| assert(Index.Fields.size() == 1); | |
| if (Index.Fields[0].Enum) { | |
| Record *EnumEntry = Rec->getValueAsDef(Index.Fields[0].Name); | |
| return Index.Fields[0].Enum->EntryMap[EnumEntry]->second; | |
| } | |
| return getInt(Rec, Index.Fields[0].Name); | |
| } | |
| /// Less-than style comparison between \p LHS and \p RHS according to the | |
| /// key of \p Index. | |
| bool SearchableTableEmitter::compareBy(Record *LHS, Record *RHS, | |
| const SearchIndex &Index) { | |
| for (const auto &Field : Index.Fields) { | |
| Init *LHSI = LHS->getValueInit(Field.Name); | |
| 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) { | |
| CodeGenIntrinsic &LHSi = getIntrinsic(LHSI); | |
| 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! | |
| Record *LHSr = cast<DefInit>(LHSI)->getDef(); | |
| 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(Field, LHSI); | |
| std::string RHSs = primaryRepresentation(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(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<Record *> IndexRowsStorage; | |
| ArrayRef<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(Field, TypeInStaticStruct) << " " | |
| << Field.Name << ";\n"; | |
| } | |
| OS << " unsigned _index;\n"; | |
| OS << " };\n"; | |
| OS << " static const struct IndexType Index[] = {\n"; | |
| std::vector<std::pair<Record *, unsigned>> Entries; | |
| Entries.reserve(Table.Entries.size()); | |
| for (unsigned i = 0; i < Table.Entries.size(); ++i) | |
| Entries.emplace_back(Table.Entries[i], i); | |
| std::stable_sort(Entries.begin(), Entries.end(), | |
| [&](const std::pair<Record *, unsigned> &LHS, | |
| const std::pair<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 << " { "; | |
| bool NeedComma = false; | |
| for (const auto &Field : Index.Fields) { | |
| if (NeedComma) | |
| OS << ", "; | |
| NeedComma = true; | |
| std::string Repr = | |
| primaryRepresentation(Field, Entry.first->getValueInit(Field.Name)); | |
| if (isa<StringRecTy>(Field.RecType)) | |
| Repr = StringRef(Repr).upper(); | |
| OS << 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))) { | |
| IsContiguous = true; | |
| for (unsigned i = 0; i < IndexRows.size(); ++i) { | |
| if (getNumericKey(Index, IndexRows[i]) != i) { | |
| IsContiguous = false; | |
| break; | |
| } | |
| } | |
| } | |
| if (IsContiguous) { | |
| OS << " auto Table = makeArrayRef(" << IndexName << ");\n"; | |
| OS << " size_t Idx = " << Index.Fields[0].Name << ";\n"; | |
| OS << " return Idx >= Table.size() ? nullptr : "; | |
| if (IsPrimary) | |
| OS << "&Table[Idx]"; | |
| else | |
| OS << "&" << Table.Name << "[Table[Idx]._index]"; | |
| OS << ";\n"; | |
| OS << "}\n"; | |
| return; | |
| } | |
| if (Index.EarlyOut) { | |
| const GenericField &Field = Index.Fields[0]; | |
| std::string FirstRepr = | |
| primaryRepresentation(Field, IndexRows[0]->getValueInit(Field.Name)); | |
| std::string LastRepr = primaryRepresentation( | |
| Field, IndexRows.back()->getValueInit(Field.Name)); | |
| OS << " if ((" << Field.Name << " < " << FirstRepr << ") ||\n"; | |
| OS << " (" << Field.Name << " > " << LastRepr << "))\n"; | |
| OS << " return nullptr;\n\n"; | |
| } | |
| OS << " struct KeyType {\n"; | |
| for (const auto &Field : Index.Fields) { | |
| OS << " " << searchableFieldType(Field, TypeInTempStruct) << " " | |
| << Field.Name << ";\n"; | |
| } | |
| OS << " };\n"; | |
| OS << " KeyType Key = { "; | |
| bool NeedComma = false; | |
| for (const auto &Field : Index.Fields) { | |
| if (NeedComma) | |
| OS << ", "; | |
| NeedComma = true; | |
| OS << Field.Name; | |
| if (isa<StringRecTy>(Field.RecType)) { | |
| OS << ".upper()"; | |
| if (IsPrimary) | |
| PrintFatalError(Twine("Use a secondary index for case-insensitive " | |
| "comparison of field '") + | |
| Field.Name + "' in table '" + Table.Name + "'"); | |
| } | |
| } | |
| OS << " };\n"; | |
| OS << " auto Table = makeArrayRef(" << IndexName << ");\n"; | |
| OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,\n"; | |
| OS << " [](const " << IndexTypeName << " &LHS, const KeyType &RHS) {\n"; | |
| 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 { | |
| 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\n"; | |
| OS << " if (Idx == Table.end()"; | |
| for (const auto &Field : Index.Fields) | |
| OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name; | |
| OS << ")\n return nullptr;\n"; | |
| if (IsPrimary) | |
| OS << " return &*Idx;\n"; | |
| else | |
| OS << " return &" << Table.Name << "[Idx->_index];\n"; | |
| OS << "}\n"; | |
| } | |
| void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table, | |
| const SearchIndex &Index, | |
| raw_ostream &OS) { | |
| OS << "const " << Table.CppTypeName << " *" << Index.Name << "("; | |
| bool NeedComma = false; | |
| for (const auto &Field : Index.Fields) { | |
| if (NeedComma) | |
| OS << ", "; | |
| NeedComma = true; | |
| OS << searchableFieldType(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 << "const " << Table.CppTypeName << " " << Table.Name << "[] = {\n"; | |
| for (unsigned i = 0; i < Table.Entries.size(); ++i) { | |
| Record *Entry = Table.Entries[i]; | |
| OS << " { "; | |
| bool NeedComma = false; | |
| for (const auto &Field : Table.Fields) { | |
| if (NeedComma) | |
| OS << ", "; | |
| NeedComma = true; | |
| OS << primaryRepresentation(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, true, OS); | |
| for (const auto &Index : Table.Indices) | |
| emitLookupFunction(Table, *Index, false, OS); | |
| OS << "#endif\n\n"; | |
| } | |
| bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *II) { | |
| if (auto DI = dyn_cast<DefInit>(II)) { | |
| Record *TypeRec = DI->getDef(); | |
| 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, StringRef Name, | |
| const std::vector<StringRef> &Key, | |
| bool EarlyOut) { | |
| auto Index = llvm::make_unique<SearchIndex>(); | |
| Index->Name = Name; | |
| Index->EarlyOut = EarlyOut; | |
| for (const auto &FieldName : Key) { | |
| const GenericField *Field = Table.getFieldByName(FieldName); | |
| if (!Field) | |
| PrintFatalError(Twine("Search index '") + Name + | |
| "' refers to non-existing field '" + FieldName + | |
| "' in table '" + Table.Name + "'"); | |
| Index->Fields.push_back(*Field); | |
| } | |
| if (EarlyOut && isa<StringRecTy>(Index->Fields[0].RecType)) { | |
| PrintFatalError( | |
| "Early-out is not supported for string types (in search index '" + | |
| Twine(Name) + "'"); | |
| } | |
| return Index; | |
| } | |
| void SearchableTableEmitter::collectEnumEntries( | |
| GenericEnum &Enum, StringRef NameField, StringRef ValueField, | |
| const std::vector<Record *> &Items) { | |
| for (auto 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(llvm::make_unique<GenericEnum::Entry>(Name, Value)); | |
| Enum.EntryMap.insert(std::make_pair(EntryRec, Enum.Entries.back().get())); | |
| } | |
| if (ValueField.empty()) { | |
| std::stable_sort(Enum.Entries.begin(), Enum.Entries.end(), | |
| [](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, const std::vector<Record *> &Items) { | |
| for (auto EntryRec : Items) { | |
| for (auto &Field : Table.Fields) { | |
| auto TI = dyn_cast<TypedInit>(EntryRec->getValueInit(Field.Name)); | |
| if (!TI) { | |
| PrintFatalError(Twine("Record '") + EntryRec->getName() + | |
| "' in table '" + Table.Name + "' is missing field '" + | |
| Field.Name + "'"); | |
| } | |
| if (!Field.RecType) { | |
| Field.RecType = TI->getType(); | |
| } else { | |
| RecTy *Ty = resolveTypes(Field.RecType, TI->getType()); | |
| if (!Ty) | |
| PrintFatalError(Twine("Field '") + Field.Name + "' of table '" + | |
| Table.Name + "' has incompatible type: " + | |
| Ty->getAsString() + " vs. " + | |
| TI->getType()->getAsString()); | |
| Field.RecType = Ty; | |
| } | |
| } | |
| Table.Entries.push_back(EntryRec); | |
| } | |
| Record *IntrinsicClass = Records.getClass("Intrinsic"); | |
| Record *InstructionClass = Records.getClass("Instruction"); | |
| for (auto &Field : Table.Fields) { | |
| 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; | |
| } | |
| } | |
| } | |
| void SearchableTableEmitter::run(raw_ostream &OS) { | |
| // Emit tables in a deterministic order to avoid needless rebuilds. | |
| SmallVector<std::unique_ptr<GenericTable>, 4> Tables; | |
| DenseMap<Record *, GenericTable *> TableMap; | |
| // Collect all definitions first. | |
| for (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 = llvm::make_unique<GenericEnum>(); | |
| Enum->Name = EnumRec->getName(); | |
| Enum->PreprocessorGuard = EnumRec->getName(); | |
| StringRef FilterClass = EnumRec->getValueAsString("FilterClass"); | |
| Enum->Class = Records.getClass(FilterClass); | |
| if (!Enum->Class) | |
| PrintFatalError(Twine("Enum FilterClass '") + FilterClass + | |
| "' does not exist"); | |
| collectEnumEntries(*Enum, NameField, ValueField, | |
| Records.getAllDerivedDefinitions(FilterClass)); | |
| EnumMap.insert(std::make_pair(EnumRec, Enum.get())); | |
| Enums.emplace_back(std::move(Enum)); | |
| } | |
| for (auto TableRec : Records.getAllDerivedDefinitions("GenericTable")) { | |
| auto Table = llvm::make_unique<GenericTable>(); | |
| Table->Name = TableRec->getName(); | |
| Table->PreprocessorGuard = TableRec->getName(); | |
| Table->CppTypeName = TableRec->getValueAsString("CppTypeName"); | |
| std::vector<StringRef> Fields = TableRec->getValueAsListOfStrings("Fields"); | |
| for (const auto &FieldName : Fields) { | |
| Table->Fields.emplace_back(FieldName); | |
| if (auto TypeOfVal = TableRec->getValue(("TypeOf_" + FieldName).str())) { | |
| if (!parseFieldType(Table->Fields.back(), TypeOfVal->getValue())) { | |
| PrintFatalError(Twine("Table '") + Table->Name + | |
| "' has bad 'TypeOf_" + FieldName + "': " + | |
| TypeOfVal->getValue()->getAsString()); | |
| } | |
| } | |
| } | |
| collectTableEntries(*Table, Records.getAllDerivedDefinitions( | |
| TableRec->getValueAsString("FilterClass"))); | |
| if (!TableRec->isValueUnset("PrimaryKey")) { | |
| Table->PrimaryKey = | |
| parseSearchIndex(*Table, TableRec->getValueAsString("PrimaryKeyName"), | |
| TableRec->getValueAsListOfStrings("PrimaryKey"), | |
| TableRec->getValueAsBit("PrimaryKeyEarlyOut")); | |
| std::stable_sort(Table->Entries.begin(), Table->Entries.end(), | |
| [&](Record *LHS, Record *RHS) { | |
| return compareBy(LHS, RHS, *Table->PrimaryKey); | |
| }); | |
| } | |
| TableMap.insert(std::make_pair(TableRec, Table.get())); | |
| Tables.emplace_back(std::move(Table)); | |
| } | |
| for (Record *IndexRec : Records.getAllDerivedDefinitions("SearchIndex")) { | |
| Record *TableRec = IndexRec->getValueAsDef("Table"); | |
| auto It = TableMap.find(TableRec); | |
| if (It == TableMap.end()) | |
| PrintFatalError(Twine("SearchIndex '") + IndexRec->getName() + | |
| "' refers to non-existing table '" + TableRec->getName()); | |
| GenericTable &Table = *It->second; | |
| Table.Indices.push_back(parseSearchIndex( | |
| Table, IndexRec->getName(), IndexRec->getValueAsListOfStrings("Key"), | |
| IndexRec->getValueAsBit("EarlyOut"))); | |
| } | |
| // Translate legacy tables. | |
| Record *SearchableTable = Records.getClass("SearchableTable"); | |
| for (auto &NameRec : Records.getClasses()) { | |
| Record *Class = NameRec.second.get(); | |
| if (Class->getSuperClasses().size() != 1 || | |
| !Class->isSubClassOf(SearchableTable)) | |
| continue; | |
| StringRef TableName = Class->getName(); | |
| std::vector<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 = llvm::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 = llvm::make_unique<GenericTable>(); | |
| Table->Name = (Twine(Class->getName()) + "sList").str(); | |
| Table->PreprocessorGuard = Class->getName().upper(); | |
| Table->CppTypeName = Class->getName(); | |
| for (const RecordVal &Field : Class->getValues()) { | |
| std::string FieldName = 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, Name, {Field}, 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"; | |
| } | |
| namespace llvm { | |
| void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS) { | |
| SearchableTableEmitter(RK).run(OS); | |
| } | |
| } // End llvm namespace. |