Skip to content

Commit 8ae0485

Browse files
authored
[TableGen] Extend direct lookup to instruction values in generic tables. (#80486)
Currently, for some tables involving a single primary key field which is integral and densely numbered, a direct lookup is generated rather than a binary search. This patch extends the direct lookup function generation to instructions, where the integral value corresponds to the instruction's enum value. While this isn't as common as for other tables, it does occur in at least one downstream backend and one in-tree backend. Added a unit test and minimally updated the documentation.
1 parent 8ea7f1d commit 8ae0485

File tree

5 files changed

+106
-11
lines changed

5 files changed

+106
-11
lines changed

llvm/docs/TableGen/BackEnds.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -817,7 +817,9 @@ The table entries in ``ATable`` are sorted in order by ``Val1``, and within
817817
each of those values, by ``Val2``. This allows a binary search of the table,
818818
which is performed in the lookup function by ``std::lower_bound``. The
819819
lookup function returns a reference to the found table entry, or the null
820-
pointer if no entry is found.
820+
pointer if no entry is found. If the table has a single primary key field
821+
which is integral and densely numbered, a direct lookup is generated rather
822+
than a binary search.
821823

822824
This example includes a field whose type TableGen cannot deduce. The ``Kind``
823825
field uses the enumerated type ``CEnum`` defined above. To inform TableGen

llvm/test/TableGen/generic-tables-instruction.td

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
// XFAIL: vg_leak
33

44
include "llvm/TableGen/SearchableTable.td"
5+
include "llvm/Target/Target.td"
6+
7+
def ArchInstrInfo : InstrInfo { }
8+
def Arch : Target { let InstructionSet = ArchInstrInfo; }
59

610
// CHECK-LABEL: GET_InstrTable_IMPL
711
// CHECK: constexpr MyInstr InstrTable[] = {
@@ -11,11 +15,20 @@ include "llvm/TableGen/SearchableTable.td"
1115
// CHECK: { D, 0x8 },
1216
// CHECK: };
1317

14-
class Instruction {
15-
bit isPseudo = 0;
16-
}
18+
// A contiguous primary (Instruction) key should get a direct lookup instead of
19+
// binary search.
20+
// CHECK: const MyInstr *getCustomEncodingHelper(unsigned Opcode) {
21+
// CHECK: if ((Opcode < B) ||
22+
// CHECK: (Opcode > D))
23+
// CHECK: return nullptr;
24+
// CHECK: auto Table = ArrayRef(InstrTable);
25+
// CHECK: size_t Idx = Opcode - B;
26+
// CHECK: return &Table[Idx];
27+
1728

1829
class MyInstr<int op> : Instruction {
30+
let OutOperandList = (outs);
31+
let InOperandList = (ins);
1932
Instruction Opcode = !cast<Instruction>(NAME);
2033
bits<16> CustomEncoding = op;
2134
}
@@ -34,3 +47,45 @@ def InstrTable : GenericTable {
3447
let PrimaryKey = ["Opcode"];
3548
let PrimaryKeyName = "getCustomEncodingHelper";
3649
}
50+
51+
52+
// Non-contiguous instructions should get a binary search instead of direct
53+
// lookup.
54+
// CHECK: const MyInfoEntry *getTable2ByOpcode(unsigned Opcode) {
55+
// CHECK: auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,
56+
//
57+
// Verify contiguous check for SearchIndex.
58+
// const MyInfoEntry *getTable2ByValue(uint8_t Value) {
59+
// CHECK: if ((Value < 0xB) ||
60+
// CHECK: (Value > 0xD))
61+
// CHECK: return nullptr;
62+
// CHECK: auto Table = ArrayRef(Index);
63+
// CHECK: size_t Idx = Value - 0xB;
64+
// CHECK: return &InstrTable2[Table[Idx]._index];
65+
66+
67+
class MyInfoEntry<int V, string S> {
68+
Instruction Opcode = !cast<Instruction>(NAME);
69+
bits<4> Value = V;
70+
string Name = S;
71+
}
72+
73+
let OutOperandList = (outs), InOperandList = (ins) in {
74+
def W : Instruction, MyInfoEntry<12, "IW">;
75+
def X : Instruction;
76+
def Y : Instruction, MyInfoEntry<13, "IY">;
77+
def Z : Instruction, MyInfoEntry<11, "IZ">;
78+
}
79+
80+
def InstrTable2 : GenericTable {
81+
let FilterClass = "MyInfoEntry";
82+
let Fields = ["Opcode", "Value", "Name"];
83+
84+
let PrimaryKey = ["Opcode"];
85+
let PrimaryKeyName = "getTable2ByOpcode";
86+
}
87+
88+
def getTable2ByValue : SearchIndex {
89+
let Table = InstrTable2;
90+
let Key = ["Value"];
91+
}

llvm/utils/TableGen/CodeGenTarget.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,13 @@ void CodeGenTarget::ComputeInstrsByEnum() const {
538538
return std::make_tuple(!D1.getValueAsBit("isPseudo"), D1.getName()) <
539539
std::make_tuple(!D2.getValueAsBit("isPseudo"), D2.getName());
540540
});
541+
542+
// Build the instruction-to-int map using the same values emitted by
543+
// InstrInfoEmitter::emitEnums.
544+
assert(InstrToIntMap.empty());
545+
unsigned Num = 0;
546+
for (const CodeGenInstruction *Inst : InstrsByEnum)
547+
InstrToIntMap[Inst->TheDef] = Num++;
541548
}
542549

