[llvm] [SelectionDAG] Verify SDTCisVT and SDTCVecEltisVT constraints (PR #150125)
Sergei Barannikov via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 22 18:35:21 PDT 2025
https://github.com/s-barannikov updated https://github.com/llvm/llvm-project/pull/150125
>From 50a82224c26055fc5726ca8cd40132442710dbd6 Mon Sep 17 00:00:00 2001
From: Sergei Barannikov <barannikov88 at gmail.com>
Date: Wed, 23 Jul 2025 01:06:57 +0300
Subject: [PATCH] [SelectionDAG] Verify SDTCisVT and SDTCVecEltisVT constraints
---
llvm/include/llvm/CodeGen/SDNodeInfo.h | 18 ++-
llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp | 105 ++++++++++++++++++
llvm/lib/Target/AArch64/AArch64InstrInfo.td | 2 +-
.../AArch64/AArch64SelectionDAGInfo.cpp | 44 +++++---
llvm/lib/Target/M68k/M68kSelectionDAGInfo.cpp | 15 +++
llvm/lib/Target/M68k/M68kSelectionDAGInfo.h | 3 +
.../Target/RISCV/RISCVSelectionDAGInfo.cpp | 19 +---
llvm/lib/Target/Sparc/SparcInstrInfo.td | 2 +-
.../ambiguous-constraints.td | 12 +-
llvm/test/TableGen/SDNodeInfoEmitter/basic.td | 58 ++++++----
.../TableGen/SDNodeInfoEmitter/namespace.td | 18 +--
.../SDNodeInfoEmitter/skipped-nodes.td | 6 +-
.../TableGen/Basic/SequenceToOffsetTable.h | 3 +-
llvm/utils/TableGen/Common/InfoByHwMode.h | 2 +
llvm/utils/TableGen/SDNodeInfoEmitter.cpp | 72 ++++++++++--
15 files changed, 295 insertions(+), 84 deletions(-)
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 6ecddad72c078..26a98637d0560 100644
--- a/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVSelectionDAGInfo.cpp
@@ -20,27 +20,22 @@ RISCVSelectionDAGInfo::~RISCVSelectionDAGInfo() = default;
void RISCVSelectionDAGInfo::verifyTargetNode(const SelectionDAG &DAG,
const SDNode *N) const {
+ 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::TUPLE_EXTRACT:
- assert(N->getNumOperands() == 2 && "Expected three operands!");
assert(N->getOperand(1).getOpcode() == ISD::TargetConstant &&
- N->getOperand(1).getValueType() == MVT::i32 &&
- "Expected index to be an i32 target constant!");
+ "Expected index to be a target constant!");
break;
case RISCVISD::TUPLE_INSERT:
- assert(N->getNumOperands() == 3 && "Expected three operands!");
assert(N->getOperand(2).getOpcode() == ISD::TargetConstant &&
- N->getOperand(2).getValueType() == MVT::i32 &&
- "Expected index to be an i32 target constant!");
+ "Expected index to be a target constant!");
break;
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");
@@ -50,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..bd8d927ce5655 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 VTByHwModePair 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 VTByHwModePair 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 VTByHwModePair 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: {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: /* 0 */ {SDTCisVT, 1, 0, 0, MVT::i2},
+// CHECK-SAME: {SDTCisVT, 0, 0, 0, MVT::i1},
+// CHECK-NEXT: /* 2 */ {SDTCisSameSizeAs, 19, 18, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisSameNumEltsAs, 17, 16, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCVecEltisVT, 15, 0, 0, MVT::i32},
+// CHECK-SAME: {SDTCisSubVecOfVec, 14, 13, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisEltOfVec, 12, 11, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisOpSmallerThanOp, 10, 9, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisVTSmallerThanOp, 8, 7, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisSameAs, 6, 5, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisVec, 4, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisFP, 3, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisInt, 2, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisPtrTy, 1, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
+// CHECK-SAME: {SDTCisVT, 0, 0, 0, MVT::i1},
// CHECK-NEXT: };
// CHECK-EMPTY:
// CHECK-NEXT: static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -185,5 +197,5 @@ def my_node_3 : SDNode<
// CHECK-NEXT: };
// CHECK-EMPTY:
// CHECK-NEXT: static const SDNodeInfo MyTargetGenSDNodeInfo(
-// CHECK-NEXT: /*NumOpcodes=*/3, MyTargetSDNodeDescs,
-// CHECK-NEXT: MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// CHECK-NEXT: /*NumOpcodes=*/3, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// CHECK-NEXT: MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/namespace.td b/llvm/test/TableGen/SDNodeInfoEmitter/namespace.td
index 217fb7c9fd475..578016469fccd 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/namespace.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/namespace.td
@@ -24,15 +24,15 @@ def node_2 : SDNode<"MyCustomISD::NODE", SDTypeProfile<0, 1, [SDTCisVT<0, i2>]>>
// EMPTY-NEXT: ;
// EMPTY: static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// EMPTY-NEXT: /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// EMPTY-NEXT: /* dummy */ {SDTCisVT, 0, 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);
+// EMPTY-NEXT: /*NumOpcodes=*/0, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// EMPTY-NEXT: MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
// COMMON: namespace llvm::[[NS]] {
// COMMON-EMPTY:
@@ -49,9 +49,13 @@ def node_2 : SDNode<"MyCustomISD::NODE", SDTypeProfile<0, 1, [SDTCisVT<0, i2>]>>
// COMMON-NEXT: "[[NS]]::NODE\0"
// COMMON-NEXT: ;
+// COMMON: static const VTByHwModePair MyTargetVTByHwModeTable[] = {
+// COMMON-NEXT: /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}
+// COMMON-NEXT: };
+
// COMMON: static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
-// TARGET-NEXT: /* 0 */ {SDTCisVT, 0, 0, MVT::i1},
-// CUSTOM-NEXT: /* 0 */ {SDTCisVT, 0, 0, MVT::i2},
+// TARGET-NEXT: /* 0 */ {SDTCisVT, 0, 0, 0, MVT::i1},
+// CUSTOM-NEXT: /* 0 */ {SDTCisVT, 0, 0, 0, MVT::i2},
// COMMON-NEXT: };
// COMMON-EMPTY:
// COMMON-NEXT: static const SDNodeDesc MyTargetSDNodeDescs[] = {
@@ -60,5 +64,5 @@ def node_2 : SDNode<"MyCustomISD::NODE", SDTypeProfile<0, 1, [SDTCisVT<0, i2>]>>
// COMMON-NEXT: };
// COMMON-EMPTY:
// COMMON-NEXT: static const SDNodeInfo MyTargetGenSDNodeInfo(
-// COMMON-NEXT: /*NumOpcodes=*/1, MyTargetSDNodeDescs,
-// COMMON-NEXT: MyTargetSDNodeNames, MyTargetSDTypeConstraints);
+// COMMON-NEXT: /*NumOpcodes=*/1, MyTargetSDNodeDescs, MyTargetSDNodeNames,
+// COMMON-NEXT: MyTargetVTByHwModeTable, MyTargetSDTypeConstraints);
diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td b/llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td
index abd6ad3bda3bc..f6c2d174f636b 100644
--- a/llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td
+++ b/llvm/test/TableGen/SDNodeInfoEmitter/skipped-nodes.td
@@ -75,7 +75,7 @@ def node_5b : SDNode<"MyTargetISD::NODE_5", SDTypeProfile<0, 0, []>, [SDNPHasCha
// 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[] = {
@@ -83,8 +83,8 @@ def node_5b : SDNode<"MyTargetISD::NODE_5", SDTypeProfile<0, 0, []>, [SDNPHasCha
// 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);
def compat_a : SDNode<"MyTargetISD::COMPAT", SDTypeProfile<1, -1, []>>;
def compat_b : SDNode<"MyTargetISD::COMPAT", SDTypeProfile<1, -1, [SDTCisVT<0, untyped>]>>;
diff --git a/llvm/utils/TableGen/Basic/SequenceToOffsetTable.h b/llvm/utils/TableGen/Basic/SequenceToOffsetTable.h
index 8da6fbef0672e..a1c4dc09d3212 100644
--- a/llvm/utils/TableGen/Basic/SequenceToOffsetTable.h
+++ b/llvm/utils/TableGen/Basic/SequenceToOffsetTable.h
@@ -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 {
assert(IsLaidOut && "Call layout() before emit()");
for (const auto &[Seq, Offset] : Seqs) {
OS << " /* " << Offset << " */ ";
diff --git a/llvm/utils/TableGen/Common/InfoByHwMode.h b/llvm/utils/TableGen/Common/InfoByHwMode.h
index 7925599a98a0c..16c5a36c135e6 100644
--- a/llvm/utils/TableGen/Common/InfoByHwMode.h
+++ b/llvm/utils/TableGen/Common/InfoByHwMode.h
@@ -102,6 +102,8 @@ template <typename InfoT> struct InfoByHwMode {
LLVM_ATTRIBUTE_ALWAYS_INLINE
const_iterator end() const { return Map.end(); }
LLVM_ATTRIBUTE_ALWAYS_INLINE
+ size_t size() const { return Map.size(); }
+ LLVM_ATTRIBUTE_ALWAYS_INLINE
bool empty() const { return Map.empty(); }
LLVM_ATTRIBUTE_ALWAYS_INLINE
diff --git a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
index 64f03dae83e7d..ce74e7d1ebca0 100644
--- a/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
+++ b/llvm/utils/TableGen/SDNodeInfoEmitter.cpp
@@ -198,15 +198,29 @@ static StringRef getTypeConstraintKindName(SDTypeConstraint::KindTy Kind) {
#undef CASE
}
-static void emitTypeConstraint(raw_ostream &OS, SDTypeConstraint C) {
+static void emitTypeConstraint(
+ raw_ostream &OS, SDTypeConstraint C,
+ const std::map<ValueTypeByHwMode, unsigned> &VTByHwModeTable) {
unsigned OtherOpNo = 0;
- MVT VT;
+ unsigned NumHwModes = 0;
+ unsigned VTByHwModeOffset = 0;
+ MVT::SimpleValueType VT = MVT::INVALID_SIMPLE_VALUE_TYPE;
switch (C.ConstraintType) {
case SDTypeConstraint::SDTCisVT:
+ // SequenceToOffsetTable::emit() prints a "dummy" (default-constructed)
+ // element if the table would otherwise be empty. VVT is empty in this case.
+ if (C.VVT.empty())
+ break;
+ [[fallthrough]];
case SDTypeConstraint::SDTCVecEltisVT:
- if (C.VVT.isSimple())
- VT = C.VVT.getSimple();
+ if (C.VVT.isSimple()) {
+ VT = C.VVT.getSimple().SimpleTy;
+ } else {
+ NumHwModes = C.VVT.size();
+ assert(NumHwModes && "Empty type set?");
+ VTByHwModeOffset = VTByHwModeTable.at(C.VVT);
+ }
break;
case SDTypeConstraint::SDTCisPtrTy:
case SDTypeConstraint::SDTCisInt:
@@ -224,15 +238,22 @@ static void emitTypeConstraint(raw_ostream &OS, SDTypeConstraint C) {
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);
+ OS << '{' << getTypeConstraintKindName(C.ConstraintType) << ", "
+ << C.OperandNo << ", " << OtherOpNo << ", " << NumHwModes << ", ";
+ if (NumHwModes) {
+ OS << VTByHwModeOffset;
+ } else {
+ OS << (VT == MVT::INVALID_SIMPLE_VALUE_TYPE
+ ? "MVT::INVALID_SIMPLE_VALUE_TYPE"
+ : getEnumName(VT));
+ }
+ OS << '}';
}
std::vector<std::pair<unsigned, unsigned>>
SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
+ std::map<ValueTypeByHwMode, unsigned> VTByHwModeTable;
+
using ConstraintsVecTy = SmallVector<SDTypeConstraint, 0>;
SequenceToOffsetTable<ConstraintsVecTy> ConstraintTable(
/*Terminator=*/std::nullopt);
@@ -261,6 +282,16 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
if (Constraints.empty())
continue;
+ for (const SDTypeConstraint &C : Constraints) {
+ if (C.ConstraintType == SDTypeConstraint::SDTCisVT ||
+ C.ConstraintType == SDTypeConstraint::SDTCVecEltisVT) {
+ if (!C.VVT.isSimple()) {
+ assert(!C.VVT.empty() && "Unexpected empty type set");
+ VTByHwModeTable.try_emplace(C.VVT);
+ }
+ }
+ }
+
// 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.
@@ -269,9 +300,26 @@ SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const {
ConstraintTable.layout();
+ OS << "static const VTByHwModePair " << Target.getName()
+ << "VTByHwModeTable[] = {\n";
+ unsigned VTByHwModeOffset = 0;
+ for (auto &[VTByHwMode, Offset] : VTByHwModeTable) {
+ OS << " /* " << VTByHwModeOffset << " */ ";
+ for (auto [Mode, VT] : VTByHwMode)
+ OS << '{' << Mode << ", " << getEnumName(VT.SimpleTy) << "}, ";
+ OS << '\n';
+ Offset = VTByHwModeOffset;
+ VTByHwModeOffset += VTByHwMode.size();
+ }
+ // Avoid "zero size arrays are an extension" warning.
+ if (VTByHwModeTable.empty())
+ OS << " /* dummy */ {0, MVT::INVALID_SIMPLE_VALUE_TYPE}\n";
+ OS << "};\n\n";
+
OS << "static const SDTypeConstraint " << Target.getName()
<< "SDTypeConstraints[] = {\n";
- ConstraintTable.emit(OS, emitTypeConstraint);
+ ConstraintTable.emit(OS, std::bind(emitTypeConstraint, std::placeholders::_1,
+ std::placeholders::_2, VTByHwModeTable));
OS << "};\n\n";
for (const auto &[EnumName, Nodes] : NodesByName) {
@@ -342,8 +390,8 @@ void SDNodeInfoEmitter::emitDescs(raw_ostream &OS) const {
OS << "};\n\n";
OS << formatv("static const SDNodeInfo {0}GenSDNodeInfo(\n"
- " /*NumOpcodes=*/{1}, {0}SDNodeDescs,\n"
- " {0}SDNodeNames, {0}SDTypeConstraints);\n\n",
+ " /*NumOpcodes=*/{1}, {0}SDNodeDescs, {0}SDNodeNames,\n"
+ " {0}VTByHwModeTable, {0}SDTypeConstraints);\n\n",
TargetName, NodesByName.size());
OS << "} // namespace llvm\n\n";
More information about the llvm-commits
mailing list