Skip to content

[TableGen] Add a field to filter out GenericTable entries #65458

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 72 additions & 6 deletions llvm/docs/TableGen/BackEnds.rst
Original file line number Diff line number Diff line change
Expand Up @@ -689,6 +689,11 @@ This class provides six fields.
* ``string FilterClass``. The table will have one entry for each record
that derives from this class.

* ``string FilterClassField``. This is an optional field of ``FilterClass``
which should be `bit` type. If specified, only those records with this field
being true will have corresponding entries in the table. This field won't be
included in generated C++ fields if it isn't included in ``Fields`` list.

* ``string CppTypeName``. The name of the C++ struct/class type of the
table that holds the entries. If unspecified, the ``FilterClass`` name is
used.
Expand Down Expand Up @@ -734,22 +739,25 @@ irrelevant.

def ATable : GenericTable {
let FilterClass = "AEntry";
let FilterClassField = "IsNeeded";
let Fields = ["Str", "Val1", "Val2"];
let PrimaryKey = ["Val1", "Val2"];
let PrimaryKeyName = "lookupATableByValues";
}

class AEntry<string str, int val1, int val2> {
class AEntry<string str, int val1, int val2, bit isNeeded> {
string Str = str;
bits<8> Val1 = val1;
bits<10> Val2 = val2;
bit IsNeeded = isNeeded;
}

def : AEntry<"Bob", 5, 3>;
def : AEntry<"Carol", 2, 6>;
def : AEntry<"Ted", 4, 4>;
def : AEntry<"Alice", 4, 5>;
def : AEntry<"Costa", 2, 1>;
def : AEntry<"Bob", 5, 3, 1>;
def : AEntry<"Carol", 2, 6, 1>;
def : AEntry<"Ted", 4, 4, 1>;
def : AEntry<"Alice", 4, 5, 1>;
def : AEntry<"Costa", 2, 1, 1>;
def : AEntry<"Dale", 2, 1, 0>;

Here is the generated C++ code. The declaration of ``lookupATableByValues``
is guarded by ``GET_ATable_DECL``, while the definitions are guarded by
Expand All @@ -768,6 +776,7 @@ is guarded by ``GET_ATable_DECL``, while the definitions are guarded by
{ "Ted", 0x4, 0x4 }, // 2
{ "Alice", 0x4, 0x5 }, // 3
{ "Bob", 0x5, 0x3 }, // 4
/* { "Dale", 0x2, 0x1 }, // 5 */ // We don't generate this line as `IsNeeded` is 0.
};

