[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
Fri Apr 10 08:30:58 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/2] [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/2] 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:



More information about the llvm-commits mailing list