Skip to content

[SelectionDAG] Verify SDTCisVT and SDTCVecEltisVT constraints #150125

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

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

s-barannikov
Copy link
Contributor

No description provided.

@llvmbot
Copy link
Member

llvmbot commented Jul 22, 2025

@llvm/pr-subscribers-backend-sparc
@llvm/pr-subscribers-llvm-selectiondag
@llvm/pr-subscribers-tablegen
@llvm/pr-subscribers-backend-aarch64

@llvm/pr-subscribers-backend-m68k

Author: Sergei Barannikov (s-barannikov)

Changes

Patch is 31.75 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/150125.diff

15 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/SDNodeInfo.h (+15-3)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp (+105)
  • (modified) llvm/lib/Target/AArch64/AArch64InstrInfo.td (+1-1)
  • (modified) llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp (+31-13)
  • (modified) llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp (+15)
  • (modified) llvm/lib/Target/M68k/M68kSelectionDAGInfo.h (+3)
  • (modified) llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp (+11-8)
  • (modified) llvm/lib/Target/Sparc/SparcInstrInfo.td (+1-1)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td (+6-6)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/basic.td (+35-23)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/namespace.td (+11-7)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td (+3-3)
  • (modified) llvm/utils/TableGen/Basic/SequenceToOffsetTable.h (+2-1)
  • (modified) llvm/utils/TableGen/Common/InfoByHwMode.h (+2)
  • (modified) llvm/utils/TableGen/SDNodeInfoEmitter.cpp (+60-12)
diff --git a/llvm/include/llvm/CodeGen/SDNodeInfo.h b/llvm/include/llvm/CodeGen/SDNodeInfo.h
index ba6c343ee1838..a1954645f210e 100644
--- a/llvm/include/llvm/CodeGen/SDNodeInfo.h
+++ b/llvm/include/llvm/CodeGen/SDNodeInfo.h
@@ -48,11 +48,21 @@ enum SDNF {
   SDNFIsStrictFP,
 };
 
+struct VTByHwModePair {
+  uint8_t Mode;
+  MVT::SimpleValueType VT;
+};
+
 struct SDTypeConstraint {
   SDTC Kind;
   uint8_t OpNo;
   uint8_t OtherOpNo;
-  MVT::SimpleValueType VT;
+  /// For Kind == SDTCisVT or SDTCVecEltisVT:
+  /// - if not using HwMode, NumHwModes == 0 and VT is MVT::SimpleValueType;
+  /// - otherwise, VT is offset into VTByHwModeTable and NumHwModes specifies
+  ///   the number of entries.
+  uint8_t NumHwModes;
+  uint16_t VT;
 };
 
 using SDNodeTSFlags = uint32_t;
@@ -76,13 +86,15 @@ class SDNodeInfo final {
   unsigned NumOpcodes;
   const SDNodeDesc *Descs;
   StringTable Names;
+  const VTByHwModePair *VTByHwModeTable;
   const SDTypeConstraint *Constraints;
 
 public:
   constexpr SDNodeInfo(unsigned NumOpcodes, const SDNodeDesc *Descs,
-                       StringTable Names, const SDTypeConstraint *Constraints)
+                       StringTable Names, const VTByHwModePair *VTByHwModeTable,
+                       const SDTypeConstraint *Constraints)
       : NumOpcodes(NumOpcodes), Descs(Descs), Names(Names),
-        Constraints(Constraints) {}
+        VTByHwModeTable(VTByHwModeTable), Constraints(Constraints) {}
 
   /// Returns true if there is a generated description for a node with the given
   /// target-specific opcode.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp b/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp
index e3f6c98a9a90a..fc2bc64dc39c8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp
@@ -7,7 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/CodeGen/SDNodeInfo.h"
+#include "llvm/CodeGen/SelectionDAG.h"
 #include "llvm/CodeGen/SelectionDAGNodes.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
 
 using namespace llvm;
 
@@ -40,6 +43,26 @@ static void checkOperandType(const SelectionDAG &DAG, const SDNode *N,
             ExpectedVT.getEVTString() + ", got " + ActualVT.getEVTString());
 }
 
+namespace {
+
+struct ConstraintOp {
+  const SDNode *N;
+  unsigned Idx;
+  bool IsRes;
+
+  SDValue getValue() const {
+    return IsRes ? SDValue(const_cast<SDNode *>(N), Idx) : N->getOperand(Idx);
+  }
+
+  EVT getValueType() const { return getValue().getValueType(); }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const ConstraintOp &Info) {
+  return OS << (Info.IsRes ? "result" : "operand") << " #" << Info.Idx;
+}
+
+} // namespace
+
 void SDNodeInfo::verifyNode(const SelectionDAG &DAG, const SDNode *N) const {
   const SDNodeDesc &Desc = getDesc(N->getOpcode());
   bool HasChain = Desc.hasProperty(SDNPHasChain);
@@ -125,4 +148,86 @@ void SDNodeInfo::verifyNode(const SelectionDAG &DAG, const SDNode *N) const {
                             " must be Register or RegisterMask");
     }
   }
