Skip to content

Commit f449305

Browse files
committed
[RISCV]Add support for resolving encoding conflicts among vendor specific CSRs
This patch adds the framework for resolving encoding conflicts among CSRs. Specifically, this patch adds a support for emitting a new lookup function for the primary key which return a pair of iterators pointing to first and last value hence giving a range of values which satisfies the query. While printing the CSR name during objdump, iterate over the range and print the name of only that CSR which satisifes the feature requirement of subtarget. Below is the signature of the new function that will be emitted for primary key: ``` llvm::iterator_range<const SysReg *> lookupSysRegByEncoding(uint16_t Encoding) { struct KeyType { uint16_t Encoding; }; KeyType Key = {Encoding}; struct Comp { bool operator()(const SysReg &LHS, const KeyType &RHS) const { if (LHS.Encoding < RHS.Encoding) return true; if (LHS.Encoding > RHS.Encoding) return false; return false; } bool operator()(const KeyType &LHS, const SysReg &RHS) const { if (LHS.Encoding < RHS.Encoding) return true; if (LHS.Encoding > RHS.Encoding) return false; return false; } }; auto Table = ArrayRef(SysRegsList); auto It = std::equal_range(Table.begin(), Table.end(), Key, Comp()); return llvm::make_range(It.first, It.second); } ``` NOTE: Emitting a different signature for returning a range of results is only supported by primary key.
1 parent e258bb3 commit f449305

File tree

4 files changed

+232
-45
lines changed

4 files changed

+232
-45
lines changed

llvm/docs/TableGen/BackEnds.rst

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,12 @@ This class provides six fields.
717717

718718
* ``bit PrimaryKeyEarlyOut``. See the third example below.
719719

720+
* ``bit PrimaryKeyReturnRange``. when set to 1, modifies the lookup function’s
721+
definition to return a range of results rather than a single pointer to the
722+
object. This feature proves useful when multiple objects meet the criteria
723+
specified by the lookup function. Currently, it is supported only for primary
724+
lookup functions. Refer to the second example below for further details.
725+
720726
TableGen attempts to deduce the type of each of the table fields so that it
721727
can format the C++ initializers in the emitted table. It can deduce ``bit``,
722728
``bits<n>``, ``string``, ``Intrinsic``, and ``Instruction``. These can be
@@ -883,6 +889,84 @@ Here is the generated C++ code.
883889
return &*Idx;
884890
}
885891
892+
In the above example, lets add one more record with encoding same as that of
893+
record ``CEntry<"Pear", CBaz, 15>``.
894+
895+
.. code-block:: text
896+
897+
def CFoobar : CEnum;
898+
def : CEntry<"Banana", CFoobar, 15>;
899+
900+
Below is the new generated ``CTable``
901+
902+
.. code-block:: text
903+
904+
#ifdef GET_Table_IMPL
905+
constexpr CEntry Table[] = {
906+
{ "Apple", CFoo, 0xA }, // 0
907+
{ "Apple", CBar, 0xD }, // 1
908+
{ "Banana", CFoobar, 0xF }, // 2
909+
{ "Pear", CBaz, 0xF }, // 3
910+
};
911+
912+
Since ``Banana`` lexicographically appears first, therefore in the ``CEntry``
913+
table, record with name ``Banana`` will come before the record with name
914+
``Pear``. Because of this, the ``lookupCEntryByEncoding`` function will always
915+
return a pointer to the record with name ``Banana`` even though in some cases
916+
the correct result can be the record with name ``Pear``. Such kind of scenario
917+
makes the exisitng lookup function insufficient because they always return a
918+
pointer to a single entry from the table, but instead it should return a range
919+
of results because multiple entries match the criteria sought by the lookup
920+
function. In this case, the definition of the lookup function needs to be
921+
modified to return a range of results which can be done by setting
922+
``PrimaryKeyReturnRange``.
923+
924+
.. code-block:: text
925+
926+
def CTable : GenericTable {
927+
let FilterClass = "CEntry";
928+
let Fields = ["Name", "Kind", "Encoding"];
929+
string TypeOf_Kind = "CEnum";
930+
let PrimaryKey = ["Encoding"];
931+
let PrimaryKeyName = "lookupCEntryByEncoding";
932+
let PrimaryKeyReturnRange = true;
933+
}
934+
935+
Here is the modified lookup function.
936+
937+
.. code-block:: text
938+
939+
llvm::iterator_range<const CEntry *> lookupCEntryByEncoding(uint16_t Encoding) {
940+
struct KeyType {
941+
uint16_t Encoding;
942+
};
943+
KeyType Key = {Encoding};
944+
struct Comp {
945+
bool operator()(const CEntry &LHS, const KeyType &RHS) const {
946+
if (LHS.Encoding < RHS.Encoding)
947+
return true;
948+
if (LHS.Encoding > RHS.Encoding)
949+
return false;
950+
return false;
951+
}
952+
bool operator()(const KeyType &LHS, const CEntry &RHS) const {
953+
if (LHS.Encoding < RHS.Encoding)
954+
return true;
955+
if (LHS.Encoding > RHS.Encoding)
956+
return false;
957+
return false;
958+
}
959+
};
960+
auto Table = ArrayRef(Table);
961+
auto It = std::equal_range(Table.begin(), Table.end(), Key, Comp());
962+
return llvm::make_range(It.first, It.second);
963+
}
964+
965+
The new lookup function will return an iterator range with first pointer to the
966+
first result and the last pointer to the last matching result from the table.
967+
However, please note that the support for emitting modified definition exists
968+
for ``PrimaryKeyName`` only.
969+
886970
The ``PrimaryKeyEarlyOut`` field, when set to 1, modifies the lookup
887971
function so that it tests the first field of the primary key to determine
888972
whether it is within the range of the collected records' primary keys. If
@@ -987,6 +1071,8 @@ function. This class provides three fields.
9871071

