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