1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2025-01-31 20:51:52 +01:00

Add detailed reference for the SearchableTables backend.

This commit is contained in:
Paul C. Anagnostopoulos 2020-09-02 11:50:30 -04:00
parent 34fd9f9953
commit 88dabfb171

View File

@ -226,16 +226,14 @@ SearchableTables
**Purpose**: Generate custom searchable tables.
**Output**: Enums, global tables and lookup helper functions.
**Output**: Enums, global tables, and lookup helper functions.
**Usage**: This backend allows generating free-form, target-specific tables
from TableGen records. The ARM and AArch64 targets use this backend to generate
tables of system registers; the AMDGPU target uses it to generate meta-data
about complex image and memory buffer instructions.
More documentation is available in ``include/llvm/TableGen/SearchableTable.td``,
which also contains the definitions of TableGen classes which must be
instantiated in order to define the enums and tables emitted by this backend.
See `SearchableTables Reference`_ for a detailed description.
CTags
-----
@ -438,6 +436,381 @@ used for documenting user-facing attributes.
General BackEnds
================
SearchableTables Reference
--------------------------
A TableGen include file, ``SearchableTable.td``, provides classes for
generating C++ searchable tables. These tables are described in the
following sections. To generate the C++ code, run ``llvm-tblgen`` with the
``--gen-searchable-tables`` option, which invokes the backend that generates
the tables from the records you provide.
Each of the data structures generated for searchable tables is guarded by an
``#ifdef``. This allows you to include the generated ``.inc`` file and select only
certain data structures for inclusion. The examples below show the macro
names used in these guards.
Generic Enumerated Types
~~~~~~~~~~~~~~~~~~~~~~~~
The ``GenericEnum`` class makes it easy to define a C++ enumerated type and
the enumerated *elements* of that type. To define the type, define a record
whose parent class is ``GenericEnum`` and whose name is the desired enum
type. This class provides three fields, which you can set in the record
using the ``let`` statement.
* ``string FilterClass``. The enum type will have one element for each record
that derives from this class. These records are collected to assemble the
complete set of elements.
* ``string NameField``. The name of a field *in the collected records* that specifies
the name of the element. If a record has no such field, the record's
name will be used.
* ``string ValueField``. The name of a field *in the collected records* that
specifies the numerical value of the element. If a record has no such
field, it will be assigned an integer value. Values are assigned in
alphabetical order starting with 0.
Here is an example where the values of the elements are specified
explicitly, as a template argument to the ``BEntry`` class. The resulting
C++ code is shown.
.. code-block:: text
def BValues : GenericEnum {
let FilterClass = "BEntry";
let NameField = "Name";
let ValueField = "Encoding";
}
class BEntry<bits<16> enc> {
string Name = NAME;
bits<16> Encoding = enc;
}
def BFoo : BEntry<0xac>;
def BBar : BEntry<0x14>;
def BZoo : BEntry<0x80>;
def BSnork : BEntry<0x4c>;
.. code-block:: text
#ifdef GET_BValues_DECL
enum BValues {
BBar = 20,
BFoo = 172,
BSnork = 76,
BZoo = 128,
};
#endif
In the following example, the values of the elements are assigned
automatically. Note that values are assigned from 0, in alphabetical order
by element name.
.. code-block:: text
def CEnum : GenericEnum {
let FilterClass = "CEnum";
}
class CEnum;
def CFoo : CEnum;
def CBar : CEnum;
def CBaz : CEnum;
.. code-block:: text
#ifdef GET_CEnum_DECL
enum CEnum {
CBar = 0,
CBaz = 1,
CFoo = 2,
};
#endif
Generic Tables
~~~~~~~~~~~~~~
The ``GenericTable`` class is used to define a searchable generic table.
TableGen produces C++ code to define the table entries and also produces
the declaration and definition of a function to search the table based on a
primary key. To define the table, define a record whose parent class is
``GenericTable`` and whose name is the name of the global table of entries.
This class provides six fields.
* ``string FilterClass``. The table will have one entry for each record
that derives from this class.
* ``string CppTypeName``. The name of the C++ struct/class type of the
table that holds the entries. If unspecified, the ``FilterClass`` name is
used.
* ``list<string> Fields``. A list of the names of the fields in the
collected records that contain the data for the table entries. The order of
this list determines the order of the values in the C++ initializers. See
below for information about the types of these fields.
* ``list<string> PrimaryKey``. The list of fields that make up the
primary key.
* ``string PrimaryKeyName``. The name of the generated C++ function
that performs a lookup on the primary key.
* ``bit PrimaryKeyEarlyOut``. See the third example below.
TableGen attempts to deduce the type of each of the table fields. It can
deduce ``bit``, ``bits<n>``, ``string``, ``Intrinsic``, and ``Instruction``.
These can be used in the primary key. TableGen also deduces ``code``, but it
cannot be used in the primary key. Any other field types must be specified
explicitly; this is done as shown in the second example below. Such fields
cannot be used in the primary key.
Here is an example where TableGen can deduce the field types. Note that the
table entry records are anonymous; the names of entry records are
irrelevant.
.. code-block:: text
def ATable : GenericTable {
let FilterClass = "AEntry";
let Fields = ["Str", "Val1", "Val2"];
let PrimaryKey = ["Val1", "Val2"];
let PrimaryKeyName = "lookupATableByValues";
}
class AEntry<string str, int val1, int val2> {
string Str = str;
bits<8> Val1 = val1;
bits<10> Val2 = val2;
}
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>;
Here is the generated C++ code. The declaration of ``lookupATableByValues``
is guarded by ``GET_ATable_DECL``, while the definitions are guarded by
``GET_ATable_IMPL``.
.. code-block:: text
#ifdef GET_ATable_DECL
const AEntry *lookupATableByValues(uint8_t Val1, uint16_t Val2);
#endif
#ifdef GET_ATable_IMPL
constexpr AEntry ATable[] = {
{ "Costa", 0x2, 0x1 }, // 0
{ "Carol", 0x2, 0x6 }, // 1
{ "Ted", 0x4, 0x4 }, // 2
{ "Alice", 0x4, 0x5 }, // 3
{ "Bob", 0x5, 0x3 }, // 4
};
const AEntry *lookupATableByValues(uint8_t Val1, uint16_t Val2) {
struct KeyType {
uint8_t Val1;
uint16_t Val2;
};
KeyType Key = { Val1, Val2 };
auto Table = makeArrayRef(ATable);
auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,
[](const AEntry &LHS, const KeyType &RHS) {
if (LHS.Val1 < RHS.Val1)
return true;
if (LHS.Val1 > RHS.Val1)
return false;
if (LHS.Val2 < RHS.Val2)
return true;
if (LHS.Val2 > RHS.Val2)
return false;
return false;
});
if (Idx == Table.end() ||
Key.Val1 != Idx->Val1 ||
Key.Val2 != Idx->Val2)
return nullptr;
return &*Idx;
}
#endif
The table entries in ``ATable`` are sorted in order by ``Val1``, and within
each of those values, by ``Val2``. This allows a binary search of the table,
which is performed in the lookup function by ``std::lower_bound``. The
lookup function returns a reference to the found table entry, or the null
pointer if no entry is found.
This example includes a field whose type TableGen cannot deduce. The ``Kind``
field uses the enumerated type ``CEnum`` defined above. To inform TableGen
of the type, the class derived from ``GenericTable`` must include a field
named ``TypeOf_``\ *field*, where *field* is the name of the field whose type
is required.
.. code-block:: text
def CTable : GenericTable {
let FilterClass = "CEntry";
let Fields = ["Name", "Kind", "Encoding"];
GenericEnum TypeOf_Kind = CEnum;
let PrimaryKey = ["Encoding"];
let PrimaryKeyName = "lookupCEntryByEncoding";
}
class CEntry<string name, CEnum kind, int enc> {
string Name = name;
CEnum Kind = kind;
bits<16> Encoding = enc;
}
def : CEntry<"Apple", CFoo, 10>;
def : CEntry<"Pear", CBaz, 15>;
def : CEntry<"Apple", CBar, 13>;
Here is the generated C++ code.
.. code-block:: text
#ifdef GET_CTable_DECL
const CEntry *lookupCEntryByEncoding(uint16_t Encoding);
#endif
#ifdef GET_CTable_IMPL
constexpr CEntry CTable[] = {
{ "Apple", CFoo, 0xA }, // 0
{ "Apple", CBar, 0xD }, // 1
{ "Pear", CBaz, 0xF }, // 2
};
const CEntry *lookupCEntryByEncoding(uint16_t Encoding) {
struct KeyType {
uint16_t Encoding;
};
KeyType Key = { Encoding };
auto Table = makeArrayRef(CTable);
auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,
[](const CEntry &LHS, const KeyType &RHS) {
if (LHS.Encoding < RHS.Encoding)
return true;
if (LHS.Encoding > RHS.Encoding)
return false;
return false;
});
if (Idx == Table.end() ||
Key.Encoding != Idx->Encoding)
return nullptr;
return &*Idx;
}
The ``PrimaryKeyEarlyOut`` field, when set to 1, modifies the lookup
function so that it tests the first field of the primary key to determine
whether it is within the range of the collected records' primary keys. If
not, the function returns the null pointer without performing the binary
search. This is useful for tables that provide data for only some of the
elements of a larger enum-based space. The first field of the primary key
must be an integral type; it cannot be a string.
Adding ``let PrimaryKeyEarlyOut = 1`` to the ``ATable`` above:
.. code-block:: text
def ATable : GenericTable {
let FilterClass = "AEntry";
let Fields = ["Str", "Val1", "Val2"];
let PrimaryKey = ["Val1", "Val2"];
let PrimaryKeyName = "lookupATableByValues";
let PrimaryKeyEarlyOut = 1;
}
causes the lookup function to change as follows:
.. code-block:: text
const AEntry *lookupATableByValues(uint8_t Val1, uint16_t Val2) {
if ((Val1 < 0x2) ||
(Val1 > 0x5))
return nullptr;
struct KeyType {
...
Search Indexes
~~~~~~~~~~~~~~
The ``SearchIndex`` class is used to define additional lookup functions for
generic tables. To define an additional function, define a record whose parent
class is ``SearchIndex`` and whose name is the name of the desired lookup
function. This class provides three fields.
* ``GenericTable Table``. The name of the table that is to receive another
lookup function.
* ``list<string> Key``. The list of fields that make up the secondary key.
* ``bit EarlyOut``. See the third example in `Generic Tables`_.
Here is an example of a secondary key added to the ``CTable`` above. The
generated function looks up entries based on the ``Name`` and ``Kind`` fields.
.. code-block:: text
def lookupCEntry : SearchIndex {
let Table = CTable;
let Key = ["Name", "Kind"];
}
This use of ``SearchIndex`` generates the following additional C++ code.
.. code-block:: text
const CEntry *lookupCEntry(StringRef Name, unsigned Kind);
...
const CEntry *lookupCEntryByName(StringRef Name, unsigned Kind) {
struct IndexType {
const char * Name;
unsigned Kind;
unsigned _index;
};
static const struct IndexType Index[] = {
{ "APPLE", CBar, 1 },
{ "APPLE", CFoo, 0 },
{ "PEAR", CBaz, 2 },
};
struct KeyType {
std::string Name;
unsigned Kind;
};
KeyType Key = { Name.upper(), Kind };
auto Table = makeArrayRef(Index);
auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,
[](const IndexType &LHS, const KeyType &RHS) {
int CmpName = StringRef(LHS.Name).compare(RHS.Name);
if (CmpName < 0) return true;
if (CmpName > 0) return false;
if ((unsigned)LHS.Kind < (unsigned)RHS.Kind)
return true;
if ((unsigned)LHS.Kind > (unsigned)RHS.Kind)
return false;
return false;
});
if (Idx == Table.end() ||
Key.Name != Idx->Name ||
Key.Kind != Idx->Kind)
return nullptr;
return &CTable[Idx->_index];
}
JSON
----