+
+  unsigned VTHwMode =
+      DAG.getSubtarget().getHwMode(MCSubtargetInfo::HwMode_ValueType);
+
+  auto GetConstraintOp = [&](unsigned Idx) {
+    if (Idx < Desc.NumResults)
+      return ConstraintOp{N, Idx, /*IsRes=*/true};
+    return ConstraintOp{N, HasChain + (Idx - Desc.NumResults), /*IsRes=*/false};
+  };
+
+  auto GetConstraintVT = [&](const SDTypeConstraint &C) {
+    if (!C.NumHwModes)
+      return static_cast<MVT::SimpleValueType>(C.VT);
+    for (auto [Mode, VT] : ArrayRef(&VTByHwModeTable[C.VT], C.NumHwModes))
+      if (Mode == VTHwMode)
+        return VT;
+    llvm_unreachable("No value type for this HW mode");
+  };
+
+  SmallString<128> ES;
+  raw_svector_ostream SS(ES);
+
+  for (const SDTypeConstraint &C : getConstraints(N->getOpcode())) {
+    ConstraintOp Op = GetConstraintOp(C.OpNo);
+    EVT OpVT = Op.getValue().getValueType();
+
+    switch (C.Kind) {
+    case SDTCisVT: {
+      EVT ExpectedVT = GetConstraintVT(C);
+
+      bool IsPtr = ExpectedVT == MVT::iPTR;
+      if (IsPtr)
+        ExpectedVT =
+            DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout());
+
+      if (OpVT != ExpectedVT) {
+        SS << Op << " must have type " << ExpectedVT;
+        if (IsPtr)
+          SS << " (iPTR)";
+        SS << ", but has type " << OpVT;
+        reportNodeError(DAG, N, SS.str());
+      }
+      break;
+    }
+    case SDTCisPtrTy:
+      break;
+    case SDTCisInt:
+      break;
+    case SDTCisFP:
+      break;
+    case SDTCisVec:
+      break;
+    case SDTCisSameAs:
+      break;
+    case SDTCisVTSmallerThanOp:
+      break;
+    case SDTCisOpSmallerThanOp:
+      break;
+    case SDTCisEltOfVec:
+      break;
+    case SDTCisSubVecOfVec:
+      break;
+    case SDTCVecEltisVT: {
+      EVT ExpectedVT = GetConstraintVT(C);
+
+      if (!OpVT.isVector()) {
+        SS << Op << " must have vector type";
+        reportNodeError(DAG, N, SS.str());
+      }
+      if (OpVT.getVectorElementType() != ExpectedVT) {
+        SS << Op << " must have " << ExpectedVT << " element type, but has "
+           << OpVT.getVectorElementType() << " element type";
+        reportNodeError(DAG, N, SS.str());
+      }
+      break;
+    }
+    case SDTCisSameNumEltsAs:
+      break;
+    case SDTCisSameSizeAs:
+      break;
+    }
+  }
 }
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 9f8a2571b076e..ed255924b0eef 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -1144,7 +1144,7 @@ def AArch64msrr : SDNode<"AArch64ISD::MSRR",
                                                SDTCisVT<2, i64>]>,
                           [SDNPHasChain]>;
 
-def SD_AArch64rshrnb : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, SDTCisInt<2>]>;
+def SD_AArch64rshrnb : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, SDTCisVT<2, i32>]>;
 // Vector narrowing shift by immediate (bottom)
 def AArch64rshrnb : SDNode<"AArch64ISD::RSHRNB_I", SD_AArch64rshrnb>;
 def AArch64rshrnb_pf : PatFrags<(ops node:$rs, node:$i),
diff --git a/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp b/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp
index bafb8d0773232..35d0d9b79ae33 100644
--- a/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp
@@ -32,16 +32,41 @@ AArch64SelectionDAGInfo::AArch64SelectionDAGInfo()
 
 void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
                                                const SDNode *N) const {
+  switch (N->getOpcode()) {
+  case AArch64ISD::ADC:
+  case AArch64ISD::SBC:
+  case AArch64ISD::ADCS:
+  case AArch64ISD::SBCS:
+    // operand #2 must have type i32, but has type glue
+    return;
+  case AArch64ISD::SUBS:
+    // result #1 must have type i32, but has type glue
+    return;
+  case AArch64ISD::CSEL:
+  case AArch64ISD::CSINC:
+  case AArch64ISD::BRCOND:
+    // operand #3 must have type i32, but has type glue
+    return;
+  case AArch64ISD::WrapperLarge:
+    // operand #0 must have type i32, but has type i64
+    return;
+  case AArch64ISD::LDNP:
+    // result #0 must have type v4i32, but has type v2f64
+    return;
+  case AArch64ISD::STNP:
+    // operand #1 must have type v4i32, but has type v2i64
+    return;
+  }
+
+  SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
+
 #ifndef NDEBUG
+  // Some additional checks not yet implemented by verifyTargetNode.
   switch (N->getOpcode()) {
-  default:
-    return SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
   case AArch64ISD::SADDWT:
   case AArch64ISD::SADDWB:
   case AArch64ISD::UADDWT:
   case AArch64ISD::UADDWB: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 2 && "Expected two operands!");
     EVT VT = N->getValueType(0);
     EVT Op0VT = N->getOperand(0).getValueType();
     EVT Op1VT = N->getOperand(1).getValueType();
@@ -61,8 +86,6 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
   case AArch64ISD::SUNPKHI:
   case AArch64ISD::UUNPKLO:
   case AArch64ISD::UUNPKHI: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 1 && "Expected one operand!");
     EVT VT = N->getValueType(0);
     EVT OpVT = N->getOperand(0).getValueType();
     assert(OpVT.isVector() && VT.isVector() && OpVT.isInteger() &&
@@ -79,8 +102,6 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
   case AArch64ISD::UZP2:
   case AArch64ISD::ZIP1:
   case AArch64ISD::ZIP2: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 2 && "Expected two operands!");
     EVT VT = N->getValueType(0);
     EVT Op0VT = N->getOperand(0).getValueType();
     EVT Op1VT = N->getOperand(1).getValueType();