543550

llvm/utils/TableGen/CodeGenTarget.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ class CodeGenTarget {
7474

7575
mutable StringRef InstNamespace;
7676
mutable std::vector<const CodeGenInstruction*> InstrsByEnum;
77+
mutable DenseMap<const Record *, unsigned> InstrToIntMap;
7778
mutable unsigned NumPseudoInstructions = 0;
7879
public:
7980
CodeGenTarget(RecordKeeper &Records);
@@ -192,6 +193,15 @@ class CodeGenTarget {
192193
return InstrsByEnum;
193194
}
194195

196+
/// Return the integer enum value corresponding to this instruction record.
197+
unsigned getInstrIntValue(const Record *R) const {
198+
if (InstrsByEnum.empty())
199+
ComputeInstrsByEnum();
200+
auto V = InstrToIntMap.find(R);
201+
assert(V != InstrToIntMap.end() && "instr not in InstrToIntMap");
202+
return V->second;
203+
}
204+
195205
typedef ArrayRef<const CodeGenInstruction *>::const_iterator inst_iterator;
196206
inst_iterator inst_begin() const{return getInstructionsByEnumValue().begin();}
197207
inst_iterator inst_end() const { return getInstructionsByEnumValue().end(); }

llvm/utils/TableGen/SearchableTableEmitter.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@
77
//===----------------------------------------------------------------------===//
88
//
99
// This tablegen backend emits a generic array initialized by specified fields,
10-
// together with companion index tables and lookup functions (binary search,
11-
// currently).
10+
// together with companion index tables and lookup functions. The lookup
11+
// function generated is either a direct lookup (when a single primary key field
12+
// is integral and densely numbered) or a binary search otherwise.
1213
//
1314
//===----------------------------------------------------------------------===//
1415

1516
#include "CodeGenIntrinsics.h"
17+
#include "CodeGenTarget.h"
1618
#include "llvm/ADT/ArrayRef.h"
1719
#include "llvm/ADT/DenseMap.h"
1820
#include "llvm/ADT/STLExtras.h"
@@ -90,6 +92,7 @@ struct GenericTable {
9092

9193
class SearchableTableEmitter {
9294
RecordKeeper &Records;
95+
std::unique_ptr<CodeGenTarget> Target;
9396
DenseMap<Init *, std::unique_ptr<CodeGenIntrinsic>> Intrinsics;
9497
std::vector<std::unique_ptr<GenericEnum>> Enums;
9598
DenseMap<Record *, GenericEnum *> EnumMap;
@@ -201,18 +204,23 @@ class SearchableTableEmitter {
201204
const std::vector<Record *> &Items);
202205
void collectTableEntries(GenericTable &Table,
203206
const std::vector<Record *> &Items);
207+
int64_t getNumericKey(const SearchIndex &Index, Record *Rec);
204208
};
205209

206210
} // End anonymous namespace.
207211

208212
// For search indices that consists of a single field whose numeric value is
209213
// known, return that numeric value.
210-
static int64_t getNumericKey(const SearchIndex &Index, Record *Rec) {
214+
int64_t SearchableTableEmitter::getNumericKey(const SearchIndex &Index,
215+
Record *Rec) {
211216
assert(Index.Fields.size() == 1);
212217

213218
if (Index.Fields[0].Enum) {
214219
Record *EnumEntry = Rec->getValueAsDef(Index.Fields[0].Name);
215220
return Index.Fields[0].Enum->EntryMap[EnumEntry]->second;
221+
} else if (Index.Fields[0].IsInstruction) {
222+
Record *TheDef = Rec->getValueAsDef(Index.Fields[0].Name);
223+
return Target->getInstrIntValue(TheDef);
216224
}
217225

218226
return getInt(Rec, Index.Fields[0].Name);
@@ -370,20 +378,31 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
370378
bool IsContiguous = false;
371379

372380
if (Index.Fields.size() == 1 &&
373-
(Index.Fields[0].Enum || isa<BitsRecTy>(Index.Fields[0].RecType))) {
381+
(Index.Fields[0].Enum || isa<BitsRecTy>(Index.Fields[0].RecType) ||
382+
Index.Fields[0].IsInstruction)) {
383+
int64_t FirstKeyVal = getNumericKey(Index, IndexRows[0]);
374384
IsContiguous = true;
375385
for (unsigned i = 0; i < IndexRows.size(); ++i) {
376-
if (getNumericKey(Index, IndexRows[i]) != i) {
386+
if (getNumericKey(Index, IndexRows[i]) != (FirstKeyVal + i)) {
377387
IsContiguous = false;
378388
break;
379389
}
380390
}
381391
}
382392

383393
if (IsContiguous) {
394+
const GenericField &Field = Index.Fields[0];
395+
std::string FirstRepr = primaryRepresentation(
396+
Index.Loc, Field, IndexRows[0]->getValueInit(Field.Name));
397+
std::string LastRepr = primaryRepresentation(
398+
Index.Loc, Field, IndexRows.back()->getValueInit(Field.Name));
399+
OS << " if ((" << Field.Name << " < " << FirstRepr << ") ||\n";
400+
OS << " (" << Field.Name << " > " << LastRepr << "))\n";
401+
OS << " return nullptr;\n";
384402
OS << " auto Table = ArrayRef(" << IndexName << ");\n";
385-
OS << " size_t Idx = " << Index.Fields[0].Name << ";\n";
386-
OS << " return Idx >= Table.size() ? nullptr : ";
403+
OS << " size_t Idx = " << Index.Fields[0].Name << " - " << FirstRepr
404+
<< ";\n";
405+
OS << " return ";
387406
if (IsPrimary)
388407
OS << "&Table[Idx]";
389408
else
@@ -663,6 +682,8 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
663682
// Emit tables in a deterministic order to avoid needless rebuilds.
664683
SmallVector<std::unique_ptr<GenericTable>, 4> Tables;
665684
DenseMap<Record *, GenericTable *> TableMap;
685+
if (!Records.getAllDerivedDefinitionsIfDefined("Instruction").empty())
686+
Target = std::make_unique<CodeGenTarget>(Records);
666687

667688
// Collect all definitions first.
668689
for (auto *EnumRec : Records.getAllDerivedDefinitions("GenericEnum")) {

0 commit comments

Comments
 (0)