| //===- 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/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 <algorithm> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| using namespace llvm; |
| |
| #define DEBUG_TYPE "searchable-table-emitter" |
| |
| namespace { |
| |
| class SearchableTableEmitter { |
| RecordKeeper &Records; |
| |
| public: |
| SearchableTableEmitter(RecordKeeper &R) : Records(R) {} |
| |
| void run(raw_ostream &OS); |
| |
| private: |
| typedef std::pair<Init *, int> SearchTableEntry; |
| |
| int getAsInt(BitsInit *B) { |
| return cast<IntInit>(B->convertInitializerTo(IntRecTy::get()))->getValue(); |
| } |
| int getInt(Record *R, StringRef Field) { |
| return getAsInt(R->getValueAsBitsInit(Field)); |
| } |
| |
| std::string primaryRepresentation(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(); |
| } |
| PrintFatalError(SMLoc(), |
| "invalid field type, expected: string, bits, bit or code"); |
| } |
| |
| std::string searchRepresentation(Init *I) { |
| std::string PrimaryRep = primaryRepresentation(I); |
| if (!isa<StringInit>(I)) |
| return PrimaryRep; |
| return StringRef(PrimaryRep).upper(); |
| } |
| |
| std::string searchableFieldType(Init *I) { |
| if (isa<StringInit>(I)) |
| return "const char *"; |
| else if (BitsInit *BI = dyn_cast<BitsInit>(I)) { |
| 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(SMLoc(), "bitfield too large to search"); |
| return "uint" + utostr(NumBits) + "_t"; |
| } |
| PrintFatalError(SMLoc(), "Unknown type to search by"); |
| } |
| |
| void emitMapping(Record *MappingDesc, raw_ostream &OS); |
| void emitMappingEnum(std::vector<Record *> &Items, Record *InstanceClass, |
| raw_ostream &OS); |
| void |
| emitPrimaryTable(StringRef Name, std::vector<std::string> &FieldNames, |
| std::vector<std::string> &SearchFieldNames, |
| std::vector<std::vector<SearchTableEntry>> &SearchTables, |
| std::vector<Record *> &Items, raw_ostream &OS); |
| void emitSearchTable(StringRef Name, StringRef Field, |
| std::vector<SearchTableEntry> &SearchTable, |
| raw_ostream &OS); |
| void emitLookupDeclaration(StringRef Name, StringRef Field, Init *I, |
| raw_ostream &OS); |
| void emitLookupFunction(StringRef Name, StringRef Field, Init *I, |
| raw_ostream &OS); |
| }; |
| |
| } // End anonymous namespace. |
| |
| /// Emit an enum providing symbolic access to some preferred field from |
| /// C++. |
| void SearchableTableEmitter::emitMappingEnum(std::vector<Record *> &Items, |
| Record *InstanceClass, |
| raw_ostream &OS) { |
| std::string EnumNameField = InstanceClass->getValueAsString("EnumNameField"); |
| std::string EnumValueField; |
| if (!InstanceClass->isValueUnset("EnumValueField")) |
| EnumValueField = InstanceClass->getValueAsString("EnumValueField"); |
| |
| OS << "enum " << InstanceClass->getName() << "Values {\n"; |
| for (auto Item : Items) { |
| OS << " " << Item->getValueAsString(EnumNameField); |
| if (EnumValueField != StringRef()) |
| OS << " = " << getInt(Item, EnumValueField); |
| OS << ",\n"; |
| } |
| OS << "};\n\n"; |
| } |
| |
| void SearchableTableEmitter::emitPrimaryTable( |
| StringRef Name, std::vector<std::string> &FieldNames, |
| std::vector<std::string> &SearchFieldNames, |
| std::vector<std::vector<SearchTableEntry>> &SearchTables, |
| std::vector<Record *> &Items, raw_ostream &OS) { |
| OS << "const " << Name << " " << Name << "sList[] = {\n"; |
| |
| for (auto Item : Items) { |
| OS << " { "; |
| for (unsigned i = 0; i < FieldNames.size(); ++i) { |
| OS << primaryRepresentation(Item->getValueInit(FieldNames[i])); |
| if (i != FieldNames.size() - 1) |
| OS << ", "; |
| } |
| OS << "},\n"; |
| } |
| OS << "};\n\n"; |
| } |
| |
| void SearchableTableEmitter::emitSearchTable( |
| StringRef Name, StringRef Field, std::vector<SearchTableEntry> &SearchTable, |
| raw_ostream &OS) { |
| OS << "const std::pair<" << searchableFieldType(SearchTable[0].first) |
| << ", int> " << Name << "sBy" << Field << "[] = {\n"; |
| |
| if (isa<BitsInit>(SearchTable[0].first)) { |
| std::stable_sort(SearchTable.begin(), SearchTable.end(), |
| [this](const SearchTableEntry &LHS, |
| const SearchTableEntry &RHS) { |
| return getAsInt(cast<BitsInit>(LHS.first)) < |
| getAsInt(cast<BitsInit>(RHS.first)); |
| }); |
| } else { |
| std::stable_sort(SearchTable.begin(), SearchTable.end(), |
| [this](const SearchTableEntry &LHS, |
| const SearchTableEntry &RHS) { |
| return searchRepresentation(LHS.first) < |
| searchRepresentation(RHS.first); |
| }); |
| } |
| |
| for (auto Entry : SearchTable) { |
| OS << " { " << searchRepresentation(Entry.first) << ", " << Entry.second |
| << " },\n"; |
| } |
| OS << "};\n\n"; |
| } |
| |
| void SearchableTableEmitter::emitLookupFunction(StringRef Name, StringRef Field, |
| Init *I, raw_ostream &OS) { |
| bool IsIntegral = isa<BitsInit>(I); |
| std::string FieldType = searchableFieldType(I); |
| std::string PairType = "std::pair<" + FieldType + ", int>"; |
| |
| // const SysRegs *lookupSysRegByName(const char *Name) { |
| OS << "const " << Name << " *" |
| << "lookup" << Name << "By" << Field; |
| OS << "(" << (IsIntegral ? FieldType : "StringRef") << " " << Field |
| << ") {\n"; |
| |
| if (IsIntegral) { |
| OS << " auto CanonicalVal = " << Field << ";\n"; |
| OS << " " << PairType << " Val = {CanonicalVal, 0};\n"; |
| } else { |
| // Make sure the result is null terminated because it's going via "char *". |
| OS << " std::string CanonicalVal = " << Field << ".upper();\n"; |
| OS << " " << PairType << " Val = {CanonicalVal.data(), 0};\n"; |
| } |
| |
| OS << " ArrayRef<" << PairType << "> Table(" << Name << "sBy" << Field |
| << ");\n"; |
| OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Val"; |
| |
| if (IsIntegral) |
| OS << ");\n"; |
| else { |
| OS << ",\n "; |
| OS << "[](const " << PairType << " &LHS, const " << PairType |
| << " &RHS) {\n"; |
| OS << " return StringRef(LHS.first) < StringRef(RHS.first);\n"; |
| OS << " });\n\n"; |
| } |
| |
| OS << " if (Idx == Table.end() || CanonicalVal != Idx->first)\n"; |
| OS << " return nullptr;\n"; |
| |
| OS << " return &" << Name << "sList[Idx->second];\n"; |
| OS << "}\n\n"; |
| } |
| |
| void SearchableTableEmitter::emitLookupDeclaration(StringRef Name, |
| StringRef Field, Init *I, |
| raw_ostream &OS) { |
| bool IsIntegral = isa<BitsInit>(I); |
| std::string FieldType = searchableFieldType(I); |
| OS << "const " << Name << " *" |
| << "lookup" << Name << "By" << Field; |
| OS << "(" << (IsIntegral ? FieldType : "StringRef") << " " << Field |
| << ");\n\n"; |
| } |
| |
| void SearchableTableEmitter::emitMapping(Record *InstanceClass, |
| raw_ostream &OS) { |
| const std::string &TableName = InstanceClass->getName(); |
| std::vector<Record *> Items = Records.getAllDerivedDefinitions(TableName); |
| |
| // Gather all the records we're going to need for this particular mapping. |
| std::vector<std::vector<SearchTableEntry>> SearchTables; |
| std::vector<std::string> SearchFieldNames; |
| |
| std::vector<std::string> FieldNames; |
| for (const RecordVal &Field : InstanceClass->getValues()) { |
| std::string FieldName = Field.getName(); |
| |
| // Skip uninteresting fields: either built-in, special to us, or injected |
| // template parameters (if they contain a ':'). |
| if (FieldName.find(':') != std::string::npos || FieldName == "NAME" || |
| FieldName == "SearchableFields" || FieldName == "EnumNameField" || |
| FieldName == "EnumValueField") |
| continue; |
| |
| FieldNames.push_back(FieldName); |
| } |
| |
| for (auto *Field : *InstanceClass->getValueAsListInit("SearchableFields")) { |
| SearchTables.emplace_back(); |
| SearchFieldNames.push_back(Field->getAsUnquotedString()); |
| } |
| |
| int Idx = 0; |
| for (Record *Item : Items) { |
| for (unsigned i = 0; i < SearchFieldNames.size(); ++i) { |
| Init *SearchVal = Item->getValueInit(SearchFieldNames[i]); |
| SearchTables[i].emplace_back(SearchVal, Idx); |
| } |
| ++Idx; |
| } |
| |
| OS << "#ifdef GET_" << StringRef(TableName).upper() << "_DECL\n"; |
| OS << "#undef GET_" << StringRef(TableName).upper() << "_DECL\n"; |
| |
| // Next emit the enum containing the top-level names for use in C++ code if |
| // requested |
| if (!InstanceClass->isValueUnset("EnumNameField")) { |
| emitMappingEnum(Items, InstanceClass, OS); |
| } |
| |
| // And the declarations for the functions that will perform lookup. |
| for (unsigned i = 0; i < SearchFieldNames.size(); ++i) |
| emitLookupDeclaration(TableName, SearchFieldNames[i], |
| SearchTables[i][0].first, OS); |
| |
| OS << "#endif\n\n"; |
| |
| OS << "#ifdef GET_" << StringRef(TableName).upper() << "_IMPL\n"; |
| OS << "#undef GET_" << StringRef(TableName).upper() << "_IMPL\n"; |
| |
| // The primary data table contains all the fields defined for this map. |
| emitPrimaryTable(TableName, FieldNames, SearchFieldNames, SearchTables, Items, |
| OS); |
| |
| // Indexes are sorted "{ Thing, PrimaryIdx }" arrays, so that a binary |
| // search can be performed by "Thing". |
| for (unsigned i = 0; i < SearchTables.size(); ++i) { |
| emitSearchTable(TableName, SearchFieldNames[i], SearchTables[i], OS); |
| emitLookupFunction(TableName, SearchFieldNames[i], SearchTables[i][0].first, |
| OS); |
| } |
| |
| OS << "#endif\n"; |
| } |
| |
| void SearchableTableEmitter::run(raw_ostream &OS) { |
| // Tables are defined to be the direct descendents of "SearchableEntry". |
| Record *SearchableTable = Records.getClass("SearchableTable"); |
| for (auto &NameRec : Records.getClasses()) { |
| Record *Class = NameRec.second.get(); |
| if (Class->getSuperClasses().size() != 1 || |
| !Class->isSubClassOf(SearchableTable)) |
| continue; |
| emitMapping(Class, OS); |
| } |
| } |
| |
| namespace llvm { |
| |
| void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS) { |
| SearchableTableEmitter(RK).run(OS); |
| } |
| |
| } // End llvm namespace. |