Skip to content

Commit

Permalink
[RISCV]Add support for resolving encoding conflicts among vendor spec…
Browse files Browse the repository at this point in the history
…ific 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.
  • Loading branch information
quic-garvgupt committed Jul 9, 2024
1 parent e258bb3 commit f19b276
Show file tree
Hide file tree
Showing 3 changed files with 194 additions and 41 deletions.
9 changes: 9 additions & 0 deletions llvm/include/llvm/TableGen/SearchableTable.td
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,9 @@ class GenericTable {

// See SearchIndex.EarlyOut
bit PrimaryKeyEarlyOut = false;

// See SearchIndex.ReturnRange
bit PrimaryKeyReturnRange = false;
}

// Define a record derived from this class to generate an additional search
Expand All @@ -135,6 +138,12 @@ class SearchIndex {
//
// Can only be used when the first field is an integral (non-string) type.
bit EarlyOut = false;

// If true, will generate a different function signature which will return an
// iterator range of pointers giving the starting and end value of the range.
// This feature is only supported for primary key only.
// e.g. lookupSysRegByEncoding returns multiple CSRs for same encoding.
bit ReturnRange = false;
}

// Legacy table type with integrated enum.
Expand Down
108 changes: 108 additions & 0 deletions llvm/test/TableGen/ReturnRange.td
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// RUN: llvm-tblgen -gen-searchable-tables -I %p/../../include %s | FileCheck %s
// RUN: not llvm-tblgen -DERROR -gen-searchable-tables -I %p/../../include %s 2>&1 \
// RUN: | FileCheck --check-prefix=ERROR %s

include "llvm/TableGen/SearchableTable.td"

class SysReg<string name, bits<12> op> {
string Name = name;
bits<12> Encoding = op;
code FeaturesRequired = [{ {} }];
}

def List1 : GenericTable {
let FilterClass = "SysReg";
let Fields = [
"Name", "Encoding", "FeaturesRequired",
];

let PrimaryKey = [ "Encoding" ];
let PrimaryKeyName = "lookupSysRegByEncoding";
let PrimaryKeyReturnRange = true;
}

let FeaturesRequired = [{ {Feature1} }] in {
def : SysReg<"csr1", 0x7C0>;
}

let FeaturesRequired = [{ {Feature2} }] in {
def : SysReg<"csr2", 0x7C0>;
}

def lookupSysRegByName : SearchIndex {
let Table = List1;
let Key = [ "Name" ];
#ifdef ERROR
// ERROR: Emitting different signature for returning a range of results is only supported for Primary Key.
let ReturnRange = true;
#endif
}

// CHECK: #ifdef GET_List1_DECL
// CHECK-NEXT: llvm::iterator_range<const SysReg *> lookupSysRegByEncoding(uint16_t Encoding);
// CHECK-NEXT: onst SysReg *lookupSysRegByName(StringRef Name);
// CHECK-NEXT: #endif

// CHECK: #ifdef GET_List1_IMPL
// CHECK-NEXT: constexpr SysReg List1[] = {
// CHECK-NEXT: { "csr1", 0x7C0, {Feature1} }, // 0
// CHECK-NEXT: { "csr2", 0x7C0, {Feature2} }, // 1
// CHECK-NEXT: };

// CHECK: llvm::iterator_range<const SysReg *> lookupSysRegByEncoding(uint16_t Encoding) {
// CHECK-NEXT: struct KeyType {
// CHECK-NEXT: uint16_t Encoding;
// CHECK-NEXT: };
// CHECK-NEXT: KeyType Key = {Encoding};
// CHECK-NEXT: struct Comp {
// CHECK-NEXT: bool operator()(const SysReg &LHS, const KeyType &RHS) const {
// CHECK-NEXT: if (LHS.Encoding < RHS.Encoding)
// CHECK-NEXT: return true;
// CHECK-NEXT: if (LHS.Encoding > RHS.Encoding)
// CHECK-NEXT: return false;
// CHECK-NEXT: return false;
// CHECK-NEXT: }
// CHECK-NEXT: bool operator()(const KeyType &LHS, const SysReg &RHS) const {
// CHECK-NEXT: if (LHS.Encoding < RHS.Encoding)
// CHECK-NEXT: return true;
// CHECK-NEXT: if (LHS.Encoding > RHS.Encoding)
// CHECK-NEXT: return false;
// CHECK-NEXT: return false;
// CHECK-NEXT: }
// CHECK-NEXT: };
// CHECK-NEXT: auto Table = ArrayRef(List1);
// CHECK-NEXT: auto It = std::equal_range(Table.begin(), Table.end(), Key, Comp());
// CHECK-NEXT: return llvm::make_range(It.first, It.second);
// CHECK-NEXT: }

// CHECK: const SysReg *lookupSysRegByName(StringRef Name) {
// CHECK-NEXT: struct IndexType {
// CHECK-NEXT: const char * Name;
// CHECK-NEXT: unsigned _index;
// CHECK-NEXT: };
// CHECK-NEXT: static const struct IndexType Index[] = {
// CHECK-NEXT: { "CSR1", 0 },
// CHECK-NEXT: { "CSR2", 1 },
// CHECK-NEXT: };