9881072
* ``bit EarlyOut``. See the third example in `Generic Tables`_.
9891073

1074+
* ``bit ReturnRange``. See the second example in `Generic Tables`_.
1075+
9901076
Here is an example of a secondary key added to the ``CTable`` above. The
9911077
generated function looks up entries based on the ``Name`` and ``Kind`` fields.
9921078

llvm/include/llvm/TableGen/SearchableTable.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,11 @@ class GenericTable {
114114

115115
// See SearchIndex.EarlyOut
116116
bit PrimaryKeyEarlyOut = false;
117+
118+
// If true, will generate a different function signature which will return an
119+
// iterator range of pointers giving the starting and end value of the range.
120+
// e.g. lookupSysRegByEncoding returns multiple CSRs for same encoding.
121+
bit PrimaryKeyReturnRange = false;
117122
}
118123

119124
// Define a record derived from this class to generate an additional search
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// RUN: llvm-tblgen -gen-searchable-tables -I %p/../../include %s | FileCheck %s
2+
3+
include "llvm/TableGen/SearchableTable.td"
4+
5+
class SysReg<string name, bits<12> op> {
6+
string Name = name;
7+
bits<12> Encoding = op;
8+
code FeaturesRequired = [{ {} }];
9+
}
10+
11+
def List1 : GenericTable {
12+
let FilterClass = "SysReg";
13+
let Fields = [
14+
"Name", "Encoding", "FeaturesRequired",
15+
];
16+
17+
let PrimaryKey = [ "Encoding" ];
18+
let PrimaryKeyName = "lookupSysRegByEncoding";
19+
let PrimaryKeyReturnRange = true;
20+
}
21+
22+
let FeaturesRequired = [{ {Feature1} }] in {
23+
def : SysReg<"csr1", 0x7C0>;
24+
}
25+
26+
let FeaturesRequired = [{ {Feature2} }] in {
27+
def : SysReg<"csr2", 0x7C0>;
28+
}
29+
30+
// CHECK: #ifdef GET_List1_DECL
31+
// CHECK-NEXT: llvm::iterator_range<const SysReg *> lookupSysRegByEncoding(uint16_t Encoding);
32+
// CHECK-NEXT: #endif
33+
34+
// CHECK: #ifdef GET_List1_IMPL
35+
// CHECK-NEXT: constexpr SysReg List1[] = {
36+
// CHECK-NEXT: { "csr1", 0x7C0, {Feature1} }, // 0
37+
// CHECK-NEXT: { "csr2", 0x7C0, {Feature2} }, // 1
38+
// CHECK-NEXT: };
39+
40+
// CHECK: llvm::iterator_range<const SysReg *> lookupSysRegByEncoding(uint16_t Encoding) {
41+
// CHECK-NEXT: struct KeyType {
42+
// CHECK-NEXT: uint16_t Encoding;
43+
// CHECK-NEXT: };
44+
// CHECK-NEXT: KeyType Key = {Encoding};
45+
// CHECK-NEXT: struct Comp {
46+
// CHECK-NEXT: bool operator()(const SysReg &LHS, const KeyType &RHS) const {
47+
// CHECK-NEXT: if (LHS.Encoding < RHS.Encoding)
48+
// CHECK-NEXT: return true;
49+
// CHECK-NEXT: if (LHS.Encoding > RHS.Encoding)
50+
// CHECK-NEXT: return false;
51+
// CHECK-NEXT: return false;
52+
// CHECK-NEXT: }
53+
// CHECK-NEXT: bool operator()(const KeyType &LHS, const SysReg &RHS) const {
54+
// CHECK-NEXT: if (LHS.Encoding < RHS.Encoding)
55+
// CHECK-NEXT: return true;
56+
// CHECK-NEXT: if (LHS.Encoding > RHS.Encoding)
57+
// CHECK-NEXT: return false;
58+
// CHECK-NEXT: return false;
59+
// CHECK-NEXT: }
60+
// CHECK-NEXT: };
61+
// CHECK-NEXT: auto Table = ArrayRef(List1);
62+
// CHECK-NEXT: auto It = std::equal_range(Table.begin(), Table.end(), Key, Comp());
63+
// CHECK-NEXT: return llvm::make_range(It.first, It.second);
64+
// CHECK-NEXT: }
65+
// CHECK-NEXT: #endif

