From bc106a06450e79bb525d5fa187ecc3f49184658b Mon Sep 17 00:00:00 2001 From: Garvit Gupta Date: Thu, 27 Jun 2024 10:31:25 -0700 Subject: [PATCH] [RISCV]Add support for resolving encoding conflicts among vendor specific 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: ``` std::pairlookupSysRegByEncoding(uint16_t Encoding) { SysReg Key; Key.Encoding = Encoding; auto Table = ArrayRef(SysRegsList); auto It = std::equal_range(Table.begin(), Table.end(), Key, [](const SysReg &LHS, const SysReg &RHS) { if (LHS.Encoding < RHS.Encoding) return true; if (LHS.Encoding > RHS.Encoding) return false; return false; }); auto Idx = It.first; if (Idx == Table.end() || Key.Encoding != Idx->Encoding) return {nullptr, nullptr}; return It; ``` --- llvm/include/llvm/TableGen/SearchableTable.td | 8 + llvm/lib/Target/RISCV/RISCVSystemOperands.td | 1 + llvm/test/TableGen/EncodingConflict.td | 118 +++++++++++++ .../utils/TableGen/SearchableTableEmitter.cpp | 155 ++++++++++++------ 4 files changed, 229 insertions(+), 53 deletions(-) create mode 100644 llvm/test/TableGen/EncodingConflict.td diff --git a/llvm/include/llvm/TableGen/SearchableTable.td b/llvm/include/llvm/TableGen/SearchableTable.td index 9dddd5e578ff12..b9dfda0e23b734 100644 --- a/llvm/include/llvm/TableGen/SearchableTable.td +++ b/llvm/include/llvm/TableGen/SearchableTable.td @@ -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 @@ -135,6 +138,11 @@ 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 + // a std::pair<> of iterators pointing to start and end value of the range + // e.g. lookupSysRegByEncoding returns multiple CSRs for same encoding. + bit ReturnRange = false; } // Legacy table type with integrated enum. diff --git a/llvm/lib/Target/RISCV/RISCVSystemOperands.td b/llvm/lib/Target/RISCV/RISCVSystemOperands.td index db840b3027492f..c64ea20bef4416 100644 --- a/llvm/lib/Target/RISCV/RISCVSystemOperands.td +++ b/llvm/lib/Target/RISCV/RISCVSystemOperands.td @@ -49,6 +49,7 @@ def SysRegsList : GenericTable { let PrimaryKey = [ "Encoding" ]; let PrimaryKeyName = "lookupSysRegByEncoding"; + let PrimaryKeyReturnRange = false; } def lookupSysRegByName : SearchIndex { diff --git a/llvm/test/TableGen/EncodingConflict.td b/llvm/test/TableGen/EncodingConflict.td new file mode 100644 index 00000000000000..3990169d60f25c --- /dev/null +++ b/llvm/test/TableGen/EncodingConflict.td @@ -0,0 +1,118 @@ +// RUN: llvm-tblgen -gen-searchable-tables -I %p/../../include %s | FileCheck %s + +include "llvm/TableGen/SearchableTable.td" + +class SysReg 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", 0x7C9>; +} + +let FeaturesRequired = [{ {Feature2} }] in { +def : SysReg<"csr2", 0x7C9>; +} + +// CHECK: #ifdef GET_List1_DECL +// CHECK-NEXT: std::pairlookupSysRegByEncoding(uint16_t Encoding); +// CHECK-NEXT: #endif + +// CHECK: #ifdef GET_List1_IMPL +// CHECK-NEXT: constexpr SysReg List1[] = { +// CHECK-NEXT: { "csr1", 0x7C9, {Feature1} }, // 0 +// CHECK-NEXT: { "csr2", 0x7C9, {Feature2} }, // 1 +// CHECK-NEXT: }; + +// CHECK: std::pairlookupSysRegByEncoding(uint16_t Encoding) { +// CHECK-NEXT: SysReg Key; +// CHECK-NEXT: Key.Encoding = Encoding; +// CHECK-NEXT: auto Table = ArrayRef(List1); +// CHECK-NEXT: auto It = std::equal_range(Table.begin(), Table.end(), Key, +// CHECK-NEXT: [](const SysReg &LHS, const SysReg &RHS) { +// 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: auto Idx = It.first; +// CHECK-NEXT: if (Idx == Table.end() || +// CHECK-NEXT: Key.Encoding != Idx->Encoding) +// CHECK-NEXT: return {nullptr, nullptr}; + +// CHECK: return It; +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +def List2 : GenericTable { + let FilterClass = "SysReg"; + let Fields = [ + "Name", "Encoding", "FeaturesRequired", + ]; +} + +def lookupSysRegByName : SearchIndex { + let Table = List2; + let Key = [ "Name" ]; + let ReturnRange = true; +} + +// CHECK: #ifdef GET_List2_DECL +// CHECK-NEXT: std::pairlookupSysRegByName(StringRef Name); +// CHECK-NEXT: #endif + +// CHECK: #ifdef GET_List2_IMPL +// CHECK-NEXT: constexpr SysReg List2[] = { +// CHECK-NEXT: { "csr1", 0x7C9, {Feature1} }, // 0 +// CHECK-NEXT: { "csr2", 0x7C9, {Feature2} }, // 1 +// CHECK-NEXT: }; + +// CHECK: std::pairlookupSysRegByName(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: IndexType Key; +// CHECK-NEXT: Key.Name = Name.upper(); +// CHECK-NEXT: auto Table = ArrayRef(Index); +// CHECK-NEXT: auto It = std::equal_range(Table.begin(), Table.end(), Key, +// CHECK-NEXT: [](const IndexType &LHS, const IndexType &RHS) { +// 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: auto Idx = It.first; +// CHECK-NEXT: if (Idx == Table.end() || +// CHECK-NEXT: Key.Name != Idx->Name) +// CHECK-NEXT: return {nullptr, nullptr}; + +// CHECK: return It; +// CHECK-NEXT: } +// CHECK-NEXT: #endif + +// CHECK: #undef GET_List1_DECL +// CHECK-NEXT: #undef GET_List1_IMPL +// CHECK-NEXT: #undef GET_List2_DECL +// CHECK-NEXT: #undef GET_List2_IMPL diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp index 48ee23db957def..904ef776bf6bda 100644 --- a/llvm/utils/TableGen/SearchableTableEmitter.cpp +++ b/llvm/utils/TableGen/SearchableTableEmitter.cpp @@ -68,6 +68,7 @@ struct SearchIndex { SMLoc Loc; // Source location of PrimaryKey or Key field definition. SmallVector Fields; bool EarlyOut = false; + bool ReturnRange = false; }; struct GenericTable { @@ -190,15 +191,18 @@ class SearchableTableEmitter { void emitGenericTable(const GenericTable &Table, raw_ostream &OS); void emitGenericEnum(const GenericEnum &Enum, raw_ostream &OS); void emitLookupDeclaration(const GenericTable &Table, - const SearchIndex &Index, raw_ostream &OS); + const SearchIndex &Index, bool ShouldReturnRange, + raw_ostream &OS); void emitLookupFunction(const GenericTable &Table, const SearchIndex &Index, - bool IsPrimary, raw_ostream &OS); + bool IsPrimary, bool ShouldReturnRange, + raw_ostream &OS); void emitIfdef(StringRef Guard, raw_ostream &OS); bool parseFieldType(GenericField &Field, Init *II); std::unique_ptr parseSearchIndex(GenericTable &Table, const RecordVal *RecVal, StringRef Name, - const std::vector &Key, bool EarlyOut); + const std::vector &Key, bool EarlyOut, + bool ReturnRange); void collectEnumEntries(GenericEnum &Enum, StringRef NameField, StringRef ValueField, const std::vector &Items); @@ -319,9 +323,10 @@ void SearchableTableEmitter::emitGenericEnum(const GenericEnum &Enum, void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, const SearchIndex &Index, bool IsPrimary, + bool ShouldReturnRange, raw_ostream &OS) { OS << "\n"; - emitLookupDeclaration(Table, Index, OS); + emitLookupDeclaration(Table, Index, ShouldReturnRange, OS); OS << " {\n"; std::vector IndexRowsStorage; @@ -426,16 +431,24 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, OS << " return nullptr;\n\n"; } - OS << " struct KeyType {\n"; - for (const auto &Field : Index.Fields) { - OS << " " << searchableFieldType(Table, Index, Field, TypeInTempStruct) - << " " << Field.Name << ";\n"; + if (ShouldReturnRange) + OS << " " << IndexTypeName<< " Key;\n"; + else { + OS << " struct KeyType {\n"; + for (const auto &Field : Index.Fields) { + OS << " " << searchableFieldType(Table, Index, Field, TypeInTempStruct) + << " " << Field.Name << ";\n"; + } + OS << " };\n"; + OS << " KeyType Key = {"; } - OS << " };\n"; - OS << " KeyType Key = {"; + ListSeparator LS; for (const auto &Field : Index.Fields) { - OS << LS << Field.Name; + if (ShouldReturnRange) + OS << " Key." << Field.Name << " = " << Field.Name; + else + OS << LS << Field.Name; if (isa(Field.RecType)) { OS << ".upper()"; if (IsPrimary) @@ -445,62 +458,88 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table, "case-insensitive comparison of field '" + Field.Name + "'"); } + if (ShouldReturnRange) + OS << ";\n"; } - OS << "};\n"; + if (!ShouldReturnRange) + 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"; - - for (const auto &Field : Index.Fields) { - if (isa(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"; - } + if (ShouldReturnRange) { + OS << " auto It = std::equal_range(Table.begin(), Table.end(), Key,\n"; + OS << " [](const " << IndexTypeName << " &LHS, const " << IndexTypeName + << " &RHS) {\n"; + } else { + OS << " auto Idx = std::lower_bound(Table.begin(), Table.end(), Key,\n"; + OS << " [](const " << IndexTypeName << " &LHS, const KeyType &RHS) {\n"; } + auto emitComparator = [&]() { + for (const auto &Field : Index.Fields) { + if (isa(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\n"; + }; + emitComparator(); - OS << " return false;\n"; - OS << " });\n\n"; + if (ShouldReturnRange) + OS << " auto Idx = It.first;\n"; OS << " if (Idx == Table.end()"; - for (const auto &Field : Index.Fields) OS << " ||\n Key." << Field.Name << " != Idx->" << Field.Name; - OS << ")\n return nullptr;\n"; - if (IsPrimary) + if (ShouldReturnRange) { + OS << ")\n return {nullptr, nullptr};\n\n"; + OS << " return It;\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, + bool ShouldReturnRange, raw_ostream &OS) { - OS << "const " << Table.CppTypeName << " *" << Index.Name << "("; - + if (ShouldReturnRange) + OS << "std::pair"; + else + OS << "const " << Table.CppTypeName << " *"; + OS << Index.Name << "("; ListSeparator LS; - for (const auto &Field : Index.Fields) + for (const auto &Field : Index.Fields) { OS << LS << searchableFieldType(Table, Index, Field, TypeInArgument) << " " << Field.Name; + } OS << ")"; } @@ -510,11 +549,14 @@ void SearchableTableEmitter::emitGenericTable(const GenericTable &Table, // Emit the declarations for the functions that will perform lookup. if (Table.PrimaryKey) { - emitLookupDeclaration(Table, *Table.PrimaryKey, OS); + auto &Index = *Table.PrimaryKey; + emitLookupDeclaration(Table, Index, /*ShouldReturnRange=*/Index.ReturnRange, + OS); OS << ";\n"; } for (const auto &Index : Table.Indices) { - emitLookupDeclaration(Table, *Index, OS); + emitLookupDeclaration(Table, *Index, /*ShouldReturnRange=*/Index->ReturnRange, + OS); OS << ";\n"; } @@ -540,10 +582,14 @@ 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); + if (Table.PrimaryKey) { + auto &Index = *Table.PrimaryKey; + emitLookupFunction(Table, Index, /*IsPrimary=*/true, + /*ShouldReturnRange=*/Index.ReturnRange, OS); + } for (const auto &Index : Table.Indices) - emitLookupFunction(Table, *Index, false, OS); + emitLookupFunction(Table, *Index, /*IsPrimary=*/false, + /*ShouldReturnRange=*/Index->ReturnRange, OS); OS << "#endif\n\n"; } @@ -569,11 +615,12 @@ bool SearchableTableEmitter::parseFieldType(GenericField &Field, Init *TypeOf) { std::unique_ptr SearchableTableEmitter::parseSearchIndex( GenericTable &Table, const RecordVal *KeyRecVal, StringRef Name, - const std::vector &Key, bool EarlyOut) { + const std::vector &Key, bool EarlyOut, bool ReturnRange) { auto Index = std::make_unique(); 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); @@ -769,7 +816,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); @@ -793,7 +841,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.