Skip to content

Commit 3ebfbac

Browse files
committed
[TableGen] Support for optional chain in Selection DAG nodes
This change adds a new property for Selection DAG nodes used in pattern descriptions: SDNPMayHaveChain. A node with this property may have or may not have a chain operand. For example, both of the following variants become valid: t3: f32,ch = fnearbyint t0, t2 t3: f32 = fnearbyint t2 The specific variant is determined during pattern matching, based on whether the first operand is a chain (i.e. has the type MVT::Other). This feature is intended to be used for floating point operations. They have side effects in a strictfp environment and are pure functions in the default FP environment. Currently each such operation requires two opcodes - one for each kind of FP environment. These opcodes represent the same operation and are processed similarly, which increase amount of code. With this feature the support of strictfp environment should be easier, as it can use the same opcode as the default environment.
1 parent 6345222 commit 3ebfbac

19 files changed

+248
-57
lines changed

llvm/include/llvm/CodeGen/SDNodeInfo.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ enum SDNP {
2626
SDNPOptInGlue,
2727
SDNPMemOperand,
2828
SDNPVariadic,
29+
SDNPMayHaveChain
2930
};
3031

3132
enum SDTC : uint8_t {

llvm/include/llvm/CodeGen/SDNodeProperties.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ def SDNPMayLoad : SDNodeProperty; // May read memory, sets 'mayLoad'.
2929
def SDNPSideEffect : SDNodeProperty; // Sets 'HasUnmodelledSideEffects'.
3030
def SDNPMemOperand : SDNodeProperty; // Touches memory, has assoc MemOperand
3131
def SDNPVariadic : SDNodeProperty; // Node has variable arguments.
32+
def SDNPMayHaveChain: SDNodeProperty; // Optionally has chain operand/result.

llvm/include/llvm/CodeGen/SelectionDAGISel.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,7 @@ class SelectionDAGISel {
151151
OPC_RecordChild6,
152152
OPC_RecordChild7,
153153
OPC_RecordMemRef,
154+
OPC_RecordOptionalChain,
154155
OPC_CaptureGlueInput,
155156
OPC_MoveChild,
156157
OPC_MoveChild0,
@@ -493,7 +494,8 @@ class SelectionDAGISel {
493494
private:
494495
void DoInstructionSelection();
495496
SDNode *MorphNode(SDNode *Node, unsigned TargetOpc, SDVTList VTList,
496-
ArrayRef<SDValue> Ops, unsigned EmitNodeInfo);
497+
ArrayRef<SDValue> Ops, unsigned EmitNodeInfo,
498+
bool OptionalChain);
497499

498500
/// Prepares the landing pad to take incoming values or do other EH
499501
/// personality specific tasks. Returns true if the block should be

llvm/include/llvm/CodeGen/SelectionDAGNodes.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,13 +717,31 @@ END_TWO_BYTE_PACK()
717717
case ISD::STRICT_FP_TO_FP16:
718718
case ISD::STRICT_BF16_TO_FP:
719719
case ISD::STRICT_FP_TO_BF16:
720+
#define FP_OPERATION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN)
720721
#define DAG_INSTRUCTION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) \
721722
case ISD::STRICT_##DAGN:
722723
#include "llvm/IR/ConstrainedOps.def"
723724
return true;
724725
}
725726
}
726727

