Enhance TableGen so that backends can produce better error messages.

Modify SearchableTableEmitter.cpp to take advantage.
Clean up formatting and capitalization issues.
This commit is contained in:
Paul C. Anagnostopoulos 2020-09-03 09:41:09 -04:00
parent 7d0bbe4090
commit b3931188fd
11 changed files with 182 additions and 87 deletions

View File

@ -15,6 +15,7 @@
#define LLVM_TABLEGEN_ERROR_H #define LLVM_TABLEGEN_ERROR_H
#include "llvm/Support/SourceMgr.h" #include "llvm/Support/SourceMgr.h"
#include "llvm/TableGen/Record.h"
namespace llvm { namespace llvm {
@ -34,6 +35,10 @@ void PrintError(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalError(const Twine &Msg); LLVM_ATTRIBUTE_NORETURN void PrintFatalError(const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalError(ArrayRef<SMLoc> ErrorLoc, LLVM_ATTRIBUTE_NORETURN void PrintFatalError(ArrayRef<SMLoc> ErrorLoc,
const Twine &Msg); const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalError(const Record *Rec,
const Twine &Msg);
LLVM_ATTRIBUTE_NORETURN void PrintFatalError(const RecordVal *RecVal,
const Twine &Msg);
extern SourceMgr SrcMgr; extern SourceMgr SrcMgr;
extern unsigned ErrorsPrinted; extern unsigned ErrorsPrinted;

View File

@ -1402,11 +1402,13 @@ class RecordVal {
friend class Record; friend class Record;
Init *Name; Init *Name;
SMLoc Loc; // Source location of definition of name.
PointerIntPair<RecTy *, 1, bool> TyAndPrefix; PointerIntPair<RecTy *, 1, bool> TyAndPrefix;
Init *Value; Init *Value;
public: public:
RecordVal(Init *N, RecTy *T, bool P); RecordVal(Init *N, RecTy *T, bool P);
RecordVal(Init *N, SMLoc Loc, RecTy *T, bool P);
StringRef getName() const; StringRef getName() const;
Init *getNameInit() const { return Name; } Init *getNameInit() const { return Name; }
@ -1415,11 +1417,13 @@ public:
return getNameInit()->getAsUnquotedString(); return getNameInit()->getAsUnquotedString();
} }
const SMLoc &getLoc() const { return Loc; }
bool getPrefix() const { return TyAndPrefix.getInt(); } bool getPrefix() const { return TyAndPrefix.getInt(); }
RecTy *getType() const { return TyAndPrefix.getPointer(); } RecTy *getType() const { return TyAndPrefix.getPointer(); }
Init *getValue() const { return Value; } Init *getValue() const { return Value; }
bool setValue(Init *V); bool setValue(Init *V);
bool setValue(Init *V, SMLoc NewLoc);
void dump() const; void dump() const;
void print(raw_ostream &OS, bool PrintSem = true) const; void print(raw_ostream &OS, bool PrintSem = true) const;

View File

@ -11,11 +11,12 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "llvm/TableGen/Error.h"
#include "llvm/ADT/Twine.h" #include "llvm/ADT/Twine.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/Support/Signals.h" #include "llvm/Support/Signals.h"
#include "llvm/Support/WithColor.h" #include "llvm/Support/WithColor.h"
#include "llvm/Support/raw_ostream.h" #include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h"
#include <cstdlib> #include <cstdlib>
namespace llvm { namespace llvm {
@ -86,4 +87,22 @@ void PrintFatalError(ArrayRef<SMLoc> ErrorLoc, const Twine &Msg) {
std::exit(1); std::exit(1);
} }
// This method takes a Record and uses the source location
// stored in it.
void PrintFatalError(const Record *Rec, const Twine &Msg) {
PrintError(Rec->getLoc(), Msg);
// The following call runs the file cleanup handlers.
sys::RunInterruptHandlers();
std::exit(1);
}
// This method takes a RecordVal and uses the source location
// stored in it.
void PrintFatalError(const RecordVal *RecVal, const Twine &Msg) {
PrintError(RecVal->getLoc(), Msg);
// The following call runs the file cleanup handlers.
sys::RunInterruptHandlers();
std::exit(1);
}
} // end namespace llvm } // end namespace llvm

View File

@ -2032,6 +2032,14 @@ RecordVal::RecordVal(Init *N, RecTy *T, bool P)
assert(Value && "Cannot create unset value for current type!"); assert(Value && "Cannot create unset value for current type!");
} }
// This constructor accepts the same arguments as the above, but also
// a source location.
RecordVal::RecordVal(Init *N, SMLoc Loc, RecTy *T, bool P)
: Name(N), Loc(Loc), TyAndPrefix(T, P) {
setValue(UnsetInit::get());
assert(Value && "Cannot create unset value for current type!");
}
StringRef RecordVal::getName() const { StringRef RecordVal::getName() const {
return cast<StringInit>(getNameInit())->getValue(); return cast<StringInit>(getNameInit())->getValue();
} }
@ -2046,8 +2054,8 @@ bool RecordVal::setValue(Init *V) {
if (!isa<BitsInit>(Value)) { if (!isa<BitsInit>(Value)) {
SmallVector<Init *, 64> Bits; SmallVector<Init *, 64> Bits;
Bits.reserve(BTy->getNumBits()); Bits.reserve(BTy->getNumBits());
for (unsigned i = 0, e = BTy->getNumBits(); i < e; ++i) for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I)
Bits.push_back(Value->getBit(i)); Bits.push_back(Value->getBit(I));
Value = BitsInit::get(Bits); Value = BitsInit::get(Bits);
} }
} }
@ -2058,6 +2066,32 @@ bool RecordVal::setValue(Init *V) {
return false; return false;
} }
// This version of setValue takes an source location and resets the
// location in the RecordVal.
bool RecordVal::setValue(Init *V, SMLoc NewLoc) {
Loc = NewLoc;
if (V) {
Value = V->getCastTo(getType());
if (Value) {
assert(!isa<TypedInit>(Value) ||
cast<TypedInit>(Value)->getType()->typeIsA(getType()));
if (BitsRecTy *BTy = dyn_cast<BitsRecTy>(getType())) {
if (!isa<BitsInit>(Value)) {
SmallVector<Init *, 64> Bits;
Bits.reserve(BTy->getNumBits());
for (unsigned I = 0, E = BTy->getNumBits(); I < E; ++I)
Bits.push_back(Value->getBit(I));
Value = BitsInit::get(Bits);
}
}
}
return Value == nullptr;
}
Value = nullptr;
return false;
}
#include "llvm/TableGen/Record.h"
#if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP)
LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; } LLVM_DUMP_METHOD void RecordVal::dump() const { errs() << *this; }
#endif #endif
@ -2217,8 +2251,8 @@ StringRef Record::getValueAsString(StringRef FieldName) const {
if (CodeInit *CI = dyn_cast<CodeInit>(R->getValue())) if (CodeInit *CI = dyn_cast<CodeInit>(R->getValue()))
return CI->getValue(); return CI->getValue();
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName +
FieldName + "' does not have a string initializer!"); "' exists but does not have a string value");
} }
BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const { BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const {
@ -2229,8 +2263,8 @@ BitsInit *Record::getValueAsBitsInit(StringRef FieldName) const {
if (BitsInit *BI = dyn_cast<BitsInit>(R->getValue())) if (BitsInit *BI = dyn_cast<BitsInit>(R->getValue()))
return BI; return BI;
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName +
FieldName + "' does not have a BitsInit initializer!"); "' exists but does not have a bits value");
} }
ListInit *Record::getValueAsListInit(StringRef FieldName) const { ListInit *Record::getValueAsListInit(StringRef FieldName) const {
@ -2241,8 +2275,8 @@ ListInit *Record::getValueAsListInit(StringRef FieldName) const {
if (ListInit *LI = dyn_cast<ListInit>(R->getValue())) if (ListInit *LI = dyn_cast<ListInit>(R->getValue()))
return LI; return LI;
PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + PrintFatalError(getLoc(), "Record `" + getName() + "', field `" + FieldName +
FieldName + "' does not have a list initializer!"); "' exists but does not have a list value");
} }
std::vector<Record*> std::vector<Record*>
@ -2269,7 +2303,7 @@ int64_t Record::getValueAsInt(StringRef FieldName) const {
return II->getValue(); return II->getValue();
PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" + PrintFatalError(getLoc(), Twine("Record `") + getName() + "', field `" +
FieldName + FieldName +
"' does not have an int initializer: " + "' exists but does not have an int value: " +
R->getValue()->getAsString()); R->getValue()->getAsString());
} }
@ -2283,7 +2317,7 @@ Record::getValueAsListOfInts(StringRef FieldName) const {
else else
PrintFatalError(getLoc(), PrintFatalError(getLoc(),
Twine("Record `") + getName() + "', field `" + FieldName + Twine("Record `") + getName() + "', field `" + FieldName +
"' does not have a list of ints initializer: " + "' exists but does not have a list of ints value: " +
I->getAsString()); I->getAsString());
} }
return Ints; return Ints;
@ -2301,7 +2335,7 @@ Record::getValueAsListOfStrings(StringRef FieldName) const {
else else
PrintFatalError(getLoc(), PrintFatalError(getLoc(),
Twine("Record `") + getName() + "', field `" + FieldName + Twine("Record `") + getName() + "', field `" + FieldName +
"' does not have a list of strings initializer: " + "' exists but does not have a list of strings value: " +
I->getAsString()); I->getAsString());
} }
return Strings; return Strings;