// CHECK: struct KeyType {
// CHECK-NEXT: std::string Name;
// CHECK-NEXT: };
// CHECK-NEXT: KeyType Key = {Name.upper()};
// CHECK-NEXT: struct Comp {
// CHECK-NEXT: bool operator()(const IndexType &LHS, const KeyType &RHS) const {
// CHECK-NEXT: int CmpName = StringRef(LHS.Name).compare(RHS.Name);
// CHECK-NEXT: if (CmpName < 0) return true;
// CHECK-NEXT: if (CmpName > 0) return false;
// CHECK-NEXT: return false;
// CHECK-NEXT: }
// CHECK-NEXT: };
// CHECK-NEXT: auto Table = ArrayRef(Index);
// CHECK-NEXT: auto Idx = std::lower_bound(Table.begin(), Table.end(), Key, Comp());
// CHECK-NEXT: if (Idx == Table.end() ||
// CHECK-NEXT: Key.Name != Idx->Name)
// CHECK-NEXT: return nullptr;

// CHECK: return &List1[Idx->_index];
// CHECK-NEXT: }
// CHECK-NEXT: #endif
118 changes: 77 additions & 41 deletions llvm/utils/TableGen/SearchableTableEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ struct SearchIndex {
SMLoc Loc; // Source location of PrimaryKey or Key field definition.
SmallVector<GenericField, 1> Fields;
bool EarlyOut = false;
bool ReturnRange = false;
};

struct GenericTable {
Expand Down Expand Up @@ -198,7 +199,8 @@ class SearchableTableEmitter {
bool parseFieldType(GenericField &Field, Init *II);
std::unique_ptr<SearchIndex>
parseSearchIndex(GenericTable &Table, const RecordVal *RecVal, StringRef Name,
const std::vector<StringRef> &Key, bool EarlyOut);
const std::vector<StringRef> &Key, bool EarlyOut,
bool ReturnRange);
void collectEnumEntries(GenericEnum &Enum, StringRef NameField,
StringRef ValueField,
const std::vector<Record *> &Items);
Expand Down Expand Up @@ -320,6 +322,12 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
const SearchIndex &Index,
bool IsPrimary,
raw_ostream &OS) {
bool ShouldReturnRange = Index.ReturnRange;
if (ShouldReturnRange && !IsPrimary)
PrintFatalError(Index.Loc,
"Emitting different signature for returning a range of "
"results is only supported for Primary Key.");

OS << "\n";
emitLookupDeclaration(Table, Index, OS);
OS << " {\n";
Expand Down Expand Up @@ -448,55 +456,80 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
}
OS << "};\n";

OS << " auto Table = ArrayRef(" << IndexName << ");\n";
OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,\n";
OS << " [](const " << IndexTypeName << " &LHS, const KeyType &RHS) {\n";
OS << " struct Comp {\n";
OS << " bool operator()(const " << IndexTypeName
<< " &LHS, const KeyType &RHS) const {\n";

