[llvm] [TableGen] Add a backend generating SDNode descriptions (PR #123002)

Sergei Barannikov via llvm-commits llvm-commits at lists.llvm.org
Fri Jan 17 15:57:34 PST 2025


https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/123002

>From 9ec5e4855fadd8e4ebc2e15d6f4b9c4d95dd6db7 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Wed, 15 Jan 2025 06:01:54 +0300
Subject: [PATCH 1/7] [TableGen] Add a backend generating SDNode descriptions

This patch adds a simplistic backend that gathers all target-specific
SelectionDAG nodes and emits descriptions for most of them.

This includes generating node enumeration, node names, and information
about node "prototype" that can be used to verify that a node is valid.

The patch also extends SDNode by adding target-specific flags, which
are also included in the generated tables.

Part of #119709, [RFC](https://discourse.llvm.org/t/rfc-tablegen-erating-sdnode-descriptions/83627).
---
 .../include/llvm/Target/TargetSelectionDAG.td |   2 +
 .../ambiguous-constraints.td                  |  73 ++++
 llvm/test/TableGen/SDNodeInfoEmitter/basic.td |  85 ++++
 .../TableGen/SDNodeInfoEmitter/namespace.td   |  62 +++
 .../SDNodeInfoEmitter/skipped-nodes.td        |  91 +++++
 llvm/utils/TableGen/CMakeLists.txt            |   1 +
 .../TableGen/Common/CodeGenDAGPatterns.cpp    | 103 +++--
 .../TableGen/Common/CodeGenDAGPatterns.h      |  42 +-
 llvm/utils/TableGen/SDNodeInfoEmitter.cpp     | 367 ++++++++++++++++++
 9 files changed, 777 insertions(+), 49 deletions(-)
 create mode 100644 llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
 create mode 100644 llvm/test/TableGen/SDNodeInfoEmitter/basic.td
 create mode 100644 llvm/test/TableGen/SDNodeInfoEmitter/namespace.td
 create mode 100644 llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td
 create mode 100644 llvm/utils/TableGen/SDNodeInfoEmitter.cpp

diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index bee0a4298c786f..cd2ca3a08bd691 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -353,6 +353,8 @@ class SDNode<string opcode, SDTypeProfile typeprof,
   string SDClass = sdclass;
   let Properties = props;
   SDTypeProfile TypeProfile = typeprof;
+  bit IsStrictFP = false;
+  bits<64> TSFlags = 0;
 }
 
 // Special TableGen-recognized dag nodes
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
new file mode 100644
index 00000000000000..668464190e6d89
--- /dev/null
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
@@ -0,0 +1,73 @@
+// RUN: split-file %s %t
+
+//--- test1.td
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %t/test1.td | FileCheck %t/test1.td
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+def my_node_a : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>>;
+def my_node_b : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, f32>]>>;
+
+// CHECK:       enum GenNodeType : unsigned {
+// CHECK-NEXT:    NODE = ISD::BUILTIN_OP_END,
+// CHECK-NEXT:  };
+
+// CHECK:       static const char MyTargetSDNodeNames[] =
+// CHECK-NEXT:    "MyTargetISD::NODE\0"
+// CHECK-NEXT:    "\0";
+
+// CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// CHECK-NEXT:      {1, 0, 0, 0, 0, 0, 0, 0}, // NODE
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
+// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
+// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+
+
+//--- test2.td
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %t/test2.td | FileCheck %t/test2.td
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+def my_node_1a : SDNode<"MyTargetISD::NODE_1", SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>>;
+def my_node_1b : SDNode<"MyTargetISD::NODE_1", SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>>;
+def my_node_2a : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<1, 0, [SDTCisVT<0, i32>]>>;
+def my_node_2b : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<1, 0, [SDTCisVT<0, untyped>]>>;
+
+// CHECK:       namespace llvm::MyTargetISD {
+// CHECK-EMPTY:
+// CHECK-NEXT:  enum GenNodeType : unsigned {
+// CHECK-NEXT:    NODE_1 = ISD::BUILTIN_OP_END,
+// CHECK-NEXT:    NODE_2,
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr unsigned GENERATED_OPCODE_END = NODE_2 + 1;
+// CHECK-EMPTY:
+// CHECK-NEXT:  } // namespace llvm::MyTargetISD
+
+// CHECK:       static const char MyTargetSDNodeNames[] =
+// CHECK-NEXT:    "MyTargetISD::NODE_1\0"
+// CHECK-NEXT:    "MyTargetISD::NODE_2\0"
+// CHECK-NEXT:    "\0";
+
+// CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// CHECK-NEXT:    /* 0 */ {SDTCisVT, 0, 0, MVT::i32},
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// CHECK-NEXT:      {1, 0, 0, 0, 0, 0, 0, 1}, // NODE_1
+// CHECK-NEXT:      {1, 0, 0, 0, 0, 20, 0, 0}, // NODE_2
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
+// CHECK-NEXT:      /*NumOpcodes=*/2, MyTargetSDNodeDescs,
+// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
new file mode 100644
index 00000000000000..7f5085e31e02c4
--- /dev/null
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
@@ -0,0 +1,85 @@
+// RUN: split-file %s %t
+
+//--- no-nodes.td
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %t/no-nodes.td \
+// RUN:   | FileCheck %t/no-nodes.td
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+// CHECK:       #ifdef GET_SDNODE_ENUM
+// CHECK-NEXT:  #undef GET_SDNODE_ENUM
+// CHECK-EMPTY:
+// CHECK-NEXT:  namespace llvm::MyTargetISD {
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr unsigned GENERATED_OPCODE_END = ISD::BUILTIN_OP_END;
+// CHECK-EMPTY:
+// CHECK-NEXT:  } // namespace llvm::MyTargetISD
+// CHECK-EMPTY:
+// CHECK-NEXT:  #endif // GET_SDNODE_ENUM
+// CHECK-EMPTY:
+// CHECK-NEXT:  #ifdef GET_SDNODE_DESC
+// CHECK-NEXT:  #undef GET_SDNODE_DESC
+// CHECK-EMPTY:
+// CHECK-NEXT:  namespace llvm {
+// CHECK-EMPTY:
+// CHECK-NEXT:  #ifdef __GNUC__
+// CHECK-NEXT:  #pragma GCC diagnostic push
+// CHECK-NEXT:  #pragma GCC diagnostic ignored "-Woverlength-strings"
+// CHECK-NEXT:  #endif
+// CHECK-NEXT:  static const char MyTargetSDNodeNames[] =
+// CHECK-NEXT:    "\0";
+// CHECK-NEXT:  #ifdef __GNUC__
+// CHECK-NEXT:  #pragma GCC diagnostic pop
+// CHECK-NEXT:  #endif
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
+// CHECK-NEXT:      /*NumOpcodes=*/0, MyTargetSDNodeDescs,
+// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-EMPTY:
+// CHECK-NEXT:  } // namespace llvm
+
+
+//--- trivial-node.td
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %t/trivial-node.td \
+// RUN:   | FileCheck %t/trivial-node.td
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+def my_noop : SDNode<"MyTargetISD::NOOP", SDTypeProfile<0, 0, []>>;
+
+// CHECK:       namespace llvm::MyTargetISD {
+// CHECK-EMPTY:
+// CHECK-NEXT:  enum GenNodeType : unsigned {
+// CHECK-NEXT:    NOOP = ISD::BUILTIN_OP_END,
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr unsigned GENERATED_OPCODE_END = NOOP + 1;
+// CHECK-EMPTY:
+// CHECK-NEXT:  } // namespace llvm::MyTargetISD
+
+// CHECK:       static const char MyTargetSDNodeNames[] =
+// CHECK-NEXT:    "MyTargetISD::NOOP\0"
+// CHECK-NEXT:    "\0";
+
+// CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// CHECK-NEXT:      {0, 0, 0, 0, 0, 0, 0, 0}, // NOOP
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
+// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
+// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/namespace.td b/llvm/test/TableGen/SDNodeInfoEmitter/namespace.td
new file mode 100644
index 00000000000000..844c12bd182fc6
--- /dev/null
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/namespace.td
@@ -0,0 +1,62 @@
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %s -sdnode-namespace=EmptyISD \
+// RUN:   | FileCheck %s -check-prefix=EMPTY
+
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %s \
+// RUN:   | FileCheck %s --check-prefixes=COMMON,TARGET -DNS=MyTargetISD
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %s -sdnode-namespace=MyCustomISD \
+// RUN:   | FileCheck %s -check-prefixes=COMMON,CUSTOM -DNS=MyCustomISD
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+def node_1 : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, i1>]>>;
+def node_2 : SDNode<"MyCustomISD::NODE", SDTypeProfile<0, 1, [SDTCisVT<0, i2>]>>;
+
+// EMPTY:        namespace llvm::EmptyISD {
+// EMPTY-EMPTY:
+// EMPTY-NEXT:   static constexpr unsigned GENERATED_OPCODE_END = ISD::BUILTIN_OP_END;
+// EMPTY-EMPTY:
+// EMPTY-NEXT:   } // namespace llvm::EmptyISD
+
+// EMPTY:        static const char MyTargetSDNodeNames[] =
+// EMPTY-NEXT:     "\0";
+
+// EMPTY:        static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// EMPTY-NEXT:     /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// EMPTY-NEXT:   };
+// EMPTY-EMPTY:
+// EMPTY-NEXT:   static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// EMPTY-NEXT:   };
+// EMPTY-EMPTY:
+// EMPTY-NEXT:   static const SDNodeInfo MyTargetGenSDNodeInfo(
+// EMPTY-NEXT:       /*NumOpcodes=*/0, MyTargetSDNodeDescs,
+// EMPTY-NEXT:       MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+
+// COMMON:       namespace llvm::[[NS]] {
+// COMMON-EMPTY:
+// COMMON-NEXT:  enum GenNodeType : unsigned {
+// COMMON-NEXT:    NODE = ISD::BUILTIN_OP_END,
+// COMMON-NEXT:  };
+// COMMON-EMPTY:
+// COMMON-NEXT:  static constexpr unsigned GENERATED_OPCODE_END = NODE + 1;
+// COMMON-EMPTY:
+// COMMON-NEXT:  } // namespace llvm::[[NS]]
+
+// COMMON:       static const char MyTargetSDNodeNames[] =
+// COMMON-NEXT:    "[[NS]]::NODE\0"
+// COMMON-NEXT:    "\0";
+
+// COMMON:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// TARGET-NEXT:    /* 0 */ {SDTCisVT, 0, 0, MVT::i1},
+// CUSTOM-NEXT:    /* 0 */ {SDTCisVT, 0, 0, MVT::i2},
+// COMMON-NEXT:  };
+// COMMON-EMPTY:
+// COMMON-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// TARGET-NEXT:      {1, 0, 0, 0, 0, 0, 0, 1}, // NODE
+// CUSTOM-NEXT:      {0, 1, 0, 0, 0, 0, 0, 1}, // NODE
+// COMMON-NEXT:  };
+// COMMON-EMPTY:
+// COMMON-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
+// COMMON-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
+// COMMON-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td b/llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td
new file mode 100644
index 00000000000000..ed278f262ca8f7
--- /dev/null
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td
@@ -0,0 +1,91 @@
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %s 2> %t.warn | FileCheck %s
+// RUN: FileCheck --check-prefix=WARN --implicit-check-not=warning %s < %t.warn
+
+// RUN: llvm-tblgen -gen-sd-node-info -warn-on-skipped-nodes=false \
+// RUN:   -I %p/../../../include %s 2> %t.nowarn | FileCheck %s
+// RUN: not test -s %t.nowarn
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+// WARN: [[#@LINE+1]]:5: warning: skipped node: invalid enum name
+def bad_name_1 : SDNode<"", SDTypeProfile<0, 0, []>>;
+
+// WARN: [[#@LINE+1]]:5: warning: skipped node: invalid enum name
+def bad_name_2 : SDNode<"NODE", SDTypeProfile<0, 0, []>>;
+
+// WARN: [[#@LINE+1]]:5: warning: skipped node: invalid enum name
+def bad_name_3 : SDNode<"MyTargetISD::", SDTypeProfile<0, 0, []>>;
+
+// WARN: [[#@LINE+1]]:5: warning: skipped node: invalid enum name
+def bad_name_4 : SDNode<"MyISD::", SDTypeProfile<0, 0, []>>;
+
+// WARN: [[#@LINE+1]]:5: warning: skipped node: invalid enum name
+def bad_name_5 : SDNode<"::NODE", SDTypeProfile<0, 0, []>>;
+
+
+// Standard namespace.
+def silent_1 : SDNode<"ISD::SILENT", SDTypeProfile<0, 0, []>>;
+
+// Different namespace.
+def silent_2 : SDNode<"MyISD::SILENT", SDTypeProfile<0, 0, []>>;
+
+
+// Different number of results.
+// WARN: [[#@LINE+2]]:5: warning: skipped node: incompatible description
+// WARN: [[#@LINE+2]]:5: warning: skipped node: incompatible description
+def node_1a : SDNode<"MyTargetISD::NODE_1", SDTypeProfile<0, 0, []>>;
+def node_1b : SDNode<"MyTargetISD::NODE_1", SDTypeProfile<1, 0, []>>;
+
+// Different number of operands.
+// WARN: [[#@LINE+2]]:5: warning: skipped node: incompatible description
+// WARN: [[#@LINE+2]]:5: warning: skipped node: incompatible description
+def node_2a : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<0, 0, []>>;
+def node_2b : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<0, 1, []>>;
+
+// Different value of IsStrictFP.
+// WARN: [[#@LINE+3]]:5: warning: skipped node: incompatible description
+// WARN: [[#@LINE+3]]:5: warning: skipped node: incompatible description
+let IsStrictFP = true in
+def node_3a : SDNode<"MyTargetISD::NODE_3", SDTypeProfile<0, 0, []>>;
+def node_3b : SDNode<"MyTargetISD::NODE_3", SDTypeProfile<0, 0, []>>;
+
+// Different value of TSFlags.
+// WARN: [[#@LINE+3]]:5: warning: skipped node: incompatible description
+// WARN: [[#@LINE+3]]:5: warning: skipped node: incompatible description
+let TSFlags = 1 in
+def node_4a : SDNode<"MyTargetISD::NODE_4", SDTypeProfile<0, 0, []>>;
+def node_4b : SDNode<"MyTargetISD::NODE_4", SDTypeProfile<0, 0, []>>;
+
+// Different properties.
+// WARN: [[#@LINE+2]]:5: warning: skipped node: incompatible description
+// WARN: [[#@LINE+2]]:5: warning: skipped node: incompatible description
+def node_5a : SDNode<"MyTargetISD::NODE_5", SDTypeProfile<0, 0, []>>;
+def node_5b : SDNode<"MyTargetISD::NODE_5", SDTypeProfile<0, 0, []>, [SDNPHasChain]>;
+
+
+// CHECK:       enum GenNodeType : unsigned {
+// CHECK-NEXT:    COMPAT = ISD::BUILTIN_OP_END,
+// CHECK-NEXT:  };
+
+// CHECK:       static const char MyTargetSDNodeNames[] =
+// CHECK-NEXT:    "MyTargetISD::COMPAT\0"
+// CHECK-NEXT:    "\0";
+
+// CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// CHECK-NEXT:      {1, -1, 0, 0, 0, 0, 0, 0}, // COMPAT
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
+// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
+// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+
+def compat_a : SDNode<"MyTargetISD::COMPAT", SDTypeProfile<1, -1, []>>;
+def compat_b : SDNode<"MyTargetISD::COMPAT", SDTypeProfile<1, -1, [SDTCisVT<0, untyped>]>>;
+def compat_c : SDNode<"MyTargetISD::COMPAT", SDTypeProfile<1, -1, [SDTCisVT<0, untyped>]>,
+    [SDNPCommutative, SDNPAssociative, SDNPMayStore, SDNPMayLoad, SDNPSideEffect]>;
diff --git a/llvm/utils/TableGen/CMakeLists.txt b/llvm/utils/TableGen/CMakeLists.txt
index 96a74c6fd89f73..67291214c14e60 100644
--- a/llvm/utils/TableGen/CMakeLists.txt
+++ b/llvm/utils/TableGen/CMakeLists.txt
@@ -60,6 +60,7 @@ add_tablegen(llvm-tblgen LLVM
   PseudoLoweringEmitter.cpp
   RegisterBankEmitter.cpp
   RegisterInfoEmitter.cpp
+  SDNodeInfoEmitter.cpp
   SearchableTableEmitter.cpp
   SubtargetEmitter.cpp
   WebAssemblyDisassemblerEmitter.cpp
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index 1a61d32b4869a4..6629baa6e071ea 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -1537,21 +1537,19 @@ SDTypeConstraint::SDTypeConstraint(const Record *R, const CodeGenHwModes &CGH) {
     ConstraintType = SDTCisVec;
   } else if (R->isSubClassOf("SDTCisSameAs")) {
     ConstraintType = SDTCisSameAs;
-    x.SDTCisSameAs_Info.OtherOperandNum = R->getValueAsInt("OtherOperandNum");
+    OtherOperandNo = R->getValueAsInt("OtherOperandNum");
   } else if (R->isSubClassOf("SDTCisVTSmallerThanOp")) {
     ConstraintType = SDTCisVTSmallerThanOp;
-    x.SDTCisVTSmallerThanOp_Info.OtherOperandNum =
-        R->getValueAsInt("OtherOperandNum");
+    OtherOperandNo = R->getValueAsInt("OtherOperandNum");
   } else if (R->isSubClassOf("SDTCisOpSmallerThanOp")) {
     ConstraintType = SDTCisOpSmallerThanOp;
-    x.SDTCisOpSmallerThanOp_Info.BigOperandNum =
-        R->getValueAsInt("BigOperandNum");
+    OtherOperandNo = R->getValueAsInt("BigOperandNum");
   } else if (R->isSubClassOf("SDTCisEltOfVec")) {
     ConstraintType = SDTCisEltOfVec;
-    x.SDTCisEltOfVec_Info.OtherOperandNum = R->getValueAsInt("OtherOpNum");
+    OtherOperandNo = R->getValueAsInt("OtherOpNum");
   } else if (R->isSubClassOf("SDTCisSubVecOfVec")) {
     ConstraintType = SDTCisSubVecOfVec;
-    x.SDTCisSubVecOfVec_Info.OtherOperandNum = R->getValueAsInt("OtherOpNum");
+    OtherOperandNo = R->getValueAsInt("OtherOpNum");
   } else if (R->isSubClassOf("SDTCVecEltisVT")) {
     ConstraintType = SDTCVecEltisVT;
     VVT = getValueTypeByHwMode(R->getValueAsDef("VT"), CGH);
@@ -1566,12 +1564,10 @@ SDTypeConstraint::SDTypeConstraint(const Record *R, const CodeGenHwModes &CGH) {
     }
   } else if (R->isSubClassOf("SDTCisSameNumEltsAs")) {
     ConstraintType = SDTCisSameNumEltsAs;
-    x.SDTCisSameNumEltsAs_Info.OtherOperandNum =
-        R->getValueAsInt("OtherOperandNum");
+    OtherOperandNo = R->getValueAsInt("OtherOperandNum");
   } else if (R->isSubClassOf("SDTCisSameSizeAs")) {
     ConstraintType = SDTCisSameSizeAs;
-    x.SDTCisSameSizeAs_Info.OtherOperandNum =
-        R->getValueAsInt("OtherOperandNum");
+    OtherOperandNo = R->getValueAsInt("OtherOperandNum");
   } else {
     PrintFatalError(R->getLoc(),
                     "Unrecognized SDTypeConstraint '" + R->getName() + "'!\n");
@@ -1632,7 +1628,7 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode &N,
   case SDTCisSameAs: {
     unsigned OResNo = 0;
     TreePatternNode &OtherNode =
-        getOperandNum(x.SDTCisSameAs_Info.OtherOperandNum, N, NodeInfo, OResNo);
+        getOperandNum(OtherOperandNo, N, NodeInfo, OResNo);
     return (int)NodeToApply.UpdateNodeType(ResNo, OtherNode.getExtType(OResNo),
                                            TP) |
            (int)OtherNode.UpdateNodeType(OResNo, NodeToApply.getExtType(ResNo),
@@ -1654,23 +1650,23 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode &N,
     TypeSetByHwMode TypeListTmp(VVT);
 
     unsigned OResNo = 0;
-    TreePatternNode &OtherNode = getOperandNum(
-        x.SDTCisVTSmallerThanOp_Info.OtherOperandNum, N, NodeInfo, OResNo);
+    TreePatternNode &OtherNode =
+        getOperandNum(OtherOperandNo, N, NodeInfo, OResNo);
 
     return TI.EnforceSmallerThan(TypeListTmp, OtherNode.getExtType(OResNo),
                                  /*SmallIsVT*/ true);
   }
   case SDTCisOpSmallerThanOp: {
     unsigned BResNo = 0;
-    TreePatternNode &BigOperand = getOperandNum(
-        x.SDTCisOpSmallerThanOp_Info.BigOperandNum, N, NodeInfo, BResNo);
+    TreePatternNode &BigOperand =
+        getOperandNum(OtherOperandNo, N, NodeInfo, BResNo);
     return TI.EnforceSmallerThan(NodeToApply.getExtType(ResNo),
                                  BigOperand.getExtType(BResNo));
   }
   case SDTCisEltOfVec: {
     unsigned VResNo = 0;
-    TreePatternNode &VecOperand = getOperandNum(
-        x.SDTCisEltOfVec_Info.OtherOperandNum, N, NodeInfo, VResNo);
+    TreePatternNode &VecOperand =
+        getOperandNum(OtherOperandNo, N, NodeInfo, VResNo);
     // Filter vector types out of VecOperand that don't have the right element
     // type.
     return TI.EnforceVectorEltTypeIs(VecOperand.getExtType(VResNo),
@@ -1678,8 +1674,8 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode &N,
   }
   case SDTCisSubVecOfVec: {
     unsigned VResNo = 0;
-    TreePatternNode &BigVecOperand = getOperandNum(
-        x.SDTCisSubVecOfVec_Info.OtherOperandNum, N, NodeInfo, VResNo);
+    TreePatternNode &BigVecOperand =
+        getOperandNum(OtherOperandNo, N, NodeInfo, VResNo);
 
     // Filter vector types out of BigVecOperand that don't have the
     // right subvector type.
@@ -1691,15 +1687,15 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode &N,
   }
   case SDTCisSameNumEltsAs: {
     unsigned OResNo = 0;
-    TreePatternNode &OtherNode = getOperandNum(
-        x.SDTCisSameNumEltsAs_Info.OtherOperandNum, N, NodeInfo, OResNo);
+    TreePatternNode &OtherNode =
+        getOperandNum(OtherOperandNo, N, NodeInfo, OResNo);
     return TI.EnforceSameNumElts(OtherNode.getExtType(OResNo),
                                  NodeToApply.getExtType(ResNo));
   }
   case SDTCisSameSizeAs: {
     unsigned OResNo = 0;
-    TreePatternNode &OtherNode = getOperandNum(
-        x.SDTCisSameSizeAs_Info.OtherOperandNum, N, NodeInfo, OResNo);
+    TreePatternNode &OtherNode =
+        getOperandNum(OtherOperandNo, N, NodeInfo, OResNo);
     return TI.EnforceSameSize(OtherNode.getExtType(OResNo),
                               NodeToApply.getExtType(ResNo));
   }
@@ -1707,6 +1703,58 @@ bool SDTypeConstraint::ApplyTypeConstraint(TreePatternNode &N,
   llvm_unreachable("Invalid ConstraintType!");
 }
 
+bool llvm::operator==(const SDTypeConstraint &LHS,
+                      const SDTypeConstraint &RHS) {
+  if (std::tie(LHS.OperandNo, LHS.ConstraintType) !=
+      std::tie(RHS.OperandNo, RHS.ConstraintType))
+    return false;
+  switch (LHS.ConstraintType) {
+  case SDTypeConstraint::SDTCisVT:
+  case SDTypeConstraint::SDTCVecEltisVT:
+    return LHS.VVT == RHS.VVT;
+  case SDTypeConstraint::SDTCisPtrTy:
+  case SDTypeConstraint::SDTCisInt:
+  case SDTypeConstraint::SDTCisFP:
+  case SDTypeConstraint::SDTCisVec:
+    break;
+  case SDTypeConstraint::SDTCisSameAs:
+  case SDTypeConstraint::SDTCisVTSmallerThanOp:
+  case SDTypeConstraint::SDTCisOpSmallerThanOp:
+  case SDTypeConstraint::SDTCisEltOfVec:
+  case SDTypeConstraint::SDTCisSubVecOfVec:
+  case SDTypeConstraint::SDTCisSameNumEltsAs:
+  case SDTypeConstraint::SDTCisSameSizeAs:
+    return LHS.OtherOperandNo == RHS.OtherOperandNo;
+  }
+  return true;
+}
+
+bool llvm::operator<(const SDTypeConstraint &LHS, const SDTypeConstraint &RHS) {
+  if (std::tie(LHS.OperandNo, LHS.ConstraintType) !=
+      std::tie(RHS.OperandNo, RHS.ConstraintType))
+    return std::tie(LHS.OperandNo, LHS.ConstraintType) <
+           std::tie(RHS.OperandNo, RHS.ConstraintType);
+  switch (LHS.ConstraintType) {
+  case SDTypeConstraint::SDTCisVT:
+  case SDTypeConstraint::SDTCVecEltisVT:
+    return LHS.VVT < RHS.VVT;
+  case SDTypeConstraint::SDTCisPtrTy:
+  case SDTypeConstraint::SDTCisInt:
+  case SDTypeConstraint::SDTCisFP:
+  case SDTypeConstraint::SDTCisVec:
+    break;
+  case SDTypeConstraint::SDTCisSameAs:
+  case SDTypeConstraint::SDTCisVTSmallerThanOp:
+  case SDTypeConstraint::SDTCisOpSmallerThanOp:
+  case SDTypeConstraint::SDTCisEltOfVec:
+  case SDTypeConstraint::SDTCisSubVecOfVec:
+  case SDTypeConstraint::SDTCisSameNumEltsAs:
+  case SDTypeConstraint::SDTCisSameSizeAs:
+    return LHS.OtherOperandNo < RHS.OtherOperandNo;
+  }
+  return false;
+}
+
 // Update the node type to match an instruction operand or result as specified
 // in the ins or outs lists on the instruction definition. Return true if the
 // type was actually changed.
@@ -1797,6 +1845,13 @@ SDNodeInfo::SDNodeInfo(const Record *R, const CodeGenHwModes &CGH) : Def(R) {
 
   // Parse the properties.
   Properties = parseSDPatternOperatorProperties(R);
+  IsStrictFP = R->getValueAsBit("IsStrictFP");
+
+  std::optional<uint64_t> MaybeTSFlags =
+      R->getValueAsBitsInit("TSFlags")->convertInitializerToInt();
+  if (!MaybeTSFlags)
+    PrintFatalError(R->getLoc(), "Invalid TSFlags");
+  TSFlags = *MaybeTSFlags;
 
   // Parse the type constraints.
   for (const Record *R : TypeProfile->getValueAsListOfDefs("Constraints"))
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
index f8c39172938256..5735a6ccbcda9c 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
@@ -354,10 +354,11 @@ typedef StringSet<> MultipleUseVarSet;
 /// SDTypeConstraint - This is a discriminated union of constraints,
 /// corresponding to the SDTypeConstraint tablegen class in Target.td.
 struct SDTypeConstraint {
+  SDTypeConstraint() = default;
   SDTypeConstraint(const Record *R, const CodeGenHwModes &CGH);
 
   unsigned OperandNo; // The operand # this constraint applies to.
-  enum {
+  enum KindTy {
     SDTCisVT,
     SDTCisPtrTy,
     SDTCisInt,
@@ -373,29 +374,7 @@ struct SDTypeConstraint {
     SDTCisSameSizeAs
   } ConstraintType;
 
-  union { // The discriminated union.
-    struct {
-      unsigned OtherOperandNum;
-    } SDTCisSameAs_Info;
-    struct {
-      unsigned OtherOperandNum;
-    } SDTCisVTSmallerThanOp_Info;
-    struct {
-      unsigned BigOperandNum;
-    } SDTCisOpSmallerThanOp_Info;
-    struct {
-      unsigned OtherOperandNum;
-    } SDTCisEltOfVec_Info;
-    struct {
-      unsigned OtherOperandNum;
-    } SDTCisSubVecOfVec_Info;
-    struct {
-      unsigned OtherOperandNum;
-    } SDTCisSameNumEltsAs_Info;
-    struct {
-      unsigned OtherOperandNum;
-    } SDTCisSameSizeAs_Info;
-  } x;
+  unsigned OtherOperandNo;
 
   // The VT for SDTCisVT and SDTCVecEltisVT.
   // Must not be in the union because it has a non-trivial destructor.
@@ -407,6 +386,11 @@ struct SDTypeConstraint {
   /// is flagged.
   bool ApplyTypeConstraint(TreePatternNode &N, const SDNodeInfo &NodeInfo,
                            TreePattern &TP) const;
+
+  friend bool operator==(const SDTypeConstraint &LHS,
+                         const SDTypeConstraint &RHS);
+  friend bool operator<(const SDTypeConstraint &LHS,
+                        const SDTypeConstraint &RHS);
 };
 
 /// ScopedName - A name of a node associated with a "scope" that indicates
@@ -438,9 +422,11 @@ class SDNodeInfo {
   const Record *Def;
   StringRef EnumName;
   StringRef SDClassName;
-  unsigned Properties;
   unsigned NumResults;
   int NumOperands;
+  unsigned Properties;
+  bool IsStrictFP;
+  uint64_t TSFlags;
   std::vector<SDTypeConstraint> TypeConstraints;
 
 public:
@@ -465,10 +451,16 @@ class SDNodeInfo {
   /// MVT::SimpleValueType.  Otherwise, return MVT::Other.
   MVT::SimpleValueType getKnownType(unsigned ResNo) const;
 
+  unsigned getProperties() const { return Properties; }
+
   /// hasProperty - Return true if this node has the specified property.
   ///
   bool hasProperty(enum SDNP Prop) const { return Properties & (1 << Prop); }
 
+  bool isStrictFP() const { return IsStrictFP; }
+
+  uint64_t getTSFlags() const { return TSFlags; }
+
   /// ApplyTypeConstraints - Given a node in a pattern, apply the type
   /// constraints for this node to the operands of the node.  This returns
   /// true if it makes a change, false otherwise.  If a type contradiction is
diff --git a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
new file mode 100644
index 00000000000000..fad23027de720c
--- /dev/null
+++ b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
@@ -0,0 +1,367 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Basic/SequenceToOffsetTable.h"
+#include "Common/CodeGenDAGPatterns.h" // For SDNodeInfo.
+#include "llvm/Support/CommandLine.h"
+#include "llvm/TableGen/Error.h"
+#include "llvm/TableGen/StringToOffsetTable.h"
+#include "llvm/TableGen/TableGenBackend.h"
+
+using namespace llvm;
+
+static cl::OptionCategory SDNodeInfoEmitterCat("Options for -gen-sdnode-info");
+
+static cl::opt<std::string> TargetSDNodeNamespace(
+    "sdnode-namespace", cl::cat(SDNodeInfoEmitterCat),
+    cl::desc("Specify target SDNode namespace (default=<Target>ISD)"));
+
+static cl::opt<bool> WarnOnSkippedNodes(
+    "warn-on-skipped-nodes", cl::cat(SDNodeInfoEmitterCat),
+    cl::desc("Explain why a node was skipped (default=true)"), cl::init(true));
+
+namespace {
+
+class SDNodeInfoEmitter {
+  const RecordKeeper &RK;
+  const CodeGenTarget Target;
+  std::vector<SDNodeInfo> AllNodes;
+  std::map<StringRef, SmallVector<const SDNodeInfo *, 2>> TargetNodesByName;
+
+public:
+  explicit SDNodeInfoEmitter(const RecordKeeper &RK);
+
+  void run(raw_ostream &OS) const;
+
+private:
+  void emitEnum(raw_ostream &OS) const;
+
+  std::vector<unsigned> emitNodeNames(raw_ostream &OS) const;
+
+  std::vector<std::pair<unsigned, unsigned>>
+  emitTypeConstraints(raw_ostream &OS) const;
+
+  void emitDescs(raw_ostream &OS) const;
+};
+
+} // namespace
+
+static bool haveCompatibleDescriptions(const SDNodeInfo *N1,
+                                       const SDNodeInfo *N2) {
+  // Number of results/operands must match.
+  if (N1->getNumResults() != N2->getNumResults() ||
+      N1->getNumOperands() != N2->getNumOperands())
+    return false;
+
+  // Flags must match.
+  if (N1->isStrictFP() != N2->isStrictFP() ||
+      N1->getTSFlags() != N2->getTSFlags())
+    return false;
+
+  // We're only interested in a subset of node properties. Properties like
+  // SDNPAssociative and SDNPCommutative do not impose constraints on nodes,
+  // and sometimes differ between nodes sharing the same enum name.
+  constexpr unsigned PropMask = (1 << SDNPHasChain) | (1 << SDNPOutGlue) |
+                                (1 << SDNPInGlue) | (1 << SDNPOptInGlue) |
+                                (1 << SDNPMemOperand) | (1 << SDNPVariadic);
+
+  return (N1->getProperties() & PropMask) == (N2->getProperties() & PropMask);
+}
+
+static bool haveCompatibleDescriptions(ArrayRef<const SDNodeInfo *> Nodes) {
+  const SDNodeInfo *N = Nodes.front();
+  return all_of(drop_begin(Nodes), [&](const SDNodeInfo *Other) {
+    return haveCompatibleDescriptions(Other, N);
+  });
+}
+
+static void warnOnSkippedNode(const SDNodeInfo *N, const Twine &Reason) {
+  PrintWarning(N->getRecord()->getLoc(), "skipped node: " + Reason);
+}
+
+SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
+    : RK(RK), Target(RK) {
+  const CodeGenHwModes &HwModes = Target.getHwModes();
+
+  // Figure out target SDNode namespace.
+  if (!TargetSDNodeNamespace.getNumOccurrences())
+    TargetSDNodeNamespace = Target.getName().str() + "ISD";
+
+  // Parse all SDNode records.
+  for (const Record *R : RK.getAllDerivedDefinitions("SDNode"))
+    AllNodes.emplace_back(R, HwModes);
+
+  // Filter nodes by the target SDNode namespace and create a mapping
+  // from an enum name to a list of nodes that have that name.
+  // The mapping is usually 1:1, but in rare cases it can be 1:N.
+  for (const SDNodeInfo &Node : AllNodes) {
+    StringRef QualifiedEnumName = Node.getEnumName();
+    auto [NS, EnumName] = QualifiedEnumName.split("::");
+
+    if (NS.empty() || EnumName.empty()) {
+      if (WarnOnSkippedNodes)
+        warnOnSkippedNode(&Node, "invalid enum name");
+      continue;
+    }
+
+    if (NS != TargetSDNodeNamespace)
+      continue;
+
+    TargetNodesByName[EnumName].push_back(&Node);
+  }
+
+  // Filter out nodes that have different "prototypes" and/or flags.
+  // Don't look at type constraints though, we will simply skip emitting
+  // the constraints if they differ.
+  decltype(TargetNodesByName)::iterator Next;
+  for (auto I = TargetNodesByName.begin(), E = TargetNodesByName.end(); I != E;
+       I = Next) {
+    Next = std::next(I);
+
+    if (haveCompatibleDescriptions(I->second))
+      continue;
+
+    if (WarnOnSkippedNodes)
+      for (const SDNodeInfo *N : I->second)
+        warnOnSkippedNode(N, "incompatible description");
+
+    TargetNodesByName.erase(I);
+  }
+}
+
+void SDNodeInfoEmitter::emitEnum(raw_ostream &OS) const {
+  OS << "#ifdef GET_SDNODE_ENUM\n";
+  OS << "#undef GET_SDNODE_ENUM\n\n";
+  OS << "namespace llvm::" << TargetSDNodeNamespace << " {\n\n";
+
+  if (!TargetNodesByName.empty()) {
+    StringRef FirstName = TargetNodesByName.begin()->first;
+    StringRef LastName = TargetNodesByName.rbegin()->first;
+
+    OS << "enum GenNodeType : unsigned {\n";
+    OS << "  " << FirstName << " = ISD::BUILTIN_OP_END,\n";
+
+    for (StringRef EnumName : make_first_range(drop_begin(TargetNodesByName)))
+      OS << "  " << EnumName << ",\n";
+
+    OS << "};\n\n";
+    OS << "static constexpr unsigned GENERATED_OPCODE_END = " << LastName
+       << " + 1;\n\n";
+  } else {
+    OS << "static constexpr unsigned GENERATED_OPCODE_END = "
+          "ISD::BUILTIN_OP_END;\n\n";
+  }
+
+  OS << "} // namespace llvm::" << TargetSDNodeNamespace << "\n\n";
+  OS << "#endif // GET_SDNODE_ENUM\n\n";
+}
+
+std::vector<unsigned> SDNodeInfoEmitter::emitNodeNames(raw_ostream &OS) const {
+  StringToOffsetTable NameTable;
+
+  std::vector<unsigned> NameOffsets;
+  NameOffsets.reserve(TargetNodesByName.size());
+
+  for (StringRef EnumName : make_first_range(TargetNodesByName)) {
+    SmallString<64> DebugName;
+    (TargetSDNodeNamespace + "::" + EnumName).toVector(DebugName);
+    NameOffsets.push_back(NameTable.GetOrAddStringOffset(DebugName));
+  }
+
+  NameTable.EmitStringLiteralDef(
+      OS, "static const char " + Target.getName() + "SDNodeNames[]",
+      /*Indent=*/"");
+  OS << "\n";
+
+  return NameOffsets;
+}
+
+static StringRef getTypeConstraintKindName(SDTypeConstraint::KindTy Kind) {
+#define CASE(NAME)                                                             \
+  case SDTypeConstraint::NAME:                                                 \
+    return #NAME
+
+  switch (Kind) {
+    CASE(SDTCisVT);
+    CASE(SDTCisPtrTy);
+    CASE(SDTCisInt);
+    CASE(SDTCisFP);
+    CASE(SDTCisVec);
+    CASE(SDTCisSameAs);
+    CASE(SDTCisVTSmallerThanOp);
+    CASE(SDTCisOpSmallerThanOp);
+    CASE(SDTCisEltOfVec);
+    CASE(SDTCisSubVecOfVec);
+    CASE(SDTCVecEltisVT);
+    CASE(SDTCisSameNumEltsAs);
+    CASE(SDTCisSameSizeAs);
+  }
+  llvm_unreachable("Unknown constraint kind"); // Make MSVC happy.
+#undef CASE
+}
+
+static void emitTypeConstraint(raw_ostream &OS, SDTypeConstraint C) {
+  unsigned OtherOpNo = 0;
+  MVT VT;
+
+  switch (C.ConstraintType) {
+  case SDTypeConstraint::SDTCisVT:
+  case SDTypeConstraint::SDTCVecEltisVT:
+    if (C.VVT.isSimple())
+      VT = C.VVT.getSimple();
+    break;
+  case SDTypeConstraint::SDTCisPtrTy:
+  case SDTypeConstraint::SDTCisInt:
+  case SDTypeConstraint::SDTCisFP:
+  case SDTypeConstraint::SDTCisVec:
+    break;
+  case SDTypeConstraint::SDTCisSameAs:
+  case SDTypeConstraint::SDTCisVTSmallerThanOp:
+  case SDTypeConstraint::SDTCisOpSmallerThanOp:
+  case SDTypeConstraint::SDTCisEltOfVec:
+  case SDTypeConstraint::SDTCisSubVecOfVec:
+  case SDTypeConstraint::SDTCisSameNumEltsAs:
+  case SDTypeConstraint::SDTCisSameSizeAs:
+    OtherOpNo = C.OtherOperandNo;
+    break;
+  }
+
+  StringRef KindName = getTypeConstraintKindName(C.ConstraintType);
+  StringRef VTName = VT.SimpleTy == MVT::INVALID_SIMPLE_VALUE_TYPE
+                         ? "MVT::INVALID_SIMPLE_VALUE_TYPE"
+                         : getEnumName(VT.SimpleTy);
+  OS << formatv("{{{}, {}, {}, {}}", KindName, C.OperandNo, OtherOpNo, VTName);
+}
+
+std::vector<std::pair<unsigned, unsigned>>
+SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
+  using ConstraintsVecTy = SmallVector<SDTypeConstraint, 0>;
+  SequenceToOffsetTable<ConstraintsVecTy> ConstraintTable(
+      /*Terminator=*/std::nullopt);
+
+  std::vector<std::pair<unsigned, unsigned>> ConstraintOffsetsAndCounts;
+  ConstraintOffsetsAndCounts.reserve(TargetNodesByName.size());
+
+  SmallVector<StringRef> SkippedNodes;
+  for (const auto &[EnumName, Nodes] : TargetNodesByName) {
+    ArrayRef Constraints = Nodes.front()->getTypeConstraints();
+
+    bool IsAmbiguous = any_of(drop_begin(Nodes), [&](const SDNodeInfo *Other) {
+      return ArrayRef(Other->getTypeConstraints()) != Constraints;
+    });
+
+    // If nodes with the same enum name have different constraints,
+    // treat them as if they had no constraints at all.
+    if (IsAmbiguous) {
+      SkippedNodes.push_back(EnumName);
+      continue;
+    }
+
+    // Don't add empty sequences to the table. This slightly simplifies
+    // the implementation and makes the output less confusing if the table
+    // ends up empty.
+    if (Constraints.empty())
+      continue;
+
+    // SequenceToOffsetTable reuses the storage if a sequence matches another
+    // sequence's *suffix*. It is more likely that we have a matching *prefix*,
+    // so reverse the order to increase the likelihood of a match.
+    ConstraintTable.add(ConstraintsVecTy(reverse(Constraints)));
+  }
+
+  ConstraintTable.layout();
+
+  OS << "static const SDTypeConstraint " << Target.getName()
+     << "SDTypeConstraints[] = {\n";
+  ConstraintTable.emit(OS, emitTypeConstraint);
+  OS << "};\n\n";
+
+  for (const auto &[EnumName, Nodes] : TargetNodesByName) {
+    ArrayRef Constraints = Nodes.front()->getTypeConstraints();
+
+    if (Constraints.empty() || is_contained(SkippedNodes, EnumName)) {
+      ConstraintOffsetsAndCounts.emplace_back(/*Offset=*/0, /*Size=*/0);
+      continue;
+    }
+
+    unsigned ConstraintsOffset =
+        ConstraintTable.get(ConstraintsVecTy(reverse(Constraints)));
+    ConstraintOffsetsAndCounts.emplace_back(ConstraintsOffset,
+                                            Constraints.size());
+  }
+
+  return ConstraintOffsetsAndCounts;
+}
+
+static void emitDesc(raw_ostream &OS, StringRef EnumName,
+                     ArrayRef<const SDNodeInfo *> Nodes, unsigned NameOffset,
+                     unsigned ConstraintsOffset, unsigned ConstraintCount) {
+  assert(haveCompatibleDescriptions(Nodes));
+  const SDNodeInfo *N = Nodes.front();
+  OS << "    {" << N->getNumResults() << ", " << N->getNumOperands() << ", 0";
+
+  // Emitted properties must be kept in sync with haveCompatibleDescriptions.
+  unsigned Properties = N->getProperties();
+  if (Properties & (1 << SDNPHasChain))
+    OS << "|1<<SDNPHasChain";
+  if (Properties & (1 << SDNPOutGlue))
+    OS << "|1<<SDNPOutGlue";
+  if (Properties & (1 << SDNPInGlue))
+    OS << "|1<<SDNPInGlue";
+  if (Properties & (1 << SDNPOptInGlue))
+    OS << "|1<<SDNPOptInGlue";
+  if (Properties & (1 << SDNPVariadic))
+    OS << "|1<<SDNPVariadic";
+  if (Properties & (1 << SDNPMemOperand))
+    OS << "|1<<SDNPMemOperand";
+
+  OS << ", 0";
+  if (N->isStrictFP())
+    OS << "|1<<SDNFIsStrictFP";
+
+  OS << formatv(", {}, {}, {}, {}}, // {}\n", N->getTSFlags(), NameOffset,
+                ConstraintsOffset, ConstraintCount, EnumName);
+}
+
+void SDNodeInfoEmitter::emitDescs(raw_ostream &OS) const {
+  StringRef TargetName = Target.getName();
+
+  OS << "#ifdef GET_SDNODE_DESC\n";
+  OS << "#undef GET_SDNODE_DESC\n\n";
+  OS << "namespace llvm {\n";
+
+  std::vector<unsigned> NameOffsets = emitNodeNames(OS);
+  std::vector<std::pair<unsigned, unsigned>> ConstraintOffsetsAndCounts =
+      emitTypeConstraints(OS);
+
+  OS << "static const SDNodeDesc " << TargetName << "SDNodeDescs[] = {\n";
+
+  for (const auto &[NameAndNodes, NameOffset, ConstraintOffsetAndCount] :
+       zip_equal(TargetNodesByName, NameOffsets, ConstraintOffsetsAndCounts))
+    emitDesc(OS, NameAndNodes.first, NameAndNodes.second, NameOffset,
+             ConstraintOffsetAndCount.first, ConstraintOffsetAndCount.second);
+
+  OS << "};\n\n";
+
+  OS << formatv("static const SDNodeInfo {0}GenSDNodeInfo(\n"
+                "    /*NumOpcodes=*/{1}, {0}SDNodeDescs,\n"
+                "    {0}SDNodeNames, {0}SDTypeConstraints);\n\n",
+                TargetName, TargetNodesByName.size());
+
+  OS << "} // namespace llvm\n\n";
+  OS << "#endif // GET_SDNODE_DESC\n\n";
+}
+
+void SDNodeInfoEmitter::run(raw_ostream &OS) const {
+  emitSourceFileHeader("Target SDNode descriptions", OS, RK);
+  emitEnum(OS);
+  emitDescs(OS);
+}
+
+static TableGen::Emitter::OptClass<SDNodeInfoEmitter>
+    X("gen-sd-node-info", "Generate target SDNode descriptions");

>From b34b6470960e27f09add4cbcb50517ee433e7d32 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <s.barannikov at module.ru>
Date: Wed, 15 Jan 2025 08:26:19 +0300
Subject: [PATCH 2/7] Add missing check lines to a test

---
 llvm/test/TableGen/SDNodeInfoEmitter/basic.td | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
index 7f5085e31e02c4..dbe6def7106890 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
@@ -46,6 +46,8 @@ def MyTarget : Target;
 // CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
 // CHECK-EMPTY:
 // CHECK-NEXT:  } // namespace llvm
+// CHECK-EMPTY:
+// CHECK-NEXT:  #endif // GET_SDNODE_DESC
 
 
 //--- trivial-node.td

>From 0b27718caaa1ded117375123bc8cdc1d142e5056 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <s.barannikov at module.ru>
Date: Wed, 15 Jan 2025 08:34:53 +0300
Subject: [PATCH 3/7] Address review comments

* Add missing template parameter to ArrayRef
* Shrink TSFlags to 32 bits
---
 llvm/include/llvm/Target/TargetSelectionDAG.td    | 2 +-
 llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp | 3 ++-
 llvm/utils/TableGen/Common/CodeGenDAGPatterns.h   | 4 ++--
 llvm/utils/TableGen/SDNodeInfoEmitter.cpp         | 6 ++++--
 4 files changed, 9 insertions(+), 6 deletions(-)

diff --git a/llvm/include/llvm/Target/TargetSelectionDAG.td b/llvm/include/llvm/Target/TargetSelectionDAG.td
index cd2ca3a08bd691..7f3c2be90d8207 100644
--- a/llvm/include/llvm/Target/TargetSelectionDAG.td
+++ b/llvm/include/llvm/Target/TargetSelectionDAG.td
@@ -354,7 +354,7 @@ class SDNode<string opcode, SDTypeProfile typeprof,
   let Properties = props;
   SDTypeProfile TypeProfile = typeprof;
   bit IsStrictFP = false;
-  bits<64> TSFlags = 0;
+  bits<32> TSFlags = 0;
 }
 
 // Special TableGen-recognized dag nodes
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
index 6629baa6e071ea..d155da3bed48f1 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.cpp
@@ -1847,10 +1847,11 @@ SDNodeInfo::SDNodeInfo(const Record *R, const CodeGenHwModes &CGH) : Def(R) {
   Properties = parseSDPatternOperatorProperties(R);
   IsStrictFP = R->getValueAsBit("IsStrictFP");
 
-  std::optional<uint64_t> MaybeTSFlags =
+  std::optional<int64_t> MaybeTSFlags =
       R->getValueAsBitsInit("TSFlags")->convertInitializerToInt();
   if (!MaybeTSFlags)
     PrintFatalError(R->getLoc(), "Invalid TSFlags");
+  assert(isUInt<32>(*MaybeTSFlags) && "TSFlags bit width out of sync");
   TSFlags = *MaybeTSFlags;
 
   // Parse the type constraints.
diff --git a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
index 5735a6ccbcda9c..6a6f1a6ac437cc 100644
--- a/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
+++ b/llvm/utils/TableGen/Common/CodeGenDAGPatterns.h
@@ -426,7 +426,7 @@ class SDNodeInfo {
   int NumOperands;
   unsigned Properties;
   bool IsStrictFP;
-  uint64_t TSFlags;
+  uint32_t TSFlags;
   std::vector<SDTypeConstraint> TypeConstraints;
 
 public:
@@ -459,7 +459,7 @@ class SDNodeInfo {
 
   bool isStrictFP() const { return IsStrictFP; }
 
-  uint64_t getTSFlags() const { return TSFlags; }
+  uint32_t getTSFlags() const { return TSFlags; }
 
   /// ApplyTypeConstraints - Given a node in a pattern, apply the type
   /// constraints for this node to the operands of the node.  This returns
diff --git a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
index fad23027de720c..17b55810045650 100644
--- a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
+++ b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
@@ -249,7 +249,8 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
 
   SmallVector<StringRef> SkippedNodes;
   for (const auto &[EnumName, Nodes] : TargetNodesByName) {
-    ArrayRef Constraints = Nodes.front()->getTypeConstraints();
+    ArrayRef<SDTypeConstraint> Constraints =
+        Nodes.front()->getTypeConstraints();
 
     bool IsAmbiguous = any_of(drop_begin(Nodes), [&](const SDNodeInfo *Other) {
       return ArrayRef(Other->getTypeConstraints()) != Constraints;
@@ -282,7 +283,8 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
   OS << "};\n\n";
 
   for (const auto &[EnumName, Nodes] : TargetNodesByName) {
-    ArrayRef Constraints = Nodes.front()->getTypeConstraints();
+    ArrayRef<SDTypeConstraint> Constraints =
+        Nodes.front()->getTypeConstraints();
 
     if (Constraints.empty() || is_contained(SkippedNodes, EnumName)) {
       ConstraintOffsetsAndCounts.emplace_back(/*Offset=*/0, /*Size=*/0);

>From 4db8ac318f62590cbfd97e53f4c94c80de12d349 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <s.barannikov at module.ru>
Date: Wed, 15 Jan 2025 09:32:45 +0300
Subject: [PATCH 4/7] Add a more complex test

---
 llvm/test/TableGen/SDNodeInfoEmitter/basic.td | 96 +++++++++++++++++++
 1 file changed, 96 insertions(+)

diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
index dbe6def7106890..5332b4f458dfd9 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
@@ -85,3 +85,99 @@ def my_noop : SDNode<"MyTargetISD::NOOP", SDTypeProfile<0, 0, []>>;
 // CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
 // CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
 // CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+
+//--- advanced.td
+// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %t/advanced.td \
+// RUN:   | FileCheck %t/advanced.td
+
+include "llvm/Target/Target.td"
+
+def MyTarget : Target;
+
+def my_node_1 : SDNode<
+    "MyTargetISD::NODE_1",
+    SDTypeProfile<1, 1, [SDTCisVT<0, i1>, SDTCisVT<1, i2>]>,
+    [SDNPHasChain]
+>;
+
+let TSFlags = 42 in
+def my_node_2 : SDNode<
+    "MyTargetISD::NODE_2",
+    SDTypeProfile<3, 1, [
+        // Prefix of my_node_3 constraints.
+        SDTCisVT<0, i1>,
+        SDTCisPtrTy<1>,
+        SDTCisInt<2>,
+        SDTCisFP<3>,
+    ]>,
+    [SDNPMayStore, SDNPMayLoad, SDNPSideEffect,
+     SDNPMemOperand, SDNPVariadic]
+>;
+
+let IsStrictFP = true, TSFlags = 24 in
+def my_node_3 : SDNode<
+    "MyTargetISD::NODE_3",
+    SDTypeProfile<2, -1, [
+        SDTCisVT<0, i1>,
+        SDTCisPtrTy<1>,
+        SDTCisInt<2>,
+        SDTCisFP<3>,
+        SDTCisVec<4>,
+        SDTCisSameAs<6, 5>,
+        SDTCisVTSmallerThanOp<8, 7>,
+        SDTCisOpSmallerThanOp<10, 9>,
+        SDTCisEltOfVec<12, 11>,
+        SDTCisSubVecOfVec<14, 13>,
+        SDTCVecEltisVT<15, i32>,
+        SDTCisSameNumEltsAs<17, 16>,
+        SDTCisSameSizeAs<19, 18>,
+    ]>,
+    [SDNPCommutative, SDNPAssociative, SDNPHasChain,
+     SDNPOutGlue, SDNPInGlue, SDNPOptInGlue]
+>;
+
+// CHECK:       namespace llvm::MyTargetISD {
+// CHECK-EMPTY:
+// CHECK-NEXT:  enum GenNodeType : unsigned {
+// CHECK-NEXT:    NODE_1 = ISD::BUILTIN_OP_END,
+// CHECK-NEXT:    NODE_2,
+// CHECK-NEXT:    NODE_3,
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static constexpr unsigned GENERATED_OPCODE_END = NODE_3 + 1;
+// CHECK-EMPTY:
+// CHECK-NEXT:  } // namespace llvm::MyTargetISD
+
+// CHECK:       static const char MyTargetSDNodeNames[] =
+// CHECK-NEXT:    "MyTargetISD::NODE_1\0"
+// CHECK-NEXT:    "MyTargetISD::NODE_2\0"
+// CHECK-NEXT:    "MyTargetISD::NODE_3\0"
+// CHECK-NEXT:    "\0";
+
+// CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
+// CHECK-NEXT:    /* 0 */ {SDTCisVT, 1, 0, MVT::i2},
+// CHECK-SAME:            {SDTCisVT, 0, 0, MVT::i1},
+// CHECK-NEXT:    /* 2 */ {SDTCisSameSizeAs, 19, 18, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisSameNumEltsAs, 17, 16, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCVecEltisVT, 15, 0, MVT::i32},
+// CHECK-SAME:            {SDTCisSubVecOfVec, 14, 13, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisEltOfVec, 12, 11, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisOpSmallerThanOp, 10, 9, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisVTSmallerThanOp, 8, 7, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisSameAs, 6, 5, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisVec, 4, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisFP, 3, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisInt, 2, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisPtrTy, 1, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME:            {SDTCisVT, 0, 0, MVT::i1},
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
+// CHECK-NEXT:          {1, 1, 0|1<<SDNPHasChain, 0, 0, 0, 0, 2}, // NODE_1
+// CHECK-NEXT:          {3, 1, 0|1<<SDNPVariadic|1<<SDNPMemOperand, 0, 42, 20, 11, 4}, // NODE_2
+// CHECK-NEXT:          {2, -1, 0|1<<SDNPHasChain|1<<SDNPOutGlue|1<<SDNPInGlue|1<<SDNPOptInGlue, 0|1<<SDNFIsStrictFP, 24, 40, 2, 13}, // NODE_3
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
+// CHECK-NEXT: static const SDNodeInfo MyTargetGenSDNodeInfo(
+// CHECK-NEXT:     /*NumOpcodes=*/3, MyTargetSDNodeDescs,
+// CHECK-NEXT:     MyTargetSDNodeNames, MyTargetSDTypeConstraints);

>From 3f20514c49368296e96adc3df11906d69f0472b7 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Fri, 17 Jan 2025 22:58:15 +0300
Subject: [PATCH 5/7] Use raw_svector_ostream

---
 llvm/utils/TableGen/SDNodeInfoEmitter.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
index 17b55810045650..51e64f1689f5c4 100644
--- a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
+++ b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
@@ -169,14 +169,15 @@ std::vector<unsigned> SDNodeInfoEmitter::emitNodeNames(raw_ostream &OS) const {
 
   for (StringRef EnumName : make_first_range(TargetNodesByName)) {
     SmallString<64> DebugName;
-    (TargetSDNodeNamespace + "::" + EnumName).toVector(DebugName);
+    raw_svector_ostream SS(DebugName);
+    SS << TargetSDNodeNamespace << "::" << EnumName;
     NameOffsets.push_back(NameTable.GetOrAddStringOffset(DebugName));
   }
 
   NameTable.EmitStringLiteralDef(
       OS, "static const char " + Target.getName() + "SDNodeNames[]",
       /*Indent=*/"");
-  OS << "\n";
+  OS << '\n';
 
   return NameOffsets;
 }

>From 01ef09f36be8e6074ad634bc71b2761747983b6d Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Sat, 18 Jan 2025 02:53:47 +0300
Subject: [PATCH 6/7] Merge loops

---
 llvm/utils/TableGen/SDNodeInfoEmitter.cpp | 64 ++++++++++-------------
 1 file changed, 28 insertions(+), 36 deletions(-)

diff --git a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
index 51e64f1689f5c4..4116dd50588876 100644
--- a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
+++ b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
@@ -30,8 +30,7 @@ namespace {
 class SDNodeInfoEmitter {
   const RecordKeeper &RK;
   const CodeGenTarget Target;
-  std::vector<SDNodeInfo> AllNodes;
-  std::map<StringRef, SmallVector<const SDNodeInfo *, 2>> TargetNodesByName;
+  std::map<StringRef, SmallVector<SDNodeInfo, 2>> TargetNodesByName;
 
 public:
   explicit SDNodeInfoEmitter(const RecordKeeper &RK);
@@ -51,16 +50,15 @@ class SDNodeInfoEmitter {
 
 } // namespace
 
-static bool haveCompatibleDescriptions(const SDNodeInfo *N1,
-                                       const SDNodeInfo *N2) {
+static bool haveCompatibleDescriptions(const SDNodeInfo &N1,
+                                       const SDNodeInfo &N2) {
   // Number of results/operands must match.
-  if (N1->getNumResults() != N2->getNumResults() ||
-      N1->getNumOperands() != N2->getNumOperands())
+  if (N1.getNumResults() != N2.getNumResults() ||
+      N1.getNumOperands() != N2.getNumOperands())
     return false;
 
   // Flags must match.
-  if (N1->isStrictFP() != N2->isStrictFP() ||
-      N1->getTSFlags() != N2->getTSFlags())
+  if (N1.isStrictFP() != N2.isStrictFP() || N1.getTSFlags() != N2.getTSFlags())
     return false;
 
   // We're only interested in a subset of node properties. Properties like
@@ -70,18 +68,18 @@ static bool haveCompatibleDescriptions(const SDNodeInfo *N1,
                                 (1 << SDNPInGlue) | (1 << SDNPOptInGlue) |
                                 (1 << SDNPMemOperand) | (1 << SDNPVariadic);
 
-  return (N1->getProperties() & PropMask) == (N2->getProperties() & PropMask);
+  return (N1.getProperties() & PropMask) == (N2.getProperties() & PropMask);
 }
 
-static bool haveCompatibleDescriptions(ArrayRef<const SDNodeInfo *> Nodes) {
-  const SDNodeInfo *N = Nodes.front();
-  return all_of(drop_begin(Nodes), [&](const SDNodeInfo *Other) {
+static bool haveCompatibleDescriptions(ArrayRef<SDNodeInfo> Nodes) {
+  const SDNodeInfo &N = Nodes.front();
+  return all_of(drop_begin(Nodes), [&](const SDNodeInfo &Other) {
     return haveCompatibleDescriptions(Other, N);
   });
 }
 
-static void warnOnSkippedNode(const SDNodeInfo *N, const Twine &Reason) {
-  PrintWarning(N->getRecord()->getLoc(), "skipped node: " + Reason);
+static void warnOnSkippedNode(const SDNodeInfo &N, const Twine &Reason) {
+  PrintWarning(N.getRecord()->getLoc(), "skipped node: " + Reason);
 }
 
 SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
@@ -92,27 +90,23 @@ SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
   if (!TargetSDNodeNamespace.getNumOccurrences())
     TargetSDNodeNamespace = Target.getName().str() + "ISD";
 
-  // Parse all SDNode records.
-  for (const Record *R : RK.getAllDerivedDefinitions("SDNode"))
-    AllNodes.emplace_back(R, HwModes);
-
   // Filter nodes by the target SDNode namespace and create a mapping
   // from an enum name to a list of nodes that have that name.
   // The mapping is usually 1:1, but in rare cases it can be 1:N.
-  for (const SDNodeInfo &Node : AllNodes) {
-    StringRef QualifiedEnumName = Node.getEnumName();
-    auto [NS, EnumName] = QualifiedEnumName.split("::");
+  for (const Record *R : RK.getAllDerivedDefinitions("SDNode")) {
+    SDNodeInfo Node(R, HwModes);
+    auto [NS, EnumName] = Node.getEnumName().split("::");
 
     if (NS.empty() || EnumName.empty()) {
       if (WarnOnSkippedNodes)
-        warnOnSkippedNode(&Node, "invalid enum name");
+        warnOnSkippedNode(Node, "invalid enum name");
       continue;
     }
 
     if (NS != TargetSDNodeNamespace)
       continue;
 
-    TargetNodesByName[EnumName].push_back(&Node);
+    TargetNodesByName[EnumName].push_back(std::move(Node));
   }
 
   // Filter out nodes that have different "prototypes" and/or flags.
@@ -127,7 +121,7 @@ SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
       continue;
 
     if (WarnOnSkippedNodes)
-      for (const SDNodeInfo *N : I->second)
+      for (const SDNodeInfo &N : I->second)
         warnOnSkippedNode(N, "incompatible description");
 
     TargetNodesByName.erase(I);
@@ -250,11 +244,10 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
 
   SmallVector<StringRef> SkippedNodes;
   for (const auto &[EnumName, Nodes] : TargetNodesByName) {
-    ArrayRef<SDTypeConstraint> Constraints =
-        Nodes.front()->getTypeConstraints();
+    ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();
 
-    bool IsAmbiguous = any_of(drop_begin(Nodes), [&](const SDNodeInfo *Other) {
-      return ArrayRef(Other->getTypeConstraints()) != Constraints;
+    bool IsAmbiguous = any_of(drop_begin(Nodes), [&](const SDNodeInfo &Other) {
+      return ArrayRef(Other.getTypeConstraints()) != Constraints;
     });
 
     // If nodes with the same enum name have different constraints,
@@ -284,8 +277,7 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
   OS << "};\n\n";
 
   for (const auto &[EnumName, Nodes] : TargetNodesByName) {
-    ArrayRef<SDTypeConstraint> Constraints =
-        Nodes.front()->getTypeConstraints();
+    ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();
 
     if (Constraints.empty() || is_contained(SkippedNodes, EnumName)) {
       ConstraintOffsetsAndCounts.emplace_back(/*Offset=*/0, /*Size=*/0);
@@ -302,14 +294,14 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
 }
 
 static void emitDesc(raw_ostream &OS, StringRef EnumName,
-                     ArrayRef<const SDNodeInfo *> Nodes, unsigned NameOffset,
+                     ArrayRef<SDNodeInfo> Nodes, unsigned NameOffset,
                      unsigned ConstraintsOffset, unsigned ConstraintCount) {
   assert(haveCompatibleDescriptions(Nodes));
-  const SDNodeInfo *N = Nodes.front();
-  OS << "    {" << N->getNumResults() << ", " << N->getNumOperands() << ", 0";
+  const SDNodeInfo &N = Nodes.front();
+  OS << "    {" << N.getNumResults() << ", " << N.getNumOperands() << ", 0";
 
   // Emitted properties must be kept in sync with haveCompatibleDescriptions.
-  unsigned Properties = N->getProperties();
+  unsigned Properties = N.getProperties();
   if (Properties & (1 << SDNPHasChain))
     OS << "|1<<SDNPHasChain";
   if (Properties & (1 << SDNPOutGlue))
@@ -324,10 +316,10 @@ static void emitDesc(raw_ostream &OS, StringRef EnumName,
     OS << "|1<<SDNPMemOperand";
 
   OS << ", 0";
-  if (N->isStrictFP())
+  if (N.isStrictFP())
     OS << "|1<<SDNFIsStrictFP";
 
-  OS << formatv(", {}, {}, {}, {}}, // {}\n", N->getTSFlags(), NameOffset,
+  OS << formatv(", {}, {}, {}, {}}, // {}\n", N.getTSFlags(), NameOffset,
                 ConstraintsOffset, ConstraintCount, EnumName);
 }
 

>From 87f46689ec056b6627524353b271836f29e62d95 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Sat, 18 Jan 2025 02:57:19 +0300
Subject: [PATCH 7/7] Rename TargetNodesByName -> NodesByName

---
 llvm/utils/TableGen/SDNodeInfoEmitter.cpp | 33 +++++++++++------------
 1 file changed, 16 insertions(+), 17 deletions(-)

diff --git a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
index 4116dd50588876..cb971b089f5a4a 100644
--- a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
+++ b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
@@ -30,7 +30,7 @@ namespace {
 class SDNodeInfoEmitter {
   const RecordKeeper &RK;
   const CodeGenTarget Target;
-  std::map<StringRef, SmallVector<SDNodeInfo, 2>> TargetNodesByName;
+  std::map<StringRef, SmallVector<SDNodeInfo, 2>> NodesByName;
 
 public:
   explicit SDNodeInfoEmitter(const RecordKeeper &RK);
@@ -106,15 +106,14 @@ SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
     if (NS != TargetSDNodeNamespace)
       continue;
 
-    TargetNodesByName[EnumName].push_back(std::move(Node));
+    NodesByName[EnumName].push_back(std::move(Node));
   }
 
   // Filter out nodes that have different "prototypes" and/or flags.
   // Don't look at type constraints though, we will simply skip emitting
   // the constraints if they differ.
-  decltype(TargetNodesByName)::iterator Next;
-  for (auto I = TargetNodesByName.begin(), E = TargetNodesByName.end(); I != E;
-       I = Next) {
+  decltype(NodesByName)::iterator Next;
+  for (auto I = NodesByName.begin(), E = NodesByName.end(); I != E; I = Next) {
     Next = std::next(I);
 
     if (haveCompatibleDescriptions(I->second))
@@ -124,7 +123,7 @@ SDNodeInfoEmitter::SDNodeInfoEmitter(const RecordKeeper &RK)
       for (const SDNodeInfo &N : I->second)
         warnOnSkippedNode(N, "incompatible description");
 
-    TargetNodesByName.erase(I);
+    NodesByName.erase(I);
   }
 }
 
@@ -133,14 +132,14 @@ void SDNodeInfoEmitter::emitEnum(raw_ostream &OS) const {
   OS << "#undef GET_SDNODE_ENUM\n\n";
   OS << "namespace llvm::" << TargetSDNodeNamespace << " {\n\n";
 
-  if (!TargetNodesByName.empty()) {
-    StringRef FirstName = TargetNodesByName.begin()->first;
-    StringRef LastName = TargetNodesByName.rbegin()->first;
+  if (!NodesByName.empty()) {
+    StringRef FirstName = NodesByName.begin()->first;
+    StringRef LastName = NodesByName.rbegin()->first;
 
     OS << "enum GenNodeType : unsigned {\n";
     OS << "  " << FirstName << " = ISD::BUILTIN_OP_END,\n";
 
-    for (StringRef EnumName : make_first_range(drop_begin(TargetNodesByName)))
+    for (StringRef EnumName : make_first_range(drop_begin(NodesByName)))
       OS << "  " << EnumName << ",\n";
 
     OS << "};\n\n";
@@ -159,9 +158,9 @@ std::vector<unsigned> SDNodeInfoEmitter::emitNodeNames(raw_ostream &OS) const {
   StringToOffsetTable NameTable;
 
   std::vector<unsigned> NameOffsets;
-  NameOffsets.reserve(TargetNodesByName.size());
+  NameOffsets.reserve(NodesByName.size());
 
-  for (StringRef EnumName : make_first_range(TargetNodesByName)) {
+  for (StringRef EnumName : make_first_range(NodesByName)) {
     SmallString<64> DebugName;
     raw_svector_ostream SS(DebugName);
     SS << TargetSDNodeNamespace << "::" << EnumName;
@@ -240,10 +239,10 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
       /*Terminator=*/std::nullopt);
 
   std::vector<std::pair<unsigned, unsigned>> ConstraintOffsetsAndCounts;
-  ConstraintOffsetsAndCounts.reserve(TargetNodesByName.size());
+  ConstraintOffsetsAndCounts.reserve(NodesByName.size());
 
   SmallVector<StringRef> SkippedNodes;
-  for (const auto &[EnumName, Nodes] : TargetNodesByName) {
+  for (const auto &[EnumName, Nodes] : NodesByName) {
     ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();
 
     bool IsAmbiguous = any_of(drop_begin(Nodes), [&](const SDNodeInfo &Other) {
@@ -276,7 +275,7 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
   ConstraintTable.emit(OS, emitTypeConstraint);
   OS << "};\n\n";
 
-  for (const auto &[EnumName, Nodes] : TargetNodesByName) {
+  for (const auto &[EnumName, Nodes] : NodesByName) {
     ArrayRef<SDTypeConstraint> Constraints = Nodes.front().getTypeConstraints();
 
     if (Constraints.empty() || is_contained(SkippedNodes, EnumName)) {
@@ -337,7 +336,7 @@ void SDNodeInfoEmitter::emitDescs(raw_ostream &OS) const {
   OS << "static const SDNodeDesc " << TargetName << "SDNodeDescs[] = {\n";
 
   for (const auto &[NameAndNodes, NameOffset, ConstraintOffsetAndCount] :
-       zip_equal(TargetNodesByName, NameOffsets, ConstraintOffsetsAndCounts))
+       zip_equal(NodesByName, NameOffsets, ConstraintOffsetsAndCounts))
     emitDesc(OS, NameAndNodes.first, NameAndNodes.second, NameOffset,
              ConstraintOffsetAndCount.first, ConstraintOffsetAndCount.second);
 
@@ -346,7 +345,7 @@ void SDNodeInfoEmitter::emitDescs(raw_ostream &OS) const {
   OS << formatv("static const SDNodeInfo {0}GenSDNodeInfo(\n"
                 "    /*NumOpcodes=*/{1}, {0}SDNodeDescs,\n"
                 "    {0}SDNodeNames, {0}SDTypeConstraints);\n\n",
-                TargetName, TargetNodesByName.size());
+                TargetName, NodesByName.size());
 
   OS << "} // namespace llvm\n\n";
   OS << "#endif // GET_SDNODE_DESC\n\n";



More information about the llvm-commits mailing list