|
|
@@ -0,0 +1,320 @@ |
|
|
//===- 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) { |
|
|
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. |