diff --git a/llvm/docs/TableGen/BackEnds.rst b/llvm/docs/TableGen/BackEnds.rst index 07dff5a30e216..5cbb3232ef023 100644 --- a/llvm/docs/TableGen/BackEnds.rst +++ b/llvm/docs/TableGen/BackEnds.rst @@ -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. @@ -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 { + class AEntry { 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 @@ -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) { @@ -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 value> { + bits<8> Value = value; + bit IsEven = !eq(!and(value, 1), 0); + bit IsOdd = !not(IsEven); + } + + foreach i = {1-10} in { + def : EEntry; + } + + 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 ~~~~~~~~~~~~~~ diff --git a/llvm/include/llvm/TableGen/SearchableTable.td b/llvm/include/llvm/TableGen/SearchableTable.td index 61dfa5c707064..9dddd5e578ff1 100644 --- a/llvm/include/llvm/TableGen/SearchableTable.td +++ b/llvm/include/llvm/TableGen/SearchableTable.td @@ -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; diff --git a/llvm/test/TableGen/generic-tables.td b/llvm/test/TableGen/generic-tables.td index dc9debf152cef..f604f7777b3df 100644 --- a/llvm/test/TableGen/generic-tables.td +++ b/llvm/test/TableGen/generic-tables.td @@ -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) { @@ -38,8 +39,10 @@ class AEntry { 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>; @@ -47,6 +50,7 @@ def : AEntry<"foobar", 4, 5>; def ATable : GenericTable { let FilterClass = "AEntry"; + let FilterClassField = "IsNeeded"; let Fields = ["Str", "Val1", "Val2"]; let PrimaryKey = ["Val1", "Val2"]; @@ -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 value> { + bits<8> Value = value; + bit IsEven = !eq(!and(value, 1), 0); + bit IsOdd = !not(IsEven); +} + +foreach i = {1-10} in { + def : EEntry; +} + +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"; +} diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp index b6af02c28a806..ee1af0ec70df0 100644 --- a/llvm/utils/TableGen/SearchableTableEmitter.cpp +++ b/llvm/utils/TableGen/SearchableTableEmitter.cpp @@ -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 Definitions = + Records.getAllDerivedDefinitions(FilterClass); + if (auto *FilterClassFieldInit = + dyn_cast(FilterClassFieldVal->getValue())) { + StringRef FilterClassField = FilterClassFieldInit->getValue(); + llvm::erase_if(Definitions, [&](const Record *R) { + const RecordVal *Filter = R->getValue(FilterClassField); + if (auto *BitV = dyn_cast(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 =