@@ -90,11 +111,8 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
     break;
   }
   case AArch64ISD::RSHRNB_I: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 2 && "Expected two operands!");
     EVT VT = N->getValueType(0);
     EVT Op0VT = N->getOperand(0).getValueType();
-    EVT Op1VT = N->getOperand(1).getValueType();
     assert(VT.isVector() && VT.isInteger() &&
            "Expected integer vector result type!");
     assert(Op0VT.isVector() && Op0VT.isInteger() &&
@@ -103,8 +121,8 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
            "Expected vectors of equal size!");
     assert(VT.getVectorElementCount() == Op0VT.getVectorElementCount() * 2 &&
            "Expected input vector with half the lanes of its result!");
-    assert(Op1VT == MVT::i32 && isa<ConstantSDNode>(N->getOperand(1)) &&
-           "Expected second operand to be a constant i32!");
+    assert(isa<ConstantSDNode>(N->getOperand(1)) &&
+           "Expected second operand to be a constant!");
     break;
   }
   }
diff --git a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
index dd1bfdf00af8c..a402c7721129c 100644
--- a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
@@ -16,4 +16,19 @@ using namespace llvm;
 M68kSelectionDAGInfo::M68kSelectionDAGInfo()
     : SelectionDAGGenTargetInfo(M68kGenSDNodeInfo) {}
 
+void M68kSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
+                                            const SDNode *N) const {
+  switch (N->getOpcode()) {
+  case M68kISD::ADD:
+  case M68kISD::SUBX:
+    // result #1 must have type i8, but has type i32
+    return;
+  case M68kISD::SETCC:
+    // operand #1 must have type i8, but has type i32
+    return;
+  }
+
+  SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
+}
+
 M68kSelectionDAGInfo::~M68kSelectionDAGInfo() = default;
diff --git a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
index 87a8c08d2591e..de4667f830d41 100644
--- a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
+++ b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
@@ -21,6 +21,9 @@ class M68kSelectionDAGInfo : public SelectionDAGGenTargetInfo {
   M68kSelectionDAGInfo();
 
   ~M68kSelectionDAGInfo() override;
+
+  void verifyTargetNode(const SelectionDAG &DAG,
+                        const SDNode *N) const override;
 };
 
 } // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp b/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
index 668e59686664e..0ae47b71e1fe5 100644
--- a/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
@@ -20,15 +20,22 @@ RISCVSelectionDAGInfo::~RISCVSelectionDAGInfo() = default;
 
 void RISCVSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
                                              const SDNode *N) const {
+  switch (N->getOpcode()) {
+  case RISCVISD::TUPLE_INSERT:
+    // operand #2 must have type i32, but has type i64
+  case RISCVISD::TUPLE_EXTRACT:
+    // operand #1 must have type i32, but has type i64
+    return;
+  }
+
+  SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
+
 #ifndef NDEBUG
+  // Some additional checks not yet implemented by verifyTargetNode.
   switch (N->getOpcode()) {
-  default:
-    return SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
   case RISCVISD::VQDOT_VL:
   case RISCVISD::VQDOTU_VL:
   case RISCVISD::VQDOTSU_VL: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 5 && "Expected five operands!");
     EVT VT = N->getValueType(0);
     assert(VT.isScalableVector() && VT.getVectorElementType() == MVT::i32 &&
            "Expected result to be an i32 scalable vector");
@@ -38,13 +45,9 @@ void RISCVSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
            "Expected result and first 3 operands to have the same type!");
     EVT MaskVT = N->getOperand(3).getValueType();
     assert(MaskVT.isScalableVector() &&
-           MaskVT.getVectorElementType() == MVT::i1 &&
            MaskVT.getVectorElementCount() == VT.getVectorElementCount() &&
            "Expected mask VT to be an i1 scalable vector with same number of "
            "elements as the result");
