-
Notifications
You must be signed in to change notification settings - Fork 14.9k
[TableGen] Implement getOperandIdxName #154944
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@llvm/pr-subscribers-backend-amdgpu @llvm/pr-subscribers-tablegen Author: Robert Imschweiler (ro-i) ChangesThis is meant as the inverse of getNamedOperandIdx and returns the OpName for a given operand index for a given opcode. Full diff: https://github.com/llvm/llvm-project/pull/154944.diff 2 Files Affected:
diff --git a/llvm/test/TableGen/get-named-operand-idx.td b/llvm/test/TableGen/get-named-operand-idx.td
index ab23edd54c723..3d07ba1278a53 100644
--- a/llvm/test/TableGen/get-named-operand-idx.td
+++ b/llvm/test/TableGen/get-named-operand-idx.td
@@ -48,34 +48,75 @@ def InstD : InstBase {
let UseNamedOperandTable = 0;
}
-// CHECK: #ifdef GET_INSTRINFO_OPERAND_ENUM
-// CHECK: #undef GET_INSTRINFO_OPERAND_ENUM
-// CHECK: namespace llvm::MyNamespace {
-// CHECK: enum class OpName {
-// CHECK: a = 0,
-// CHECK: b = 1,
-// CHECK: c = 2,
-// CHECK: d = 3,
-// CHECK: x = 4,
-// CHECK: NUM_OPERAND_NAMES = 5,
-// CHECK: }; // enum class OpName
-// CHECK: } // end namespace llvm::MyNamespace
-// CHECK: #endif //GET_INSTRINFO_OPERAND_ENUM
+// CHECK-LABEL: #ifdef GET_INSTRINFO_OPERAND_ENUM
+// CHECK-NEXT: #undef GET_INSTRINFO_OPERAND_ENUM
+// CHECK-NEXT: namespace llvm::MyNamespace {
+// CHECK-NEXT: enum class OpName {
+// CHECK-NEXT: a = 0,
+// CHECK-NEXT: b = 1,
+// CHECK-NEXT: c = 2,
+// CHECK-NEXT: d = 3,
+// CHECK-NEXT: x = 4,
+// CHECK-NEXT: NUM_OPERAND_NAMES = 5,
+// CHECK-NEXT: }; // enum class OpName
+// CHECK-EMPTY:
+// CHECK-NEXT: LLVM_READONLY
+// CHECK-NEXT: int16_t getNamedOperandIdx(uint16_t Opcode, OpName Name);
+// CHECK-NEXT: LLVM_READONLY
+// CHECK-NEXT: OpName getOperandIdxName(uint16_t Opcode, int16_t Idx);
+// CHECK-NEXT: } // end namespace llvm::MyNamespace
+// CHECK-NEXT: #endif //GET_INSTRINFO_OPERAND_ENUM
-// CHECK: #ifdef GET_INSTRINFO_NAMED_OPS
-// CHECK: #undef GET_INSTRINFO_NAMED_OPS
-// CHECK: namespace llvm::MyNamespace {
-// CHECK: LLVM_READONLY
-// CHECK: int16_t getNamedOperandIdx(uint16_t Opcode, OpName Name) {
-// CHECK: assert(Name != OpName::NUM_OPERAND_NAMES);
-// CHECK: static constexpr int8_t OperandMap[][5] = {
-// CHECK: {0, 1, 2, -1, -1, },
-// CHECK: {-1, -1, -1, 0, 1, },
-// CHECK: };
-// CHECK: static constexpr uint8_t InstructionIndex[] = {
-// CHECK: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-// CHECK: };
-// CHECK: return OperandMap[InstructionIndex[Opcode]][(unsigned)Name];
-// CHECK: }
-// CHECK: } // end namespace llvm::MyNamespace
-// CHECK: #endif //GET_INSTRINFO_NAMED_OPS
+// CHECK-LABEL: #ifdef GET_INSTRINFO_NAMED_OPS
+// CHECK-NEXT: #undef GET_INSTRINFO_NAMED_OPS
+// CHECK-NEXT: namespace llvm::MyNamespace {
+// CHECK-NEXT: LLVM_READONLY
+// CHECK-NEXT: static uint8_t getInstructionIndexForOpLookup(uint16_t Opcode) {
+// CHECK-NEXT: static constexpr uint8_t InstructionIndex[] = {
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+// CHECK-NEXT: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 2, 0,
+// CHECK-NEXT: };
+// CHECK-NEXT: return InstructionIndex[Opcode];
+// CHECK-NEXT: }
+// CHECK-NEXT: LLVM_READONLY
+// CHECK-NEXT: int16_t getNamedOperandIdx(uint16_t Opcode, OpName Name) {
+// CHECK-NEXT: assert(Name != OpName::NUM_OPERAND_NAMES);
+// CHECK-NEXT: static constexpr int8_t OperandMap[][5] = {
+// CHECK-NEXT: {-1, -1, -1, -1, -1, },
+// CHECK-NEXT: {0, 1, 2, -1, -1, },
+// CHECK-NEXT: {-1, -1, -1, 0, 1, },
+// CHECK-NEXT: };
+// CHECK-NEXT: unsigned InstrIdx = getInstructionIndexForOpLookup(Opcode);
+// CHECK-NEXT: return OperandMap[InstrIdx][(unsigned)Name];
+// CHECK-NEXT: }
+// CHECK-NEXT: LLVM_READONLY
+// CHECK-NEXT: OpName getOperandIdxName(uint16_t Opcode, int16_t Idx) {
+// CHECK-NEXT: assert(Idx >= 0 && Idx < 3);
+// CHECK-NEXT: static constexpr uint8_t OperandMap[][3] = {
+// CHECK-NEXT: {5, 5, 5, },
+// CHECK-NEXT: {0, 1, 2, },
+// CHECK-NEXT: {3, 4, 5, },
+// CHECK-NEXT: };
+// CHECK-NEXT: unsigned InstrIdx = getInstructionIndexForOpLookup(Opcode);
+// CHECK-NEXT: return (OpName) OperandMap[InstrIdx][(unsigned)Idx];
+// CHECK-NEXT: }
+// CHECK-NEXT: } // end namespace llvm::MyNamespace
+// CHECK-NEXT: #endif //GET_INSTRINFO_NAMED_OPS
diff --git a/llvm/utils/TableGen/InstrInfoEmitter.cpp b/llvm/utils/TableGen/InstrInfoEmitter.cpp
index 6f72b51963f87..eedceb19e5783 100644
--- a/llvm/utils/TableGen/InstrInfoEmitter.cpp
+++ b/llvm/utils/TableGen/InstrInfoEmitter.cpp
@@ -223,17 +223,104 @@ void InstrInfoEmitter::EmitOperandInfo(raw_ostream &OS,
}
}
+static void emitGetInstructionIndexForOpLookup(
+ raw_ostream &OS, const MapVector<SmallVector<int>, unsigned> &OperandMap,
+ ArrayRef<unsigned> InstructionIndex) {
+ StringRef Type = OperandMap.size() <= UINT8_MAX + 1 ? "uint8_t" : "uint16_t";
+ OS << "LLVM_READONLY\n";
+ OS << "static " << Type
+ << " getInstructionIndexForOpLookup(uint16_t Opcode) {\n";
+ OS << " static constexpr " << Type << " InstructionIndex[] = {";
+ for (auto [TableIndex, Entry] : enumerate(InstructionIndex))
+ OS << (TableIndex % 16 == 0 ? "\n " : " ") << Entry << ',';
+ OS << "\n };\n";
+ OS << " return InstructionIndex[Opcode];\n";
+ OS << "}\n";
+}
+
+static void
+emitGetNamedOperandIdx(raw_ostream &OS, StringRef Namespace,
+ const MapVector<StringRef, unsigned> &OperandNameToID,
+ const MapVector<SmallVector<int>, unsigned> &OperandMap,
+ unsigned MaxOperandNo, unsigned NumOperandNames) {
+ OS << "LLVM_READONLY\n";
+ OS << "int16_t getNamedOperandIdx(uint16_t Opcode, OpName Name) {\n";
+ OS << " assert(Name != OpName::NUM_OPERAND_NAMES);\n";
+ if (!NumOperandNames) {
+ // There are no operands, so no need to emit anything
+ OS << " return -1;\n}\n";
+ return;
+ }
+ assert(MaxOperandNo <= INT16_MAX &&
+ "Too many operands for the operand name -> index table");
+ StringRef Type = MaxOperandNo <= INT8_MAX ? "int8_t" : "int16_t";
+ OS << " static constexpr " << Type << " OperandMap[][" << NumOperandNames
+ << "] = {\n";
+ for (const auto &[OpList, _] : OperandMap) {
+ // Emit a row of the OperandMap table.
+ OS << " {";
+ for (unsigned ID = 0; ID < NumOperandNames; ++ID)
+ OS << (ID < OpList.size() ? OpList[ID] : -1) << ", ";
+ OS << "},\n";
+ }
+ OS << " };\n";
+
+ OS << " unsigned InstrIdx = getInstructionIndexForOpLookup(Opcode);\n";
+ OS << " return OperandMap[InstrIdx][(unsigned)Name];\n";
+ OS << "}\n";
+}
+
+static void
+emitGetOperandIdxName(raw_ostream &OS, StringRef Namespace,
+ const MapVector<StringRef, unsigned> &OperandNameToID,
+ const MapVector<SmallVector<int>, unsigned> &OperandMap,
+ unsigned MaxOperandNo, unsigned NumOperandNames) {
+ OS << "LLVM_READONLY\n";
+ OS << "OpName getOperandIdxName(uint16_t Opcode, int16_t Idx) {\n";
+ OS << " assert(Idx >= 0 && Idx < " << MaxOperandNo << ");\n";
+ if (!MaxOperandNo) {
+ // There are no operands, so no need to emit anything
+ OS << " return -1;\n}\n";
+ return;
+ }
+ assert(NumOperandNames <= UINT16_MAX &&
+ "Too many operands for the operand index -> name table");
+ StringRef Type = NumOperandNames <= UINT8_MAX ? "uint8_t" : "uint16_t";
+ OS << " static constexpr " << Type << " OperandMap[][" << MaxOperandNo
+ << "] = {\n";
+ for (const auto &[OpList, _] : OperandMap) {
+ SmallVector<unsigned> IDs(MaxOperandNo, NumOperandNames);
+ for (const auto &[ID, Idx] : enumerate(OpList)) {
+ if (Idx >= 0)
+ IDs[Idx] = ID;
+ }
+ // Emit a row of the OperandMap table. Map operand indices to enum values.
+ OS << " {";
+ for (unsigned ID : IDs)
+ OS << ID << ", ";
+ OS << "},\n";
+ }
+ OS << " };\n";
+
+ OS << " unsigned InstrIdx = getInstructionIndexForOpLookup(Opcode);\n";
+ OS << " return (OpName) OperandMap[InstrIdx][(unsigned)Idx];\n";
+ OS << "}\n";
+}
+
/// Generate a table and function for looking up the indices of operands by
/// name.
///
/// This code generates:
/// - An enum in the llvm::TargetNamespace::OpName namespace, with one entry
/// for each operand name.
-/// - A 2-dimensional table called OperandMap for mapping OpName enum values to
-/// operand indices.
-/// - A function called getNamedOperandIdx(uint16_t Opcode, uint16_t NamedIdx)
+/// - A 2-dimensional table for mapping OpName enum values to operand indices.
+/// - A function called getNamedOperandIdx(uint16_t Opcode, OpName Name)
/// for looking up the operand index for an instruction, given a value from
/// OpName enum
+/// - A 2-dimensional table for mapping operand indices to OpName enum values.
+/// - A function called getOperandIdxName(uint16_t Opcode, int16_t Idx)
+/// for looking up the OpName enum for an instruction, given the operand
+/// index. This is the inverse of getNamedOperandIdx().
///
/// Fixed/Predefined instructions do not have UseNamedOperandTable enabled, so
/// we can just skip them. Hence accept just the TargetInstructions.
@@ -242,11 +329,6 @@ void InstrInfoEmitter::emitOperandNameMappings(
ArrayRef<const CodeGenInstruction *> TargetInstructions) {
StringRef Namespace = Target.getInstNamespace();
- /// To facilitate assigning OpName enum values in the sorted alphabetical
- /// order, we go through an indirection from OpName -> ID, and Enum -> ID.
- /// This allows us to build the OpList and assign IDs to OpNames in a single
- /// scan of the instructions below.
-
// Map of operand names to their ID.
MapVector<StringRef, unsigned> OperandNameToID;
@@ -257,7 +339,7 @@ void InstrInfoEmitter::emitOperandNameMappings(
/// instructions that have identical OpName -> Operand index mapping.
MapVector<SmallVector<int>, unsigned> OperandMap;
- // Max operand index seen.
+ // Max operand index seen + 1.
unsigned MaxOperandNo = 0;
// Fixed/Predefined instructions do not have UseNamedOperandTable enabled, so
@@ -277,7 +359,7 @@ void InstrInfoEmitter::emitOperandNameMappings(
.first->second;
OpList.resize(std::max((unsigned)OpList.size(), ID + 1), -1);
OpList[ID] = Info.MIOperandNo;
- MaxOperandNo = std::max(MaxOperandNo, Info.MIOperandNo);
+ MaxOperandNo = std::max(MaxOperandNo, Info.MIOperandNo + 1);
}
auto [It, Inserted] =
OperandMap.try_emplace(std::move(OpList), OperandMap.size());
@@ -296,42 +378,22 @@ void InstrInfoEmitter::emitOperandNameMappings(
OS << "}; // enum class OpName\n\n";
OS << "LLVM_READONLY\n";
OS << "int16_t getNamedOperandIdx(uint16_t Opcode, OpName Name);\n";
+ OS << "LLVM_READONLY\n";
+ OS << "OpName getOperandIdxName(uint16_t Opcode, int16_t Idx);\n";
OS << "} // end namespace llvm::" << Namespace << '\n';
OS << "#endif //GET_INSTRINFO_OPERAND_ENUM\n\n";
OS << "#ifdef GET_INSTRINFO_NAMED_OPS\n";
OS << "#undef GET_INSTRINFO_NAMED_OPS\n";
OS << "namespace llvm::" << Namespace << " {\n";
- OS << "LLVM_READONLY\n";
- OS << "int16_t getNamedOperandIdx(uint16_t Opcode, OpName Name) {\n";
- OS << " assert(Name != OpName::NUM_OPERAND_NAMES);\n";
- if (NumOperandNames != 0) {
- assert(MaxOperandNo <= INT16_MAX &&
- "Too many operands for the operand name -> index table");
- StringRef Type = MaxOperandNo <= INT8_MAX ? "int8_t" : "int16_t";
- OS << " static constexpr " << Type << " OperandMap[][" << NumOperandNames
- << "] = {\n";
- for (const auto &[OpList, _] : OperandMap) {
- // Emit a row of the OperandMap table.
- OS << " {";
- for (unsigned ID = 0; ID < NumOperandNames; ++ID)
- OS << (ID < OpList.size() ? OpList[ID] : -1) << ", ";
- OS << "},\n";
- }
- OS << " };\n";
- Type = OperandMap.size() <= UINT8_MAX + 1 ? "uint8_t" : "uint16_t";
- OS << " static constexpr " << Type << " InstructionIndex[] = {";
- for (auto [TableIndex, Entry] : enumerate(InstructionIndex))
- OS << (TableIndex % 16 == 0 ? "\n " : " ") << Entry << ',';
- OS << "\n };\n";
+ emitGetInstructionIndexForOpLookup(OS, OperandMap, InstructionIndex);
+
+ emitGetNamedOperandIdx(OS, Namespace, OperandNameToID, OperandMap,
+ MaxOperandNo, NumOperandNames);
+ emitGetOperandIdxName(OS, Namespace, OperandNameToID, OperandMap,
+ MaxOperandNo, NumOperandNames);
- OS << " return OperandMap[InstructionIndex[Opcode]][(unsigned)Name];\n";
- } else {
- // There are no operands, so no need to emit anything
- OS << " return -1;\n";
- }
- OS << "}\n";
OS << "} // end namespace llvm::" << Namespace << '\n';
OS << "#endif //GET_INSTRINFO_NAMED_OPS\n\n";
}
|
I don't really like the separate definition of |
StringRef Type = MaxOperandNo <= INT8_MAX ? "int8_t" : "int16_t"; | ||
OS << " static constexpr " << Type << " OperandMap[][" << NumOperandNames | ||
<< "] = {\n"; | ||
for (const auto &[OpList, _] : OperandMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is _
a C++ thing? I'm not sure if you could use that to ignore unused variable warning.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is currently no warning intended for unused member of structured bindings: #58953
This might change in the future, and then, _
will probably be supported to explicitly ignore structured binding members. Currently, it's just a visual helper for the reader.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that this idiom is already used heavily in LLVM code. _ is a valid variable name in C++ I think, which is what this uses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC there's a proposal to make this blessed syntax in a future C++
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was one time I indeed used that as an unused variable name, and then the compiler generates a warning of unused variable. Lol.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The current behavior does seem a little inconsistent: https://godbolt.org/z/eM1xj54P7
Interestingly, a standalone variable with the name _
is not flagged as unused
StringRef Type = MaxOperandNo <= INT8_MAX ? "int8_t" : "int16_t"; | ||
OS << " static constexpr " << Type << " OperandMap[][" << NumOperandNames | ||
<< "] = {\n"; | ||
for (const auto &[OpList, _] : OperandMap) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC there's a proposal to make this blessed syntax in a future C++
raw_ostream &OS, const MapVector<SmallVector<int>, unsigned> &OperandMap, | ||
ArrayRef<unsigned> InstructionIndex) { | ||
StringRef Type = OperandMap.size() <= UINT8_MAX + 1 ? "uint8_t" : "uint16_t"; | ||
OS << "LLVM_READONLY static " << Type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OS << "LLVM_READONLY static " << Type | |
OS << "LLVM_READONLY static " << Type |
I think this technically qualifies for readnone, but the optimizer will take care of it anyway
✅ With the latest revision this PR passed the C/C++ code formatter. |
This is meant as the inverse of getNamedOperandIdx and returns the OpName for a given operand index for a given opcode.
Co-authored-by: Matt Arsenault <Matthew.Arsenault@amd.com>
a58bcb6
to
6a50747
Compare
rebased on current main to (hopefully) fix the lldb test failures (that have nothing to do with this PR) |
They don't and have been broken for days, just ignore them |
Ok, done. Thanks for the reviews everyone |
if (OpName == AMDGPU::OpName::NUM_OPERAND_NAMES) | ||
continue; | ||
int16_t RetrievedIdx = AMDGPU::getNamedOperandIdx(Opcode, OpName); | ||
EXPECT_EQ(Idx, RetrievedIdx) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello @ro-i ,
Warning on this line:
../../third-party/unittest/googletest/include/gtest/gtest.h:1379:11: error: comparison of integers of different signs: 'const unsigned int' and 'const short' [-Werror,-Wsign-compare]
1379 | if (lhs == rhs) {
| ~~~ ^ ~~~
../../third-party/unittest/googletest/include/gtest/gtest.h:1398:12: note: in instantiation of function template specialization 'testing::internal::CmpHelperEQ<unsigned int, short>' requested here
1398 | return CmpHelperEQ(lhs_expression, rhs_expression, lhs, rhs);
| ^
../unittests/Target/AMDGPU/AMDGPUUnitTests.cpp:338:7: note: in instantiation of function template specialization 'testing::internal::EqHelper::Compare<unsigned int, short, nullptr>' requested here
338 | EXPECT_EQ(Idx, RetrievedIdx)
| ^
``
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is meant as the inverse of getNamedOperandIdx and returns the OpName for a given operand index for a given opcode.