llvm/utils/TableGen/SearchableTableEmitter.cpp

Lines changed: 76 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ struct SearchIndex {
6868
SMLoc Loc; // Source location of PrimaryKey or Key field definition.
6969
SmallVector<GenericField, 1> Fields;
7070
bool EarlyOut = false;
71+
bool ReturnRange = false;
7172
};
7273

7374
struct GenericTable {
@@ -198,7 +199,8 @@ class SearchableTableEmitter {
198199
bool parseFieldType(GenericField &Field, Init *II);
199200
std::unique_ptr<SearchIndex>
200201
parseSearchIndex(GenericTable &Table, const RecordVal *RecVal, StringRef Name,
201-
const std::vector<StringRef> &Key, bool EarlyOut);
202+
const std::vector<StringRef> &Key, bool EarlyOut,
203+
bool ReturnRange);
202204
void collectEnumEntries(GenericEnum &Enum, StringRef NameField,
203205
StringRef ValueField,
204206
const std::vector<Record *> &Items);
@@ -448,55 +450,81 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
448450
}
449451
OS << "};\n";
450452

451-
OS << " auto Table = ArrayRef(" << IndexName << ");\n";
452-
OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,\n";
453-
OS << " [](const " << IndexTypeName << " &LHS, const KeyType &RHS) {\n";
453+
OS << " struct Comp {\n";
454+
OS << " bool operator()(const " << IndexTypeName
455+
<< " &LHS, const KeyType &RHS) const {\n";
454456

