[llvm] [RISCV]Add support for resolving encoding conflicts among vendor specific CSRs (PR #96174)

Garvit Gupta via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 4 12:45:53 PDT 2024


https://github.com/quic-garvgupt updated https://github.com/llvm/llvm-project/pull/96174

>From d6c09569084d842f7d20f92ec124d796c8dc2701 Mon Sep 17 00:00:00 2001
From: Garvit Gupta <quic_garvgupt at quicinc.com>
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:

```
llvm::iterator_range<const SysReg *>
lookupSysRegByEncoding(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;
    });

  return llvm::make_range(It.first, It.second);
}
```
---
 llvm/include/llvm/TableGen/SearchableTable.td |   8 +
 llvm/test/TableGen/ReturnRange.td             | 100 +++++++++++++
 .../utils/TableGen/SearchableTableEmitter.cpp | 139 +++++++++++++-----
 3 files changed, 211 insertions(+), 36 deletions(-)
 create mode 100644 llvm/test/TableGen/ReturnRange.td

diff --git a/llvm/include/llvm/TableGen/SearchableTable.td b/llvm/include/llvm/TableGen/SearchableTable.td
index 9dddd5e578ff1..b4ea851cd8330 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 an
+  // iterator range of pointers giving the starting 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/test/TableGen/ReturnRange.td b/llvm/test/TableGen/ReturnRange.td
new file mode 100644
index 0000000000000..b0c4f6e547cca
--- /dev/null
+++ b/llvm/test/TableGen/ReturnRange.td
@@ -0,0 +1,100 @@
+// RUN: llvm-tblgen -gen-searchable-tables -I %p/../../include %s | FileCheck %s
+
+include "llvm/TableGen/SearchableTable.td"
+
+class SysReg<string name, bits<12> op> {
+  string Name = name;
+  bits<12> Encoding = op;
+  code FeaturesRequired = [{ {} }];
+}
+
+def List : GenericTable {
+  let FilterClass = "SysReg";
+  let Fields = [
+     "Name", "Encoding", "FeaturesRequired",
+  ];
+
+  let PrimaryKey = [ "Encoding" ];
+  let PrimaryKeyName = "lookupSysRegByEncoding";
+  let PrimaryKeyReturnRange = true;
+}
+
+def lookupSysRegByName : SearchIndex {
+  let Table = List;
+  let Key = [ "Name" ];
+  let ReturnRange = true;
+}
+
+let FeaturesRequired = [{ {Feature1} }] in {
+def : SysReg<"csr1", 0x7C0>;
+}
+
+let FeaturesRequired = [{ {Feature2} }] in {
+def : SysReg<"csr2", 0x7C0>;
+}
+
+let FeaturesRequired = [{ {Feature1} }] in {
+def : SysReg<"aabbc", 0x7C1>;
+}
+
+let FeaturesRequired = [{ {Feature2} }] in {
+def : SysReg<"aaabb", 0x7C2>;
+}
+
+// CHECK: #ifdef GET_List_DECL
+// CHECK-NEXT: llvm::iterator_range<const SysReg *>
+// CHECK-NEXT: lookupSysRegByEncoding(uint16_t Encoding);
+// CHECK-NEXT: llvm::iterator_range<const SysReg *>
+// CHECK-NEXT: lookupSysRegByName(StringRef Name);
+// CHECK-NEXT: #endif
+
+// CHECK: #ifdef GET_List_IMPL
+// CHECK-NEXT: constexpr SysReg List[] = {
+// CHECK-NEXT:   { "csr1", 0x7C0,  {Feature1}  }, // 0
+// CHECK-NEXT:   { "csr2", 0x7C0,  {Feature2}  }, // 1
+// CHECK-NEXT:   { "aabbc", 0x7C1,  {Feature1}  }, // 2
+// CHECK-NEXT:   { "aaabb", 0x7C2,  {Feature2}  }, // 3
+// CHECK-NEXT:  };
+// CHECK-NEXT: constexpr SysReg ListforlookupSysRegByName[] = {
+// CHECK-NEXT:   { "aaabb", 0x7C2,  {Feature2}  },
+// CHECK-NEXT:   { "aabbc", 0x7C1,  {Feature1}  },
+// CHECK-NEXT:   { "csr1", 0x7C0,  {Feature1}  },
+// CHECK-NEXT: { "csr2", 0x7C0,  {Feature2}  },
+// CHECK-NEXT:  };
+
+// CHECK: llvm::iterator_range<const SysReg *> 
+// CHECK-NEXT: lookupSysRegByEncoding(uint16_t Encoding) {
+// CHECK-NEXT:   SysReg Key;
+// CHECK-NEXT:   Key.Encoding = Encoding;
+// CHECK-NEXT:   auto Table = ArrayRef(List);
+// 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:   return llvm::make_range(It.first, It.second);
+// CHECK-NEXT: }
+
+// CHECK: llvm::iterator_range<const SysReg *>
+// CHECK-NEXT: lookupSysRegByName(StringRef Name) {
+// CHECK-NEXT:  SysReg Key;
+// CHECK-NEXT:  Key.Name = Name.data();
+// CHECK-NEXT:  auto Table = ArrayRef(ListforlookupSysRegByName);
+// CHECK-NEXT:  auto It = std::equal_range(Table.begin(), Table.end(), Key,
+// CHECK-NEXT:    [](const SysReg &LHS, const SysReg &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:   return llvm::make_range(It.first, It.second);
+// CHECK-NEXT: }
+// CHECK-NEXT: #endif
+
+// CHECK: #undef GET_List_DECL
+// CHECK-NEXT: #undef GET_List_IMPL
diff --git a/llvm/utils/TableGen/SearchableTableEmitter.cpp b/llvm/utils/TableGen/SearchableTableEmitter.cpp
index 48ee23db957de..5f7deb894d04f 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<GenericField, 1> Fields;
   bool EarlyOut = false;
+  bool ReturnRange = false;
 };
 
 struct GenericTable {
@@ -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);
@@ -328,8 +330,9 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
   ArrayRef<Record *> IndexRows;
   StringRef IndexTypeName;
   StringRef IndexName;
