[llvm] [AArch64] optimize lowering for icmp on i128 when RHS is an immediate (PR #181822)

Cheng Lingfei via llvm-commits llvm-commits at lists.llvm.org
Thu Jun 11 06:52:28 PDT 2026


https://github.com/clingfei updated https://github.com/llvm/llvm-project/pull/181822

>From 3caeb6658972cbd04ebd67842908df102bc3aebc Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Tue, 17 Feb 2026 21:22:02 +0800
Subject: [PATCH 1/4] [AArch64] optimize lowering for icmp on i128 when RHS is
 an immediate

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 118 ++++++++++++++++++
 .../CodeGen/AArch64/i128-imm-compare-ccmp.ll  |  61 +++++++++
 llvm/test/CodeGen/AArch64/isinf.ll            |  10 +-
 3 files changed, 184 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 66c22db0491d1..0880e5ce210f5 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27145,6 +27145,121 @@ performVecReduceBitwiseCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
   return SDValue();
 }
 
+static bool splitI128ValueToI64Halves(SDValue V, SelectionDAG &DAG,
+                                      const SDLoc &DL, SDValue &Lo,
+                                      SDValue &Hi) {
+  EVT VT = V.getValueType();
+  if (!VT.isInteger() || VT.getFixedSizeInBits() != 128)
+    return false;
+  Lo = DAG.getNode(ISD::TRUNCATE, DL, MVT::i64, V);
+  Hi = DAG.getNode(
+      ISD::TRUNCATE, DL, MVT::i64,
+      DAG.getNode(ISD::SRL, DL, VT, V, DAG.getShiftAmountConstant(64, VT, DL)));
+  return true;
+}
+
+static SDValue
+performExpandedI128CmpCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
+                              SelectionDAG &DAG, ISD::CondCode CCCode) {
+  SDValue NewLHS = N->getOperand(0);
+  SDValue NewRHS = N->getOperand(1);
+  if (N->hasOneUse()) {
+    auto User = N->user_begin();
+    if ((User)->getOpcode() == ISD::BRCOND)
+      return SDValue();
+  }
+  // Keep the dedicated shift+cmp-zero combine opportunities for wide integers.
+  // Canonicalizing these into split compares here tends to introduce an extra
+  // EXTR on AArch64 (see icmp-shift-opt.ll).
+  if ((CCCode == ISD::SETEQ || CCCode == ISD::SETNE) &&
+      isNullConstant(NewRHS) &&
+      (NewLHS.getOpcode() == ISD::SRL || NewLHS.getOpcode() == ISD::SHL))
+    return SDValue();
+
+  if (NewLHS.getNumOperands() != 2)
+    return SDValue();
+  if (NewLHS.getValueType().isScalableVT() ||
+      NewRHS.getValueType().isScalableVT() || NewLHS.getNumOperands() != 2 ||
+      !(NewLHS.getValueType().isInteger() &&
+        NewLHS.getValueSizeInBits() == 128) ||
+      !(NewRHS.getValueType().isInteger() &&
+        NewRHS.getValueSizeInBits() == 128))
+    return SDValue();
+  ConstantSDNode *ConstLHS = dyn_cast<ConstantSDNode>(NewLHS.getNode());
+  ConstantSDNode *ConstRHS = dyn_cast<ConstantSDNode>(NewRHS.getNode());
+  if (ConstLHS || !ConstRHS)
+    return SDValue();
+
+  SDValue LHSLo = NewLHS.getOperand(0);
+  SDValue LHSHi = NewLHS.getOperand(1);
+  SDValue RHSLo =
+      DAG.getConstant(ConstRHS->getAPIntValue().trunc(64), SDLoc(N), MVT::i64);
+  SDValue RHSHi = DAG.getConstant(ConstRHS->getAPIntValue().lshr(64).trunc(64),
+                                  SDLoc(N), MVT::i64);
+  if (!splitI128ValueToI64Halves(NewLHS, DAG, SDLoc(N), LHSLo, LHSHi)) {
+    return SDValue();
+  }
+
+  if (CCCode == ISD::SETEQ || CCCode == ISD::SETNE) {
+    SDValue LoCmpF =
+        DAG.FoldSetCC(N->getValueType(0), LHSLo, RHSLo, CCCode, SDLoc(N));
+    SDValue HiCmpF =
+        DAG.FoldSetCC(N->getValueType(0), LHSHi, RHSHi, CCCode, SDLoc(N));
+    auto IsConstBool = [](SDValue V) {
+      return isa_and_nonnull<ConstantSDNode>(V.getNode());
+    };
+    if (IsConstBool(LoCmpF) || IsConstBool(HiCmpF))
+      return SDValue();
+
+    SDValue LoCmp =
+        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
+    SDValue HiCmp =
+        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, CCCode);
+    unsigned Opcode = (CCCode == ISD::SETEQ) ? ISD::AND : ISD::OR;
+    return DAG.getNode(Opcode, SDLoc(N), LoCmp.getValueType(), LoCmp, HiCmp);
+  }
+
+  if (CCCode == ISD::SETUGT || CCCode == ISD::SETUGE) {
+    // x >  K  <=> (xhi > khi)  || (xhi==khi && xlo >  klo)
+    // x >= K  <=> (xhi > khi)  || (xhi==khi && xlo >= klo)
+    SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
+
+    ISD::CondCode LoCC = (CCCode == ISD::SETUGT) ? ISD::SETULE : ISD::SETULT;
+
+    SDValue HiEq =
+        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, ZeroHi, ISD::SETEQ);
+    SDValue LoCmp =
+        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, LoCC);
+    SDValue Tree =
+        DAG.getNode(ISD::AND, SDLoc(N), N->getValueType(0), HiEq, LoCmp);
+
+    SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
+    return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETEQ);
+  }
+
+  if (CCCode == ISD::SETULT || CCCode == ISD::SETULE) {
+    // x <  K  <=> (xhi < khi)  || (xhi==khi && xlo <  klo)
+    // x <= K  <=> (xhi < khi)  || (xhi==khi && xlo <= klo)
+    ISD::CondCode Opcode = ISD::SETULT;
+
+    SDValue HiCmp =
+        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, Opcode);
+    SDValue HiEq =
+        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, ISD::SETEQ);
+    SDValue LoCmp =
+        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
+
+    SDValue LoAnd =
+        DAG.getNode(ISD::AND, SDLoc(N), HiEq.getValueType(), HiEq, LoCmp);
+    SDValue Tree =
+        DAG.getNode(ISD::OR, SDLoc(N), N->getValueType(0), HiCmp, LoAnd);
+
+    SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
+    return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETNE);
+  }
+  return SDValue();
+}
+
 static SDValue performSETCCCombine(SDNode *N,
                                    TargetLowering::DAGCombinerInfo &DCI,
                                    SelectionDAG &DAG) {
@@ -27225,6 +27340,9 @@ static SDValue performSETCCCombine(SDNode *N,
       SplatLHSVal.isOne())
     return DAG.getSetCC(DL, VT, DAG.getConstant(0, DL, CmpVT), RHS, ISD::SETGE);
 
+  if (SDValue V = performExpandedI128CmpCombine(N, DCI, DAG, Cond))
+    return V;
+
   return SDValue();
 }
 