-    assert((N->getOperand(4).getValueType() == MVT::i32 ||
-            N->getOperand(4).getValueType() == MVT::i64) &&
-           "Expect VL operand to be i32 or i64");
     break;
   }
   }
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.td b/llvm/lib/Target/Sparc/SparcInstrInfo.td
index 1a32eafb0e83d..46181b3f2dd33 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.td
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.td
@@ -352,7 +352,7 @@ def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_SPCallSeqStart,
 def callseq_end   : SDNode<"ISD::CALLSEQ_END",   SDT_SPCallSeqEnd,
                            [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
 
-def SDT_SPCall    : SDTypeProfile<0, -1, [SDTCisVT<0, i32>]>;
+def SDT_SPCall    : SDTypeProfile<0, -1, [SDTCisVT<0, iPTR>]>;
 def call          : SDNode<"SPISD::CALL", SDT_SPCall,
                            [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
                             SDNPVariadic]>;
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
index c09e2198dbeba..43b5561f0c694 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
@@ -20,7 +20,7 @@ def my_node_b : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, f32
 // CHECK-NEXT:    ;
 
 // CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -28,8 +28,8 @@ def my_node_b : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, f32
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
-// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
-// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
 
 
 //--- test2.td
@@ -62,7 +62,7 @@ def my_node_2b : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<1, 0, [SDTCisVT<0,
 // CHECK-NEXT:    ;
 
 // CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* 0 */ {SDTCisVT, 0, 0, MVT::i32},
+// CHECK-NEXT:    /* 0 */ {SDTCisVT, 0, 0, 0, MVT::i32},
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -71,5 +71,5 @@ def my_node_2b : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<1, 0, [SDTCisVT<0,
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
-// CHECK-NEXT:      /*NumOpcodes=*/2, MyTargetSDNodeDescs,
-// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-NEXT:      /*NumOpcodes=*/2, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
index 2b4c76a0d4543..8a41934f478c5 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
@@ -38,16 +38,20 @@ def MyTarget : Target;
 // CHECK-NEXT:  static constexpr llvm::StringTable
 // CHECK-NEXT:  MyTargetSDNodeNames = MyTargetSDNodeNamesStorage;
 // CHECK-EMPTY:
+// CHECK-NEXT:  static const HwModeVTPair MyTargetVTByHwModeTable[] = {
+// CHECK-NEXT:    /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
 // CHECK-NEXT:  static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 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-NEXT:      /*NumOpcodes=*/0, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetSDTypeConstraints);
 // CHECK-EMPTY:
 // CHECK-NEXT:  } // namespace llvm
 // CHECK-EMPTY:
@@ -79,8 +83,12 @@ def my_noop : SDNode<"MyTargetISD::NOOP", SDTypeProfile<0, 0, []>>;
 // CHECK-NEXT:    "MyTargetISD::NOOP\0"
 // CHECK-NEXT:    ;
 
+// CHECK:       static const HwModeVTPair MyTargetVTByHwModeTable[] = {
+// CHECK-NEXT:    /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+
 // CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -88,8 +96,8 @@ def my_noop : SDNode<"MyTargetISD::NOOP", SDTypeProfile<0, 0, []>>;
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
-// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
-// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
 
 //--- advanced.td
 // RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %t/advanced.td \
@@ -160,22 +168,26 @@ def my_node_3 : SDNode<
 // CHECK-NEXT:    "MyTargetISD::NODE_3\0"
 // CHECK-NEXT:    ;
 
+// CHECK:       static const HwModeVTPair MyTargetVTByHwModeTable[] = {
+// CHECK-NEXT:    /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+
 // 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:...
[truncated]

@llvmbot
Copy link
Member

llvmbot commented Jul 22, 2025

@llvm/pr-subscribers-backend-risc-v

Author: Sergei Barannikov (s-barannikov)

Changes

Patch is 31.75 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/150125.diff

15 Files Affected:

  • (modified) llvm/include/llvm/CodeGen/SDNodeInfo.h (+15-3)
  • (modified) llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp (+105)
  • (modified) llvm/lib/Target/AArch64/AArch64InstrInfo.td (+1-1)
  • (modified) llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp (+31-13)
  • (modified) llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp (+15)
  • (modified) llvm/lib/Target/M68k/M68kSelectionDAGInfo.h (+3)
  • (modified) llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp (+11-8)
  • (modified) llvm/lib/Target/Sparc/SparcInstrInfo.td (+1-1)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td (+6-6)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/basic.td (+35-23)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/namespace.td (+11-7)
  • (modified) llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td (+3-3)
  • (modified) llvm/utils/TableGen/Basic/SequenceToOffsetTable.h (+2-1)
  • (modified) llvm/utils/TableGen/Common/InfoByHwMode.h (+2)
  • (modified) llvm/utils/TableGen/SDNodeInfoEmitter.cpp (+60-12)
diff --git a/llvm/include/llvm/CodeGen/SDNodeInfo.h b/llvm/include/llvm/CodeGen/SDNodeInfo.h
index ba6c343ee1838..a1954645f210e 100644
--- a/llvm/include/llvm/CodeGen/SDNodeInfo.h
+++ b/llvm/include/llvm/CodeGen/SDNodeInfo.h
@@ -48,11 +48,21 @@ enum SDNF {
   SDNFIsStrictFP,
 };
 
+struct VTByHwModePair {
+  uint8_t Mode;
+  MVT::SimpleValueType VT;
+};
+
 struct SDTypeConstraint {
   SDTC Kind;
   uint8_t OpNo;
   uint8_t OtherOpNo;
-  MVT::SimpleValueType VT;
+  /// For Kind == SDTCisVT or SDTCVecEltisVT:
+  /// - if not using HwMode, NumHwModes == 0 and VT is MVT::SimpleValueType;
+  /// - otherwise, VT is offset into VTByHwModeTable and NumHwModes specifies
+  ///   the number of entries.
+  uint8_t NumHwModes;
+  uint16_t VT;
 };
 
 using SDNodeTSFlags = uint32_t;
@@ -76,13 +86,15 @@ class SDNodeInfo final {
   unsigned NumOpcodes;
   const SDNodeDesc *Descs;
   StringTable Names;
+  const VTByHwModePair *VTByHwModeTable;
   const SDTypeConstraint *Constraints;
 
 public:
   constexpr SDNodeInfo(unsigned NumOpcodes, const SDNodeDesc *Descs,
-                       StringTable Names, const SDTypeConstraint *Constraints)
+                       StringTable Names, const VTByHwModePair *VTByHwModeTable,
+                       const SDTypeConstraint *Constraints)
       : NumOpcodes(NumOpcodes), Descs(Descs), Names(Names),
-        Constraints(Constraints) {}
+        VTByHwModeTable(VTByHwModeTable), Constraints(Constraints) {}
 
   /// Returns true if there is a generated description for a node with the given
   /// target-specific opcode.
diff --git a/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp b/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp
index e3f6c98a9a90a..fc2bc64dc39c8 100644
--- a/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp
@@ -7,7 +7,10 @@
 //===----------------------------------------------------------------------===//
 
 #include "llvm/CodeGen/SDNodeInfo.h"
+#include "llvm/CodeGen/SelectionDAG.h"
 #include "llvm/CodeGen/SelectionDAGNodes.h"
+#include "llvm/CodeGen/TargetLowering.h"
+#include "llvm/CodeGen/TargetSubtargetInfo.h"
 
 using namespace llvm;
 
@@ -40,6 +43,26 @@ static void checkOperandType(const SelectionDAG &DAG, const SDNode *N,
             ExpectedVT.getEVTString() + ", got " + ActualVT.getEVTString());
 }
 
+namespace {
+
+struct ConstraintOp {
+  const SDNode *N;
+  unsigned Idx;
+  bool IsRes;
+
+  SDValue getValue() const {
+    return IsRes ? SDValue(const_cast<SDNode *>(N), Idx) : N->getOperand(Idx);
+  }
+
+  EVT getValueType() const { return getValue().getValueType(); }
+};
+
+raw_ostream &operator<<(raw_ostream &OS, const ConstraintOp &Info) {
+  return OS << (Info.IsRes ? "result" : "operand") << " #" << Info.Idx;
+}
+
+} // namespace
+
 void SDNodeInfo::verifyNode(const SelectionDAG &DAG, const SDNode *N) const {
   const SDNodeDesc &Desc = getDesc(N->getOpcode());
   bool HasChain = Desc.hasProperty(SDNPHasChain);
@@ -125,4 +148,86 @@ void SDNodeInfo::verifyNode(const SelectionDAG &DAG, const SDNode *N) const {
                             " must be Register or RegisterMask");
     }
   }
+
+  unsigned VTHwMode =
+      DAG.getSubtarget().getHwMode(MCSubtargetInfo::HwMode_ValueType);
+
+  auto GetConstraintOp = [&](unsigned Idx) {
+    if (Idx < Desc.NumResults)
+      return ConstraintOp{N, Idx, /*IsRes=*/true};
+    return ConstraintOp{N, HasChain + (Idx - Desc.NumResults), /*IsRes=*/false};
+  };
+
+  auto GetConstraintVT = [&](const SDTypeConstraint &C) {
+    if (!C.NumHwModes)
+      return static_cast<MVT::SimpleValueType>(C.VT);
+    for (auto [Mode, VT] : ArrayRef(&VTByHwModeTable[C.VT], C.NumHwModes))
+      if (Mode == VTHwMode)
+        return VT;
+    llvm_unreachable("No value type for this HW mode");
+  };
+
+  SmallString<128> ES;
+  raw_svector_ostream SS(ES);
+
+  for (const SDTypeConstraint &C : getConstraints(N->getOpcode())) {
+    ConstraintOp Op = GetConstraintOp(C.OpNo);
+    EVT OpVT = Op.getValue().getValueType();
+
+    switch (C.Kind) {
+    case SDTCisVT: {
+      EVT ExpectedVT = GetConstraintVT(C);
+
+      bool IsPtr = ExpectedVT == MVT::iPTR;
+      if (IsPtr)
+        ExpectedVT =
+            DAG.getTargetLoweringInfo().getPointerTy(DAG.getDataLayout());
+
+      if (OpVT != ExpectedVT) {
+        SS << Op << " must have type " << ExpectedVT;
+        if (IsPtr)
+          SS << " (iPTR)";
+        SS << ", but has type " << OpVT;
+        reportNodeError(DAG, N, SS.str());
+      }
+      break;
+    }
+    case SDTCisPtrTy:
+      break;
+    case SDTCisInt:
+      break;
+    case SDTCisFP:
+      break;
+    case SDTCisVec:
+      break;
+    case SDTCisSameAs:
+      break;
+    case SDTCisVTSmallerThanOp:
+      break;
+    case SDTCisOpSmallerThanOp:
+      break;
+    case SDTCisEltOfVec:
+      break;
+    case SDTCisSubVecOfVec:
+      break;
+    case SDTCVecEltisVT: {
+      EVT ExpectedVT = GetConstraintVT(C);
+
+      if (!OpVT.isVector()) {
+        SS << Op << " must have vector type";
+        reportNodeError(DAG, N, SS.str());
+      }
+      if (OpVT.getVectorElementType() != ExpectedVT) {
+        SS << Op << " must have " << ExpectedVT << " element type, but has "
+           << OpVT.getVectorElementType() << " element type";
+        reportNodeError(DAG, N, SS.str());
+      }
+      break;
+    }
+    case SDTCisSameNumEltsAs:
+      break;
+    case SDTCisSameSizeAs:
+      break;
+    }
+  }
 }
diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.td b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
index 9f8a2571b076e..ed255924b0eef 100644
--- a/llvm/lib/Target/AArch64/AArch64InstrInfo.td
+++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.td
@@ -1144,7 +1144,7 @@ def AArch64msrr : SDNode<"AArch64ISD::MSRR",
                                                SDTCisVT<2, i64>]>,
                           [SDNPHasChain]>;
 
-def SD_AArch64rshrnb : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, SDTCisInt<2>]>;
+def SD_AArch64rshrnb : SDTypeProfile<1, 2, [SDTCisVec<0>, SDTCisVec<1>, SDTCisVT<2, i32>]>;
 // Vector narrowing shift by immediate (bottom)
 def AArch64rshrnb : SDNode<"AArch64ISD::RSHRNB_I", SD_AArch64rshrnb>;
 def AArch64rshrnb_pf : PatFrags<(ops node:$rs, node:$i),
diff --git a/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp b/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp
index bafb8d0773232..35d0d9b79ae33 100644
--- a/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp
+++ b/llvm/lib/Target/AArch64/AArch64SelectionDAGInfo.cpp
@@ -32,16 +32,41 @@ AArch64SelectionDAGInfo::AArch64SelectionDAGInfo()
 
 void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
                                                const SDNode *N) const {
+  switch (N->getOpcode()) {
+  case AArch64ISD::ADC:
+  case AArch64ISD::SBC:
+  case AArch64ISD::ADCS:
+  case AArch64ISD::SBCS:
+    // operand #2 must have type i32, but has type glue
+    return;
+  case AArch64ISD::SUBS:
+    // result #1 must have type i32, but has type glue
+    return;
+  case AArch64ISD::CSEL:
+  case AArch64ISD::CSINC:
+  case AArch64ISD::BRCOND:
+    // operand #3 must have type i32, but has type glue
+    return;
+  case AArch64ISD::WrapperLarge:
+    // operand #0 must have type i32, but has type i64
+    return;
+  case AArch64ISD::LDNP:
+    // result #0 must have type v4i32, but has type v2f64
+    return;
+  case AArch64ISD::STNP:
+    // operand #1 must have type v4i32, but has type v2i64
+    return;
+  }
+
+  SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
+
 #ifndef NDEBUG
+  // Some additional checks not yet implemented by verifyTargetNode.
   switch (N->getOpcode()) {
-  default:
-    return SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
   case AArch64ISD::SADDWT:
   case AArch64ISD::SADDWB:
   case AArch64ISD::UADDWT:
   case AArch64ISD::UADDWB: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 2 && "Expected two operands!");
     EVT VT = N->getValueType(0);
     EVT Op0VT = N->getOperand(0).getValueType();
     EVT Op1VT = N->getOperand(1).getValueType();
@@ -61,8 +86,6 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
   case AArch64ISD::SUNPKHI:
   case AArch64ISD::UUNPKLO:
   case AArch64ISD::UUNPKHI: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 1 && "Expected one operand!");
     EVT VT = N->getValueType(0);
     EVT OpVT = N->getOperand(0).getValueType();
     assert(OpVT.isVector() && VT.isVector() && OpVT.isInteger() &&
@@ -79,8 +102,6 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
   case AArch64ISD::UZP2:
   case AArch64ISD::ZIP1:
   case AArch64ISD::ZIP2: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 2 && "Expected two operands!");
     EVT VT = N->getValueType(0);
     EVT Op0VT = N->getOperand(0).getValueType();
     EVT Op1VT = N->getOperand(1).getValueType();