728+
/// Test if this node is a floating-point operation which can exist in two
729+
/// forms, - with chain or without it.
730+
bool isFPOperation() const {
731+
switch (NodeType) {
732+
default:
733+
return false;
734+
#define FP_OPERATION(NAME, NARG, ROUND_MODE, INTRINSIC, DAGN) case ISD::DAGN:
735+
#include "llvm/IR/ConstrainedOps.def"
736+
return true;
737+
}
738+
}
739+
740+
/// Test if this node has an input chain.
741+
bool hasChain() const {
742+
return NumOperands > 0 && OperandList[0].getValueType() == MVT::Other;
743+
}
744+
727745
/// Test if this node is an assert operation.
728746
bool isAssert() const {
729747
switch (NodeType) {

llvm/lib/CodeGen/SelectionDAG/SDNodeInfo.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ void SDNodeInfo::verifyNode(const SelectionDAG &DAG, const SDNode *N) const {
4747
bool HasInGlue = Desc.hasProperty(SDNPInGlue);
4848
bool HasOptInGlue = Desc.hasProperty(SDNPOptInGlue);
4949
bool IsVariadic = Desc.hasProperty(SDNPVariadic);
50+
bool MayHaveChain = Desc.hasProperty(SDNPMayHaveChain);
51+
52+
if (HasChain && MayHaveChain)
53+
reportNodeError(
54+
DAG, N, "Flags 'HasChain' and 'MayHaveChain' cannot be both specified");
55+
56+
if (MayHaveChain && N->getNumOperands() > 0 &&
57+
N->getOperand(0).getValueType() == MVT::Other) {
58+
HasChain = true;
59+
}
5060

5161
unsigned ActualNumResults = N->getNumValues();
5262
unsigned ExpectedNumResults = Desc.NumResults + HasChain + HasOutGlue;

llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp

Lines changed: 53 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2836,9 +2836,9 @@ HandleMergeInputChains(SmallVectorImpl<SDNode*> &ChainNodesMatched,
28362836
}
28372837

28382838
/// MorphNode - Handle morphing a node in place for the selector.
2839-
SDNode *SelectionDAGISel::
2840-
MorphNode(SDNode *Node, unsigned TargetOpc, SDVTList VTList,
2841-
ArrayRef<SDValue> Ops, unsigned EmitNodeInfo) {
2839+
SDNode *SelectionDAGISel::MorphNode(SDNode *Node, unsigned TargetOpc,
2840+
SDVTList VTList, ArrayRef<SDValue> Ops,
2841+
unsigned EmitNodeInfo, bool OptionalChain) {
28422842
// It is possible we're using MorphNodeTo to replace a node with no
28432843
// normal results with one that has a normal result (or we could be
28442844
// adding a chain) and the input could have glue and chains as well.
@@ -2880,7 +2880,7 @@ MorphNode(SDNode *Node, unsigned TargetOpc, SDVTList VTList,
28802880
--ResNumResults;
28812881

28822882
// Move the chain reference if needed.
2883-
if ((EmitNodeInfo & OPFL_Chain) && OldChainResultNo != -1 &&
2883+
if ((EmitNodeInfo & OPFL_Chain || OptionalChain) && OldChainResultNo != -1 &&
28842884
static_cast<unsigned>(OldChainResultNo) != ResNumResults - 1)
28852885
ReplaceUses(SDValue(Node, OldChainResultNo),
28862886
SDValue(Res, ResNumResults - 1));
@@ -3385,6 +3385,12 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
33853385
// update the chain results when the pattern is complete.
33863386
SmallVector<SDNode*, 3> ChainNodesMatched;
33873387

3388+
bool HasNodesWithOptionalChain = false;
3389+
3390+
// List of pattern matches nodes that may have input/output chains and
3391+
// actually have them.
3392+
SmallVector<SDNode *, 8> OptionalChainNodes;
3393+
33883394
LLVM_DEBUG(dbgs() << "ISEL: Starting pattern match\n");
33893395

33903396
// Determine where to start the interpreter. Normally we start at opcode #0,
@@ -3505,7 +3511,8 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
35053511
case OPC_RecordChild2: case OPC_RecordChild3:
35063512
case OPC_RecordChild4: case OPC_RecordChild5:
35073513
case OPC_RecordChild6: case OPC_RecordChild7: {
3508-
unsigned ChildNo = Opcode-OPC_RecordChild0;
3514+
unsigned ChildNo =
3515+
Opcode - OPC_RecordChild0 + !OptionalChainNodes.empty();
35093516
if (ChildNo >= N.getNumOperands())
35103517
break; // Match fails if out of range child #.
35113518

@@ -3523,6 +3530,17 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
35233530

35243531
continue;
35253532

3533+
case OPC_RecordOptionalChain:
3534+
HasNodesWithOptionalChain = true;
3535+
// If the current node has input chain, record it.
3536+
if (N->getNumOperands() != 0) {
3537+
SDValue FirstOperand = N->getOperand(0);
3538+
if (FirstOperand.getValueType() == MVT::Other) {
3539+
OptionalChainNodes.push_back(N.getNode());
3540+
}
3541+
}
3542+
continue;
3543+
35263544
case OPC_CaptureGlueInput:
35273545
// If the current node has an input glue, capture it in InputGlue.
35283546
if (N->getNumOperands() != 0 &&
@@ -3531,7 +3549,8 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
35313549
continue;
35323550

35333551
case OPC_MoveChild: {
3534-
unsigned ChildNo = MatcherTable[MatcherIndex++];
3552+
unsigned ChildNo =
3553+
MatcherTable[MatcherIndex++] + !OptionalChainNodes.empty();
35353554
if (ChildNo >= N.getNumOperands())
35363555
break; // Match fails if out of range child #.
35373556
N = N.getOperand(ChildNo);
@@ -3543,7 +3562,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
35433562
case OPC_MoveChild2: case OPC_MoveChild3:
35443563
case OPC_MoveChild4: case OPC_MoveChild5:
35453564
case OPC_MoveChild6: case OPC_MoveChild7: {
3546-
unsigned ChildNo = Opcode-OPC_MoveChild0;
3565+
unsigned ChildNo = Opcode - OPC_MoveChild0 + !OptionalChainNodes.empty();
35473566
if (ChildNo >= N.getNumOperands())
35483567
break; // Match fails if out of range child #.
35493568
N = N.getOperand(ChildNo);
@@ -3568,6 +3587,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
35683587
unsigned SiblingNo = Opcode == OPC_MoveSibling
35693588
? MatcherTable[MatcherIndex++]
35703589
: Opcode - OPC_MoveSibling0;
3590+
SiblingNo += !OptionalChainNodes.empty();
35713591
if (SiblingNo >= N.getNumOperands())
35723592
break; // Match fails if out of range sibling #.
35733593
N = N.getOperand(SiblingNo);
@@ -3998,11 +4018,16 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
39984018
// Ignore these because the newly token factored chain should not refer to
39994019
// the old nodes.
40004020
unsigned NumChains = MatcherTable[MatcherIndex++];
4001-
assert(NumChains != 0 && "Can't TF zero chains");
4021+
assert((NumChains != 0 || HasNodesWithOptionalChain) &&
4022+
"Can't TF zero chains");
40024023

40034024
assert(ChainNodesMatched.empty() &&
40044025
"Should only have one EmitMergeInputChains per match");
40054026

4027+
if (NumChains == 0 && HasNodesWithOptionalChain &&
4028+
OptionalChainNodes.empty())
4029+
continue;
4030+
40064031
// Read all of the chained nodes.
40074032
for (unsigned i = 0; i != NumChains; ++i) {
40084033
unsigned RecNo = MatcherTable[MatcherIndex++];
@@ -4020,6 +4045,8 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
40204045
}
40214046
}
40224047

4048+
ChainNodesMatched.append(OptionalChainNodes);
4049+
40234050
// If the inner loop broke out, the match fails.
40244051
if (ChainNodesMatched.empty())
40254052
break;
@@ -4164,7 +4191,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
41644191
VTs.push_back(VT);
41654192
}
41664193

4167-
if (EmitNodeInfo & OPFL_Chain)
4194+
if (EmitNodeInfo & OPFL_Chain || !OptionalChainNodes.empty())
41684195
VTs.push_back(MVT::Other);
41694196
if (EmitNodeInfo & OPFL_GlueOutput)
41704197
VTs.push_back(MVT::Glue);
@@ -4186,6 +4213,10 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
41864213
unsigned RecNo = MatcherTable[MatcherIndex++];
41874214
if (RecNo & 128)
41884215
RecNo = GetVBR(RecNo, MatcherTable, MatcherIndex);
4216+
if (HasNodesWithOptionalChain) {
4217+
assert(RecNo > 0);
4218+
RecNo--;
4219+
}
41894220

41904221
assert(RecNo < RecordedNodes.size() && "Invalid EmitNode");
41914222
Ops.push_back(RecordedNodes[RecNo].first);
@@ -4209,7 +4240,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
42094240
}
42104241

42114242
// If this has chain/glue inputs, add them.
4212-
if (EmitNodeInfo & OPFL_Chain)
4243+
if (EmitNodeInfo & OPFL_Chain || !OptionalChainNodes.empty())
42134244
Ops.push_back(InputChain);
42144245
if ((EmitNodeInfo & OPFL_GlueInput) && InputGlue.getNode() != nullptr)
42154246
Ops.push_back(InputGlue);
@@ -4219,7 +4250,12 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
42194250
// We need to perform this check before potentially modifying one of the
42204251
// nodes via MorphNode.
42214252
bool MayRaiseFPException =
4222-
llvm::any_of(ChainNodesMatched, [this](SDNode *N) {
4253+
llvm::any_of(ChainNodesMatched,
4254+
[this](SDNode *N) {
4255+
return mayRaiseFPException(N) &&
4256+
!N->getFlags().hasNoFPExcept();
4257+
}) ||
4258+
llvm::any_of(OptionalChainNodes, [this](SDNode *N) {
42234259
return mayRaiseFPException(N) && !N->getFlags().hasNoFPExcept();
42244260
});
42254261

@@ -4251,8 +4287,9 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
42514287
"Chain node replaced during MorphNode");
42524288
llvm::erase(Chain, N);
42534289
});
4254-
Res = cast<MachineSDNode>(MorphNode(NodeToMatch, TargetOpc, VTList,
4255-
Ops, EmitNodeInfo));
4290+
Res = cast<MachineSDNode>(MorphNode(NodeToMatch, TargetOpc, VTList, Ops,
4291+
EmitNodeInfo,
4292+
!OptionalChainNodes.empty()));
42564293
}
42574294

42584295
// Set the NoFPExcept flag when no original matched node could
@@ -4264,9 +4301,9 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
42644301
// chain and glue.
42654302
if (EmitNodeInfo & OPFL_GlueOutput) {
42664303
InputGlue = SDValue(Res, VTs.size()-1);
4267-
if (EmitNodeInfo & OPFL_Chain)
4304+
if (EmitNodeInfo & OPFL_Chain || !OptionalChainNodes.empty())
42684305
InputChain = SDValue(Res, VTs.size()-2);
4269-
} else if (EmitNodeInfo & OPFL_Chain)
4306+
} else if (EmitNodeInfo & OPFL_Chain || !OptionalChainNodes.empty())
42704307
InputChain = SDValue(Res, VTs.size()-1);
42714308

42724309
// If the OPFL_MemRefs glue is set on this node, slap all of the
@@ -4430,7 +4467,7 @@ bool SelectionDAGISel::mayRaiseFPException(SDNode *N) const {
44304467
const SelectionDAGTargetInfo &TSI = CurDAG->getSelectionDAGInfo();
44314468
return TSI.mayRaiseFPException(N->getOpcode());
44324469
}
4433-
return N->isStrictFPOpcode();
4470+
return N->isStrictFPOpcode() || N->isFPOperation();
44344471
}
44354472

44364473
bool SelectionDAGISel::isOrEquivalentToAdd(const SDNode *N) const {

llvm/test/TableGen/SDNodeInfoEmitter/advanced.td

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,22 @@ def my_node_3 : SDNode<
4646
SDNPOutGlue, SDNPInGlue, SDNPOptInGlue]
4747
>;
4848

49+
def my_node_4 : SDNode<
50+
"MyTargetISD::NODE_4",
51+
SDTypeProfile<1, 1, [SDTCisVT<0, f32>, SDTCisVT<1, f32>]>,
52+
[SDNPMayHaveChain]
53+
>;
54+
4955
// CHECK: namespace llvm::MyTargetISD {
5056
// CHECK-EMPTY:
5157
// CHECK-NEXT: enum GenNodeType : unsigned {
5258
// CHECK-NEXT: NODE_1 = ISD::BUILTIN_OP_END,
5359
// CHECK-NEXT: NODE_2,
5460
// CHECK-NEXT: NODE_3,
61+
// CHECK-NEXT: NODE_4
5562
// CHECK-NEXT: };
5663
// CHECK-EMPTY:
57-
// CHECK-NEXT: static constexpr unsigned GENERATED_OPCODE_END = NODE_3 + 1;
64+
// CHECK-NEXT: static constexpr unsigned GENERATED_OPCODE_END = NODE_4 + 1;
5865
// CHECK-EMPTY:
5966
// CHECK-NEXT: } // namespace llvm::MyTargetISD
6067

@@ -63,6 +70,7 @@ def my_node_3 : SDNode<
6370
// CHECK-NEXT: "MyTargetISD::NODE_1\0"
6471
// CHECK-NEXT: "MyTargetISD::NODE_2\0"
6572
// CHECK-NEXT: "MyTargetISD::NODE_3\0"
73+
// CHECK-NEXT: "MyTargetISD::NODE_4\0"
6674
// CHECK-NEXT: ;
6775

6876
// CHECK: static const SDTypeConstraint MyTargetSDTypeConstraints[] = {
@@ -81,14 +89,16 @@ def my_node_3 : SDNode<
8189
// CHECK-SAME: {SDTCisInt, 2, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
8290
// CHECK-SAME: {SDTCisPtrTy, 1, 0, MVT::INVALID_SIMPLE_VALUE_TYPE},
8391
// CHECK-SAME: {SDTCisVT, 0, 0, MVT::i1},
92+
// CHECK-NEXT: /* 15 */ {SDTCisVT, 1, 0, MVT::f32}, {SDTCisVT, 0, 0, MVT::f32},
8493
// CHECK-NEXT: };
8594
// CHECK-EMPTY:
8695
// CHECK-NEXT: static const SDNodeDesc MyTargetSDNodeDescs[] = {
8796
// CHECK-NEXT: {1, 1, 0|1<<SDNPHasChain, 0, 0, 1, 0, 2}, // NODE_1
8897
// CHECK-NEXT: {3, 1, 0|1<<SDNPVariadic|1<<SDNPMemOperand, 0, 42, 21, 11, 4}, // NODE_2
8998
// CHECK-NEXT: {2, -1, 0|1<<SDNPHasChain|1<<SDNPOutGlue|1<<SDNPInGlue|1<<SDNPOptInGlue, 0|1<<SDNFIsStrictFP, 24, 41, 2, 13}, // NODE_3
99+
// CHECK-NEXT: {1, 1, 0|1<<SDNPMayHaveChain, 0, 0, 61, 15, 2}, // NODE_4
90100
// CHECK-NEXT: };
91101
// CHECK-EMPTY:
92102
// CHECK-NEXT: static const SDNodeInfo MyTargetGenSDNodeInfo(
93-
// CHECK-NEXT: /*NumOpcodes=*/3, MyTargetSDNodeDescs,
103+
// CHECK-NEXT: /*NumOpcodes=*/4, MyTargetSDNodeDescs,
94104
// CHECK-NEXT: MyTargetSDNodeNames, MyTargetSDTypeConstraints);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// RUN: llvm-tblgen -gen-dag-isel -I %p/../../include %s 2>&1 | FileCheck %s
2+
3+
include "llvm/Target/Target.td"
4+
5+
def TestTargetInstrInfo : InstrInfo;
6+
7+
8+
def TestTarget : Target {
9+
let InstructionSet = TestTargetInstrInfo;
10+
}
11+
12+
def R0 : Register<"r0"> { let Namespace = "MyTarget"; }
13+
def GPR : RegisterClass<"MyTarget", [i32, f32], 32, (add R0)>;
14+
15+
def a_nearbyint : SDNode<
16+
"MyTargetISD::A_NEARBYINT",
17+
SDTypeProfile<1, 1, [SDTCisFP<0>, SDTCisFP<1>]>,
18+
[SDNPMayHaveChain]
19+
>;
20+
21+
def I_NEARBYINT : Instruction {
22+
let OutOperandList = (outs GPR:$out);
23+
let InOperandList = (ins GPR:$x);
24+
}
25+
26+
def : Pat<(a_nearbyint GPR:$x), (I_NEARBYINT GPR:$x)>;
27+
def : Pat<(a_nearbyint (a_nearbyint GPR:$x)), (I_NEARBYINT GPR:$x)>;
28+
29+
// CHECK-LABEL: OPC_CheckOpcode, TARGET_VAL(MyTargetISD::A_NEARBYINT),
30+
// CHECK-NEXT: OPC_RecordOptionalChain,
31+
// CHECK-NEXT: OPC_Scope, 17
32+
// CHECK-NEXT: OPC_MoveChild0,
33+
// CHECK-NEXT: OPC_CheckOpcode, TARGET_VAL(MyTargetISD::A_NEARBYINT),
34+
// CHECK-NEXT: OPC_RecordOptionalChain,
35+
// CHECK-NEXT: OPC_CheckFoldableChainNode,
36+
// CHECK-NEXT: OPC_RecordChild0,
37+
// CHECK-NEXT: OPC_MoveParent,
38+
// CHECK-NEXT: OPC_EmitMergeInputChains, 0,
39+
// CHECK-NEXT: OPC_MorphNodeTo1, TARGET_VAL(::I_NEARBYINT), 0,
40+
// CHECK-NEXT: /*MVT::f32*/12, 1/*#Ops*/, 2,
41+
42+
// CHECK: /*Scope*/ 10,
43+
// CHECK-NEXT: OPC_RecordChild0,
44+
// CHECK-NEXT: OPC_EmitMergeInputChains, 0,
45+
// CHECK-NEXT: OPC_MorphNodeTo1, TARGET_VAL(::I_NEARBYINT), 0,
46+
// CHECK-NEXT: /*MVT::f32*/12, 1/*#Ops*/, 1,

llvm/utils/TableGen/Basic/SDNodeProperties.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ unsigned llvm::parseSDPatternOperatorProperties(const Record *R) {
2828
.Case("SDNPSideEffect", SDNPSideEffect)
2929
.Case("SDNPMemOperand", SDNPMemOperand)
3030
.Case("SDNPVariadic", SDNPVariadic)
31+
.Case("SDNPMayHaveChain", SDNPMayHaveChain)
3132
.Default(-1u);
3233
if (Offset != -1u)
3334
Properties |= 1 << Offset;

llvm/utils/TableGen/Basic/SDNodeProperties.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ enum SDNP {
2828
SDNPSideEffect,
2929
SDNPMemOperand,
3030
SDNPVariadic,
31+
SDNPMayHaveChain
3132
};
3233

3334
unsigned parseSDPatternOperatorProperties(const Record *R);

0 commit comments

Comments
 (0)