diff --git a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
new file mode 100644
index 0000000000000..7422cbbfd19a0
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
@@ -0,0 +1,61 @@
+; RUN: llc -mtriple=aarch64-linux-gnu -O3 -o - < %s | FileCheck %s
+
+define i1 @eq_imm(i128 %x) {
+; CHECK-LABEL: eq_imm:
+; CHECK:       cmp x0, #5
+; CHECK-NEXT:  ccmp x1, #0, #0, eq
+; CHECK-NEXT:  cset w0, eq
+; CHECK-NEXT:  ret
+  %cmp = icmp eq i128 %x, 5
+  ret i1 %cmp
+}
+
+define i1 @ne_imm(i128 %x) {
+; CHECK-LABEL: ne_imm:
+; CHECK:       cmp x0, #5
+; CHECK-NEXT:  ccmp x1, #0, #0, eq
+; CHECK-NEXT:  cset w0, ne
+; CHECK-NEXT:  ret
+  %cmp = icmp ne i128 %x, 5
+  ret i1 %cmp
+}
+
+define i1 @ult_imm(i128 %x) {
+; CHECK-LABEL: ult_imm:
+; CHECK:       cmp x1, #0
+; CHECK-NEXT:  ccmp x0, #5, #2, eq
+; CHECK-NEXT:  cset w0, {{cc|lo}}
+; CHECK-NEXT:  ret
+  %cmp = icmp ult i128 %x, 5
+  ret i1 %cmp
+}
+
+define i1 @ule_imm(i128 %x) {
+; CHECK-LABEL: ule_imm:
+; CHECK:       cmp x1, #0
+; CHECK-NEXT:  ccmp x0, #6, #2, eq
+; CHECK-NEXT:  cset w0, {{cc|lo}}
+; CHECK-NEXT:  ret
+  %cmp = icmp ule i128 %x, 5
+  ret i1 %cmp
+}
+
+define i1 @ugt_imm(i128 %x) {
+; CHECK-LABEL: ugt_imm:
+; CHECK:       cmp x1, #0
+; CHECK-NEXT:  ccmp x0, #5, #2, eq
+; CHECK-NEXT:  cset	w0, hi
+; CHECK-NEXT:  ret
+  %cmp = icmp ugt i128 %x, 5
+  ret i1 %cmp
+}
+
+define i1 @uge_imm(i128 %x) {
+; CHECK-LABEL: uge_imm:
+; CHECK:       cmp	x1, #0
+; CHECK-NEXT:  ccmp	x0, #4, #2, eq
+; CHECK-NEXT:  cset	w0, hi
+; CHECK-NEXT:  ret
+  %cmp = icmp uge i128 %x, 5
+  ret i1 %cmp
+}
\ No newline at end of file
diff --git a/llvm/test/CodeGen/AArch64/isinf.ll b/llvm/test/CodeGen/AArch64/isinf.ll
index e8bbaf96395f0..59e1ab44f16b4 100644
--- a/llvm/test/CodeGen/AArch64/isinf.ll
+++ b/llvm/test/CodeGen/AArch64/isinf.ll
@@ -58,11 +58,11 @@ define i32 @replace_isinf_call_f128(fp128 %x) {
 ; CHECK:       // %bb.0:
 ; CHECK-NEXT:    str q0, [sp, #-16]!
 ; CHECK-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-NEXT:    ldp x9, x8, [sp], #16
-; CHECK-NEXT:    and x8, x8, #0x7fffffffffffffff
-; CHECK-NEXT:    eor x8, x8, #0x7fff000000000000
-; CHECK-NEXT:    orr x8, x9, x8
-; CHECK-NEXT:    cmp x8, #0
+; CHECK-NEXT:    mov	x8, #-562949953421312
+; CHECK-NEXT:    ldp	x10, x9, [sp], #16
+; CHECK-NEXT:    lsl	x9, x9, #1
+; CHECK-NEXT:    cmp	x10, #0
+; CHECK-NEXT:    ccmp	x8, x9, #0, eq
 ; CHECK-NEXT:    cset w0, eq
 ; CHECK-NEXT:    ret
   %abs = tail call fp128 @llvm.fabs.f128(fp128 %x)

>From bf60f9f64421d695b97c73be30d5c71bfbf0d5e4 Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Fri, 10 Apr 2026 23:30:38 +0800
Subject: [PATCH 2/4] fix

---
 llvm/lib/Target/AArch64/AArch64ISelLowering.cpp |  8 +++++++-
 llvm/test/CodeGen/AArch64/isinf.ll              | 14 +++++++-------
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index c9f6e0d651251..305d22859d51d 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27769,8 +27769,14 @@ performExpandedI128CmpCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
   if (CCCode == ISD::SETUGT || CCCode == ISD::SETUGE) {
     // x >  K  <=> (xhi > khi)  || (xhi==khi && xlo >  klo)
     // x >= K  <=> (xhi > khi)  || (xhi==khi && xlo >= klo)
-    SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
+    // This specialized form relies on the immediate living entirely in the low
+    // 64 bits. When the high half is non-zero, reducing the compare to a
+    // high==0 test is invalid and can miscompile widened expressions such as
+    // (shl i128 X, 1) > C.
+    if (!isNullConstant(RHSHi))
+      return SDValue();
 
+    SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
     ISD::CondCode LoCC = (CCCode == ISD::SETUGT) ? ISD::SETULE : ISD::SETULT;
 
     SDValue HiEq =
diff --git a/llvm/test/CodeGen/AArch64/isinf.ll b/llvm/test/CodeGen/AArch64/isinf.ll
index f3283d2cf7ec2..92d53d7086e46 100644
--- a/llvm/test/CodeGen/AArch64/isinf.ll
+++ b/llvm/test/CodeGen/AArch64/isinf.ll
@@ -80,14 +80,14 @@ define i32 @replace_isinf_call_f64(double %x) {
 define i32 @replace_isinf_call_f128(fp128 %x) {
 ; CHECK-SD-LABEL: replace_isinf_call_f128:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    str q0, [sp, #-16]!
+; CHECK-SD-NEXT:    str	q0, [sp, #-16]!
 ; CHECK-SD-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-SD-NEXT:    ldp x9, x8, [sp], #16
-; CHECK-SD-NEXT:    and x8, x8, #0x7fffffffffffffff
-; CHECK-SD-NEXT:    eor x8, x8, #0x7fff000000000000
-; CHECK-SD-NEXT:    orr x8, x9, x8
-; CHECK-SD-NEXT:    cmp x8, #0
-; CHECK-SD-NEXT:    cset w0, eq
+; CHECK-SD-NEXT:    mov	x8, #-562949953421312           // =0xfffe000000000000
+; CHECK-SD-NEXT:    ldp	x10, x9, [sp], #16
+; CHECK-SD-NEXT:    lsl	x9, x9, #1
+; CHECK-SD-NEXT:    cmp	x10, #0
+; CHECK-SD-NEXT:    ccmp	x8, x9, #0, eq
+; CHECK-SD-NEXT:    cset	w0, eq
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: replace_isinf_call_f128:

>From 25d8e9f018ae8558682bbbc1ad9bbe588c5a1a1e Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Thu, 7 May 2026 23:45:29 +0800
Subject: [PATCH 3/4] Optimize at later stage

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 266 +++++++++---------
 .../CodeGen/AArch64/i128-imm-compare-ccmp.ll  | 116 +++++++-
 2 files changed, 242 insertions(+), 140 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index fb95b6e2cb67c..071149ef46585 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1155,6 +1155,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
 
   // Try and combine setcc with csel
   setTargetDAGCombine(ISD::SETCC);
+  setTargetDAGCombine(ISD::SETCCCARRY);
 
   setTargetDAGCombine(ISD::INTRINSIC_WO_CHAIN);
 
@@ -11863,9 +11864,62 @@ SDValue AArch64TargetLowering::LowerBitreverse(SDValue Op,
 }
 
 // Check whether the continuous comparison sequence.
+static bool hasLegalCmpImmediateOperand(SDValue V) {
+  ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
+  return !C || isLegalCmpImmed(C->getAPIntValue());
+}
+
+static bool hasLegalCmpImmediate(SDValue LHS, SDValue RHS) {
+  return hasLegalCmpImmediateOperand(LHS) && hasLegalCmpImmediateOperand(RHS);
+}
+
+static bool isLegalCondCmpImmediate(const APInt &Imm) {
+  if (Imm.isNegative())
+    return Imm.sgt(-32);
+  return Imm.getLimitedValue(32) <= 31;
+}
+
+static bool hasLegalCondCmpImmediateOperand(SDValue V) {
+  ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
+  return !C || isLegalCondCmpImmediate(C->getAPIntValue());
+}
+
+static bool hasLegalCondCmpImmediate(SDValue LHS, SDValue RHS) {
+  return hasLegalCondCmpImmediateOperand(LHS) &&
+         hasLegalCondCmpImmediateOperand(RHS);
+}
+
+static bool isLegalXorImmediate(const APInt &Imm) {
+  unsigned BitWidth = Imm.getBitWidth() <= 32 ? 32 : 64;
+  if (Imm.getBitWidth() > BitWidth)
+    return false;
+  return AArch64_AM::isLogicalImmediate(Imm.getZExtValue(), BitWidth);
+}
+
+static bool hasCheapXorImmediateThatNeedsCondCmpRegOperand(SDValue V) {
+  ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
+  if (!C)
+    return false;
+
+  const APInt &Imm = C->getAPIntValue();
+  return !isLegalCondCmpImmediate(Imm) && isLegalXorImmediate(Imm);
+}
+
+static bool hasCheapXorImmediateThatNeedsCondCmpReg(
+    const std::pair<SDValue, SDValue> &Pair) {
+  return hasCheapXorImmediateThatNeedsCondCmpRegOperand(Pair.first) ||
+         hasCheapXorImmediateThatNeedsCondCmpRegOperand(Pair.second);
+}
+
+static bool preferAsFirstCmp(const std::pair<SDValue, SDValue> &Pair) {
+  return hasLegalCmpImmediate(Pair.first, Pair.second) &&
+         !hasLegalCondCmpImmediate(Pair.first, Pair.second);
+}
+
 static bool
-isOrXorChain(SDValue N, unsigned &Num,
-             SmallVector<std::pair<SDValue, SDValue>, 16> &WorkList) {
+isOrXorChain(SDValue N, SelectionDAG &DAG, unsigned &Num, bool &SawXor,
+             bool RequireLegalCmpImmediates,
+             SmallVectorImpl<std::pair<SDValue, SDValue>> &WorkList) {
   if (Num == MaxXors)
     return false;
 
@@ -11873,21 +11927,36 @@ isOrXorChain(SDValue N, unsigned &Num,
   if (N->getOpcode() == ISD::ZERO_EXTEND && N->hasOneUse())
     N = N->getOperand(0);
 
-  // The leaf node must be XOR
   if (N->getOpcode() == ISD::XOR) {
+    if (RequireLegalCmpImmediates &&
+        !hasLegalCmpImmediate(N->getOperand(0), N->getOperand(1)))
+      return false;
     WorkList.push_back(std::make_pair(N->getOperand(0), N->getOperand(1)));
     Num++;
+    SawXor = true;
     return true;
   }
 
   // All the non-leaf nodes must be OR.
-  if (N->getOpcode() != ISD::OR || !N->hasOneUse())
+  if (N->getOpcode() == ISD::OR && N->hasOneUse())
+    return isOrXorChain(N->getOperand(0), DAG, Num, SawXor,
+                        RequireLegalCmpImmediates, WorkList) &&
+           isOrXorChain(N->getOperand(1), DAG, Num, SawXor,
+                        RequireLegalCmpImmediates, WorkList);
+  if (N->getOpcode() == ISD::OR)
     return false;
 
-  if (isOrXorChain(N->getOperand(0), Num, WorkList) &&
-      isOrXorChain(N->getOperand(1), Num, WorkList))
-    return true;
-  return false;
+  EVT VT = N.getValueType();
+  if (!VT.isScalarInteger())
+    return false;
+
+  // A xor with zero may have been folded away before this combine sees it.
+  // Treat such leaves as comparisons with zero so the original OR/XOR form and
+  // type-legalized wide integer equality compares converge to the same SETCC
+  // tree.
+  WorkList.push_back(std::make_pair(N, DAG.getConstant(0, SDLoc(N), VT)));
+  Num++;
+  return true;
 }
 
 // Transform chains of ORs and XORs, which usually outlined by memcmp/bmp.
@@ -11906,9 +11975,26 @@ static SDValue performOrXorChainCombine(SDNode *N, SelectionDAG &DAG) {
   // Try to express conjunction "cmp 0 (or (xor A0 A1) (xor B0 B1))" as:
   // sub A0, A1; ccmp B0, B1, 0, eq; cmp inv(Cond) flag
   unsigned NumXors = 0;
+  bool SawXor = false;
+  bool RequireLegalCmpImmediates =
+      any_of(N->users(), [](SDNode *User) {
+        return User->getOpcode() == ISD::BRCOND ||
+               User->getOpcode() == AArch64ISD::BRCOND;
+      });
   if ((Cond == ISD::SETEQ || Cond == ISD::SETNE) && isNullConstant(RHS) &&
       LHS->getOpcode() == ISD::OR && LHS->hasOneUse() &&
-      isOrXorChain(LHS, NumXors, WorkList)) {
+      isOrXorChain(LHS, DAG, NumXors, SawXor, RequireLegalCmpImmediates,
+                   WorkList) &&
+      SawXor) {
+    if (WorkList.size() > 2 &&
+        any_of(WorkList, hasCheapXorImmediateThatNeedsCondCmpReg))
+      return SDValue();
+
+    SmallVector<std::pair<SDValue, SDValue>, 16>::iterator First =
+        find_if(WorkList, preferAsFirstCmp);
+    if (First != WorkList.end())
+      std::iter_swap(WorkList.begin(), First);
+
     SDValue XOR0, XOR1;
     std::tie(XOR0, XOR1) = WorkList[0];
     unsigned LogicOp = (Cond == ISD::SETEQ) ? ISD::AND : ISD::OR;
@@ -27384,127 +27470,6 @@ performVecReduceBitwiseCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
   return SDValue();
 }
 
-static bool splitI128ValueToI64Halves(SDValue V, SelectionDAG &DAG,
-                                      const SDLoc &DL, SDValue &Lo,
-                                      SDValue &Hi) {
-  EVT VT = V.getValueType();
-  if (!VT.isInteger() || VT.getFixedSizeInBits() != 128)
-    return false;
-  Lo = DAG.getNode(ISD::TRUNCATE, DL, MVT::i64, V);
-  Hi = DAG.getNode(
-      ISD::TRUNCATE, DL, MVT::i64,
-      DAG.getNode(ISD::SRL, DL, VT, V, DAG.getShiftAmountConstant(64, VT, DL)));
-  return true;
-}
-
-static SDValue
-performExpandedI128CmpCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
-                              SelectionDAG &DAG, ISD::CondCode CCCode) {
-  SDValue NewLHS = N->getOperand(0);
-  SDValue NewRHS = N->getOperand(1);
-  if (N->hasOneUse()) {
-    auto User = N->user_begin();
-    if ((User)->getOpcode() == ISD::BRCOND)
-      return SDValue();
-  }
-  // Keep the dedicated shift+cmp-zero combine opportunities for wide integers.
-  // Canonicalizing these into split compares here tends to introduce an extra
-  // EXTR on AArch64 (see icmp-shift-opt.ll).
-  if ((CCCode == ISD::SETEQ || CCCode == ISD::SETNE) &&
-      isNullConstant(NewRHS) &&
-      (NewLHS.getOpcode() == ISD::SRL || NewLHS.getOpcode() == ISD::SHL))
-    return SDValue();
-
-  if (NewLHS.getNumOperands() != 2)
-    return SDValue();
-  if (NewLHS.getValueType().isScalableVT() ||
-      NewRHS.getValueType().isScalableVT() || NewLHS.getNumOperands() != 2 ||
-      !(NewLHS.getValueType().isInteger() &&
-        NewLHS.getValueSizeInBits() == 128) ||
-      !(NewRHS.getValueType().isInteger() &&
-        NewRHS.getValueSizeInBits() == 128))
-    return SDValue();
-  ConstantSDNode *ConstLHS = dyn_cast<ConstantSDNode>(NewLHS.getNode());
-  ConstantSDNode *ConstRHS = dyn_cast<ConstantSDNode>(NewRHS.getNode());
-  if (ConstLHS || !ConstRHS)
-    return SDValue();
-
-  SDValue LHSLo = NewLHS.getOperand(0);
-  SDValue LHSHi = NewLHS.getOperand(1);
-  SDValue RHSLo =
-      DAG.getConstant(ConstRHS->getAPIntValue().trunc(64), SDLoc(N), MVT::i64);
-  SDValue RHSHi = DAG.getConstant(ConstRHS->getAPIntValue().lshr(64).trunc(64),
-                                  SDLoc(N), MVT::i64);
-  if (!splitI128ValueToI64Halves(NewLHS, DAG, SDLoc(N), LHSLo, LHSHi)) {
-    return SDValue();
-  }
-
-  if (CCCode == ISD::SETEQ || CCCode == ISD::SETNE) {
-    SDValue LoCmpF =
-        DAG.FoldSetCC(N->getValueType(0), LHSLo, RHSLo, CCCode, SDLoc(N));
-    SDValue HiCmpF =
-        DAG.FoldSetCC(N->getValueType(0), LHSHi, RHSHi, CCCode, SDLoc(N));
-    auto IsConstBool = [](SDValue V) {
-      return isa_and_nonnull<ConstantSDNode>(V.getNode());
-    };
-    if (IsConstBool(LoCmpF) || IsConstBool(HiCmpF))
-      return SDValue();
-
-    SDValue LoCmp =
-        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
-    SDValue HiCmp =
-        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, CCCode);
-    unsigned Opcode = (CCCode == ISD::SETEQ) ? ISD::AND : ISD::OR;
-    return DAG.getNode(Opcode, SDLoc(N), LoCmp.getValueType(), LoCmp, HiCmp);
-  }
-
-  if (CCCode == ISD::SETUGT || CCCode == ISD::SETUGE) {
-    // x >  K  <=> (xhi > khi)  || (xhi==khi && xlo >  klo)
-    // x >= K  <=> (xhi > khi)  || (xhi==khi && xlo >= klo)
-    // This specialized form relies on the immediate living entirely in the low
-    // 64 bits. When the high half is non-zero, reducing the compare to a
-    // high==0 test is invalid and can miscompile widened expressions such as
-    // (shl i128 X, 1) > C.
-    if (!isNullConstant(RHSHi))
-      return SDValue();
-
-    SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
-    ISD::CondCode LoCC = (CCCode == ISD::SETUGT) ? ISD::SETULE : ISD::SETULT;
-
-    SDValue HiEq =
-        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, ZeroHi, ISD::SETEQ);
-    SDValue LoCmp =
-        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, LoCC);
-    SDValue Tree =
-        DAG.getNode(ISD::AND, SDLoc(N), N->getValueType(0), HiEq, LoCmp);
-
-    SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
-    return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETEQ);
-  }
-
-  if (CCCode == ISD::SETULT || CCCode == ISD::SETULE) {
-    // x <  K  <=> (xhi < khi)  || (xhi==khi && xlo <  klo)
-    // x <= K  <=> (xhi < khi)  || (xhi==khi && xlo <= klo)
-    ISD::CondCode Opcode = ISD::SETULT;
-
-    SDValue HiCmp =
-        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, Opcode);
-    SDValue HiEq =
-        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, ISD::SETEQ);
-    SDValue LoCmp =
-        DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
-
-    SDValue LoAnd =
-        DAG.getNode(ISD::AND, SDLoc(N), HiEq.getValueType(), HiEq, LoCmp);
-    SDValue Tree =
-        DAG.getNode(ISD::OR, SDLoc(N), N->getValueType(0), HiCmp, LoAnd);
-
-    SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
-    return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETNE);
-  }
-  return SDValue();
-}
-
 static SDValue performSETCCCombine(SDNode *N,
                                    TargetLowering::DAGCombinerInfo &DCI,
                                    SelectionDAG &DAG) {
@@ -27585,12 +27550,45 @@ static SDValue performSETCCCombine(SDNode *N,
       SplatLHSVal.isOne())
     return DAG.getSetCC(DL, VT, DAG.getConstant(0, DL, CmpVT), RHS, ISD::SETGE);
 
-  if (SDValue V = performExpandedI128CmpCombine(N, DCI, DAG, Cond))
-    return V;
-
   return SDValue();
 }
 
+static SDValue performSETCCCARRYCombine(SDNode *N, SelectionDAG &DAG) {
+  assert(N->getOpcode() == ISD::SETCCCARRY && "Unexpected opcode!");
+
+  // Rebuild narrow high/low compares from type-legalized wide unsigned compares
+  // so the existing CCMP conjunction/disjunction lowering can handle them.
+  SDValue HiLHS = N->getOperand(0);
+  SDValue HiRHS = N->getOperand(1);
+  SDValue Carry = N->getOperand(2);
+  ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(3))->get();
+  if (Cond != ISD::SETULT || Carry.getOpcode() != ISD::USUBO ||
+      Carry.getResNo() != 1)
+    return SDValue();
+
+  if (!isNullConstant(HiLHS) && !isNullConstant(HiRHS))
+    return SDValue();
+
+  SDValue LoLHS = Carry.getOperand(0);
+  SDValue LoRHS = Carry.getOperand(1);
+  if (!isa<ConstantSDNode>(LoLHS) && !isa<ConstantSDNode>(LoRHS))
+    return SDValue();
+
+  EVT VT = N->getValueType(0);
+  SDLoc DL(N);
+
+  SDValue LoCmp = DAG.getSetCC(DL, VT, LoLHS, LoRHS, ISD::SETULT);
+  if (isNullConstant(HiRHS)) {
+    SDValue HiEq = DAG.getSetCC(DL, VT, HiLHS, HiRHS, ISD::SETEQ);
+    return DAG.getNode(ISD::AND, DL, VT, HiEq, LoCmp);
+  }
+
+  SDValue HiNe = DAG.getSetCC(DL, VT, HiRHS,
+                              DAG.getConstant(0, DL, HiRHS.getValueType()),
+                              ISD::SETNE);
+  return DAG.getNode(ISD::OR, DL, VT, HiNe, LoCmp);
+}
+
 // Replace a flag-setting operator (eg ANDS) with the generic version
 // (eg AND) if the flag is unused.
 static SDValue performFlagSettingCombine(SDNode *N,
@@ -29421,6 +29419,8 @@ SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N,
     return performVSelectCombine(N, DCI, Subtarget);
   case ISD::SETCC:
     return performSETCCCombine(N, DCI, DAG);
+  case ISD::SETCCCARRY:
+    return performSETCCCARRYCombine(N, DAG);
   case ISD::LOAD:
     return performLOADCombine(N, DCI, DAG, Subtarget);
   case ISD::STORE:
diff --git a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
index 7422cbbfd19a0..eb414dbad3858 100644
--- a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
+++ b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
@@ -1,5 +1,7 @@
 ; RUN: llc -mtriple=aarch64-linux-gnu -O3 -o - < %s | FileCheck %s
 
+declare void @foo()
+
 define i1 @eq_imm(i128 %x) {
 ; CHECK-LABEL: eq_imm:
 ; CHECK:       cmp x0, #5
@@ -20,11 +22,111 @@ define i1 @ne_imm(i128 %x) {
   ret i1 %cmp
 }
 
+define i1 @eq_or_xor_zero_hi(i64 %lo, i64 %hi) {
+; CHECK-LABEL: eq_or_xor_zero_hi:
+; CHECK:       cmp x0, #5
+; CHECK-NEXT:  ccmp x1, #0, #0, eq
+; CHECK-NEXT:  cset w0, eq
+; CHECK-NEXT:  ret
+  %xorlo = xor i64 %lo, 5
+  %or = or i64 %xorlo, %hi
+  %cmp = icmp eq i64 %or, 0
+  ret i1 %cmp
+}
+
+define i1 @ne_or_xor_zero_hi(i64 %lo, i64 %hi) {
+; CHECK-LABEL: ne_or_xor_zero_hi:
+; CHECK:       cmp x0, #5
+; CHECK-NEXT:  ccmp x1, #0, #0, eq
+; CHECK-NEXT:  cset w0, ne
+; CHECK-NEXT:  ret
+  %xorlo = xor i64 %lo, 5
+  %or = or i64 %xorlo, %hi
+  %cmp = icmp ne i64 %or, 0
+  ret i1 %cmp
+}
+
+define void @eq_or_xor_large_cmp_imm_branch(i64 %lo, i64 %hi) {
+; CHECK-LABEL: eq_or_xor_large_cmp_imm_branch:
+; CHECK:       eor x8, x1, #0x8000000000000000
+; CHECK-NEXT:  orr x8, x0, x8
+; CHECK-NEXT:  cbz x8, .LBB4_2
+  %xor = xor i64 %hi, -9223372036854775808
+  %or = or i64 %lo, %xor
+  %cmp = icmp eq i64 %or, 0
+  br i1 %cmp, label %true, label %false
+
+true:
+  call void @foo()
+  ret void
+
+false:
+  ret void
+}
+
+define void @eq_or_xor_large_condcmp_imm_branch(i64 %a, i64 %b, i64 %c, i64 %d) {
+; CHECK-LABEL: eq_or_xor_large_condcmp_imm_branch:
+; CHECK:       eor x8, x3, #0xff
+; CHECK-NEXT:  orr x9, x0, x1
+; CHECK-NEXT:  orr x8, x2, x8
+; CHECK-NEXT:  orr x8, x9, x8
+; CHECK-NEXT:  cbz x8, .LBB5_2
+  %xor = xor i64 %d, 255
+  %or0 = or i64 %a, %b
+  %or1 = or i64 %c, %xor
+  %or = or i64 %or0, %or1
+  %cmp = icmp eq i64 %or, 0
+  br i1 %cmp, label %true, label %false
+
+true:
+  call void @foo()
+  ret void
+
+false:
+  ret void
+}
+
+define i1 @eq_or_xor_two_condcmp_imm_reordered(i64 %zero, i64 %value) {
+; CHECK-LABEL: eq_or_xor_two_condcmp_imm_reordered:
+; CHECK:       cmp x1, #255
+; CHECK-NEXT:  ccmp x0, #0, #0, eq
+; CHECK-NEXT:  cset w0, eq
+; CHECK-NEXT:  ret
+  %xor = xor i64 %value, 255
+  %or = or i64 %zero, %xor
+  %cmp = icmp eq i64 %or, 0
+  ret i1 %cmp
+}
+
+define void @eq_or_xor_multiple_large_condcmp_imm_branch(i64 %a, i64 %b, i64 %c, i64 %d) {
+; CHECK-LABEL: eq_or_xor_multiple_large_condcmp_imm_branch:
+; CHECK:       eor x8, x1, #0xff
+; CHECK-NEXT:  eor x9, x3, #0xffff
+; CHECK-NEXT:  orr x8, x0, x8
+; CHECK-NEXT:  orr x9, x2, x9
+; CHECK-NEXT:  orr x8, x8, x9
+; CHECK-NEXT:  cbz x8, .LBB7_2
+  %xorb = xor i64 %b, 255
+  %xord = xor i64 %d, 65535
+  %or0 = or i64 %a, %xorb
+  %or1 = or i64 %c, %xord
+  %or = or i64 %or0, %or1
+  %cmp = icmp eq i64 %or, 0
+  br i1 %cmp, label %true, label %false
+
+true:
+  call void @foo()
+  ret void
+
+false:
+  ret void
+}
+
 define i1 @ult_imm(i128 %x) {
 ; CHECK-LABEL: ult_imm:
 ; CHECK:       cmp x1, #0
 ; CHECK-NEXT:  ccmp x0, #5, #2, eq
-; CHECK-NEXT:  cset w0, {{cc|lo}}
+; CHECK-NEXT:  cset w0, lo
 ; CHECK-NEXT:  ret
   %cmp = icmp ult i128 %x, 5
   ret i1 %cmp
@@ -34,7 +136,7 @@ define i1 @ule_imm(i128 %x) {
 ; CHECK-LABEL: ule_imm:
 ; CHECK:       cmp x1, #0
 ; CHECK-NEXT:  ccmp x0, #6, #2, eq
-; CHECK-NEXT:  cset w0, {{cc|lo}}
+; CHECK-NEXT:  cset w0, lo
 ; CHECK-NEXT:  ret
   %cmp = icmp ule i128 %x, 5
   ret i1 %cmp
@@ -44,7 +146,7 @@ define i1 @ugt_imm(i128 %x) {
 ; CHECK-LABEL: ugt_imm:
 ; CHECK:       cmp x1, #0
 ; CHECK-NEXT:  ccmp x0, #5, #2, eq
-; CHECK-NEXT:  cset	w0, hi
+; CHECK-NEXT:  cset w0, hi
 ; CHECK-NEXT:  ret
   %cmp = icmp ugt i128 %x, 5
   ret i1 %cmp
@@ -52,10 +154,10 @@ define i1 @ugt_imm(i128 %x) {
 
 define i1 @uge_imm(i128 %x) {
 ; CHECK-LABEL: uge_imm:
-; CHECK:       cmp	x1, #0
-; CHECK-NEXT:  ccmp	x0, #4, #2, eq
-; CHECK-NEXT:  cset	w0, hi
+; CHECK:       cmp x1, #0
+; CHECK-NEXT:  ccmp x0, #4, #2, eq
+; CHECK-NEXT:  cset w0, hi
 ; CHECK-NEXT:  ret
   %cmp = icmp uge i128 %x, 5
   ret i1 %cmp
-}
\ No newline at end of file
+}

>From c20a1861cf0220453828b6b4450465e90cd6960e Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Fri, 8 May 2026 09:52:28 +0800
Subject: [PATCH 4/4] Refine eq/ne OR/XOR-chain CCMP immediate handling

---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 124 ++++++---------
 .../CodeGen/AArch64/i128-imm-compare-ccmp.ll  | 142 ++++++++++++------
 llvm/test/CodeGen/AArch64/isinf.ll            |  14 +-
 3 files changed, 145 insertions(+), 135 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index ce9612f6b14c9..bdfdfc2193dc9 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -11871,59 +11871,66 @@ SDValue AArch64TargetLowering::LowerBitreverse(SDValue Op,
                      DAG.getNode(ISD::BITREVERSE, DL, VST, REVB));
 }
 
-// Check whether the continuous comparison sequence.
-static bool hasLegalCmpImmediateOperand(SDValue V) {
-  ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
-  return !C || isLegalCmpImmed(C->getAPIntValue());
-}
-
-static bool hasLegalCmpImmediate(SDValue LHS, SDValue RHS) {
-  return hasLegalCmpImmediateOperand(LHS) && hasLegalCmpImmediateOperand(RHS);
-}
-
+// A CCMP folds in only a 5-bit unsigned immediate (or its negation, via CCMN);
+// any other constant must be materialized into a register first.
 static bool isLegalCondCmpImmediate(const APInt &Imm) {
   if (Imm.isNegative())
     return Imm.sgt(-32);
   return Imm.getLimitedValue(32) <= 31;
 }
 
-static bool hasLegalCondCmpImmediateOperand(SDValue V) {
-  ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
-  return !C || isLegalCondCmpImmediate(C->getAPIntValue());
+// True unless one of the operands is a constant that cannot be encoded as a
+// CMP/CMN immediate. A non-constant operand always lives in a register, so it
+// is fine.
+static bool hasLegalCmpImmediate(SDValue LHS, SDValue RHS) {
+  ConstantSDNode *C = dyn_cast<ConstantSDNode>(LHS);
+  if (C && !isLegalCmpImmed(C->getAPIntValue()))
+    return false;
+  C = dyn_cast<ConstantSDNode>(RHS);
+  if (C && !isLegalCmpImmed(C->getAPIntValue()))
+    return false;
+  return true;
 }
 
+// As hasLegalCmpImmediate, but for the tighter CCMP immediate form.
 static bool hasLegalCondCmpImmediate(SDValue LHS, SDValue RHS) {
-  return hasLegalCondCmpImmediateOperand(LHS) &&
-         hasLegalCondCmpImmediateOperand(RHS);
-}
-
-static bool isLegalXorImmediate(const APInt &Imm) {
-  unsigned BitWidth = Imm.getBitWidth() <= 32 ? 32 : 64;
-  if (Imm.getBitWidth() > BitWidth)
+  ConstantSDNode *C = dyn_cast<ConstantSDNode>(LHS);
+  if (C && !isLegalCondCmpImmediate(C->getAPIntValue()))
     return false;
-  return AArch64_AM::isLogicalImmediate(Imm.getZExtValue(), BitWidth);
-}
-
-static bool hasCheapXorImmediateThatNeedsCondCmpRegOperand(SDValue V) {
-  ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
-  if (!C)
+  C = dyn_cast<ConstantSDNode>(RHS);
+  if (C && !isLegalCondCmpImmediate(C->getAPIntValue()))
     return false;
-
-  const APInt &Imm = C->getAPIntValue();
-  return !isLegalCondCmpImmediate(Imm) && isLegalXorImmediate(Imm);
-}
-
-static bool hasCheapXorImmediateThatNeedsCondCmpReg(
-    const std::pair<SDValue, SDValue> &Pair) {
-  return hasCheapXorImmediateThatNeedsCondCmpRegOperand(Pair.first) ||
-         hasCheapXorImmediateThatNeedsCondCmpRegOperand(Pair.second);
+  return true;
 }
 
+// Only the leading compare of the chain uses the wider CMP immediate; the rest
+// become CCMPs. So a compare whose immediate is a legal CMP operand but not a
+// legal CCMP operand is cheapest at the front.
 static bool preferAsFirstCmp(const std::pair<SDValue, SDValue> &Pair) {
   return hasLegalCmpImmediate(Pair.first, Pair.second) &&
          !hasLegalCondCmpImmediate(Pair.first, Pair.second);
 }
 
+// True if either operand is a constant that does not fit a CCMP immediate but
+// is a legal logical immediate: folding it into the xor is a single cheap
+// instruction, yet the result still needs a scratch register for the CCMP.
+// Chaining more than two such compares costs more than the CCMP form saves.
+static bool hasCheapXorImmediateThatNeedsCondCmpReg(
+    const std::pair<SDValue, SDValue> &Pair) {
+  for (SDValue V : {Pair.first, Pair.second}) {
+    auto *C = dyn_cast<ConstantSDNode>(V);
+    if (!C || isLegalCondCmpImmediate(C->getAPIntValue()))
+      continue;
+    const APInt &Imm = C->getAPIntValue();
+    unsigned BitWidth = Imm.getBitWidth() <= 32 ? 32 : 64;
+    if (Imm.getBitWidth() <= BitWidth &&
+        AArch64_AM::isLogicalImmediate(Imm.getZExtValue(), BitWidth))
+      return true;
+  }
+  return false;
+}
+
+// Check whether the continuous comparison sequence.
 static bool
 isOrXorChain(SDValue N, SelectionDAG &DAG, unsigned &Num, bool &SawXor,
              bool RequireLegalCmpImmediates,
@@ -11984,11 +11991,10 @@ static SDValue performOrXorChainCombine(SDNode *N, SelectionDAG &DAG) {
   // sub A0, A1; ccmp B0, B1, 0, eq; cmp inv(Cond) flag
   unsigned NumXors = 0;
   bool SawXor = false;
-  bool RequireLegalCmpImmediates =
-      any_of(N->users(), [](SDNode *User) {
-        return User->getOpcode() == ISD::BRCOND ||
-               User->getOpcode() == AArch64ISD::BRCOND;
-      });
+  bool RequireLegalCmpImmediates = any_of(N->users(), [](SDNode *User) {
+    return User->getOpcode() == ISD::BRCOND ||
+           User->getOpcode() == AArch64ISD::BRCOND;
+  });
   if ((Cond == ISD::SETEQ || Cond == ISD::SETNE) && isNullConstant(RHS) &&
       LHS->getOpcode() == ISD::OR && LHS->hasOneUse() &&
       isOrXorChain(LHS, DAG, NumXors, SawXor, RequireLegalCmpImmediates,
@@ -27654,42 +27660,6 @@ static SDValue performSETCCCombine(SDNode *N,
   return SDValue();
 }
 
-static SDValue performSETCCCARRYCombine(SDNode *N, SelectionDAG &DAG) {
-  assert(N->getOpcode() == ISD::SETCCCARRY && "Unexpected opcode!");
-
-  // Rebuild narrow high/low compares from type-legalized wide unsigned compares
-  // so the existing CCMP conjunction/disjunction lowering can handle them.
-  SDValue HiLHS = N->getOperand(0);
-  SDValue HiRHS = N->getOperand(1);
-  SDValue Carry = N->getOperand(2);
-  ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(3))->get();
-  if (Cond != ISD::SETULT || Carry.getOpcode() != ISD::USUBO ||
-      Carry.getResNo() != 1)
-    return SDValue();
-
-  if (!isNullConstant(HiLHS) && !isNullConstant(HiRHS))
-    return SDValue();
-
-  SDValue LoLHS = Carry.getOperand(0);
-  SDValue LoRHS = Carry.getOperand(1);
-  if (!isa<ConstantSDNode>(LoLHS) && !isa<ConstantSDNode>(LoRHS))
-    return SDValue();
-
-  EVT VT = N->getValueType(0);
-  SDLoc DL(N);
-
-  SDValue LoCmp = DAG.getSetCC(DL, VT, LoLHS, LoRHS, ISD::SETULT);
-  if (isNullConstant(HiRHS)) {
-    SDValue HiEq = DAG.getSetCC(DL, VT, HiLHS, HiRHS, ISD::SETEQ);
-    return DAG.getNode(ISD::AND, DL, VT, HiEq, LoCmp);
-  }
-
-  SDValue HiNe = DAG.getSetCC(DL, VT, HiRHS,
-                              DAG.getConstant(0, DL, HiRHS.getValueType()),
-                              ISD::SETNE);
-  return DAG.getNode(ISD::OR, DL, VT, HiNe, LoCmp);
-}
-
 static SDValue performSELECT_CCCombine(SDNode *N,
                                        TargetLowering::DAGCombinerInfo &DCI,
                                        SelectionDAG &DAG) {
@@ -29584,8 +29554,6 @@ SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N,
     return performVSelectCombine(N, DCI, Subtarget);
   case ISD::SETCC:
     return performSETCCCombine(N, DCI, DAG);
-  case ISD::SETCCCARRY:
-    return performSETCCCARRYCombine(N, DAG);
   case ISD::LOAD:
     return performLOADCombine(N, DCI, DAG, Subtarget);
   case ISD::STORE:
diff --git a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
index eb414dbad3858..824d7871cd3d7 100644
--- a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
+++ b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
@@ -1,33 +1,37 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
 ; RUN: llc -mtriple=aarch64-linux-gnu -O3 -o - < %s | FileCheck %s
 
 declare void @foo()
 
 define i1 @eq_imm(i128 %x) {
 ; CHECK-LABEL: eq_imm:
-; CHECK:       cmp x0, #5
-; CHECK-NEXT:  ccmp x1, #0, #0, eq
-; CHECK-NEXT:  cset w0, eq
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x0, #5
+; CHECK-NEXT:    ccmp x1, #0, #0, eq
+; CHECK-NEXT:    cset w0, eq
+; CHECK-NEXT:    ret
   %cmp = icmp eq i128 %x, 5
   ret i1 %cmp
 }
 
 define i1 @ne_imm(i128 %x) {
 ; CHECK-LABEL: ne_imm:
-; CHECK:       cmp x0, #5
-; CHECK-NEXT:  ccmp x1, #0, #0, eq
-; CHECK-NEXT:  cset w0, ne
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x0, #5
+; CHECK-NEXT:    ccmp x1, #0, #0, eq
+; CHECK-NEXT:    cset w0, ne
+; CHECK-NEXT:    ret
   %cmp = icmp ne i128 %x, 5
   ret i1 %cmp
 }
 
 define i1 @eq_or_xor_zero_hi(i64 %lo, i64 %hi) {
 ; CHECK-LABEL: eq_or_xor_zero_hi:
-; CHECK:       cmp x0, #5
-; CHECK-NEXT:  ccmp x1, #0, #0, eq
-; CHECK-NEXT:  cset w0, eq
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x0, #5
+; CHECK-NEXT:    ccmp x1, #0, #0, eq
+; CHECK-NEXT:    cset w0, eq
+; CHECK-NEXT:    ret
   %xorlo = xor i64 %lo, 5
   %or = or i64 %xorlo, %hi
   %cmp = icmp eq i64 %or, 0
@@ -36,10 +40,11 @@ define i1 @eq_or_xor_zero_hi(i64 %lo, i64 %hi) {
 
 define i1 @ne_or_xor_zero_hi(i64 %lo, i64 %hi) {
 ; CHECK-LABEL: ne_or_xor_zero_hi:
-; CHECK:       cmp x0, #5
-; CHECK-NEXT:  ccmp x1, #0, #0, eq
-; CHECK-NEXT:  cset w0, ne
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x0, #5
+; CHECK-NEXT:    ccmp x1, #0, #0, eq
+; CHECK-NEXT:    cset w0, ne
+; CHECK-NEXT:    ret
   %xorlo = xor i64 %lo, 5
   %or = or i64 %xorlo, %hi
   %cmp = icmp ne i64 %or, 0
@@ -48,9 +53,19 @@ define i1 @ne_or_xor_zero_hi(i64 %lo, i64 %hi) {
 
 define void @eq_or_xor_large_cmp_imm_branch(i64 %lo, i64 %hi) {
 ; CHECK-LABEL: eq_or_xor_large_cmp_imm_branch:
-; CHECK:       eor x8, x1, #0x8000000000000000
-; CHECK-NEXT:  orr x8, x0, x8
-; CHECK-NEXT:  cbz x8, .LBB4_2
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    eor x8, x1, #0x8000000000000000
+; CHECK-NEXT:    orr x8, x0, x8
+; CHECK-NEXT:    cbz x8, .LBB4_2
+; CHECK-NEXT:  // %bb.1: // %common.ret
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB4_2: // %true
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl foo
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
   %xor = xor i64 %hi, -9223372036854775808
   %or = or i64 %lo, %xor
   %cmp = icmp eq i64 %or, 0
@@ -66,11 +81,21 @@ false:
 
 define void @eq_or_xor_large_condcmp_imm_branch(i64 %a, i64 %b, i64 %c, i64 %d) {
 ; CHECK-LABEL: eq_or_xor_large_condcmp_imm_branch:
-; CHECK:       eor x8, x3, #0xff
-; CHECK-NEXT:  orr x9, x0, x1
-; CHECK-NEXT:  orr x8, x2, x8
-; CHECK-NEXT:  orr x8, x9, x8
-; CHECK-NEXT:  cbz x8, .LBB5_2
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    eor x8, x3, #0xff
+; CHECK-NEXT:    orr x9, x0, x1
+; CHECK-NEXT:    orr x8, x2, x8
+; CHECK-NEXT:    orr x8, x9, x8
+; CHECK-NEXT:    cbz x8, .LBB5_2
+; CHECK-NEXT:  // %bb.1: // %common.ret
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB5_2: // %true
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl foo
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
   %xor = xor i64 %d, 255
   %or0 = or i64 %a, %b
   %or1 = or i64 %c, %xor
@@ -88,10 +113,11 @@ false:
 
 define i1 @eq_or_xor_two_condcmp_imm_reordered(i64 %zero, i64 %value) {
 ; CHECK-LABEL: eq_or_xor_two_condcmp_imm_reordered:
-; CHECK:       cmp x1, #255
-; CHECK-NEXT:  ccmp x0, #0, #0, eq
-; CHECK-NEXT:  cset w0, eq
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x1, #255
+; CHECK-NEXT:    ccmp x0, #0, #0, eq
+; CHECK-NEXT:    cset w0, eq
+; CHECK-NEXT:    ret
   %xor = xor i64 %value, 255
   %or = or i64 %zero, %xor
   %cmp = icmp eq i64 %or, 0
@@ -100,12 +126,22 @@ define i1 @eq_or_xor_two_condcmp_imm_reordered(i64 %zero, i64 %value) {
 
 define void @eq_or_xor_multiple_large_condcmp_imm_branch(i64 %a, i64 %b, i64 %c, i64 %d) {
 ; CHECK-LABEL: eq_or_xor_multiple_large_condcmp_imm_branch:
-; CHECK:       eor x8, x1, #0xff
-; CHECK-NEXT:  eor x9, x3, #0xffff
-; CHECK-NEXT:  orr x8, x0, x8
-; CHECK-NEXT:  orr x9, x2, x9
-; CHECK-NEXT:  orr x8, x8, x9
-; CHECK-NEXT:  cbz x8, .LBB7_2
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    eor x8, x1, #0xff
+; CHECK-NEXT:    eor x9, x3, #0xffff
+; CHECK-NEXT:    orr x8, x0, x8
+; CHECK-NEXT:    orr x9, x2, x9
+; CHECK-NEXT:    orr x8, x8, x9
+; CHECK-NEXT:    cbz x8, .LBB7_2
+; CHECK-NEXT:  // %bb.1: // %common.ret
+; CHECK-NEXT:    ret
+; CHECK-NEXT:  .LBB7_2: // %true
+; CHECK-NEXT:    str x30, [sp, #-16]! // 8-byte Folded Spill
+; CHECK-NEXT:    .cfi_def_cfa_offset 16
+; CHECK-NEXT:    .cfi_offset w30, -16
+; CHECK-NEXT:    bl foo
+; CHECK-NEXT:    ldr x30, [sp], #16 // 8-byte Folded Reload
+; CHECK-NEXT:    ret
   %xorb = xor i64 %b, 255
   %xord = xor i64 %d, 65535
   %or0 = or i64 %a, %xorb
@@ -124,40 +160,46 @@ false:
 
 define i1 @ult_imm(i128 %x) {
 ; CHECK-LABEL: ult_imm:
-; CHECK:       cmp x1, #0
-; CHECK-NEXT:  ccmp x0, #5, #2, eq
-; CHECK-NEXT:  cset w0, lo
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x0, #5
+; CHECK-NEXT:    sbcs xzr, x1, xzr
+; CHECK-NEXT:    cset w0, lo
+; CHECK-NEXT:    ret
   %cmp = icmp ult i128 %x, 5
   ret i1 %cmp
 }
 
 define i1 @ule_imm(i128 %x) {
 ; CHECK-LABEL: ule_imm:
-; CHECK:       cmp x1, #0
-; CHECK-NEXT:  ccmp x0, #6, #2, eq
-; CHECK-NEXT:  cset w0, lo
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x0, #6
+; CHECK-NEXT:    sbcs xzr, x1, xzr
+; CHECK-NEXT:    cset w0, lo
+; CHECK-NEXT:    ret
   %cmp = icmp ule i128 %x, 5
   ret i1 %cmp
 }
 
 define i1 @ugt_imm(i128 %x) {
 ; CHECK-LABEL: ugt_imm:
-; CHECK:       cmp x1, #0
-; CHECK-NEXT:  ccmp x0, #5, #2, eq
-; CHECK-NEXT:  cset w0, hi
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #5 // =0x5
+; CHECK-NEXT:    cmp x8, x0
+; CHECK-NEXT:    ngcs xzr, x1
+; CHECK-NEXT:    cset w0, lo
+; CHECK-NEXT:    ret
   %cmp = icmp ugt i128 %x, 5
   ret i1 %cmp
 }
 
 define i1 @uge_imm(i128 %x) {
 ; CHECK-LABEL: uge_imm:
-; CHECK:       cmp x1, #0
-; CHECK-NEXT:  ccmp x0, #4, #2, eq
-; CHECK-NEXT:  cset w0, hi
-; CHECK-NEXT:  ret
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    mov w8, #4 // =0x4
+; CHECK-NEXT:    cmp x8, x0
+; CHECK-NEXT:    ngcs xzr, x1
+; CHECK-NEXT:    cset w0, lo
+; CHECK-NEXT:    ret
   %cmp = icmp uge i128 %x, 5
   ret i1 %cmp
 }
diff --git a/llvm/test/CodeGen/AArch64/isinf.ll b/llvm/test/CodeGen/AArch64/isinf.ll
index 92d53d7086e46..249a339f2de0a 100644
--- a/llvm/test/CodeGen/AArch64/isinf.ll
+++ b/llvm/test/CodeGen/AArch64/isinf.ll
@@ -80,14 +80,14 @@ define i32 @replace_isinf_call_f64(double %x) {
 define i32 @replace_isinf_call_f128(fp128 %x) {
 ; CHECK-SD-LABEL: replace_isinf_call_f128:
 ; CHECK-SD:       // %bb.0:
-; CHECK-SD-NEXT:    str	q0, [sp, #-16]!
+; CHECK-SD-NEXT:    str q0, [sp, #-16]!
 ; CHECK-SD-NEXT:    .cfi_def_cfa_offset 16
-; CHECK-SD-NEXT:    mov	x8, #-562949953421312           // =0xfffe000000000000
-; CHECK-SD-NEXT:    ldp	x10, x9, [sp], #16
-; CHECK-SD-NEXT:    lsl	x9, x9, #1
-; CHECK-SD-NEXT:    cmp	x10, #0
-; CHECK-SD-NEXT:    ccmp	x8, x9, #0, eq
-; CHECK-SD-NEXT:    cset	w0, eq
+; CHECK-SD-NEXT:    mov x8, #-562949953421312 // =0xfffe000000000000
+; CHECK-SD-NEXT:    ldp x10, x9, [sp], #16
+; CHECK-SD-NEXT:    lsl x9, x9, #1
+; CHECK-SD-NEXT:    cmp x10, #0
+; CHECK-SD-NEXT:    ccmp x8, x9, #0, eq
+; CHECK-SD-NEXT:    cset w0, eq
 ; CHECK-SD-NEXT:    ret
 ;
 ; CHECK-GI-LABEL: replace_isinf_call_f128:



More information about the llvm-commits mailing list