@@ -90,11 +111,8 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
     break;
   }
   case AArch64ISD::RSHRNB_I: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 2 && "Expected two operands!");
     EVT VT = N->getValueType(0);
     EVT Op0VT = N->getOperand(0).getValueType();
-    EVT Op1VT = N->getOperand(1).getValueType();
     assert(VT.isVector() && VT.isInteger() &&
            "Expected integer vector result type!");
     assert(Op0VT.isVector() && Op0VT.isInteger() &&
@@ -103,8 +121,8 @@ void AArch64SelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
            "Expected vectors of equal size!");
     assert(VT.getVectorElementCount() == Op0VT.getVectorElementCount() * 2 &&
            "Expected input vector with half the lanes of its result!");
-    assert(Op1VT == MVT::i32 && isa<ConstantSDNode>(N->getOperand(1)) &&
-           "Expected second operand to be a constant i32!");
+    assert(isa<ConstantSDNode>(N->getOperand(1)) &&
+           "Expected second operand to be a constant!");
     break;
   }
   }
diff --git a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
index dd1bfdf00af8c..a402c7721129c 100644
--- a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp
@@ -16,4 +16,19 @@ using namespace llvm;
 M68kSelectionDAGInfo::M68kSelectionDAGInfo()
     : SelectionDAGGenTargetInfo(M68kGenSDNodeInfo) {}
 
+void M68kSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
+                                            const SDNode *N) const {
+  switch (N->getOpcode()) {
+  case M68kISD::ADD:
+  case M68kISD::SUBX:
+    // result #1 must have type i8, but has type i32
+    return;
+  case M68kISD::SETCC:
+    // operand #1 must have type i8, but has type i32
+    return;
+  }
+
+  SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
+}
+
 M68kSelectionDAGInfo::~M68kSelectionDAGInfo() = default;
diff --git a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
index 87a8c08d2591e..de4667f830d41 100644
--- a/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
+++ b/llvm/lib/Target/M68k/M68kSelectionDAGInfo.h
@@ -21,6 +21,9 @@ class M68kSelectionDAGInfo : public SelectionDAGGenTargetInfo {
   M68kSelectionDAGInfo();
 
   ~M68kSelectionDAGInfo() override;
+
+  void verifyTargetNode(const SelectionDAG &DAG,
+                        const SDNode *N) const override;
 };
 
 } // namespace llvm