+  bool ShouldReturnRange = Index.ReturnRange;
 
-  if (IsPrimary) {
+  if (IsPrimary || ShouldReturnRange) {
     IndexTypeName = Table.CppTypeName;
     IndexName = Table.Name;
     IndexRows = Table.Entries;
@@ -426,31 +429,55 @@ 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 (isa<StringRecTy>(Field.RecType)) {
-      OS << ".upper()";
-      if (IsPrimary)
-        PrintFatalError(Index.Loc,
-                        Twine("In table '") + Table.Name +
-                            "', use a secondary lookup method for "
-                            "case-insensitive comparison of field '" +
-                            Field.Name + "'");
+    if (ShouldReturnRange) {
+      OS << "  Key." << Field.Name << " = " << Field.Name;
+      if (isa<StringRecTy>(Field.RecType))
+        OS << ".data()";
+      OS << ";\n";
+    } else {
+      OS << LS << Field.Name;
+      if (isa<StringRecTy>(Field.RecType)) {
+        OS << ".upper()";
+        if (IsPrimary)
+          PrintFatalError(Index.Loc,
+                          Twine("In table '") + Table.Name +
+                              "', use a secondary lookup method for "
+                              "case-insensitive comparison of field '" +
+                              Field.Name + "'");
+      }
     }
   }
-  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";
+  if (!ShouldReturnRange)
+    OS << "};\n";
+
+  OS << "  auto Table = ArrayRef(" << IndexName;
+  if (ShouldReturnRange) {
+    if (!IsPrimary)
+      OS << "for" << Index.Name;
+    OS << ");\n";
+    OS << "  auto It = std::equal_range(Table.begin(), Table.end(), Key,\n";
+    OS << "    [](const " << IndexTypeName << " &LHS, const " << IndexTypeName
+       << " &RHS) {\n";
+  } else {
+    OS << ");\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<StringRecTy>(Field.RecType)) {
@@ -478,16 +505,21 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
   OS << "      return false;\n";
   OS << "    });\n\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 (!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";
 }
@@ -495,8 +527,11 @@ void SearchableTableEmitter::emitLookupFunction(const GenericTable &Table,
 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 << " *>\n";
+  else
+    OS << "const " << Table.CppTypeName << " *";
+  OS << Index.Name << "(";
   ListSeparator LS;
   for (const auto &Field : Index.Fields)
     OS << LS << searchableFieldType(Table, Index, Field, TypeInArgument) << " "
@@ -538,12 +573,41 @@ void SearchableTableEmitter::emitGenericTable(const GenericTable &Table,
   }
   OS << " };\n";
 
+  // Emit Table for all those indices which will return a range instead of a
+  // single record
+  for (const auto &Index : Table.Indices) {
+    if (Index->ReturnRange) {
+      std::vector<Record *> Entries;
+      Entries.reserve(Table.Entries.size());
+      for (unsigned i = 0; i < Table.Entries.size(); ++i)
+        Entries.emplace_back(Table.Entries[i]);
+
+      llvm::stable_sort(Entries, [&](Record *LHS, Record *RHS) {
+        return compareBy(LHS, RHS, *Index);
+      });
+
+      OS << "constexpr " << Table.CppTypeName << " " << Table.Name << "for"
+         << Index->Name << "[] = {\n";
+      for (const auto &Entry : Entries) {
+        OS << "  { ";
+        ListSeparator LS;
+        for (const auto &Field : Table.Fields)
+          OS << LS
+             << primaryRepresentation(Table.Locs[0], Field,
+                                      Entry->getValueInit(Field.Name));
+
+        OS << " },\n";
+      }
+      OS << " };\n";
+    }
+  }
+
   // 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";
 }
@@ -569,11 +633,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);
@@ -769,7 +834,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 +859,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.
@@ -848,7 +915,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));



More information about the llvm-commits mailing list