[clang] [llvm] Add support for flag output operand "=@cc" for SystemZ. (PR #125970)

Ulrich Weigand via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 24 09:49:17 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();
----------------
uweigand wrote:

Why is this check needed?   The transformation is just as valid if CCValid is not CCMASK_ANY.   Of course, the two valid masks must be equal (which they should always be as long as the CC nodes are the same, but it should still be verified).

https://github.com/llvm/llvm-project/pull/125970


More information about the cfe-commits mailing list