455-
for (const auto &Field : Index.Fields) {
456-
if (isa<StringRecTy>(Field.RecType)) {
457-
OS << " int Cmp" << Field.Name << " = StringRef(LHS." << Field.Name
458-
<< ").compare(RHS." << Field.Name << ");\n";
459-
OS << " if (Cmp" << Field.Name << " < 0) return true;\n";
460-
OS << " if (Cmp" << Field.Name << " > 0) return false;\n";
461-
} else if (Field.Enum) {
462-
// Explicitly cast to unsigned, because the signedness of enums is
463-
// compiler-dependent.
464-
OS << " if ((unsigned)LHS." << Field.Name << " < (unsigned)RHS."
465-
<< Field.Name << ")\n";
466-
OS << " return true;\n";
467-
OS << " if ((unsigned)LHS." << Field.Name << " > (unsigned)RHS."
468-
<< Field.Name << ")\n";
469-
OS << " return false;\n";
470-
} else {
471-
OS << " if (LHS." << Field.Name << " < RHS." << Field.Name << ")\n";
472-
OS << " return true;\n";
473-
OS << " if (LHS." << Field.Name << " > RHS." << Field.Name << ")\n";
474-
OS << " return false;\n";
457+
auto emitComparator = [&]() {
458+
for (const auto &Field : Index.Fields) {
459+
if (isa<StringRecTy>(Field.RecType)) {
460+
OS << " int Cmp" << Field.Name << " = StringRef(LHS." << Field.Name
461+
<< ").compare(RHS." << Field.Name << ");\n";
462+
OS << " if (Cmp" << Field.Name << " < 0) return true;\n";
463+
OS << " if (Cmp" << Field.Name << " > 0) return false;\n";
464+
} else if (Field.Enum) {
465+
// Explicitly cast to unsigned, because the signedness of enums is
466+
// compiler-dependent.
467+
OS << " if ((unsigned)LHS." << Field.Name << " < (unsigned)RHS."
468+
<< Field.Name << ")\n";
469+
OS << " return true;\n";
470+
OS << " if ((unsigned)LHS." << Field.Name << " > (unsigned)RHS."
471+
<< Field.Name << ")\n";
472+
OS << " return false;\n";
473+
} else {
474+
OS << " if (LHS." << Field.Name << " < RHS." << Field.Name
475+
<< ")\n";
476+
OS << " return true;\n";
477+
OS << " if (LHS." << Field.Name << " > RHS." << Field.Name
478+
<< ")\n";
479+
OS << " return false;\n";
480+
}
475481
}
482+
OS << " return false;\n";
483+
OS << " }\n";
484+
};
485+
emitComparator();
486+
bool ShouldReturnRange = Index.ReturnRange;
487+
if (ShouldReturnRange) {
488+
OS << " bool operator()(const KeyType &LHS, const " << IndexTypeName
489+
<< " &RHS) const {\n";
490+
emitComparator();
476491
}
477492

478-
OS << " return false;\n";
479-
OS << " });\n\n";
480-
481-
OS << " if (Idx == Table.end()";
493+
OS << " };\n";
494+
OS << " auto Table = ArrayRef(" << IndexName << ");\n";
495+
if (ShouldReturnRange)
496+
OS << " auto It = std::equal_range(Table.begin(), Table.end(), Key, ";
497+
else
498+
OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key, ";
499+
OS << "Comp());\n";
482500

483-
for (const auto &Field : Index.Fields)
484-
OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name;
485-
OS << ")\n return nullptr;\n";
501+
if (!ShouldReturnRange) {
502+
OS << " if (Idx == Table.end()";
503+
for (const auto &Field : Index.Fields)
504+
OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name;
505+
}
486506

487-
if (IsPrimary)
507+
if (ShouldReturnRange)
508+
OS << " return llvm::make_range(It.first, It.second);\n";
509+
else if (IsPrimary) {
510+
OS << ")\n return nullptr;\n\n";
488511
OS << " return &*Idx;\n";
489-
else
512+
} else {
513+
OS << ")\n return nullptr;\n\n";
490514
OS << " return &" << Table.Name << "[Idx->_index];\n";
515+
}
491516

492517
OS << "}\n";
493518
}
494519

495520
void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table,
496521
const SearchIndex &Index,
497522
raw_ostream &OS) {
498-
OS << "const " << Table.CppTypeName << " *" << Index.Name << "(";
499-
523+
if (Index.ReturnRange)
524+
OS << "llvm::iterator_range<const " << Table.CppTypeName << " *> ";
525+
else
526+
OS << "const " << Table.CppTypeName << " *";
527+
OS << Index.Name << "(";
500528
ListSeparator LS;
501529
for (const auto &Field : Index.Fields)
502530
OS << LS << searchableFieldType(Table, Index, Field, TypeInArgument) << " "
@@ -541,9 +569,9 @@ void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
541569
// Indexes are sorted "{ Thing, PrimaryIdx }" arrays, so that a binary
542570
// search can be performed by "Thing".
543571
if (Table.PrimaryKey)
544-
emitLookupFunction(Table, *Table.PrimaryKey, true, OS);
572+
emitLookupFunction(Table, *Table.PrimaryKey, /*IsPrimary=*/true, OS);
545573
for (const auto &Index : Table.Indices)
546-
emitLookupFunction(Table, *Index, false, OS);
574+
emitLookupFunction(Table, *Index, /*IsPrimary=*/false, OS);
547575

548576
OS << "#endif\n\n";
549577
}
@@ -569,11 +597,12 @@ bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *TypeOf) {
569597

570598
std::unique_ptr<SearchIndex> SearchableTableEmitter::parseSearchIndex(
571599
GenericTable &Table, const RecordVal *KeyRecVal, StringRef Name,
572-
const std::vector<StringRef> &Key, bool EarlyOut) {
600+
const std::vector<StringRef> &Key, bool EarlyOut, bool ReturnRange) {
573601
auto Index = std::make_unique<SearchIndex>();
574602
Index->Name = std::string(Name);
575603
Index->Loc = KeyRecVal->getLoc();
576604
Index->EarlyOut = EarlyOut;
605+
Index->ReturnRange = ReturnRange;
577606

578607
for (const auto &FieldName : Key) {
579608
const GenericField *Field = Table.getFieldByName(FieldName);
@@ -769,7 +798,8 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
769798
parseSearchIndex(*Table, TableRec->getValue("PrimaryKey"),
770799
TableRec->getValueAsString("PrimaryKeyName"),
771800
TableRec->getValueAsListOfStrings("PrimaryKey"),
772-
TableRec->getValueAsBit("PrimaryKeyEarlyOut"));
801+
TableRec->getValueAsBit("PrimaryKeyEarlyOut"),
802+
TableRec->getValueAsBit("PrimaryKeyReturnRange"));
773803

774804
llvm::stable_sort(Table->Entries, [&](Record *LHS, Record *RHS) {
775805
return compareBy(LHS, RHS, *Table->PrimaryKey);
@@ -790,10 +820,10 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
790820
TableRec->getName());
791821

792822
GenericTable &Table = *It->second;
793-
Table.Indices.push_back(
794-
parseSearchIndex(Table, IndexRec->getValue("Key"), IndexRec->getName(),
795-
IndexRec->getValueAsListOfStrings("Key"),
796-
IndexRec->getValueAsBit("EarlyOut")));
823+
Table.Indices.push_back(parseSearchIndex(
824+
Table, IndexRec->getValue("Key"), IndexRec->getName(),
825+
IndexRec->getValueAsListOfStrings("Key"),
826+
IndexRec->getValueAsBit("EarlyOut"), /*ReturnRange*/ false));
797827
}
798828

799829
// Translate legacy tables.
@@ -847,8 +877,9 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
847877
Class->getValueAsListOfStrings("SearchableFields")) {
848878
std::string Name =
849879
(Twine("lookup") + Table->CppTypeName + "By" + Field).str();
850-
Table->Indices.push_back(parseSearchIndex(*Table, Class->getValue(Field),
851-
Name, {Field}, false));
880+
Table->Indices.push_back(
881+
parseSearchIndex(*Table, Class->getValue(Field), Name, {Field},
882+
/*EarlyOut*/ false, /*ReturnRange*/ false));
852883
}
853884

854885
Tables.emplace_back(std::move(Table));

0 commit comments

Comments
 (0)