const AEntry *lookupATableByValues(uint8_t Val1, uint16_t Val2) {
Expand Down Expand Up @@ -898,6 +907,63 @@ causes the lookup function to change as follows:
struct KeyType {
...

We can construct two GenericTables with the same ``FilterClass``, so that they
select from the same overall set of records, but assign them with different
``FilterClassField`` values so that they include different subsets of the
records of that class.

For example, we can create two tables that contain only even or odd records.
Fields ``IsEven`` and ``IsOdd`` won't be included in generated C++ fields
because they aren't included in ``Fields`` list.

.. code-block:: text

class EEntry<bits<8> value> {
bits<8> Value = value;
bit IsEven = !eq(!and(value, 1), 0);
bit IsOdd = !not(IsEven);
}

foreach i = {1-10} in {
def : EEntry<i>;
}

def EEntryEvenTable : GenericTable {
let FilterClass = "EEntry";
let FilterClassField = "IsEven";
let Fields = ["Value"];
let PrimaryKey = ["Value"];
let PrimaryKeyName = "lookupEEntryEvenTableByValue";
}

def EEntryOddTable : GenericTable {
let FilterClass = "EEntry";
let FilterClassField = "IsOdd";
let Fields = ["Value"];
let PrimaryKey = ["Value"];
let PrimaryKeyName = "lookupEEntryOddTableByValue";
}

The generated tables are:

.. code-block:: text

constexpr EEntry EEntryEvenTable[] = {
{ 0x2 }, // 0
{ 0x4 }, // 1
{ 0x6 }, // 2
{ 0x8 }, // 3
{ 0xA }, // 4
};

constexpr EEntry EEntryOddTable[] = {
{ 0x1 }, // 0
{ 0x3 }, // 1
{ 0x5 }, // 2
{ 0x7 }, // 3
{ 0x9 }, // 4
};

Search Indexes
~~~~~~~~~~~~~~

Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/TableGen/SearchableTable.td
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,12 @@ class GenericTable {
// derives from that class.
string FilterClass;

// A field of FilterClass to filter out entries. This is an optional field
// of ``FilterClass`` which should be `bit` type. If specified, only those
// records with this field being true will have corresponding entries in the
// table.
string FilterClassField = ?;

// Name of the C++ struct/class type that holds table entries. The
// declaration of this type is not generated automatically.
string CppTypeName = FilterClass;
Expand Down
74 changes: 70 additions & 4 deletions llvm/test/TableGen/generic-tables.td
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,11 @@ include "llvm/TableGen/SearchableTable.td"

// CHECK-LABEL: GET_ATable_IMPL
// CHECK: constexpr AEntry ATable[] = {
// CHECK: { "baz"
// CHECK: { "foo"
// CHECK: { "foobar"
// CHECK: { "bar"
// CHECK-NOT: { "aaa"
// CHECK: { "baz"
// CHECK: { "foo"
// CHECK: { "foobar"
// CHECK: { "bar"
// CHECK: };

// CHECK: const AEntry *lookupATableByValues(uint8_t Val1, uint16_t Val2) {
Expand All @@ -38,15 +39,18 @@ class AEntry<string str, int val1, int val2> {
string Str = str;
bits<8> Val1 = val1;
bits<10> Val2 = val2;
bit IsNeeded = 1;
}

def : AEntry<"aaa", 0, 0> { let IsNeeded = 0; }
def : AEntry<"bar", 5, 3>;
def : AEntry<"baz", 2, 6>;
def : AEntry<"foo", 4, 4>;
def : AEntry<"foobar", 4, 5>;

def ATable : GenericTable {
let FilterClass = "AEntry";
let FilterClassField = "IsNeeded";
let Fields = ["Str", "Val1", "Val2"];

let PrimaryKey = ["Val1", "Val2"];
Expand Down Expand Up @@ -171,3 +175,65 @@ def DTable : GenericTable {
}

#endif // ERROR1

// CHECK-LABEL: GET_EEntryEvenTable_DECL
// CHECK: const EEntry *lookupEEntryEvenTableByValue(uint8_t Value);

// CHECK-LABEL: GET_EEntryEvenTable_IMPL
// CHECK: constexpr EEntry EEntryEvenTable[] = {
// CHECK: { 0x2
// CHECK: { 0x4
// CHECK: { 0x6
// CHECK: { 0x8
// CHECK: { 0xA
// CHECK: };

// CHECK: const EEntry *lookupEEntryEvenTableByValue(uint8_t Value) {
// CHECK: return &*Idx;
// CHECK: }

// CHECK-LABEL: GET_EEntryOddTable_DECL
// CHECK: const EEntry *lookupEEntryOddTableByValue(uint8_t Value);

// CHECK-LABEL: GET_EEntryOddTable_IMPL
// CHECK: constexpr EEntry EEntryOddTable[] = {
// CHECK: { 0x1
// CHECK: { 0x3
// CHECK: { 0x5
// CHECK: { 0x7
// CHECK: { 0x9
// CHECK: };

// CHECK: const EEntry *lookupEEntryOddTableByValue(uint8_t Value) {
// CHECK: return &*Idx;
// CHECK: }

// We can construct two GenericTables with the same FilterClass, so that they
// select from the same overall set of records, but assign them with different
// FilterClassField values so that they include different subsets of the records
// of that class.
class EEntry<bits<8> value> {
bits<8> Value = value;
bit IsEven = !eq(!and(value, 1), 0);
bit IsOdd = !not(IsEven);
}

foreach i = {1-10} in {
def : EEntry<i>;
}

def EEntryEvenTable : GenericTable {
let FilterClass = "EEntry";
let FilterClassField = "IsEven";
let Fields = ["Value"];
let PrimaryKey = ["Value"];
let PrimaryKeyName = "lookupEEntryEvenTableByValue";
}

def EEntryOddTable : GenericTable {
let FilterClass = "EEntry";
let FilterClassField = "IsOdd";
let Fields = ["Value"];
let PrimaryKey = ["Value"];
let PrimaryKeyName = "lookupEEntryOddTableByValue";
}
18 changes: 17 additions & 1 deletion llvm/utils/TableGen/SearchableTableEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -720,7 +720,23 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
Twine("Table FilterClass '") +
FilterClass + "' does not exist");

collectTableEntries(*Table, Records.getAllDerivedDefinitions(FilterClass));
RecordVal *FilterClassFieldVal = TableRec->getValue("FilterClassField");
std::vector<Record *> Definitions =
Records.getAllDerivedDefinitions(FilterClass);
if (auto *FilterClassFieldInit =
dyn_cast<StringInit>(FilterClassFieldVal->getValue())) {
StringRef FilterClassField = FilterClassFieldInit->getValue();
llvm::erase_if(Definitions, [&](const Record *R) {
const RecordVal *Filter = R->getValue(FilterClassField);
if (auto *BitV = dyn_cast<BitInit>(Filter->getValue()))
return !BitV->getValue();

PrintFatalError(Filter, Twine("FilterClassField '") + FilterClass +
"' should be a bit value");
return true;
});
}
collectTableEntries(*Table, Definitions);

if (!TableRec->isValueUnset("PrimaryKey")) {
Table->PrimaryKey =
Expand Down