diff --git a/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp b/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
index 668e59686664e..0ae47b71e1fe5 100644
--- a/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
@@ -20,15 +20,22 @@ RISCVSelectionDAGInfo::~RISCVSelectionDAGInfo() = default;
 
 void RISCVSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
                                              const SDNode *N) const {
+  switch (N->getOpcode()) {
+  case RISCVISD::TUPLE_INSERT:
+    // operand #2 must have type i32, but has type i64
+  case RISCVISD::TUPLE_EXTRACT:
+    // operand #1 must have type i32, but has type i64
+    return;
+  }
+
+  SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
+
 #ifndef NDEBUG
+  // Some additional checks not yet implemented by verifyTargetNode.
   switch (N->getOpcode()) {
-  default:
-    return SelectionDAGGenTargetInfo::verifyTargetNode(DAG, N);
   case RISCVISD::VQDOT_VL:
   case RISCVISD::VQDOTU_VL:
   case RISCVISD::VQDOTSU_VL: {
-    assert(N->getNumValues() == 1 && "Expected one result!");
-    assert(N->getNumOperands() == 5 && "Expected five operands!");
     EVT VT = N->getValueType(0);
     assert(VT.isScalableVector() && VT.getVectorElementType() == MVT::i32 &&
            "Expected result to be an i32 scalable vector");
@@ -38,13 +45,9 @@ void RISCVSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
            "Expected result and first 3 operands to have the same type!");
     EVT MaskVT = N->getOperand(3).getValueType();
     assert(MaskVT.isScalableVector() &&
-           MaskVT.getVectorElementType() == MVT::i1 &&
            MaskVT.getVectorElementCount() == VT.getVectorElementCount() &&
            "Expected mask VT to be an i1 scalable vector with same number of "
            "elements as the result");
-    assert((N->getOperand(4).getValueType() == MVT::i32 ||
-            N->getOperand(4).getValueType() == MVT::i64) &&
-           "Expect VL operand to be i32 or i64");
     break;
   }
   }
