[clang] [llvm] Add support for flag output operand "=@cc" for SystemZ. (PR #125970)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 24 17:55:57 PDT 2025
================
@@ -8781,8 +8961,445 @@ static bool combineCCMask(SDValue &CCReg, int &CCValid, int &CCMask) {
return false;
}
-SDValue SystemZTargetLowering::combineBR_CCMASK(
- SDNode *N, DAGCombinerInfo &DCI) const {
+// Combine (select_cc_a (select_cc_b)), where select_cc_a has one of TrueVal
+// or FalseVal has nested select_cc_b(already been combined sequence)
+SDValue
+SystemZTargetLowering::combineSELECT_CC_CCIPMMask(SDNode *N,
+ DAGCombinerInfo &DCI) const {
+ SelectionDAG &DAG = DCI.DAG;
+ // Check if CCOp1 and CCOp2 refers to the same CC condition node.
+ const auto isSameCCIPMOp = [](SDValue &CCOp1, SDValue &CCOp2,
+ int &CCValidVal) {
+ // Already combined sequence.
+ if (CCValidVal != SystemZ::CCMASK_ANY)
+ return false;
+ SDNode *N1 = CCOp1.getNode(), *N2 = CCOp2.getNode();
+ return N1 && N2 && N1 == N2;
+ };
+
+ auto *OuterTrueVal = dyn_cast<ConstantSDNode>(N->getOperand(0));
+ auto *OuterFalseVal = dyn_cast<ConstantSDNode>(N->getOperand(1));
+
+ // Already handled the case both operands constant in combineCCMask.
+ // Not yet encountered the case where both operands are sub-expressions,
+ // that case can be handled by removing this condition.
+ if (!((OuterTrueVal != nullptr) ^ (OuterFalseVal != nullptr)))
+ return SDValue();
+
+ SDValue NestedCCOp = OuterTrueVal ? N->getOperand(1) : N->getOperand(0);
+ auto *NestedCCNode = NestedCCOp.getNode();
+ // check if nested select_cc_b has already been combined.
+ if (!NestedCCNode || NestedCCNode->getOpcode() != SystemZISD::SELECT_CCMASK)
+ return SDValue();
+
+ auto *NestedTrueVal = dyn_cast<ConstantSDNode>(NestedCCNode->getOperand(0));
+ auto *NestedFalseVal = dyn_cast<ConstantSDNode>(NestedCCNode->getOperand(1));
+ if (!NestedTrueVal || !NestedFalseVal)
+ return SDValue();
+ bool Invert = false;
+ // Check if outer select_cc_a and nested select_cc_b True/False matching
+ // or inverted.
+ if (OuterTrueVal) {
+ // OuterFalseVal points to already combined nested select_cc_b.
+ if (OuterTrueVal->getZExtValue() == NestedFalseVal->getZExtValue())
+ Invert = !Invert; // Inverted.
+ else if (OuterTrueVal->getZExtValue() != NestedTrueVal->getZExtValue())
+ return SDValue();
+ } else if (OuterFalseVal) {
+ // OuterTrueVal points to already combined nested select_cc_b.
+ if (OuterFalseVal->getZExtValue() == NestedTrueVal->getZExtValue())
+ Invert = !Invert; // Inverted.
+ else if (OuterFalseVal->getZExtValue() != NestedFalseVal->getZExtValue())
+ return SDValue();
+ }
+ auto *OuterCCValid = dyn_cast<ConstantSDNode>(N->getOperand(2));
+ auto *OuterCCMask = dyn_cast<ConstantSDNode>(N->getOperand(3));
+ auto *NestedCCValid = dyn_cast<ConstantSDNode>(NestedCCOp->getOperand(2));
+ auto *NestedCCMask = dyn_cast<ConstantSDNode>(NestedCCOp->getOperand(3));
+ if (!OuterCCValid || !OuterCCMask || !NestedCCValid || !NestedCCMask)
+ return SDValue();
+
+ int OuterCCValidVal = OuterCCValid->getZExtValue();
+ int OuterCCMaskVal = OuterCCMask->getZExtValue();
+ int NestedCCValidVal = NestedCCValid->getZExtValue();
+ int NestedCCMaskVal = NestedCCMask->getZExtValue();
+ int CCMask = OuterCCMaskVal;
+ SDValue OuterCCReg = N->getOperand(4);
+ SDValue NestedCCReg = NestedCCOp->getOperand(4);
+
+ // Combine two already combined (select_cc_a (select_cc_b)), where TrueVal
+ // of select_cc_a points to select_cc_b. We return select_cc with TrueVal
+ // and FalseVal from select_cc_b with combined CCMask.
+ // One of OuterTrueVal or OuterFalseVal has select_cc_b.
+ if ((OuterTrueVal != nullptr) ^ (OuterFalseVal != nullptr)) {
+ // Both OuterCCValidVal and NestedCCValidVal have already been combined.
+ if (OuterCCValidVal == SystemZ::CCMASK_ANY &&
+ // And both points to the same CC.
+ isSameCCIPMOp(OuterCCReg, NestedCCReg, NestedCCValidVal)) {
+ CCMask |= NestedCCMaskVal;
+ // NestedCCOp has both operands constants.
+ auto Op0 = NestedCCOp->getOperand(0);
+ auto Op1 = NestedCCOp->getOperand(1);
+ // Return combined select_cc.
+ return DAG.getNode(
+ SystemZISD::SELECT_CCMASK, SDLoc(N), N->getValueType(0), Op0, Op1,
+ DAG.getTargetConstant(OuterCCValidVal, SDLoc(N), MVT::i32),
+ DAG.getTargetConstant(CCMask, SDLoc(N), MVT::i32), NestedCCReg);
+ }
+ }
+ // Now handles the case where outer select_cc_a has not yet been combined.
+ // Combine outer select_cc and check if it corresponds to the same
+ // CC as nested CC.
+ // Outer select_cc has yet not been combined.
+ if (OuterCCValidVal != SystemZ::CCMASK_ICMP ||
+ // Try combining outer select_cc.
+ !combineCCMask(OuterCCReg, OuterCCValidVal, OuterCCMaskVal) ||
+ // Check nested select_cc has already been combined and points to
+ // same Condtiion code as outer select_cc.
+ !isSameCCIPMOp(OuterCCReg, NestedCCReg, NestedCCValidVal))
+ return SDValue();
+ // Check if nested select_cc original CCMask was CCMASK_CMP_EQ.
+ // Only one-bit is set in NestedCCMaskVal for CCMASK_CMP_EQ.
+ bool IsNestedCMP_EQ =
+ NestedCCMaskVal && !(NestedCCMaskVal & (NestedCCMaskVal - 1));
+
+ // Outer select_cc is inverted with respect to nested select_cc.
+ if (Invert)
+ OuterCCMaskVal ^= SystemZ::CCMASK_ANY;
+
+ // Intersection of masks.
+ if (CCMask != SystemZ::CCMASK_CMP_EQ && !IsNestedCMP_EQ)
+ OuterCCMaskVal &= NestedCCMaskVal;
+ else // Union the masks.
+ OuterCCMaskVal |= NestedCCMaskVal;
+
+ SDValue Op0 = NestedCCOp->getOperand(0);
+ SDValue Op1 = NestedCCOp->getOperand(1);
+
+ return DAG.getNode(
+ SystemZISD::SELECT_CCMASK, SDLoc(N), N->getValueType(0), Op0, Op1,
+ DAG.getTargetConstant(OuterCCValidVal, SDLoc(N), MVT::i32),
+ DAG.getTargetConstant(OuterCCMaskVal, SDLoc(N), MVT::i32), OuterCCReg);
+}
+
+// Merging versus split in multiple branches cost.
+TargetLoweringBase::CondMergingParams
+SystemZTargetLowering::getJumpConditionMergingParams(Instruction::BinaryOps Opc,
+ const Value *Lhs,
+ const Value *Rhs) const {
+ const auto isFlagOutOpCC = [](const Value *V) {
+ using namespace llvm::PatternMatch;
+ const Value *RHSVal;
+ const APInt *RHSC;
+ if (const auto *I = dyn_cast<Instruction>(V)) {
+ // PatternMatch.h provides concise tree-based pattern match of llvm IR.
+ if (match(I->getOperand(0), m_And(m_Value(RHSVal), m_APInt(RHSC))) ||
+ match(I, m_Cmp(m_Value(RHSVal), m_APInt(RHSC)))) {
+ if (const auto *CB = dyn_cast<CallBase>(RHSVal)) {
+ if (CB->isInlineAsm()) {
+ const InlineAsm *IA = cast<InlineAsm>(CB->getCalledOperand());
+ return IA &&
+ IA->getConstraintString().find("{@cc}") != std::string::npos;
+ }
+ }
+ }
+ }
+ return false;
+ };
+ // Pattern (ICmp %asm) or (ICmp (And %asm)).
+ // Cost of longest dependency chain (ICmp, And) is 2. CostThreshold or
+ // BaseCost can be set >=2. If cost of instruction <= CostThreshold
+ // conditionals will be merged or else conditionals will be split.
+ if (isFlagOutOpCC(Lhs) && isFlagOutOpCC(Rhs))
+ return {3, 0, -1};
+ // Default.
+ return {-1, -1, -1};
+}
+
+SDValue SystemZTargetLowering::combineTM(SDNode *N,
+ DAGCombinerInfo &DCI) const {
+ SelectionDAG &DAG = DCI.DAG;
+ auto *TMOp0Node = N->getOperand(0).getNode();
+ auto *TMOp1Const = dyn_cast<ConstantSDNode>(N->getOperand(1));
+ auto *TMOp2Const = dyn_cast<ConstantSDNode>(N->getOperand(2));
+ if (!TMOp0Node || !TMOp1Const || !TMOp2Const ||
+ // Third operand of TM is false.
+ TMOp2Const->getZExtValue() != 0)
+ return SDValue();
+ auto TMOp1ConstVal = TMOp1Const->getZExtValue();
+ // Optimize (TM (IPM)).
+ if (TMOp0Node->getOpcode() == SystemZISD::IPM) {
+ int CCMask, CCValid;
+ if (TMOp1ConstVal == (1 << SystemZ::IPM_CC))
+ CCMask = SystemZ::CCMASK_CMP_GE;
+ else if (TMOp1ConstVal == (1 << (SystemZ::IPM_CC + 1)))
+ CCMask = SystemZ::CCMASK_CMP_LE;
+ else
+ return SDValue();
+ SDValue CCReg = TMOp0Node->getOperand(0);
+ CCValid = SystemZ::CCMASK_ANY;
+ // Return combined node.
+ return DAG.getNode(SystemZISD::SELECT_CCMASK, SDLoc(N), N->getValueType(0),
+ N->getOperand(0), N->getOperand(1),
+ DAG.getTargetConstant(CCValid, SDLoc(N), MVT::i32),
+ DAG.getTargetConstant(CCMask, SDLoc(N), MVT::i32),
+ CCReg);
+ }
+ // Optimize (TM (XOR (Op0 Op1))).
+ if (TMOp0Node->getOpcode() == ISD::XOR) {
+ auto *XorOp0 = TMOp0Node->getOperand(0).getNode();
+ // Op0. (SELECT_CCMASK (ICMP (SRL (IPM)))).
+ // Op1. (SRL (IPM (CC))).
+ if (XorOp0 && XorOp0->getOpcode() == SystemZISD::SELECT_CCMASK) {
+ auto *XorOp0CCValid = dyn_cast<ConstantSDNode>(XorOp0->getOperand(2));
+ auto *XorOp0CCMask = dyn_cast<ConstantSDNode>(XorOp0->getOperand(3));
+ if (!XorOp0CCValid || !XorOp0CCMask)
+ return SDValue();
+ SDValue XorOp0CCReg = XorOp0->getOperand(4);
+ int XorOp0CCMaskVal = XorOp0CCMask->getZExtValue();
+ int XorOp0CCValidVal = XorOp0CCValid->getZExtValue();
+ int CCMask = SystemZ::CCMASK_CMP_EQ, CCValid, TMCCMask;
+ SDValue CCReg = TMOp0Node->getOperand(1);
+ // (SELECT_CCMASK (ICMP (SRL (IPM)))).
+ if (!combineCCMask(XorOp0CCReg, XorOp0CCValidVal, XorOp0CCMaskVal) ||
+ // (SRL (IPM (CC))).
+ !combineCCMask(CCReg, CCValid, CCMask))
+ return SDValue();
+ auto *N0 = XorOp0CCReg.getNode(), *N1 = CCReg.getNode();
+ // Check if Op0 and Op1 point to the same CC.
+ if (!N0 || !N1 || N0 != N1)
+ return SDValue();
+ if (TMOp1ConstVal == 1)
+ TMCCMask = SystemZ::CCMASK_CMP_GE;
+ else
+ return SDValue();
+ // CCMask ^ XorOp0CCMaskVal = TMCCMask..
+ CCMask = XorOp0CCMaskVal ^ TMCCMask;
+ // Returned combined node with evaluated CCMask.
+ return DAG.getNode(
+ SystemZISD::SELECT_CCMASK, SDLoc(N), N->getValueType(0),
+ XorOp0->getOperand(0), XorOp0->getOperand(1),
+ DAG.getTargetConstant(XorOp0CCValidVal, SDLoc(N), MVT::i32),
+ DAG.getTargetConstant(CCMask, SDLoc(N), MVT::i32), CCReg);
+ }
+ }
+ return SDValue();
+}
+
+SDValue SystemZTargetLowering::combineAND(SDNode *N,
+ DAGCombinerInfo &DCI) const {
+ SelectionDAG &DAG = DCI.DAG;
+ auto *AndOp0 = N->getOperand(0).getNode();
+ auto *AndOp1 = N->getOperand(1).getNode();
+ if (!AndOp0 || !AndOp1)
+ return SDValue();
+ // Both Operands of ISD::AND are SystemZISD::SELECT_CCMASK.
+ // And CCMask of both operands and check if they points to the
+ // same CC and update CCReg. Return combined SDNode.
+ if (AndOp0->getOpcode() == SystemZISD::SELECT_CCMASK &&
+ AndOp1->getOpcode() == SystemZISD::SELECT_CCMASK) {
+ auto *Op0CCValid = dyn_cast<ConstantSDNode>(AndOp0->getOperand(2));
+ auto *Op0CCMask = dyn_cast<ConstantSDNode>(AndOp0->getOperand(3));
+ auto *Op1CCValid = dyn_cast<ConstantSDNode>(AndOp1->getOperand(2));
+ auto *Op1CCMask = dyn_cast<ConstantSDNode>(AndOp1->getOperand(3));
+ if (!Op0CCValid || !Op1CCValid || !Op0CCMask || !Op1CCMask)
+ return SDValue();
+ int Op0CCValidVal = Op0CCValid->getZExtValue();
+ int Op1CCValidVal = Op1CCValid->getZExtValue();
+ // Check if both AndOp0 and AndOp1 have aleady been combined.
+ if (Op0CCValidVal != SystemZ::CCMASK_ANY ||
+ Op1CCValidVal != SystemZ::CCMASK_ANY)
+ return SDValue();
+ SDValue Op0CCReg = AndOp0->getOperand(4), Op1CCReg = AndOp1->getOperand(4);
+ auto *CCNode0 = Op0CCReg.getNode(), *CCNode1 = Op1CCReg.getNode();
+ // Check if AndOp0 and AndOp1 refers to same CC.
+ if (!CCNode0 || !CCNode1 || CCNode0 != CCNode1)
+ return SDValue();
+ int Op0CCMaskVal = Op0CCMask->getZExtValue();
+ int Op1CCMaskVal = Op1CCMask->getZExtValue();
+ int CCMask = Op0CCMaskVal & Op1CCMaskVal;
+ return DAG.getNode(SystemZISD::SELECT_CCMASK, SDLoc(N), N->getValueType(0),
+ AndOp0->getOperand(0), AndOp0->getOperand(1),
+ DAG.getTargetConstant(Op0CCValidVal, SDLoc(N), MVT::i32),
+ DAG.getTargetConstant(CCMask, SDLoc(N), MVT::i32),
+ Op0CCReg);
+ } else if (AndOp0->getOpcode() == SystemZISD::SELECT_CCMASK) {
+ // check AndOp1 for (SRL (IPM (CC))) pattern. Second operand is CC.
+ SDValue CCReg = N->getOperand(1);
+ int CCMask = SystemZ::CCMASK_CMP_EQ, CCValid;
+ if (!combineCCMask(CCReg, CCValid, CCMask))
+ return SDValue();
+ SDValue Op0CCReg = AndOp0->getOperand(4);
+ auto *CCNode0 = Op0CCReg.getNode(), *CCNode1 = CCReg.getNode();
+ // Check if AndOp0 and AndOp1 refers to same CC.
+ if (!CCNode0 || !CCNode1 || CCNode0 != CCNode1)
+ return SDValue();
+ auto *Op0CCValid = dyn_cast<ConstantSDNode>(AndOp0->getOperand(2));
+ auto *Op0CCMask = dyn_cast<ConstantSDNode>(AndOp0->getOperand(3));
+ int Op0CCValidVal = Op0CCValid->getZExtValue();
+ if (!Op0CCMask || !Op0CCValid || Op0CCValidVal != SystemZ::CCMASK_ANY)
+ return SDValue();
+ int Op0CCMaskVal = Op0CCMask->getZExtValue();
+ // Op0CCMaskVal & CCMask = 0. Invert Op0CCMaskVal.
+ int CCMaskVal = Op0CCMaskVal ^ 0xf;
+ assert(CCMaskVal < 4 && "CC out of range");
+ CCMask = 1 << (3 - CCMaskVal);
+ return DAG.getNode(SystemZISD::SELECT_CCMASK, SDLoc(N), N->getValueType(0),
+ AndOp0->getOperand(0), AndOp0->getOperand(1),
+ DAG.getTargetConstant(Op0CCValidVal, SDLoc(N), MVT::i32),
+ DAG.getTargetConstant(CCMask, SDLoc(N), MVT::i32),
+ CCReg);
+ }
+ return SDValue();
+}
+
+SDValue SystemZTargetLowering::combineOR(SDNode *N,
+ DAGCombinerInfo &DCI) const {
+ SelectionDAG &DAG = DCI.DAG;
+ auto *OrOp0 = N->getOperand(0).getNode();
+ auto *OrOp1 = N->getOperand(1).getNode();
+ if (!OrOp0 || !OrOp1)
+ return SDValue();
+ // Both Operands of ISD::OR are SystemZISD::SELECT_CCMASK.
+ // And CCMask of both operands and check if they points to the
+ // same CC and update CCReg. Return combined SDNode.
+ if (OrOp0->getOpcode() == SystemZISD::SELECT_CCMASK &&
+ OrOp1->getOpcode() == SystemZISD::SELECT_CCMASK) {
+ auto *Op0CCValid = dyn_cast<ConstantSDNode>(OrOp0->getOperand(2));
+ auto *Op0CCMask = dyn_cast<ConstantSDNode>(OrOp0->getOperand(3));
+ auto *Op1CCValid = dyn_cast<ConstantSDNode>(OrOp1->getOperand(2));
+ auto *Op1CCMask = dyn_cast<ConstantSDNode>(OrOp1->getOperand(3));
+ if (!Op0CCValid || !Op1CCValid || !Op0CCMask || !Op1CCMask)
+ return SDValue();
+ int Op0CCValidVal = Op0CCValid->getZExtValue();
+ int Op1CCValidVal = Op1CCValid->getZExtValue();
+ // Check if both OrOp0 and OrOp1 have aleady been combined.
+ if (Op0CCValidVal != SystemZ::CCMASK_ANY ||
+ Op1CCValidVal != SystemZ::CCMASK_ANY)
+ return SDValue();
+ SDValue Op0CCReg = OrOp0->getOperand(4), Op1CCReg = OrOp1->getOperand(4);
+ auto *CCNode0 = Op0CCReg.getNode(), *CCNode1 = Op1CCReg.getNode();
+ // Check if OrOp0 and OrOp0 refers to same CC.
+ if (!CCNode0 || !CCNode1 || CCNode0 != CCNode1)
+ return SDValue();
+ int Op0CCMaskVal = Op0CCMask->getZExtValue();
+ int Op1CCMaskVal = Op1CCMask->getZExtValue();
+ // Oring Masks.
+ int CCMask = Op0CCMaskVal | Op1CCMaskVal;
+ return DAG.getNode(SystemZISD::SELECT_CCMASK, SDLoc(N), N->getValueType(0),
+ OrOp0->getOperand(0), OrOp1->getOperand(1),
+ DAG.getTargetConstant(Op0CCValidVal, SDLoc(N), MVT::i32),
+ DAG.getTargetConstant(CCMask, SDLoc(N), MVT::i32),
+ Op0CCReg);
+ }
+ return SDValue();
+}
+
+SDValue SystemZTargetLowering::combineICMP(SDNode *N,
+ DAGCombinerInfo &DCI) const {
+ SelectionDAG &DAG = DCI.DAG;
+ auto *LHS = N->getOperand(0).getNode();
+ if (!LHS)
+ return SDValue();
+ auto *RHS = dyn_cast<ConstantSDNode>(N->getOperand(1));
+ // Handle the case where RHS is const.
+ // Remove redundant ICMP left after operand 0 pattern has been combined by
+ // by combineAND, combineXOR, combineOR into select_cc on the assumption
+ // CmpVal is zero.
+ if (RHS && LHS->getOpcode() == SystemZISD::SELECT_CCMASK) {
+ auto *CCValid = dyn_cast<ConstantSDNode>(LHS->getOperand(2));
+ int CmpVal = RHS->getZExtValue();
+ // Check CmpVal is zero.
+ if (!CCValid || CCValid->getZExtValue() != SystemZ::CCMASK_ANY ||
+ CmpVal != 0)
+ return SDValue();
+ // Return operand 0 - Combined select_cc.
+ return N->getOperand(0);
----------------
anoopkg6 wrote:
It will return operand 0, which is entire (select_ccmask VAL1 VAL2 CCMASK_ANY CCOp)..
This redundant 'icmp' is created as we are combining '(icmp (and...))', 'icmp (or...))', or 'icmp (xor...))' in combineAND, combineOR, or combineXOR at 'and', 'or', 'xor' level and select_ccmask is returned.
This dangling icmp can be avoided if we combine entire sequence at 'icmp' level. This will be cleaner. But downside of this approach is most of this code will be pulled into combineICMP, and might leave some of the added pattern to PerformDAGCombine.
https://github.com/llvm/llvm-project/pull/125970
More information about the cfe-commits
mailing list