for (const auto &Field : Index.Fields) {
if (isa<StringRecTy>(Field.RecType)) {
OS << " int Cmp" << Field.Name << " = StringRef(LHS." << Field.Name
<< ").compare(RHS." << Field.Name << ");\n";
OS << " if (Cmp" << Field.Name << " < 0) return true;\n";
OS << " if (Cmp" << Field.Name << " > 0) return false;\n";
} else if (Field.Enum) {
// Explicitly cast to unsigned, because the signedness of enums is
// compiler-dependent.
OS << " if ((unsigned)LHS." << Field.Name << " < (unsigned)RHS."
<< Field.Name << ")\n";
OS << " return true;\n";
OS << " if ((unsigned)LHS." << Field.Name << " > (unsigned)RHS."
<< Field.Name << ")\n";
OS << " return false;\n";
} else {
OS << " if (LHS." << Field.Name << " < RHS." << Field.Name << ")\n";
OS << " return true;\n";
OS << " if (LHS." << Field.Name << " > RHS." << Field.Name << ")\n";
OS << " return false;\n";
auto emitComparator = [&]() {
for (const auto &Field : Index.Fields) {
if (isa<StringRecTy>(Field.RecType)) {
OS << " int Cmp" << Field.Name << " = StringRef(LHS." << Field.Name
<< ").compare(RHS." << Field.Name << ");\n";
OS << " if (Cmp" << Field.Name << " < 0) return true;\n";
OS << " if (Cmp" << Field.Name << " > 0) return false;\n";
} else if (Field.Enum) {
// Explicitly cast to unsigned, because the signedness of enums is
// compiler-dependent.
OS << " if ((unsigned)LHS." << Field.Name << " < (unsigned)RHS."
<< Field.Name << ")\n";
OS << " return true;\n";
OS << " if ((unsigned)LHS." << Field.Name << " > (unsigned)RHS."
<< Field.Name << ")\n";
OS << " return false;\n";
} else {
OS << " if (LHS." << Field.Name << " < RHS." << Field.Name
<< ")\n";
OS << " return true;\n";
OS << " if (LHS." << Field.Name << " > RHS." << Field.Name
<< ")\n";
OS << " return false;\n";
}
}
OS << " return false;\n";
OS << " }\n";
};
emitComparator();
if (ShouldReturnRange) {
OS << " bool operator()(const KeyType &LHS, const " << IndexTypeName
<< " &RHS) const {\n";
emitComparator();
}

OS << " return false;\n";
OS << " });\n\n";

OS << " if (Idx == Table.end()";
OS << " };\n";
OS << " auto Table = ArrayRef(" << IndexName << ");\n";
if (ShouldReturnRange)
OS << " auto It = std::equal_range(Table.begin(), Table.end(), Key, ";
else
OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key, ";
OS << "Comp());\n";

for (const auto &Field : Index.Fields)
OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name;
OS << ")\n return nullptr;\n";
if (!ShouldReturnRange) {
OS << " if (Idx == Table.end()";
for (const auto &Field : Index.Fields)
OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name;
}

if (IsPrimary)
if (ShouldReturnRange)
OS << " return llvm::make_range(It.first, It.second);\n";
else if (IsPrimary) {
OS << ")\n return nullptr;\n\n";
OS << " return &*Idx;\n";
else
} else {
OS << ")\n return nullptr;\n\n";
OS << " return &" << Table.Name << "[Idx->_index];\n";
}

OS << "}\n";
}

void SearchableTableEmitter::emitLookupDeclaration(const GenericTable &Table,
const SearchIndex &Index,
raw_ostream &OS) {
OS << "const " << Table.CppTypeName << " *" << Index.Name << "(";

if (Index.ReturnRange)
OS << "llvm::iterator_range<const " << Table.CppTypeName << " *> ";
else
OS << "const " << Table.CppTypeName << " *";
OS << Index.Name << "(";
ListSeparator LS;
for (const auto &Field : Index.Fields)
OS << LS << searchableFieldType(Table, Index, Field, TypeInArgument) << " "
Expand Down Expand Up @@ -541,9 +574,9 @@ void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
// Indexes are sorted "{ Thing, PrimaryIdx }" arrays, so that a binary
// search can be performed by "Thing".
if (Table.PrimaryKey)
emitLookupFunction(Table, *Table.PrimaryKey, true, OS);
emitLookupFunction(Table, *Table.PrimaryKey, /*IsPrimary=*/true, OS);
for (const auto &Index : Table.Indices)
emitLookupFunction(Table, *Index, false, OS);
emitLookupFunction(Table, *Index, /*IsPrimary=*/false, OS);

OS << "#endif\n\n";
}
Expand All @@ -569,11 +602,12 @@ bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *TypeOf) {

std::unique_ptr<SearchIndex> SearchableTableEmitter::parseSearchIndex(
GenericTable &Table, const RecordVal *KeyRecVal, StringRef Name,
const std::vector<StringRef> &Key, bool EarlyOut) {
const std::vector<StringRef> &Key, bool EarlyOut, bool ReturnRange) {
auto Index = std::make_unique<SearchIndex>();
Index->Name = std::string(Name);
Index->Loc = KeyRecVal->getLoc();
Index->EarlyOut = EarlyOut;
Index->ReturnRange = ReturnRange;

for (const auto &FieldName : Key) {
const GenericField *Field = Table.getFieldByName(FieldName);
Expand Down Expand Up @@ -769,7 +803,8 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
parseSearchIndex(*Table, TableRec->getValue("PrimaryKey"),
TableRec->getValueAsString("PrimaryKeyName"),
TableRec->getValueAsListOfStrings("PrimaryKey"),
TableRec->getValueAsBit("PrimaryKeyEarlyOut"));
TableRec->getValueAsBit("PrimaryKeyEarlyOut"),
TableRec->getValueAsBit("PrimaryKeyReturnRange"));

llvm::stable_sort(Table->Entries, [&](Record *LHS, Record *RHS) {
return compareBy(LHS, RHS, *Table->PrimaryKey);
Expand All @@ -793,7 +828,8 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
Table.Indices.push_back(
parseSearchIndex(Table, IndexRec->getValue("Key"), IndexRec->getName(),
IndexRec->getValueAsListOfStrings("Key"),
IndexRec->getValueAsBit("EarlyOut")));
IndexRec->getValueAsBit("EarlyOut"),
IndexRec->getValueAsBit("ReturnRange")));
}

// Translate legacy tables.
Expand Down Expand Up @@ -848,7 +884,7 @@ void SearchableTableEmitter::run(raw_ostream &OS) {
std::string Name =
(Twine("lookup") + Table->CppTypeName + "By" + Field).str();
Table->Indices.push_back(parseSearchIndex(*Table, Class->getValue(Field),
Name, {Field}, false));
Name, {Field}, false, false));
}

Tables.emplace_back(std::move(Table));
Expand Down

0 comments on commit f19b276

Please sign in to comment.