From 7e06e22c3870f448f6e9b45f8b4cc20b774fca9f Mon Sep 17 00:00:00 2001 From: Sergei Barannikov Date: Wed, 23 Jul 2025 01:06:57 +0300 Subject: [PATCH 1/3] [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 +- .../TableGen/SDNodeInfoEmitter/advanced.td | 40 ++++--- .../ambiguous-constraints-1.td | 12 +- .../ambiguous-constraints-2.td | 12 +- .../TableGen/SDNodeInfoEmitter/namespace.td | 18 +-- .../TableGen/SDNodeInfoEmitter/no-nodes.td | 10 +- .../SDNodeInfoEmitter/skipped-nodes.td | 6 +- .../SDNodeInfoEmitter/trivial-node.td | 12 +- .../TableGen/Basic/SequenceToOffsetTable.h | 3 +- llvm/utils/TableGen/Common/InfoByHwMode.h | 2 + llvm/utils/TableGen/SDNodeInfoEmitter.cpp | 72 ++++++++++-- 18 files changed, 307 insertions(+), 88 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(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(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(N->getOperand(1)) && - "Expected second operand to be a constant i32!"); + assert(isa(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/advanced.td b/llvm/test/TableGen/SDNodeInfoEmitter/advanced.td index d7eeaba9d8552..0c4a331be28f5 100644 --- a/llvm/test/TableGen/SDNodeInfoEmitter/advanced.td +++ b/llvm/test/TableGen/SDNodeInfoEmitter/advanced.td @@ -65,22 +65,26 @@ def my_node_3 : SDNode< // CHECK-NEXT: "MyTargetISD::NODE_3\0" // 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: 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: /* 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[] = { @@ -90,5 +94,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/ambiguous-constraints-1.td b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints-1.td index 8b86f93b1f785..3a5a70c0a2550 100644 --- a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints-1.td +++ b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints-1.td @@ -16,8 +16,12 @@ def my_node_b : SDNode<"MyTargetISD::NODE", SDTypeProfile<1, 0, [SDTCisVT<0, f32 // CHECK-NEXT: "MyTargetISD::NODE\0" // CHECK-NEXT: ; -// CHECK: static const SDTypeConstraint MyTargetSDTypeConstraints[] = { -// CHECK-NEXT: /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE} +// CHECK: 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, 0, MVT::INVALID_SIMPLE_VALUE_TYPE} // CHECK-NEXT: }; // CHECK-EMPTY: // CHECK-NEXT: static const SDNodeDesc MyTargetSDNodeDescs[] = { @@ -25,5 +29,5 @@ 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); diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints-2.td b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints-2.td index 29429e9baa300..916508426b66b 100644 --- a/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints-2.td +++ b/llvm/test/TableGen/SDNodeInfoEmitter/ambiguous-constraints-2.td @@ -26,8 +26,12 @@ def my_node_2b : SDNode<"MyTargetISD::NODE_2", SDTypeProfile<1, 0, [SDTCisVT<0, // CHECK-NEXT: "MyTargetISD::NODE_2\0" // CHECK-NEXT: ; -// CHECK: static const SDTypeConstraint MyTargetSDTypeConstraints[] = { -// CHECK-NEXT: /* 0 */ {SDTCisVT, 0, 0, MVT::i32}, +// CHECK: 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: /* 0 */ {SDTCisVT, 0, 0, 0, MVT::i32}, // CHECK-NEXT: }; // CHECK-EMPTY: // CHECK-NEXT: static const SDNodeDesc MyTargetSDNodeDescs[] = { @@ -36,5 +40,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/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/no-nodes.td b/llvm/test/TableGen/SDNodeInfoEmitter/no-nodes.td index 0c5c63db4c95b..8dbe178334722 100644 --- a/llvm/test/TableGen/SDNodeInfoEmitter/no-nodes.td +++ b/llvm/test/TableGen/SDNodeInfoEmitter/no-nodes.td @@ -34,16 +34,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: MyTargetVTByHwModeTable, MyTargetSDTypeConstraints); // CHECK-EMPTY: // CHECK-NEXT: } // namespace llvm // CHECK-EMPTY: 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/test/TableGen/SDNodeInfoEmitter/trivial-node.td b/llvm/test/TableGen/SDNodeInfoEmitter/trivial-node.td index 4bdc70a8508f6..6874e389a41eb 100644 --- a/llvm/test/TableGen/SDNodeInfoEmitter/trivial-node.td +++ b/llvm/test/TableGen/SDNodeInfoEmitter/trivial-node.td @@ -21,8 +21,12 @@ def my_noop : SDNode<"MyTargetISD::NOOP", SDTypeProfile<0, 0, []>>; // CHECK-NEXT: "MyTargetISD::NOOP\0" // CHECK-NEXT: ; -// CHECK: static const SDTypeConstraint MyTargetSDTypeConstraints[] = { -// CHECK-NEXT: /* dummy */ {SDTCisVT, 0, 0, MVT::INVALID_SIMPLE_VALUE_TYPE} +// CHECK: 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, 0, MVT::INVALID_SIMPLE_VALUE_TYPE} // CHECK-NEXT: }; // CHECK-EMPTY: // CHECK-NEXT: static const SDNodeDesc MyTargetSDNodeDescs[] = { @@ -30,5 +34,5 @@ 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); 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 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 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 &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> SDNodeInfoEmitter::emitTypeConstraints(raw_ostream &OS) const { + std::map VTByHwModeTable; + using ConstraintsVecTy = SmallVector; SequenceToOffsetTable 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"; From db18b212a0db2e68bf62b2f31cb63f76f9a1f974 Mon Sep 17 00:00:00 2001 From: Sergei Barannikov Date: Wed, 23 Jul 2025 07:32:22 +0300 Subject: [PATCH 2/3] Minor tweaks --- llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp b/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp index fc2bc64dc39c8..5bebd7d70305d 100644 --- a/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp @@ -57,8 +57,8 @@ struct ConstraintOp { EVT getValueType() const { return getValue().getValueType(); } }; -raw_ostream &operator<<(raw_ostream &OS, const ConstraintOp &Info) { - return OS << (Info.IsRes ? "result" : "operand") << " #" << Info.Idx; +raw_ostream &operator<<(raw_ostream &OS, const ConstraintOp &Op) { + return OS << (Op.IsRes ? "result" : "operand") << " #" << Op.Idx; } } // namespace @@ -172,7 +172,7 @@ void SDNodeInfo::verifyNode(const SelectionDAG &DAG, const SDNode *N) const { for (const SDTypeConstraint &C : getConstraints(N->getOpcode())) { ConstraintOp Op = GetConstraintOp(C.OpNo); - EVT OpVT = Op.getValue().getValueType(); + EVT OpVT = Op.getValueType(); switch (C.Kind) { case SDTCisVT: { From 77379f9fee6230b54d554e59649a5ee923bb0575 Mon Sep 17 00:00:00 2001 From: Sergei Barannikov Date: Wed, 23 Jul 2025 21:35:55 +0300 Subject: [PATCH 3/3] Add HW mode test --- .../TableGen/SDNodeInfoEmitter/hw-mode.td | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 llvm/test/TableGen/SDNodeInfoEmitter/hw-mode.td diff --git a/llvm/test/TableGen/SDNodeInfoEmitter/hw-mode.td b/llvm/test/TableGen/SDNodeInfoEmitter/hw-mode.td new file mode 100644 index 0000000000000..8d749ca0a11a4 --- /dev/null +++ b/llvm/test/TableGen/SDNodeInfoEmitter/hw-mode.td @@ -0,0 +1,61 @@ +// RUN: llvm-tblgen -gen-sd-node-info -I %p/../../../include %s | FileCheck %s + +include "llvm/Target/Target.td" + +def MyTarget : Target; + +def M1 : HwMode<"", []>; +def M2 : HwMode<"", []>; +def M3 : HwMode<"", []>; + +def VT1 : ValueTypeByHwMode<[M1], [i1]>; +def VT2 : ValueTypeByHwMode<[M2], [i2]>; +def VT3 : ValueTypeByHwMode<[M1, M2, M3, DefaultMode], [i1, i2, i4, i8]>; + +def my_node_1 : SDNode< + "MyTargetISD::NODE_1", + SDTypeProfile<0, 5, [ + SDTCVecEltisVT<0, VT3>, + SDTCisVT<1, i1>, + SDTCVecEltisVT<2, i2>, + SDTCisVT<3, VT1>, + SDTCVecEltisVT<4, VT2>, + ]> +>; + +def my_node_2 : SDNode< + "MyTargetISD::NODE_2", + SDTypeProfile<1, 2, [ + SDTCVecEltisVT<0, VT3>, + SDTCisVT<1, i1>, + SDTCVecEltisVT<2, i2>, + ]> +>; + +def my_node_3 : SDNode< + "MyTargetISD::NODE_3", + SDTypeProfile<1, 0, [ + SDTCisVT<0, VT3>, + ]> +>; + +// CHECK: static const VTByHwModePair MyTargetVTByHwModeTable[] = { +// CHECK-NEXT: /* 0 */ {0, MVT::i8}, {1, MVT::i1}, {2, MVT::i2}, {3, MVT::i4}, +// CHECK-NEXT: /* 4 */ {1, MVT::i1}, +// CHECK-NEXT: /* 5 */ {2, MVT::i2}, +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: static const SDTypeConstraint MyTargetSDTypeConstraints[] = { +// CHECK-NEXT: /* 0 */ {SDTCisVT, 0, 0, 4, 0}, +// CHECK-NEXT: /* 1 */ {SDTCVecEltisVT, 4, 0, 1, 5}, +// CHECK-SAME: {SDTCisVT, 3, 0, 1, 4}, +// CHECK-SAME: {SDTCVecEltisVT, 2, 0, 0, MVT::i2}, +// CHECK-SAME: {SDTCisVT, 1, 0, 0, MVT::i1}, +// CHECK-SAME: {SDTCVecEltisVT, 0, 0, 4, 0}, +// CHECK-NEXT: }; +// CHECK-EMPTY: +// CHECK-NEXT: static const SDNodeDesc MyTargetSDNodeDescs[] = { +// CHECK-NEXT: {0, 5, 0, 0, 0, 1, 1, 5}, // NODE_1 +// CHECK-NEXT: {1, 2, 0, 0, 0, 21, 3, 3}, // NODE_2 +// CHECK-NEXT: {1, 0, 0, 0, 0, 41, 0, 1}, // NODE_3 +// CHECK-NEXT: };