diff --git a/llvm/lib/Target/Sparc/SparcInstrInfo.td b/llvm/lib/Target/Sparc/SparcInstrInfo.td
index 1a32eafb0e83d..46181b3f2dd33 100644
--- a/llvm/lib/Target/Sparc/SparcInstrInfo.td
+++ b/llvm/lib/Target/Sparc/SparcInstrInfo.td
@@ -352,7 +352,7 @@ def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_SPCallSeqStart,
 def callseq_end   : SDNode<"ISD::CALLSEQ_END",   SDT_SPCallSeqEnd,
                            [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
 
-def SDT_SPCall    : SDTypeProfile<0, -1, [SDTCisVT<0, i32>]>;
+def SDT_SPCall    : SDTypeProfile<0, -1, [SDTCisVT<0, iPTR>]>;
 def call          : SDNode<"SPISD::CALL", SDT_SPCall,
                            [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
                             SDNPVariadic]>;
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
index c09e2198dbeba..43b5561f0c694 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints.td
@@ -20,7 +20,7 @@ def my_node_b : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, f32
 // CHECK-NEXT:    ;
 
 // CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -28,8 +28,8 @@ def my_node_b : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, f32
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
-// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
-// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
 
 
 //--- test2.td
@@ -62,7 +62,7 @@ def my_node_2b : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<1, 0, [SDTCisVT<0,
 // CHECK-NEXT:    ;
 
 // CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* 0 */ {SDTCisVT, 0, 0, MVT::i32},
+// CHECK-NEXT:    /* 0 */ {SDTCisVT, 0, 0, 0, MVT::i32},
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -71,5 +71,5 @@ def my_node_2b : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<1, 0, [SDTCisVT<0,
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
-// CHECK-NEXT:      /*NumOpcodes=*/2, MyTargetSDNodeDescs,
-// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-NEXT:      /*NumOpcodes=*/2, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
index 2b4c76a0d4543..8a41934f478c5 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/basic.td
@@ -38,16 +38,20 @@ def MyTarget : Target;
 // CHECK-NEXT:  static constexpr llvm::StringTable
 // CHECK-NEXT:  MyTargetSDNodeNames = MyTargetSDNodeNamesStorage;
 // CHECK-EMPTY:
+// CHECK-NEXT:  static const HwModeVTPair MyTargetVTByHwModeTable[] = {
+// CHECK-NEXT:    /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+// CHECK-EMPTY:
 // CHECK-NEXT:  static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 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-NEXT:      /*NumOpcodes=*/0, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetSDTypeConstraints);
 // CHECK-EMPTY:
 // CHECK-NEXT:  } // namespace llvm
 // CHECK-EMPTY:
@@ -79,8 +83,12 @@ def my_noop : SDNode<"MyTargetISD::NOOP", SDTypeProfile<0, 0, []>>;
 // CHECK-NEXT:    "MyTargetISD::NOOP\0"
 // CHECK-NEXT:    ;
 
+// CHECK:       static const HwModeVTPair MyTargetVTByHwModeTable[] = {
+// CHECK-NEXT:    /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+
 // CHECK:       static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:    /* dummy */ {SDTCisVT, 0, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -88,8 +96,8 @@ def my_noop : SDNode<"MyTargetISD::NOOP", SDTypeProfile<0, 0, []>>;
 // CHECK-NEXT:  };
 // CHECK-EMPTY:
 // CHECK-NEXT:  static const SDNodeInfo MyTargetGenSDNodeInfo(
-// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs,
-// CHECK-NEXT:      MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-NEXT:      /*NumOpcodes=*/1, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT:      MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
 
 //--- advanced.td
 // RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %t/advanced.td \
@@ -160,22 +168,26 @@ def my_node_3 : SDNode<
 // CHECK-NEXT:    "MyTargetISD::NODE_3\0"
 // CHECK-NEXT:    ;
 
+// CHECK:       static const HwModeVTPair MyTargetVTByHwModeTable[] = {
+// CHECK-NEXT:    /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// CHECK-NEXT:  };
+
 // 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:...
[truncated]

@s-barannikov
Copy link
Contributor Author

This is preliminary ready for review, but I'll add a couple of HW mode tests and it probably needs some description.

};

SmallString<128> ES;
raw_svector_ostream SS(ES);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Currently, it only diagnoses the first constraint violation. Would it be helpful if it reported all problems?
  2. Would it be helpful if the diagnostics included the constraint as written in *.td files (e.g. "SDTCisVT<2, i32>")?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1: preferably, yes, but this could come later?

2: I'm not sure the syntax has to be 1:1, but something to reference back to the constraint as written would be good.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, will do that after implementing more constraint checks.

@@ -162,7 +162,8 @@ class SequenceToOffsetTable {

/// emit - Print out the table as the body of an array initializer.
/// Use the Print function to print elements.
void emit(raw_ostream &OS, void (*Print)(raw_ostream &, ElemT)) const {
void emit(raw_ostream &OS,
function_ref<void(raw_ostream &, ElemT)> Print) const {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So that I can use std::bind in the emitter to pass an additional parameter to the printer function.

@@ -76,13 +86,15 @@ class SDNodeInfo final {
unsigned NumOpcodes;
const SDNodeDesc *Descs;
StringTable Names;
const VTByHwModePair *VTByHwModeTable;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of all targets, only RISCV and LoongArch got a non-empty (two element) table.

@s-barannikov s-barannikov force-pushed the sdnode-vt-constraint branch from b26d4b6 to e3b2dce Compare July 22, 2025 22:43
Copy link
Member

@lenary lenary left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

};

SmallString<128> ES;
raw_svector_ostream SS(ES);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1: preferably, yes, but this could come later?

2: I'm not sure the syntax has to be 1:1, but something to reference back to the constraint as written would be good.

@s-barannikov s-barannikov force-pushed the sdnode-vt-constraint branch from e3b2dce to 50a8222 Compare July 23, 2025 01:35
@s-barannikov
Copy link
Contributor Author

Rebased on top of #150148.

@s-barannikov s-barannikov force-pushed the sdnode-vt-constraint branch from 50a8222 to 21fa696 Compare July 23, 2025 18:36
@s-barannikov s-barannikov force-pushed the sdnode-vt-constraint branch from 21fa696 to 77379f9 Compare July 23, 2025 18:44
s-barannikov added a commit to s-barannikov/llvm-project that referenced this pull request Jul 23, 2025
Consistently use `FlagsVT` for operands/results of nodes that
consume/produce NZCV flags.
Previously, some of the operands/results had incorrect `MVT::Glue` type
while others had `MVT_CC` type, which is supposed to be used for
condition codes (`AArch64CC::CondCode` enum).

Found by llvm#150125.
s-barannikov added a commit to s-barannikov/llvm-project that referenced this pull request Jul 23, 2025
Consistently use `FlagsVT` for operands/results of nodes that
consume/produce NZCV flags.
Previously, some of the operands/results had incorrect `MVT::Glue`
type while others had `MVT_CC` type, which is supposed to be used
for condition codes (`AArch64CC::CondCode` enum).

Found by llvm#150125.
s-barannikov added a commit to s-barannikov/llvm-project that referenced this pull request Jul 23, 2025
Consistently use `FlagsVT` for operands/results of nodes that
consume/produce NZCV flags.
Previously, some of the operands/results had incorrect `MVT::Glue`
type while others had `MVT_CC` type, which is supposed to be used
for condition codes (`AArch64CC::CondCode` enum).

Found by llvm#150125.
s-barannikov added a commit to s-barannikov/llvm-project that referenced this pull request Jul 23, 2025
Consistently use `FlagsVT` for operands/results of nodes that
consume/produce NZCV flags.
Previously, some of the operands/results had incorrect `MVT::Glue`
type while others had `MVT_CC` type, which is supposed to be used
for condition codes (`AArch64CC::CondCode` enum).

Found by llvm#150125.
davemgreen added a commit to davemgreen/llvm-project that referenced this pull request Jul 24, 2025
I think this is OK, that we always use v2i64 for the type of a LDNP/STNP nodes.
Bitcasting the type should be fine for little endian. This helps with llvm#150125
s-barannikov added a commit that referenced this pull request Jul 24, 2025
Consistently use `FlagsVT` for operands/results of nodes that
consume/produce NZCV flags.
Previously, some of the operands/results had incorrect `MVT::Glue` type
while others had `MVT_CC` type, which is supposed to be used for
condition codes (`AArch64CC::CondCode` enum).

Found by #150125.
davemgreen added a commit that referenced this pull request Jul 25, 2025
I think this is OK, that we always use v2i64 for the type of a LDNP/STNP
nodes. Bitcasting the type should be fine for little endian. This helps
with #150125.
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Jul 28, 2025
Consistently use `FlagsVT` for operands/results of nodes that
consume/produce NZCV flags.
Previously, some of the operands/results had incorrect `MVT::Glue` type
while others had `MVT_CC` type, which is supposed to be used for
condition codes (`AArch64CC::CondCode` enum).

Found by llvm#150125.
mahesh-attarde pushed a commit to mahesh-attarde/llvm-project that referenced this pull request Jul 28, 2025
I think this is OK, that we always use v2i64 for the type of a LDNP/STNP
nodes. Bitcasting the type should be fine for little endian. This helps
with llvm#150125.
Comment on lines +155 to +159
auto GetConstraintOp = [&](unsigned Idx) {
if (Idx < Desc.NumResults)
return ConstraintOp{N, Idx, /*IsRes=*/true};
return ConstraintOp{N, HasChain + (Idx - Desc.NumResults), /*IsRes=*/false};
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move to static member ConstraintOp::get?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants