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