View File

@ -209,16 +209,16 @@ bool TGParser::SetValue(Record *CurRec, SMLoc Loc, Init *ValName,
V = BitsInit::get(NewBits); V = BitsInit::get(NewBits);
} }
if (RV->setValue(V)) { if (RV->setValue(V, Loc)) {
std::string InitType; std::string InitType;
if (BitsInit *BI = dyn_cast<BitsInit>(V)) if (BitsInit *BI = dyn_cast<BitsInit>(V))
InitType = (Twine("' of type bit initializer with length ") + InitType = (Twine("' of type bit initializer with length ") +
Twine(BI->getNumBits())).str(); Twine(BI->getNumBits())).str();
else if (TypedInit *TI = dyn_cast<TypedInit>(V)) else if (TypedInit *TI = dyn_cast<TypedInit>(V))
InitType = (Twine("' of type '") + TI->getType()->getAsString()).str(); InitType = (Twine("' of type '") + TI->getType()->getAsString()).str();
return Error(Loc, "Value '" + ValName->getAsUnquotedString() + return Error(Loc, "Field '" + ValName->getAsUnquotedString() +
"' of type '" + RV->getType()->getAsString() + "' of type '" + RV->getType()->getAsString() +
"' is incompatible with initializer '" + "' is incompatible with value '" +
V->getAsString() + InitType + "'"); V->getAsString() + InitType + "'");
} }
return false; return false;
@ -1294,7 +1294,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
return nullptr; return nullptr;
} }
case tgtok::XForEach: { // Value ::= !foreach '(' Id ',' Value ',' Value ')' case tgtok::XForEach: {
// Value ::= !foreach '(' Id ',' Value ',' Value ')'
SMLoc OpLoc = Lex.getLoc(); SMLoc OpLoc = Lex.getLoc();
Lex.Lex(); // eat the operation Lex.Lex(); // eat the operation
if (Lex.getCode() != tgtok::l_paren) { if (Lex.getCode() != tgtok::l_paren) {
@ -1367,8 +1368,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
return nullptr; return nullptr;
} }
// We need to create a temporary record to provide a scope for the iteration // We need to create a temporary record to provide a scope for the
// variable while parsing top-level foreach's. // iteration variable.
std::unique_ptr<Record> ParseRecTmp; std::unique_ptr<Record> ParseRecTmp;
Record *ParseRec = CurRec; Record *ParseRec = CurRec;
if (!ParseRec) { if (!ParseRec) {
@ -1544,7 +1545,7 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
return ParseOperationCond(CurRec, ItemType); return ParseOperationCond(CurRec, ItemType);
case tgtok::XFoldl: { case tgtok::XFoldl: {
// Value ::= !foldl '(' Id ',' Id ',' Value ',' Value ',' Value ')' // Value ::= !foldl '(' Value ',' Value ',' Id ',' Id ',' Expr ')'
Lex.Lex(); // eat the operation Lex.Lex(); // eat the operation
if (!consume(tgtok::l_paren)) { if (!consume(tgtok::l_paren)) {
TokError("expected '(' after !foldl"); TokError("expected '(' after !foldl");
@ -1627,8 +1628,8 @@ Init *TGParser::ParseOperation(Record *CurRec, RecTy *ItemType) {
} }
Lex.Lex(); // eat the ',' Lex.Lex(); // eat the ','
// We need to create a temporary record to provide a scope for the iteration // We need to create a temporary record to provide a scope for the
// variable while parsing top-level foreach's. // two variables.
std::unique_ptr<Record> ParseRecTmp; std::unique_ptr<Record> ParseRecTmp;
Record *ParseRec = CurRec; Record *ParseRec = CurRec;
if (!ParseRec) { if (!ParseRec) {
@ -2419,7 +2420,7 @@ Init *TGParser::ParseDeclaration(Record *CurRec,
} }
// Add the value. // Add the value.
if (AddValue(CurRec, IdLoc, RecordVal(DeclName, Type, HasField))) if (AddValue(CurRec, IdLoc, RecordVal(DeclName, IdLoc, Type, HasField)))
return nullptr; return nullptr;
// If a value is present, parse it. // If a value is present, parse it.

View File

@ -5,7 +5,7 @@ class A<int dummy> {}
class B<int dummy> : A<dummy> {} class B<int dummy> : A<dummy> {}
class C<int dummy> : A<dummy> {} class C<int dummy> : A<dummy> {}
// CHECK: Value 'x' of type 'C' is incompatible with initializer '{{.*}}' of type 'A' // CHECK: Field 'x' of type 'C' is incompatible with value '{{.*}}' of type 'A'
class X<int cc, B b, C c> { class X<int cc, B b, C c> {
C x = !cond(cc: b, 1 : c); C x = !cond(cc: b, 1 : c);
} }

View File

@ -146,7 +146,7 @@ class DEntry<string str, int val1> {
} }
def DFoo : DEntry<"foo", 1>; def DFoo : DEntry<"foo", 1>;
// ERROR1: [[@LINE+1]]:1: error: Record 'DBar' in table 'DTable' is missing field 'Val1' // ERROR1: [[@LINE+1]]:1: error: Record 'DBar' for table 'DTable' is missing field 'Val1'
def DBar : DEntry<"bar", ?>; def DBar : DEntry<"bar", ?>;
def DTable : GenericTable { def DTable : GenericTable {

View File

@ -38,7 +38,7 @@ def test {
// assignment whose LHS demands an instance of Base, so we expect a // assignment whose LHS demands an instance of Base, so we expect a
// static (parse-time) type-checking error. // static (parse-time) type-checking error.
// ERROR1: error: Value 'noCast' of type 'Base' is incompatible with initializer '!getop(orig)' of type '{}' // ERROR1: error: Field 'noCast' of type 'Base' is incompatible with value '!getop(orig)' of type '{}'
Base noCast = !getop(orig); Base noCast = !getop(orig);
#endif #endif

View File

@ -5,7 +5,7 @@ class A<int dummy> {}
class B<int dummy> : A<dummy> {} class B<int dummy> : A<dummy> {}
class C<int dummy> : A<dummy> {} class C<int dummy> : A<dummy> {}
// CHECK: Value 'x' of type 'C' is incompatible with initializer '{{.*}}' of type 'A' // CHECK: Field 'x' of type 'C' is incompatible with value '{{.*}}' of type 'A'
class X<int cc, B b, C c> { class X<int cc, B b, C c> {
C x = !if(cc, b, c); C x = !if(cc, b, c);
} }

View File

@ -9,5 +9,5 @@ class A<A x> {
// This kind of self-reference is discourage, but if you *really* want it, you // This kind of self-reference is discourage, but if you *really* want it, you
// can force it with !cast. // can force it with !cast.
// //
// CHECK: Value 'A:x' of type 'A' is incompatible with initializer // CHECK: Field 'A:x' of type 'A' is incompatible with value
def A0 : A<A0>; def A0 : A<A0>;

View File

@ -12,6 +12,8 @@
// //
//===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===//
#include "CodeGenIntrinsics.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/DenseMap.h"
#include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringExtras.h"
#include "llvm/Support/Format.h" #include "llvm/Support/Format.h"
@ -19,7 +21,6 @@
#include "llvm/Support/SourceMgr.h" #include "llvm/Support/SourceMgr.h"
#include "llvm/TableGen/Error.h" #include "llvm/TableGen/Error.h"
#include "llvm/TableGen/Record.h" #include "llvm/TableGen/Record.h"
#include "CodeGenIntrinsics.h"
#include <algorithm> #include <algorithm>
#include <set> #include <set>
#include <string> #include <string>
@ -62,12 +63,14 @@ struct GenericField {
struct SearchIndex { struct SearchIndex {
std::string Name; std::string Name;
SMLoc Loc; // Source location of PrimaryKey or Key field definition.
SmallVector<GenericField, 1> Fields; SmallVector<GenericField, 1> Fields;
bool EarlyOut = false; bool EarlyOut = false;
}; };
struct GenericTable { struct GenericTable {
std::string Name; std::string Name;
ArrayRef<SMLoc> Locs; // Source locations from the Record instance.
std::string PreprocessorGuard; std::string PreprocessorGuard;
std::string CppTypeName; std::string CppTypeName;
SmallVector<GenericField, 2> Fields; SmallVector<GenericField, 2> Fields;
@ -106,7 +109,8 @@ private:
TypeInArgument, TypeInArgument,
}; };
std::string primaryRepresentation(const GenericField &Field, Init *I) { std::string primaryRepresentation(SMLoc Loc, const GenericField &Field,
Init *I) {
if (StringInit *SI = dyn_cast<StringInit>(I)) if (StringInit *SI = dyn_cast<StringInit>(I))
return SI->getAsString(); return SI->getAsString();
else if (BitsInit *BI = dyn_cast<BitsInit>(I)) else if (BitsInit *BI = dyn_cast<BitsInit>(I))
@ -122,11 +126,12 @@ private:
else if (Field.Enum) { else if (Field.Enum) {
auto *Entry = Field.Enum->EntryMap[cast<DefInit>(I)->getDef()]; auto *Entry = Field.Enum->EntryMap[cast<DefInit>(I)->getDef()];
if (!Entry) if (!Entry)
PrintFatalError(Twine("Entry for field '") + Field.Name + "' is null"); PrintFatalError(Loc,
Twine("Entry for field '") + Field.Name + "' is null");
return std::string(Entry->first); return std::string(Entry->first);
} }
PrintFatalError(Twine("invalid field type for field '") + Field.Name + PrintFatalError(Loc, Twine("invalid field type for field '") + Field.Name +
"', expected: string, bits, bit or code"); "'; expected: bit, bits, string, or code");
} }
bool isIntrinsic(Init *I) { bool isIntrinsic(Init *I) {
@ -149,7 +154,9 @@ private:
return isa<BitsInit>(I) || isa<CodeInit>(I) || isIntrinsic(I); return isa<BitsInit>(I) || isa<CodeInit>(I) || isIntrinsic(I);
} }
std::string searchableFieldType(const GenericField &Field, TypeContext Ctx) { std::string searchableFieldType(const GenericTable &Table,
const SearchIndex &Index,
const GenericField &Field, TypeContext Ctx) {
if (isa<StringRecTy>(Field.RecType)) { if (isa<StringRecTy>(Field.RecType)) {
if (Ctx == TypeInStaticStruct) if (Ctx == TypeInStaticStruct)
return "const char *"; return "const char *";
@ -166,12 +173,16 @@ private:
return "uint32_t"; return "uint32_t";
if (NumBits <= 64) if (NumBits <= 64)
return "uint64_t"; return "uint64_t";
PrintFatalError(Twine("bitfield '") + Field.Name + PrintFatalError(Index.Loc, Twine("In table '") + Table.Name +
"' too large to search"); "' lookup method '" + Index.Name +
"', key field '" + Field.Name +
"' of type bits is too large");
} else if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction) } else if (Field.Enum || Field.IsIntrinsic || Field.IsInstruction)
return "unsigned"; return "unsigned";
PrintFatalError(Twine("Field '") + Field.Name + "' has unknown type '" + PrintFatalError(Index.Loc,
Field.RecType->getAsString() + "' to search by"); Twine("In table '") + Table.Name + "' lookup method '" +
Index.Name + "', key field '" + Field.Name +
"' has invalid type: " + Field.RecType->getAsString());
} }
void emitGenericTable(const GenericTable &Table, raw_ostream &OS); void emitGenericTable(const GenericTable &Table, raw_ostream &OS);
@ -184,7 +195,7 @@ private:
bool parseFieldType(GenericField &Field, Init *II); bool parseFieldType(GenericField &Field, Init *II);
std::unique_ptr<SearchIndex> std::unique_ptr<SearchIndex>
parseSearchIndex(GenericTable &Table, StringRef Name, parseSearchIndex(GenericTable &Table, const RecordVal *RecVal, StringRef Name,
const std::vector<StringRef> &Key, bool EarlyOut); const std::vector<StringRef> &Key, bool EarlyOut);
void collectEnumEntries(GenericEnum &Enum, StringRef NameField, void collectEnumEntries(GenericEnum &Enum, StringRef NameField,
StringRef ValueField, StringRef ValueField,
@ -259,8 +270,8 @@ bool SearchableTableEmitter::compareBy(Record *LHS, Record *RHS,
if (LHSv > RHSv) if (LHSv > RHSv)
return false; return false;
} else { } else {
std::string LHSs = primaryRepresentation(Field, LHSI); std::string LHSs = primaryRepresentation(Index.Loc, Field, LHSI);
std::string RHSs = primaryRepresentation(Field, RHSI); std::string RHSs = primaryRepresentation(Index.Loc, Field, RHSI);
if (isa<StringRecTy>(Field.RecType)) { if (isa<StringRecTy>(Field.RecType)) {
LHSs = StringRef(LHSs).upper(); LHSs = StringRef(LHSs).upper();
@ -315,7 +326,8 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
} else { } else {
OS << " struct IndexType {\n"; OS << " struct IndexType {\n";
for (const auto &Field : Index.Fields) { for (const auto &Field : Index.Fields) {
OS << " " << searchableFieldType(Field, TypeInStaticStruct) << " " OS << " "
<< searchableFieldType(Table, Index, Field, TypeInStaticStruct) << " "
<< Field.Name << ";\n"; << Field.Name << ";\n";
} }
OS << " unsigned _index;\n"; OS << " unsigned _index;\n";
@ -345,8 +357,8 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
OS << ", "; OS << ", ";
NeedComma = true; NeedComma = true;
std::string Repr = std::string Repr = primaryRepresentation(
primaryRepresentation(Field, Entry.first->getValueInit(Field.Name)); Index.Loc, Field, Entry.first->getValueInit(Field.Name));
if (isa<StringRecTy>(Field.RecType)) if (isa<StringRecTy>(Field.RecType))
Repr = StringRef(Repr).upper(); Repr = StringRef(Repr).upper();
OS << Repr; OS << Repr;
@ -389,10 +401,10 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
if (Index.EarlyOut) { if (Index.EarlyOut) {
const GenericField &Field = Index.Fields[0]; const GenericField &Field = Index.Fields[0];
std::string FirstRepr = std::string FirstRepr = primaryRepresentation(
primaryRepresentation(Field, IndexRows[0]->getValueInit(Field.Name)); Index.Loc, Field, IndexRows[0]->getValueInit(Field.Name));
std::string LastRepr = primaryRepresentation( std::string LastRepr = primaryRepresentation(
Field, IndexRows.back()->getValueInit(Field.Name)); Index.Loc, Field, IndexRows.back()->getValueInit(Field.Name));
OS << " if ((" << Field.Name << " < " << FirstRepr << ") ||\n"; OS << " if ((" << Field.Name << " < " << FirstRepr << ") ||\n";
OS << " (" << Field.Name << " > " << LastRepr << "))\n"; OS << " (" << Field.Name << " > " << LastRepr << "))\n";
OS << " return nullptr;\n\n"; OS << " return nullptr;\n\n";
@ -400,8 +412,8 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
OS << " struct KeyType {\n"; OS << " struct KeyType {\n";
for (const auto &Field : Index.Fields) { for (const auto &Field : Index.Fields) {
OS << " " << searchableFieldType(Field, TypeInTempStruct) << " " OS << " " << searchableFieldType(Table, Index, Field, TypeInTempStruct)
<< Field.Name << ";\n"; << " " << Field.Name << ";\n";
} }
OS << " };\n"; OS << " };\n";
OS << " KeyType Key = { "; OS << " KeyType Key = { ";
@ -415,9 +427,11 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
if (isa<StringRecTy>(Field.RecType)) { if (isa<StringRecTy>(Field.RecType)) {
OS << ".upper()"; OS << ".upper()";
if (IsPrimary) if (IsPrimary)
PrintFatalError(Twine("Use a secondary index for case-insensitive " PrintFatalError(Index.Loc,
"comparison of field '") + Twine("In table '") + Table.Name +
Field.Name + "' in table '" + Table.Name + "'"); "', use a secondary lookup method for "
"case-insensitive comparison of field '" +
Field.Name + "'");
} }
} }
OS << " };\n"; OS << " };\n";
@ -477,7 +491,8 @@ void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table,
OS << ", "; OS << ", ";
NeedComma = true; NeedComma = true;
OS << searchableFieldType(Field, TypeInArgument) << " " << Field.Name; OS << searchableFieldType(Table, Index, Field, TypeInArgument) << " "
<< Field.Name;
} }
OS << ")"; OS << ")";
} }
@ -512,7 +527,8 @@ void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
OS << ", "; OS << ", ";
NeedComma = true; NeedComma = true;
OS << primaryRepresentation(Field, Entry->getValueInit(Field.Name)); OS << primaryRepresentation(Table.Locs[0], Field,
Entry->getValueInit(Field.Name));
} }
OS << " }, // " << i << "\n"; OS << " }, // " << i << "\n";
@ -542,27 +558,30 @@ bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *II) {
return false; return false;
} }
std::unique_ptr<SearchIndex> std::unique_ptr<SearchIndex> SearchableTableEmitter::parseSearchIndex(
SearchableTableEmitter::parseSearchIndex(GenericTable &Table, StringRef Name, GenericTable &Table, const RecordVal *KeyRecVal, StringRef Name,
const std::vector<StringRef> &Key, const std::vector<StringRef> &Key, bool EarlyOut) {
bool EarlyOut) {
auto Index = std::make_unique<SearchIndex>(); auto Index = std::make_unique<SearchIndex>();
Index->Name = std::string(Name); Index->Name = std::string(Name);
Index->Loc = KeyRecVal->getLoc();
Index->EarlyOut = EarlyOut; Index->EarlyOut = EarlyOut;
for (const auto &FieldName : Key) { for (const auto &FieldName : Key) {
const GenericField *Field = Table.getFieldByName(FieldName); const GenericField *Field = Table.getFieldByName(FieldName);
if (!Field) if (!Field)
PrintFatalError(Twine("Search index '") + Name + PrintFatalError(
"' refers to non-existing field '" + FieldName + KeyRecVal,
"' in table '" + Table.Name + "'"); Twine("In table '") + Table.Name +
"', 'PrimaryKey' or 'Key' refers to nonexistent field '" +
FieldName + "'");
Index->Fields.push_back(*Field); Index->Fields.push_back(*Field);
} }
if (EarlyOut && isa<StringRecTy>(Index->Fields[0].RecType)) { if (EarlyOut && isa<StringRecTy>(Index->Fields[0].RecType)) {
PrintFatalError( PrintFatalError(
"Early-out is not supported for string types (in search index '" + KeyRecVal, Twine("In lookup method '") + Name + "', early-out is not " +
Twine(Name) + "'"); "supported for a first key field of type string");
} }
return Index; return Index;
@ -601,31 +620,33 @@ void SearchableTableEmitter::collectEnumEntries(
void SearchableTableEmitter::collectTableEntries( void SearchableTableEmitter::collectTableEntries(
GenericTable &Table, const std::vector<Record *> &Items) { GenericTable &Table, const std::vector<Record *> &Items) {
if (Items.empty()) if (Items.empty())
PrintWarning(Twine("Table '") + Table.Name + "' has no items"); PrintFatalError(Table.Locs,
Twine("Table '") + Table.Name + "' has no entries");
for (auto EntryRec : Items) { for (auto EntryRec : Items) {
for (auto &Field : Table.Fields) { for (auto &Field : Table.Fields) {
auto TI = dyn_cast<TypedInit>(EntryRec->getValueInit(Field.Name)); auto TI = dyn_cast<TypedInit>(EntryRec->getValueInit(Field.Name));
if (!TI || !TI->isComplete()) { if (!TI || !TI->isComplete()) {
PrintFatalError(EntryRec->getLoc(), PrintFatalError(EntryRec, Twine("Record '") + EntryRec->getName() +
Twine("Record '") + EntryRec->getName() + "' for table '" + Table.Name +
"' in table '" + Table.Name + "' is missing field '" + Field.Name +
"' is missing field '" + Field.Name + "'"); "'");
} }
if (!Field.RecType) { if (!Field.RecType) {
Field.RecType = TI->getType(); Field.RecType = TI->getType();
} else { } else {
RecTy *Ty = resolveTypes(Field.RecType, TI->getType()); RecTy *Ty = resolveTypes(Field.RecType, TI->getType());
if (!Ty) if (!Ty)
PrintFatalError(Twine("Field '") + Field.Name + "' of table '" + PrintFatalError(EntryRec->getValue(Field.Name),
Table.Name + "' has incompatible type: " + Twine("Field '") + Field.Name + "' of table '" +
Field.RecType->getAsString() + " vs. " + Table.Name + "' entry has incompatible type: " +
TI->getType()->getAsString()); TI->getType()->getAsString() + " vs. " +
Field.RecType->getAsString());
Field.RecType = Ty; Field.RecType = Ty;
} }
} }
Table.Entries.push_back(EntryRec); Table.Entries.push_back(EntryRec); // Add record to table's record list.
} }
Record *IntrinsicClass = Records.getClass("Intrinsic"); Record *IntrinsicClass = Records.getClass("Intrinsic");
@ -666,8 +687,9 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
StringRef FilterClass = EnumRec->getValueAsString("FilterClass"); StringRef FilterClass = EnumRec->getValueAsString("FilterClass");
Enum->Class = Records.getClass(FilterClass); Enum->Class = Records.getClass(FilterClass);
if (!Enum->Class) if (!Enum->Class)
PrintFatalError(EnumRec->getLoc(), Twine("Enum FilterClass '") + PrintFatalError(EnumRec->getValue("FilterClass"),
FilterClass + "' does not exist"); Twine("Enum FilterClass '") + FilterClass +
"' does not exist");
collectEnumEntries(*Enum, NameField, ValueField, collectEnumEntries(*Enum, NameField, ValueField,
Records.getAllDerivedDefinitions(FilterClass)); Records.getAllDerivedDefinitions(FilterClass));
@ -678,29 +700,36 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
for (auto TableRec : Records.getAllDerivedDefinitions("GenericTable")) { for (auto TableRec : Records.getAllDerivedDefinitions("GenericTable")) {
auto Table = std::make_unique<GenericTable>(); auto Table = std::make_unique<GenericTable>();
Table->Name = std::string(TableRec->getName()); Table->Name = std::string(TableRec->getName());
Table->Locs = TableRec->getLoc();
Table->PreprocessorGuard = std::string(TableRec->getName()); Table->PreprocessorGuard = std::string(TableRec->getName());
Table->CppTypeName = std::string(TableRec->getValueAsString("CppTypeName")); Table->CppTypeName = std::string(TableRec->getValueAsString("CppTypeName"));
std::vector<StringRef> Fields = TableRec->getValueAsListOfStrings("Fields"); std::vector<StringRef> Fields = TableRec->getValueAsListOfStrings("Fields");
for (const auto &FieldName : Fields) { for (const auto &FieldName : Fields) {
Table->Fields.emplace_back(FieldName); Table->Fields.emplace_back(FieldName); // Construct a GenericField.
if (auto TypeOfVal = TableRec->getValue(("TypeOf_" + FieldName).str())) { if (auto TypeOfVal = TableRec->getValue(("TypeOf_" + FieldName).str())) {
if (!parseFieldType(Table->Fields.back(), TypeOfVal->getValue())) { if (!parseFieldType(Table->Fields.back(), TypeOfVal->getValue())) {
PrintFatalError(TableRec->getLoc(), PrintFatalError(TypeOfVal,
Twine("Table '") + Table->Name + Twine("Table '") + Table->Name +
"' has bad 'TypeOf_" + FieldName + "' has invalid 'TypeOf_" + FieldName +
"': " + TypeOfVal->getValue()->getAsString()); "': " + TypeOfVal->getValue()->getAsString());
} }
} }
} }
collectTableEntries(*Table, Records.getAllDerivedDefinitions( StringRef FilterClass = TableRec->getValueAsString("FilterClass");
TableRec->getValueAsString("FilterClass"))); if (!Records.getClass(FilterClass))
PrintFatalError(TableRec->getValue("FilterClass"),
Twine("Table FilterClass '") +
FilterClass + "' does not exist");
collectTableEntries(*Table, Records.getAllDerivedDefinitions(FilterClass));
if (!TableRec->isValueUnset("PrimaryKey")) { if (!TableRec->isValueUnset("PrimaryKey")) {
Table->PrimaryKey = Table->PrimaryKey =
parseSearchIndex(*Table, TableRec->getValueAsString("PrimaryKeyName"), parseSearchIndex(*Table, TableRec->getValue("PrimaryKey"),
TableRec->getValueAsString("PrimaryKeyName"),
TableRec->getValueAsListOfStrings("PrimaryKey"), TableRec->getValueAsListOfStrings("PrimaryKey"),
TableRec->getValueAsBit("PrimaryKeyEarlyOut")); TableRec->getValueAsBit("PrimaryKeyEarlyOut"));
@ -718,15 +747,16 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
Record *TableRec = IndexRec->getValueAsDef("Table"); Record *TableRec = IndexRec->getValueAsDef("Table");
auto It = TableMap.find(TableRec); auto It = TableMap.find(TableRec);
if (It == TableMap.end()) if (It == TableMap.end())
PrintFatalError(IndexRec->getLoc(), PrintFatalError(IndexRec->getValue("Table"),
Twine("SearchIndex '") + IndexRec->getName() + Twine("SearchIndex '") + IndexRec->getName() +
"' refers to non-existing table '" + "' refers to nonexistent table '" +
TableRec->getName()); TableRec->getName());
GenericTable &Table = *It->second; GenericTable &Table = *It->second;
Table.Indices.push_back(parseSearchIndex( Table.Indices.push_back(
Table, IndexRec->getName(), IndexRec->getValueAsListOfStrings("Key"), parseSearchIndex(Table, IndexRec->getValue("Key"), IndexRec->getName(),
IndexRec->getValueAsBit("EarlyOut"))); IndexRec->getValueAsListOfStrings("Key"),
IndexRec->getValueAsBit("EarlyOut")));
} }
// Translate legacy tables. // Translate legacy tables.
@ -757,6 +787,7 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
auto Table = std::make_unique<GenericTable>(); auto Table = std::make_unique<GenericTable>();
Table->Name = (Twine(Class->getName()) + "sList").str(); Table->Name = (Twine(Class->getName()) + "sList").str();
Table->Locs = Class->getLoc();
Table->PreprocessorGuard = Class->getName().upper(); Table->PreprocessorGuard = Class->getName().upper();
Table->CppTypeName = std::string(Class->getName()); Table->CppTypeName = std::string(Class->getName());
@ -779,7 +810,8 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
Class->getValueAsListOfStrings("SearchableFields")) { Class->getValueAsListOfStrings("SearchableFields")) {
std::string Name = std::string Name =
(Twine("lookup") + Table->CppTypeName + "By" + Field).str(); (Twine("lookup") + Table->CppTypeName + "By" + Field).str();
Table->Indices.push_back(parseSearchIndex(*Table, Name, {Field}, false)); Table->Indices.push_back(parseSearchIndex(*Table, Class->getValue(Field),
Name, {Field}, false));
} }
Tables.emplace_back(std::move(Table)); Tables.emplace_back(std::move(Table));