//===- 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 #include #include 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 SearchTableEntry; int getAsInt(BitsInit *B) { return cast(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(I)) return SI->getAsString(); else if (BitsInit *BI = dyn_cast(I)) return "0x" + utohexstr(getAsInt(BI)); else if (BitInit *BI = dyn_cast(I)) return BI->getValue() ? "true" : "false"; else if (CodeInit *CI = dyn_cast(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(I)) return PrimaryRep; return StringRef(PrimaryRep).upper(); } std::string searchableFieldType(Init *I) { if (isa(I)) return "const char *"; else if (BitsInit *BI = dyn_cast(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 &Items, Record *InstanceClass, raw_ostream &OS); void emitPrimaryTable(StringRef Name, std::vector &FieldNames, std::vector &SearchFieldNames, std::vector> &SearchTables, std::vector &Items, raw_ostream &OS); void emitSearchTable(StringRef Name, StringRef Field, std::vector &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 &Items, Record *InstanceClass, raw_ostream &OS) { StringRef EnumNameField = InstanceClass->getValueAsString("EnumNameField"); StringRef 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 &FieldNames, std::vector &SearchFieldNames, std::vector> &SearchTables, std::vector &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 &SearchTable, raw_ostream &OS) { OS << "const std::pair<" << searchableFieldType(SearchTable[0].first) << ", int> " << Name << "sBy" << Field << "[] = {\n"; if (isa(SearchTable[0].first)) { std::stable_sort(SearchTable.begin(), SearchTable.end(), [this](const SearchTableEntry &LHS, const SearchTableEntry &RHS) { return getAsInt(cast(LHS.first)) < getAsInt(cast(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(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.c_str(), 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 std::strcmp(LHS.first, RHS.first) < 0;\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(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) { StringRef TableName = InstanceClass->getName(); std::vector Items = Records.getAllDerivedDefinitions(TableName); // Gather all the records we're going to need for this particular mapping. std::vector> SearchTables; std::vector SearchFieldNames; std::vector 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_" << TableName.upper() << "_DECL\n"; OS << "#undef GET_" << 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_" << TableName.upper() << "_IMPL\n"; OS << "#undef GET_" << 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.