2016-07-06 05:23:04 +08:00
|
|
|
//===- SearchableTableEmitter.cpp - Generate efficiently searchable tables -==//
|
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// 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
|
2016-07-06 05:23:04 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This tablegen backend emits a generic array initialized by specified fields,
|
|
|
|
// together with companion index tables and lookup functions (binary search,
|
|
|
|
// currently).
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2018-04-02 01:08:58 +08:00
|
|
|
#include "llvm/ADT/DenseMap.h"
|
2016-07-06 05:23:04 +08:00
|
|
|
#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"
|
2018-04-02 01:08:58 +08:00
|
|
|
#include "CodeGenIntrinsics.h"
|
2016-07-06 05:23:04 +08:00
|
|
|
#include <algorithm>
|
2018-06-21 21:36:22 +08:00
|
|
|
#include <set>
|
2016-07-06 05:23:04 +08:00
|
|
|
#include <string>
|
|
|
|
#include <vector>
|
2018-06-21 21:36:22 +08:00
|
|
|
|
2016-07-06 05:23:04 +08:00
|
|
|
using namespace llvm;
|
|
|
|
|
|
|
|
#define DEBUG_TYPE "searchable-table-emitter"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2016-07-06 05:23:04 +08:00
|
|
|
class SearchableTableEmitter {
|
|
|
|
RecordKeeper &Records;
|
2018-04-02 01:08:58 +08:00
|
|
|
DenseMap<Init *, std::unique_ptr<CodeGenIntrinsic>> Intrinsics;
|
2018-06-21 21:36:22 +08:00
|
|
|
std::vector<std::unique_ptr<GenericEnum>> Enums;
|
|
|
|
DenseMap<Record *, GenericEnum *> EnumMap;
|
|
|
|
std::set<std::string> PreprocessorGuards;
|
2016-07-06 05:23:04 +08:00
|
|
|
|
|
|
|
public:
|
|
|
|
SearchableTableEmitter(RecordKeeper &R) : Records(R) {}
|
|
|
|
|
|
|
|
void run(raw_ostream &OS);
|
|
|
|
|
|
|
|
private:
|
|
|
|
typedef std::pair<Init *, int> SearchTableEntry;
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
enum TypeContext {
|
|
|
|
TypeInStaticStruct,
|
|
|
|
TypeInTempStruct,
|
|
|
|
TypeInArgument,
|
|
|
|
};
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
std::string primaryRepresentation(const GenericField &Field, Init *I) {
|
2016-07-06 05:23:04 +08:00
|
|
|
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";
|
2018-04-02 01:08:58 +08:00
|
|
|
else if (CodeInit *CI = dyn_cast<CodeInit>(I))
|
2016-07-06 05:23:04 +08:00
|
|
|
return CI->getValue();
|
2018-06-21 21:36:22 +08:00
|
|
|
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");
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-04-02 01:08:58 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
bool compareBy(Record *LHS, Record *RHS, const SearchIndex &Index);
|
|
|
|
|
2018-04-02 01:08:58 +08:00
|
|
|
bool isIntegral(Init *I) {
|
2019-05-14 05:59:03 +08:00
|
|
|
return isa<BitsInit>(I) || isa<CodeInit>(I) || isIntrinsic(I);
|
2018-04-02 01:08:58 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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)) {
|
2016-07-06 05:23:04 +08:00
|
|
|
unsigned NumBits = BI->getNumBits();
|
|
|
|
if (NumBits <= 8)
|
TableGen: Fix ASAN error
Summary:
As a bonus, this arguably improves the code by making it simpler.
gcc 8 on Ubuntu 18.10 reports the following:
==39667==ERROR: AddressSanitizer: stack-use-after-scope on address 0x7fffffff8ae0 at pc 0x555555dbfc68 bp 0x7fffffff8760 sp 0x7fffffff8750
WRITE of size 8 at 0x7fffffff8ae0 thread T0
#0 0x555555dbfc67 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_Alloc_hider::_Alloc_hider(char*, std::allocator<char>&&) /usr/include/c++/8/bits/basic_string.h:149
#1 0x555555dbfc67 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&) /usr/include/c++/8/bits/basic_string.h:542
#2 0x555555dbfc67 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > std::operator+<char, std::char_traits<char>, std::allocator<char> >(char const*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&&) /usr/include/c++/8/bits/basic_string.h:6009
#3 0x555555dbfc67 in searchableFieldType /home/nha/amd/build/san/llvm-src/utils/TableGen/SearchableTableEmitter.cpp:168
(...)
Address 0x7fffffff8ae0 is located in stack of thread T0 at offset 864 in frame
#0 0x555555dbef3f in searchableFieldType /home/nha/amd/build/san/llvm-src/utils/TableGen/SearchableTableEmitter.cpp:148
Reviewers: fhahn, simon_tatham, kparzysz
Subscribers: llvm-commits
Differential Revision: https://reviews.llvm.org/D53931
llvm-svn: 345749
2018-11-01 01:46:21 +08:00
|
|
|
return "uint8_t";
|
|
|
|
if (NumBits <= 16)
|
|
|
|
return "uint16_t";
|
|
|
|
if (NumBits <= 32)
|
|
|
|
return "uint32_t";
|
|
|
|
if (NumBits <= 64)
|
|
|
|
return "uint64_t";
|
|
|
|
PrintFatalError(Twine("bitfield '") + Field.Name +
|
|
|
|
"' too large to search");
|
2018-06-21 21:36:22 +08:00
|
|
|
} else if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction)
|
2018-04-02 01:08:58 +08:00
|
|
|
return "unsigned";
|
2018-06-21 21:36:22 +08:00
|
|
|
PrintFatalError(Twine("Field '") + Field.Name + "' has unknown type '" +
|
|
|
|
Field.RecType->getAsString() + "' to search by");
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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);
|
2016-07-06 05:23:04 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
} // End anonymous namespace.
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
// 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;
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
return getInt(Rec, Index.Fields[0].Name);
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
/// 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;
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
}
|
2018-06-21 21:36:22 +08:00
|
|
|
return false;
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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;
|
2016-07-06 05:23:04 +08:00
|
|
|
} else {
|
2018-06-21 21:36:22 +08:00
|
|
|
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);
|
2016-07-06 05:23:04 +08:00
|
|
|
});
|
2018-06-21 21:36:22 +08:00
|
|
|
|
|
|
|
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;
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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";
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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";
|
2018-08-23 16:02:02 +08:00
|
|
|
} 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";
|
2018-06-21 21:36:22 +08:00
|
|
|
} 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";
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
OS << " return false;\n";
|
|
|
|
OS << " });\n\n";
|
|
|
|
|
|
|
|
OS << " if (Idx == Table.end()";
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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";
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table,
|
|
|
|
const SearchIndex &Index,
|
2016-07-06 05:23:04 +08:00
|
|
|
raw_ostream &OS) {
|
2018-06-21 21:36:22 +08:00
|
|
|
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 << ")";
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
|
|
|
|
raw_ostream &OS) {
|
|
|
|
emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_DECL").str(), OS);
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
// 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";
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
OS << "#endif\n\n";
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
emitIfdef((Twine("GET_") + Table.PreprocessorGuard + "_IMPL").str(), OS);
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
// 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 << " { ";
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
bool NeedComma = false;
|
|
|
|
for (const auto &Field : Table.Fields) {
|
|
|
|
if (NeedComma)
|
|
|
|
OS << ", ";
|
|
|
|
NeedComma = true;
|
|
|
|
|
|
|
|
OS << primaryRepresentation(Field, Entry->getValueInit(Field.Name));
|
|
|
|
}
|
|
|
|
|
|
|
|
OS << " }, // " << i << "\n";
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
2018-06-21 21:36:22 +08:00
|
|
|
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";
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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;
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
return false;
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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);
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
if (EarlyOut && isa<StringRecTy>(Index->Fields[0].RecType)) {
|
|
|
|
PrintFatalError(
|
|
|
|
"Early-out is not supported for string types (in search index '" +
|
|
|
|
Twine(Name) + "'");
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
return Index;
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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()));
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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;
|
|
|
|
});
|
2016-07-06 05:23:04 +08:00
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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) {
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(EntryRec->getLoc(),
|
|
|
|
Twine("Record '") + EntryRec->getName() +
|
|
|
|
"' in table '" + Table.Name +
|
|
|
|
"' is missing field '" + Field.Name + "'");
|
2018-06-21 21:36:22 +08:00
|
|
|
}
|
|
|
|
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: " +
|
2019-04-30 01:41:27 +08:00
|
|
|
Field.RecType->getAsString() + " vs. " +
|
2018-06-21 21:36:22 +08:00
|
|
|
TI->getType()->getAsString());
|
|
|
|
Field.RecType = Ty;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Table.Entries.push_back(EntryRec);
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
2018-06-21 21:36:22 +08:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void SearchableTableEmitter::run(raw_ostream &OS) {
|
2018-06-21 21:36:22 +08:00
|
|
|
// 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)
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(EnumRec->getLoc(), Twine("Enum FilterClass '") +
|
|
|
|
FilterClass + "' does not exist");
|
2018-06-21 21:36:22 +08:00
|
|
|
|
|
|
|
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())) {
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(TableRec->getLoc(),
|
|
|
|
Twine("Table '") + Table->Name +
|
|
|
|
"' has bad 'TypeOf_" + FieldName +
|
|
|
|
"': " + TypeOfVal->getValue()->getAsString());
|
2018-06-21 21:36:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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())
|
[tablegen] Add locations to many PrintFatalError() calls
Summary:
While working on the GISel Combiner, I noticed I was producing location-less
error messages fairly often and set about fixing this. In the process, I
noticed quite a few places elsewhere in TableGen that also neglected to include
a relevant location.
This patch adds locations to errors that relate to a specific record (or a
field within it) and also have easy access to the relevant location. This is
particularly useful when multiclasses are involved as many of these errors
refer to the full name of a record and it's difficult to guess which substring
is grep-able.
Unfortunately, tablegen currently only supports Record granularity so it's not
currently possible to point at a specific Init so these sometimes point at the
record that caused the error rather than the precise origin of the error.
Reviewers: bogner, aditya_nandakumar, volkan, aemerson, paquette, nhaehnle
Reviewed By: nhaehnle
Subscribers: jdoerfert, nhaehnle, asb, rbar, johnrusso, simoncook, apazos, sabuasal, niosHD, jrtc27, zzheng, edward-jones, rogfer01, MartinMosbeck, brucehoult, the_o, PkmX, jocewei, llvm-commits
Tags: #llvm
Differential Revision: https://reviews.llvm.org/D58077
llvm-svn: 353862
2019-02-13 01:36:57 +08:00
|
|
|
PrintFatalError(IndexRec->getLoc(),
|
|
|
|
Twine("SearchIndex '") + IndexRec->getName() +
|
|
|
|
"' refers to non-existing table '" +
|
|
|
|
TableRec->getName());
|
2018-06-21 21:36:22 +08:00
|
|
|
|
|
|
|
GenericTable &Table = *It->second;
|
|
|
|
Table.Indices.push_back(parseSearchIndex(
|
|
|
|
Table, IndexRec->getName(), IndexRec->getValueAsListOfStrings("Key"),
|
|
|
|
IndexRec->getValueAsBit("EarlyOut")));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Translate legacy tables.
|
2016-07-06 05:23:04 +08:00
|
|
|
Record *SearchableTable = Records.getClass("SearchableTable");
|
|
|
|
for (auto &NameRec : Records.getClasses()) {
|
|
|
|
Record *Class = NameRec.second.get();
|
|
|
|
if (Class->getSuperClasses().size() != 1 ||
|
|
|
|
!Class->isSubClassOf(SearchableTable))
|
|
|
|
continue;
|
2018-06-21 21:36:22 +08:00
|
|
|
|
|
|
|
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));
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
2018-06-21 21:36:22 +08:00
|
|
|
|
|
|
|
// 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";
|
2016-07-06 05:23:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
namespace llvm {
|
|
|
|
|
|
|
|
void EmitSearchableTables(RecordKeeper &RK, raw_ostream &OS) {
|
|
|
|
SearchableTableEmitter(RK).run(OS);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End llvm namespace.
|