[llvm] [AArch64] Recombine SETCCCARRY for legalized unsigned compares (PR #204504)

Cheng Lingfei via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 24 06:40:08 PDT 2026


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

>From 075500d1c90a6012b27419ca9b10a2a5ef52727f Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Thu, 18 Jun 2026 10:33:27 +0800
Subject: [PATCH 1/3] [AArch64] Recombine SETCCCARRY for legalized unsigned
 compares

Type legalization can turn wide unsigned compares into SETCCCARRY nodes fed
by USUBO carry results, hiding the original high/low compare shape from the
existing CCMP conjunction/disjunction lowering.

Add an AArch64 DAG combine for SETCCCARRY that recognizes these legalized
wide-compare patterns and rebuilds them as SETCC plus AND/OR, exposing them
to the existing CCMP lowering.
---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 38 +++++++++++++++++++
 1 file changed, 38 insertions(+)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 67ef911117eff..15c5089cdb507 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1157,6 +1157,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
   // Try and combine setcc/select_cc with csel and bool-vector bitcasts.
   setTargetDAGCombine(ISD::SETCC);
   setTargetDAGCombine(ISD::SELECT_CC);
+  setTargetDAGCombine(ISD::SETCCCARRY);
 
   setTargetDAGCombine(ISD::INTRINSIC_WO_CHAIN);
 
@@ -28078,6 +28079,41 @@ 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) {
@@ -30010,6 +30046,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:

>From 578ebbb2e5da3b2c23d518d99108acec5f4c2561 Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Thu, 18 Jun 2026 10:33:27 +0800
Subject: [PATCH 2/3] [AArch64] Recombine SETCCCARRY for legalized unsigned
 compares

Type legalization can turn wide unsigned compares into SETCCCARRY nodes fed
by USUBO carry results, hiding the original high/low compare shape from the
existing CCMP conjunction/disjunction lowering.

Add an AArch64 DAG combine for SETCCCARRY that recognizes these legalized
wide-compare patterns and rebuilds them as SETCC plus AND/OR, exposing them
to the existing CCMP lowering.
---
 .../Target/AArch64/AArch64ISelLowering.cpp    | 38 ++++++++++++++
 llvm/test/CodeGen/AArch64/i128-cmp.ll         | 49 +++++++++++++++++++
 2 files changed, 87 insertions(+)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 67ef911117eff..15c5089cdb507 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1157,6 +1157,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
   // Try and combine setcc/select_cc with csel and bool-vector bitcasts.
   setTargetDAGCombine(ISD::SETCC);
   setTargetDAGCombine(ISD::SELECT_CC);
+  setTargetDAGCombine(ISD::SETCCCARRY);
 
   setTargetDAGCombine(ISD::INTRINSIC_WO_CHAIN);
 
@@ -28078,6 +28079,41 @@ 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) {
@@ -30010,6 +30046,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-cmp.ll b/llvm/test/CodeGen/AArch64/i128-cmp.ll
index 9c23b3a603d10..c878cc5f1e6a6 100644
--- a/llvm/test/CodeGen/AArch64/i128-cmp.ll
+++ b/llvm/test/CodeGen/AArch64/i128-cmp.ll
@@ -323,3 +323,52 @@ exit:
     ret void
 }
 
+define i1 @cmp_i128_ult_small_const(i128 %a) {
+; CHECK-LABEL: cmp_i128_ult_small_const:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x1, #0
+; CHECK-NEXT:    ccmp x0, #7, #2, eq
+; CHECK-NEXT:    cset w0, lo
+; CHECK-NEXT:    ret
+    %cmp = icmp ult i128 %a, 7
+    ret i1 %cmp
+}
+
+define i1 @cmp_i128_small_const_ult(i128 %a) {
+; CHECK-LABEL: cmp_i128_small_const_ult:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x1, #0
+; CHECK-NEXT:    ccmp x0, #7, #2, eq
+; CHECK-NEXT:    cset w0, hi
+; CHECK-NEXT:    ret
+    %cmp = icmp ult i128 7, %a
+    ret i1 %cmp
+}
+
+define i1 @cmp_i128_ult_small_const_and_i64(i128 %a, i64 %b) {
+; CHECK-LABEL: cmp_i128_ult_small_const_and_i64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x0, #7
+; CHECK-NEXT:    ccmp x2, #11, #2, lo
+; CHECK-NEXT:    ccmp x1, #0, #0, lo
+; CHECK-NEXT:    cset w0, eq
+; CHECK-NEXT:    ret
+    %wide = icmp ult i128 %a, 7
+    %narrow = icmp ult i64 %b, 11
+    %cmp = and i1 %wide, %narrow
+    ret i1 %cmp
+}
+
+define i1 @cmp_i128_small_const_ult_or_i64(i128 %a, i64 %b) {
+; CHECK-LABEL: cmp_i128_small_const_ult_or_i64:
+; CHECK:       // %bb.0:
+; CHECK-NEXT:    cmp x1, #0
+; CHECK-NEXT:    ccmp x0, #7, #2, eq
+; CHECK-NEXT:    ccmp x2, #11, #0, ls
+; CHECK-NEXT:    cset w0, lo
+; CHECK-NEXT:    ret
+    %wide = icmp ult i128 7, %a
+    %narrow = icmp ult i64 %b, 11
+    %cmp = or i1 %wide, %narrow
+    ret i1 %cmp
+}

>From 809eb5632363878bc8909d398f71702ea2128bc5 Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Wed, 24 Jun 2026 21:39:35 +0800
Subject: [PATCH 3/3] Use HiLHS instead

---
 llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 6ae80bbc999f3..d33e88caa2d2c 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -28215,8 +28215,7 @@ static SDValue performSETCCCARRYCombine(SDNode *N, SelectionDAG &DAG) {
     return DAG.getNode(ISD::AND, DL, VT, HiEq, LoCmp);
   }
 
-  SDValue HiNe = DAG.getSetCC(
-      DL, VT, HiRHS, DAG.getConstant(0, DL, HiRHS.getValueType()), ISD::SETNE);
+  SDValue HiNe = DAG.getSetCC(DL, VT, HiRHS, HiLHS, ISD::SETNE);
   return DAG.getNode(ISD::OR, DL, VT, HiNe, LoCmp);
 }
 



More information about the llvm-commits mailing list