Skip to content

Commit 2db8ece

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 2db8ece

File tree

4 files changed

+282
-42
lines changed

4 files changed

+282
-42
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: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,9 @@ class GenericTable {
114114

115115
// See SearchIndex.EarlyOut
116116
bit PrimaryKeyEarlyOut = false;
117+
118+
// See SearchIndex.ReturnRange
119+
bit PrimaryKeyReturnRange = false;
117120
}
118121

119122
// Define a record derived from this class to generate an additional search
@@ -135,6 +138,12 @@ class SearchIndex {
135138
//
136139
// Can only be used when the first field is an integral (non-string) type.
137140
bit EarlyOut = false;
141+
142+
// If true, will generate a different function signature which will return an
143+
// iterator range of pointers giving the starting and end value of the range.
144+
// This feature is only supported for primary key only.
145+
// e.g. lookupSysRegByEncoding returns multiple CSRs for same encoding.
146+
bit ReturnRange = false;
138147
}
139148

140149
// Legacy table type with integrated enum.

llvm/test/TableGen/ReturnRange.td

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// RUN: llvm-tblgen -gen-searchable-tables -I %p/../../include %s | FileCheck %s
2+
// RUN: not llvm-tblgen -DERROR -gen-searchable-tables -I %p/../../include %s 2>&1 \
3+
// RUN: | FileCheck --check-prefix=ERROR %s
4+
5+
include "llvm/TableGen/SearchableTable.td"
6+
7+
class SysReg<string name, bits<12> op> {
8+
string Name = name;
9+
bits<12> Encoding = op;
10+
code FeaturesRequired = [{ {} }];
11+
}
12+
13+
def List1 : GenericTable {
14+
let FilterClass = "SysReg";
15+
let Fields = [
16+
"Name", "Encoding", "FeaturesRequired",
17+
];
18+
19+
let PrimaryKey = [ "Encoding" ];
20+
let PrimaryKeyName = "lookupSysRegByEncoding";
21+
let PrimaryKeyReturnRange = true;
22+
}
23+
24+
let FeaturesRequired = [{ {Feature1} }] in {
25+
def : SysReg<"csr1", 0x7C0>;
26+
}
27+
28+
let FeaturesRequired = [{ {Feature2} }] in {
29+
def : SysReg<"csr2", 0x7C0>;
30+
}
31+
32+
def lookupSysRegByName : SearchIndex {
33+
let Table = List1;
34+
let Key = [ "Name" ];
35+
#ifdef ERROR
36+
// ERROR: Emitting different signature for returning a range of results is only supported for Primary Key.
37+
let ReturnRange = true;
38+
#endif
39+
}
40+
41+
// CHECK: #ifdef GET_List1_DECL
42+
// CHECK-NEXT: llvm::iterator_range<const SysReg *> lookupSysRegByEncoding(uint16_t Encoding);
43+
// CHECK-NEXT: onst SysReg *lookupSysRegByName(StringRef Name);
44+
// CHECK-NEXT: #endif
45+
46+
// CHECK: #ifdef GET_List1_IMPL
47+
// CHECK-NEXT: constexpr SysReg List1[] = {
48+
// CHECK-NEXT: { "csr1", 0x7C0, {Feature1} }, // 0
49+
// CHECK-NEXT: { "csr2", 0x7C0, {Feature2} }, // 1
50+
// CHECK-NEXT: };
51+
52+
// CHECK: llvm::iterator_range<const SysReg *> lookupSysRegByEncoding(uint16_t Encoding) {
53+
// CHECK-NEXT: struct KeyType {
54+
// CHECK-NEXT: uint16_t Encoding;
55+
// CHECK-NEXT: };
56+
// CHECK-NEXT: KeyType Key = {Encoding};
57+
// CHECK-NEXT: struct Comp {
58+
// CHECK-NEXT: bool operator()(const SysReg &LHS, const KeyType &RHS) const {
59+
// CHECK-NEXT: if (LHS.Encoding < RHS.Encoding)
60+
// CHECK-NEXT: return true;
61+
// CHECK-NEXT: if (LHS.Encoding > RHS.Encoding)
62+
// CHECK-NEXT: return false;
63+
// CHECK-NEXT: return false;
64+
// CHECK-NEXT: }
65+
// CHECK-NEXT: bool operator()(const KeyType &LHS, const SysReg &RHS) const {
66+
// CHECK-NEXT: if (LHS.Encoding < RHS.Encoding)
67+
// CHECK-NEXT: return true;
68+
// CHECK-NEXT: if (LHS.Encoding > RHS.Encoding)
69+
// CHECK-NEXT: return false;
70+
// CHECK-NEXT: return false;
71+
// CHECK-NEXT: }
72+
// CHECK-NEXT: };
73+
// CHECK-NEXT: auto Table = ArrayRef(List1);
74+
// CHECK-NEXT: auto It = std::equal_range(Table.begin(), Table.end(), Key, Comp());
75+
// CHECK-NEXT: return llvm::make_range(It.first, It.second);
76+
// CHECK-NEXT: }
77+
78+
// CHECK: const SysReg *lookupSysRegByName(StringRef Name) {
79+
// CHECK-NEXT: struct IndexType {
80+
// CHECK-NEXT: const char * Name;
81+
// CHECK-NEXT: unsigned _index;
82+
// CHECK-NEXT: };
83+
// CHECK-NEXT: static const struct IndexType Index[] = {
84+
// CHECK-NEXT: { "CSR1", 0 },
85+
// CHECK-NEXT: { "CSR2", 1 },
86+
// CHECK-NEXT: };
87+
88+
// CHECK: struct KeyType {
89+
// CHECK-NEXT: std::string Name;
90+
// CHECK-NEXT: };
91+
// CHECK-NEXT: KeyType Key = {Name.upper()};
92+
// CHECK-NEXT: struct Comp {
93+
// CHECK-NEXT: bool operator()(const IndexType &LHS, const KeyType &RHS) const {
94+
// CHECK-NEXT: int CmpName = StringRef(LHS.Name).compare(RHS.Name);
95+
// CHECK-NEXT: if (CmpName < 0) return true;
96+
// CHECK-NEXT: if (CmpName > 0) return false;
97+
// CHECK-NEXT: return false;
98+
// CHECK-NEXT: }
99+
// CHECK-NEXT: };
100+
// CHECK-NEXT: auto Table = ArrayRef(Index);
101+
// CHECK-NEXT: auto Idx = std::lower_bound(Table.begin(), Table.end(), Key, Comp());
102+
// CHECK-NEXT: if (Idx == Table.end() ||
103+
// CHECK-NEXT: Key.Name != Idx->Name)
104+
// CHECK-NEXT: return nullptr;
105+
106+
// CHECK: return &List1[Idx->_index];
107+
// CHECK-NEXT: }
108+
// CHECK-NEXT: #endif

0 commit comments

Comments
 (0)