[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
Mon Jul 6 02:46:16 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/8] [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/8] 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/8] 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/8] 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:
>From 5020a2a43accdd4b6f56e4415ff0321decbe6bcb Mon Sep 17 00:00:00 2001
From: clingfei <chenglingfei at foxmail.com>
Date: Thu, 18 Jun 2026 10:29:33 +0800
Subject: [PATCH 5/8] update
---
.../Target/AArch64/AArch64ISelLowering.cpp | 22 +++++++++----------
1 file changed, 10 insertions(+), 12 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index ec4fa336b0b34..40af7f1ccfa46 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1157,7 +1157,6 @@ 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);
@@ -12084,10 +12083,10 @@ static bool isLegalCondCmpImmediate(const APInt &Imm) {
// is fine.
static bool hasLegalCmpImmediate(SDValue LHS, SDValue RHS) {
ConstantSDNode *C = dyn_cast<ConstantSDNode>(LHS);
- if (C && !isLegalCmpImmed(C->getAPIntValue()))
+ if (C && !llvm::AArch64_AM::isLegalCmpImmed(C->getAPIntValue()))
return false;
C = dyn_cast<ConstantSDNode>(RHS);
- if (C && !isLegalCmpImmed(C->getAPIntValue()))
+ if (C && !llvm::AArch64_AM::isLegalCmpImmed(C->getAPIntValue()))
return false;
return true;
}
@@ -12103,14 +12102,6 @@ static bool hasLegalCondCmpImmediate(SDValue LHS, SDValue RHS) {
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.
@@ -12204,8 +12195,15 @@ static SDValue performOrXorChainCombine(SDNode *N, SelectionDAG &DAG) {
any_of(WorkList, hasCheapXorImmediateThatNeedsCondCmpReg))
return SDValue();
+ // 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.
+ auto PreferAsFirstCmp = [](const std::pair<SDValue, SDValue> &Pair) {
+ return hasLegalCmpImmediate(Pair.first, Pair.second) &&
+ !hasLegalCondCmpImmediate(Pair.first, Pair.second);
+ };
SmallVector<std::pair<SDValue, SDValue>, 16>::iterator First =
- find_if(WorkList, preferAsFirstCmp);
+ find_if(WorkList, PreferAsFirstCmp);
if (First != WorkList.end())
std::iter_swap(WorkList.begin(), First);
>From 7470a9063041dce784a5c7020cb162a39b2a2d0d Mon Sep 17 00:00:00 2001
From: clingfei <chenglingfei at foxmail.com>
Date: Wed, 24 Jun 2026 22:03:23 +0800
Subject: [PATCH 6/8] Simplify isLegalCondCmpImmediate and restrain the length
of ccmp chain
---
.../Target/AArch64/AArch64ISelLowering.cpp | 10 ++-
llvm/test/CodeGen/AArch64/bcmp.ll | 80 +++++++++++--------
2 files changed, 52 insertions(+), 38 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 40af7f1ccfa46..f5791a5c93efc 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -12073,9 +12073,7 @@ SDValue AArch64TargetLowering::LowerBitreverse(SDValue Op,
// 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;
+ return Imm.sgt(-32) && Imm.slt(32);
}
// True unless one of the operands is a constant that cannot be encoded as a
@@ -12191,6 +12189,12 @@ static SDValue performOrXorChainCombine(SDNode *N, SelectionDAG &DAG) {
isOrXorChain(LHS, DAG, NumXors, SawXor, RequireLegalCmpImmediates,
WorkList) &&
SawXor) {
+ // A CCMP sequence serializes the comparisons through NZCV. Keep the
+ // transform to short chains where the instruction-count reduction outweighs
+ // the longer dependency chain.
+ if (WorkList.size() > 5)
+ return SDValue();
+
if (WorkList.size() > 2 &&
any_of(WorkList, hasCheapXorImmediateThatNeedsCondCmpReg))
return SDValue();
diff --git a/llvm/test/CodeGen/AArch64/bcmp.ll b/llvm/test/CodeGen/AArch64/bcmp.ll
index 436a3c2375b52..3379790797f11 100644
--- a/llvm/test/CodeGen/AArch64/bcmp.ll
+++ b/llvm/test/CodeGen/AArch64/bcmp.ll
@@ -359,20 +359,26 @@ define i1 @bcmp38(ptr %a, ptr %b) {
define i1 @bcmp45(ptr %a, ptr %b) {
; CHECK-LABEL: bcmp45:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x11, [x1]
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: ldp x12, x13, [x1, #16]
-; CHECK-NEXT: cmp x9, x8
-; CHECK-NEXT: ldp x8, x9, [x0, #16]
-; CHECK-NEXT: ccmp x10, x11, #0, eq
+; CHECK-NEXT: ldp x8, x9, [x0]
+; CHECK-NEXT: ldr x16, [x1, #32]
+; CHECK-NEXT: ldp x10, x11, [x1]
+; CHECK-NEXT: ldur x17, [x1, #37]
+; CHECK-NEXT: ldp x12, x13, [x0, #16]
+; CHECK-NEXT: ldp x14, x15, [x1, #16]
+; CHECK-NEXT: eor x8, x8, x10
+; CHECK-NEXT: eor x9, x9, x11
; CHECK-NEXT: ldr x10, [x0, #32]
-; CHECK-NEXT: ldr x11, [x1, #32]
-; CHECK-NEXT: ccmp x8, x12, #0, eq
-; CHECK-NEXT: ldur x8, [x0, #37]
-; CHECK-NEXT: ldur x12, [x1, #37]
-; CHECK-NEXT: ccmp x9, x13, #0, eq
-; CHECK-NEXT: ccmp x10, x11, #0, eq
-; CHECK-NEXT: ccmp x8, x12, #0, eq
+; CHECK-NEXT: ldur x11, [x0, #37]
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: eor x12, x12, x14
+; CHECK-NEXT: eor x13, x13, x15
+; CHECK-NEXT: eor x10, x10, x16
+; CHECK-NEXT: eor x11, x11, x17
+; CHECK-NEXT: orr x9, x12, x13
+; CHECK-NEXT: orr x10, x10, x11
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x8, x8, x10
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%cr = call i32 @bcmp(ptr %a, ptr %b, i64 45)
@@ -380,31 +386,35 @@ define i1 @bcmp45(ptr %a, ptr %b) {
ret i1 %r
}
-; Although the large cmp chain may be not profitable on high end CPU, we
-; believe it is better on most cpus, so perform the transform now.
-; 8 xor + 7 or + 1 cmp only need 6 cycles on a 4 width ALU port machine
-; 2 cycle for xor
-; 3 cycle for or
-; 1 cycle for cmp
+; Avoid turning large xor/or reductions into long CCMP chains. The CCMP form
+; saves instructions but serializes the whole chain through NZCV.
define i1 @bcmp64(ptr %a, ptr %b) {
; CHECK-LABEL: bcmp64:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x11, [x1]
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: ldp x12, x13, [x1, #16]
-; CHECK-NEXT: cmp x9, x8
-; CHECK-NEXT: ldp x8, x9, [x0, #16]
-; CHECK-NEXT: ccmp x10, x11, #0, eq
-; CHECK-NEXT: ccmp x8, x12, #0, eq
-; CHECK-NEXT: ldp x8, x11, [x0, #32]
-; CHECK-NEXT: ldp x10, x12, [x1, #32]
-; CHECK-NEXT: ccmp x9, x13, #0, eq
-; CHECK-NEXT: ldp x9, x13, [x1, #48]
-; CHECK-NEXT: ccmp x8, x10, #0, eq
-; CHECK-NEXT: ldp x8, x10, [x0, #48]
-; CHECK-NEXT: ccmp x11, x12, #0, eq
-; CHECK-NEXT: ccmp x8, x9, #0, eq
-; CHECK-NEXT: ccmp x10, x13, #0, eq
+; CHECK-NEXT: ldp x8, x9, [x0]
+; CHECK-NEXT: ldp x10, x11, [x1]
+; CHECK-NEXT: ldp x12, x13, [x0, #16]
+; CHECK-NEXT: ldp x14, x15, [x1, #16]
+; CHECK-NEXT: eor x8, x8, x10
+; CHECK-NEXT: eor x9, x9, x11
+; CHECK-NEXT: ldp x17, x18, [x0, #48]
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: eor x10, x12, x14
+; CHECK-NEXT: ldp x11, x12, [x0, #32]
+; CHECK-NEXT: ldp x14, x16, [x1, #32]
+; CHECK-NEXT: eor x13, x13, x15
+; CHECK-NEXT: ldp x0, x1, [x1, #48]
+; CHECK-NEXT: orr x9, x10, x13
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: eor x11, x11, x14
+; CHECK-NEXT: eor x12, x12, x16
+; CHECK-NEXT: eor x14, x17, x0
+; CHECK-NEXT: eor x15, x18, x1
+; CHECK-NEXT: orr x10, x11, x12
+; CHECK-NEXT: orr x11, x14, x15
+; CHECK-NEXT: orr x9, x10, x11
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%cr = call i32 @bcmp(ptr %a, ptr %b, i64 64)
>From 080ee599920ceb4409c35f86fc74b359afe82937 Mon Sep 17 00:00:00 2001
From: clingfei <chenglingfei at foxmail.com>
Date: Thu, 25 Jun 2026 13:41:53 +0800
Subject: [PATCH 7/8] Update test cases
---
.../CodeGen/AArch64/i128-imm-compare-ccmp.ll | 22 +-
llvm/test/CodeGen/AArch64/memcmp.ll | 326 ++++++++++--------
.../umulo-128-legalisation-lowering.ll | 6 +-
3 files changed, 201 insertions(+), 153 deletions(-)
diff --git a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
index 824d7871cd3d7..77e697781c006 100644
--- a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
+++ b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
@@ -161,8 +161,8 @@ false:
define i1 @ult_imm(i128 %x) {
; CHECK-LABEL: ult_imm:
; CHECK: // %bb.0:
-; CHECK-NEXT: cmp x0, #5
-; CHECK-NEXT: sbcs xzr, x1, xzr
+; CHECK-NEXT: cmp x1, #0
+; CHECK-NEXT: ccmp x0, #5, #2, eq
; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%cmp = icmp ult i128 %x, 5
@@ -172,8 +172,8 @@ define i1 @ult_imm(i128 %x) {
define i1 @ule_imm(i128 %x) {
; CHECK-LABEL: ule_imm:
; CHECK: // %bb.0:
-; CHECK-NEXT: cmp x0, #6
-; CHECK-NEXT: sbcs xzr, x1, xzr
+; CHECK-NEXT: cmp x1, #0
+; CHECK-NEXT: ccmp x0, #6, #2, eq
; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%cmp = icmp ule i128 %x, 5
@@ -183,10 +183,9 @@ define i1 @ule_imm(i128 %x) {
define i1 @ugt_imm(i128 %x) {
; CHECK-LABEL: ugt_imm:
; 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: 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
@@ -195,10 +194,9 @@ define i1 @ugt_imm(i128 %x) {
define i1 @uge_imm(i128 %x) {
; CHECK-LABEL: uge_imm:
; 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: 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
diff --git a/llvm/test/CodeGen/AArch64/memcmp.ll b/llvm/test/CodeGen/AArch64/memcmp.ll
index 6b702c54b183e..92ff1c024a24f 100644
--- a/llvm/test/CodeGen/AArch64/memcmp.ll
+++ b/llvm/test/CodeGen/AArch64/memcmp.ll
@@ -1599,18 +1599,24 @@ define i32 @length48(ptr %X, ptr %Y) nounwind {
define i1 @length48_eq(ptr %x, ptr %y) nounwind {
; CHECK-LABEL: length48_eq:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x11, [x1]
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: ldp x12, x13, [x1, #16]
-; CHECK-NEXT: cmp x9, x8
-; CHECK-NEXT: ldp x8, x9, [x0, #16]
-; CHECK-NEXT: ccmp x10, x11, #0, eq
-; CHECK-NEXT: ccmp x8, x12, #0, eq
-; CHECK-NEXT: ldp x8, x11, [x0, #32]
-; CHECK-NEXT: ldp x10, x12, [x1, #32]
-; CHECK-NEXT: ccmp x9, x13, #0, eq
-; CHECK-NEXT: ccmp x8, x10, #0, eq
-; CHECK-NEXT: ccmp x11, x12, #0, eq
+; CHECK-NEXT: ldp x8, x9, [x0]
+; CHECK-NEXT: ldp x10, x11, [x1]
+; CHECK-NEXT: ldp x12, x13, [x0, #16]
+; CHECK-NEXT: ldp x14, x15, [x1, #16]
+; CHECK-NEXT: eor x8, x8, x10
+; CHECK-NEXT: ldp x16, x17, [x1, #32]
+; CHECK-NEXT: eor x9, x9, x11
+; CHECK-NEXT: ldp x10, x11, [x0, #32]
+; CHECK-NEXT: eor x12, x12, x14
+; CHECK-NEXT: eor x13, x13, x15
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x9, x12, x13
+; CHECK-NEXT: eor x10, x10, x16
+; CHECK-NEXT: eor x11, x11, x17
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x10, x10, x11
+; CHECK-NEXT: orr x8, x8, x10
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%call = tail call i32 @memcmp(ptr %x, ptr %y, i64 48) nounwind
@@ -1739,18 +1745,24 @@ define i1 @length48_gt(ptr %x, ptr %y) nounwind {
define i1 @length48_eq_prefer128(ptr %x, ptr %y) nounwind "prefer-vector-width"="128" {
; CHECK-LABEL: length48_eq_prefer128:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x11, [x1]
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: ldp x12, x13, [x1, #16]
-; CHECK-NEXT: cmp x9, x8
-; CHECK-NEXT: ldp x8, x9, [x0, #16]
-; CHECK-NEXT: ccmp x10, x11, #0, eq
-; CHECK-NEXT: ccmp x8, x12, #0, eq
-; CHECK-NEXT: ldp x8, x11, [x0, #32]
-; CHECK-NEXT: ldp x10, x12, [x1, #32]
-; CHECK-NEXT: ccmp x9, x13, #0, eq
-; CHECK-NEXT: ccmp x8, x10, #0, eq
-; CHECK-NEXT: ccmp x11, x12, #0, eq
+; CHECK-NEXT: ldp x8, x9, [x0]
+; CHECK-NEXT: ldp x10, x11, [x1]
+; CHECK-NEXT: ldp x12, x13, [x0, #16]
+; CHECK-NEXT: ldp x14, x15, [x1, #16]
+; CHECK-NEXT: eor x8, x8, x10
+; CHECK-NEXT: ldp x16, x17, [x1, #32]
+; CHECK-NEXT: eor x9, x9, x11
+; CHECK-NEXT: ldp x10, x11, [x0, #32]
+; CHECK-NEXT: eor x12, x12, x14
+; CHECK-NEXT: eor x13, x13, x15
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x9, x12, x13
+; CHECK-NEXT: eor x10, x10, x16
+; CHECK-NEXT: eor x11, x11, x17
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x10, x10, x11
+; CHECK-NEXT: orr x8, x8, x10
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%call = tail call i32 @memcmp(ptr %x, ptr %y, i64 48) nounwind
@@ -1761,35 +1773,41 @@ define i1 @length48_eq_prefer128(ptr %x, ptr %y) nounwind "prefer-vector-width"=
define i1 @length48_eq_const(ptr %X) nounwind {
; CHECK-LABEL: length48_eq_const:
; CHECK: // %bb.0:
-; CHECK-NEXT: mov x8, #12592 // =0x3130
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: movk x8, #13106, lsl #16
-; CHECK-NEXT: ldp x11, x12, [x0, #16]
-; CHECK-NEXT: movk x8, #13620, lsl #32
-; CHECK-NEXT: movk x8, #14134, lsl #48
-; CHECK-NEXT: cmp x9, x8
+; CHECK-NEXT: mov x12, #14134 // =0x3736
; CHECK-NEXT: mov x9, #14648 // =0x3938
+; CHECK-NEXT: mov x8, #12592 // =0x3130
+; CHECK-NEXT: movk x12, #14648, lsl #16
+; CHECK-NEXT: ldp x14, x15, [x0, #16]
; CHECK-NEXT: movk x9, #12592, lsl #16
+; CHECK-NEXT: ldp x10, x11, [x0]
+; CHECK-NEXT: movk x12, #12592, lsl #32
; CHECK-NEXT: movk x9, #13106, lsl #32
+; CHECK-NEXT: mov x13, #13620 // =0x3534
+; CHECK-NEXT: movk x12, #13106, lsl #48
+; CHECK-NEXT: movk x8, #13106, lsl #16
; CHECK-NEXT: movk x9, #13620, lsl #48
-; CHECK-NEXT: ccmp x10, x9, #0, eq
-; CHECK-NEXT: mov x9, #14134 // =0x3736
-; CHECK-NEXT: movk x9, #14648, lsl #16
-; CHECK-NEXT: movk x9, #12592, lsl #32
-; CHECK-NEXT: movk x9, #13106, lsl #48
-; CHECK-NEXT: ccmp x11, x9, #0, eq
-; CHECK-NEXT: mov x9, #13620 // =0x3534
-; CHECK-NEXT: movk x9, #14134, lsl #16
-; CHECK-NEXT: ldp x10, x11, [x0, #32]
-; CHECK-NEXT: movk x9, #14648, lsl #32
-; CHECK-NEXT: movk x9, #12592, lsl #48
-; CHECK-NEXT: ccmp x12, x9, #0, eq
-; CHECK-NEXT: mov x9, #13106 // =0x3332
-; CHECK-NEXT: movk x9, #13620, lsl #16
-; CHECK-NEXT: movk x9, #14134, lsl #32
-; CHECK-NEXT: movk x9, #14648, lsl #48
-; CHECK-NEXT: ccmp x10, x9, #0, eq
-; CHECK-NEXT: ccmp x11, x8, #0, eq
+; CHECK-NEXT: movk x13, #14134, lsl #16
+; CHECK-NEXT: eor x12, x14, x12
+; CHECK-NEXT: mov x14, #13106 // =0x3332
+; CHECK-NEXT: movk x8, #13620, lsl #32
+; CHECK-NEXT: movk x13, #14648, lsl #32
+; CHECK-NEXT: eor x9, x11, x9
+; CHECK-NEXT: ldp x11, x16, [x0, #32]
+; CHECK-NEXT: movk x14, #13620, lsl #16
+; CHECK-NEXT: movk x8, #14134, lsl #48
+; CHECK-NEXT: movk x13, #12592, lsl #48
+; CHECK-NEXT: movk x14, #14134, lsl #32
+; CHECK-NEXT: movk x14, #14648, lsl #48
+; CHECK-NEXT: eor x13, x15, x13
+; CHECK-NEXT: eor x10, x10, x8
+; CHECK-NEXT: eor x11, x11, x14
+; CHECK-NEXT: eor x8, x16, x8
+; CHECK-NEXT: orr x9, x10, x9
+; CHECK-NEXT: orr x10, x12, x13
+; CHECK-NEXT: orr x8, x11, x8
+; CHECK-NEXT: orr x9, x9, x10
+; CHECK-NEXT: orr x8, x9, x8
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, ne
; CHECK-NEXT: ret
%m = tail call i32 @memcmp(ptr %X, ptr @.str, i64 48) nounwind
@@ -1870,24 +1888,32 @@ define i32 @length63(ptr %X, ptr %Y) nounwind {
define i1 @length63_eq(ptr %x, ptr %y) nounwind {
; CHECK-LABEL: length63_eq:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x11, [x1]
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: ldp x12, x13, [x1, #16]
-; CHECK-NEXT: cmp x9, x8
-; CHECK-NEXT: ldp x8, x9, [x0, #16]
-; CHECK-NEXT: ccmp x10, x11, #0, eq
-; CHECK-NEXT: ccmp x8, x12, #0, eq
-; CHECK-NEXT: ldp x8, x11, [x0, #32]
-; CHECK-NEXT: ldp x10, x12, [x1, #32]
-; CHECK-NEXT: ccmp x9, x13, #0, eq
-; CHECK-NEXT: ldr x9, [x0, #48]
-; CHECK-NEXT: ldr x13, [x1, #48]
-; CHECK-NEXT: ccmp x8, x10, #0, eq
-; CHECK-NEXT: ldur x8, [x0, #55]
-; CHECK-NEXT: ldur x10, [x1, #55]
-; CHECK-NEXT: ccmp x11, x12, #0, eq
-; CHECK-NEXT: ccmp x9, x13, #0, eq
-; CHECK-NEXT: ccmp x8, x10, #0, eq
+; CHECK-NEXT: ldp x8, x9, [x0]
+; CHECK-NEXT: ldr x17, [x0, #48]
+; CHECK-NEXT: ldp x10, x11, [x1]
+; CHECK-NEXT: ldr x18, [x1, #48]
+; CHECK-NEXT: ldp x12, x13, [x0, #16]
+; CHECK-NEXT: ldp x14, x15, [x1, #16]
+; CHECK-NEXT: eor x8, x8, x10
+; CHECK-NEXT: eor x9, x9, x11
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: eor x10, x12, x14
+; CHECK-NEXT: ldp x11, x12, [x0, #32]
+; CHECK-NEXT: ldp x14, x16, [x1, #32]
+; CHECK-NEXT: ldur x0, [x0, #55]
+; CHECK-NEXT: ldur x1, [x1, #55]
+; CHECK-NEXT: eor x13, x13, x15
+; CHECK-NEXT: orr x9, x10, x13
+; CHECK-NEXT: eor x11, x11, x14
+; CHECK-NEXT: eor x12, x12, x16
+; CHECK-NEXT: eor x14, x17, x18
+; CHECK-NEXT: eor x15, x0, x1
+; CHECK-NEXT: orr x10, x11, x12
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x11, x14, x15
+; CHECK-NEXT: orr x9, x10, x11
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, ne
; CHECK-NEXT: ret
%call = tail call i32 @memcmp(ptr %x, ptr %y, i64 63) nounwind
@@ -2044,43 +2070,51 @@ define i1 @length63_gt(ptr %x, ptr %y) nounwind {
define i1 @length63_eq_const(ptr %X) nounwind {
; CHECK-LABEL: length63_eq_const:
; CHECK: // %bb.0:
+; CHECK-NEXT: mov x10, #14134 // =0x3736
+; CHECK-NEXT: mov x13, #13620 // =0x3534
; CHECK-NEXT: mov x8, #12592 // =0x3130
-; CHECK-NEXT: ldp x9, x10, [x0]
+; CHECK-NEXT: movk x10, #14648, lsl #16
+; CHECK-NEXT: ldp x12, x15, [x0, #16]
+; CHECK-NEXT: movk x13, #14134, lsl #16
+; CHECK-NEXT: movk x10, #12592, lsl #32
; CHECK-NEXT: movk x8, #13106, lsl #16
-; CHECK-NEXT: ldp x11, x12, [x0, #16]
+; CHECK-NEXT: movk x13, #14648, lsl #32
+; CHECK-NEXT: movk x10, #13106, lsl #48
; CHECK-NEXT: movk x8, #13620, lsl #32
+; CHECK-NEXT: movk x13, #12592, lsl #48
+; CHECK-NEXT: ldp x11, x14, [x0]
+; CHECK-NEXT: eor x10, x12, x10
+; CHECK-NEXT: eor x12, x15, x13
+; CHECK-NEXT: mov x13, #13106 // =0x3332
+; CHECK-NEXT: ldp x16, x15, [x0, #32]
; CHECK-NEXT: movk x8, #14134, lsl #48
-; CHECK-NEXT: cmp x9, x8
+; CHECK-NEXT: movk x13, #13620, lsl #16
; CHECK-NEXT: mov x9, #14648 // =0x3938
+; CHECK-NEXT: eor x11, x11, x8
+; CHECK-NEXT: movk x13, #14134, lsl #32
; CHECK-NEXT: movk x9, #12592, lsl #16
+; CHECK-NEXT: ldur x17, [x0, #55]
+; CHECK-NEXT: eor x8, x15, x8
+; CHECK-NEXT: mov x15, #13877 // =0x3635
+; CHECK-NEXT: movk x13, #14648, lsl #48
+; CHECK-NEXT: movk x15, #14391, lsl #16
; CHECK-NEXT: movk x9, #13106, lsl #32
+; CHECK-NEXT: eor x13, x16, x13
+; CHECK-NEXT: ldr x16, [x0, #48]
+; CHECK-NEXT: movk x15, #12345, lsl #32
; CHECK-NEXT: movk x9, #13620, lsl #48
-; CHECK-NEXT: ccmp x10, x9, #0, eq
-; CHECK-NEXT: mov x10, #14134 // =0x3736
-; CHECK-NEXT: movk x10, #14648, lsl #16
-; CHECK-NEXT: movk x10, #12592, lsl #32
-; CHECK-NEXT: movk x10, #13106, lsl #48
-; CHECK-NEXT: ccmp x11, x10, #0, eq
-; CHECK-NEXT: mov x10, #13620 // =0x3534
-; CHECK-NEXT: movk x10, #14134, lsl #16
-; CHECK-NEXT: ldp x11, x13, [x0, #32]
-; CHECK-NEXT: movk x10, #14648, lsl #32
-; CHECK-NEXT: movk x10, #12592, lsl #48
-; CHECK-NEXT: ccmp x12, x10, #0, eq
-; CHECK-NEXT: mov x10, #13106 // =0x3332
-; CHECK-NEXT: ldr x12, [x0, #48]
-; CHECK-NEXT: movk x10, #13620, lsl #16
-; CHECK-NEXT: movk x10, #14134, lsl #32
-; CHECK-NEXT: movk x10, #14648, lsl #48
-; CHECK-NEXT: ccmp x11, x10, #0, eq
-; CHECK-NEXT: ldur x10, [x0, #55]
-; CHECK-NEXT: ccmp x13, x8, #0, eq
-; CHECK-NEXT: mov x8, #13877 // =0x3635
-; CHECK-NEXT: movk x8, #14391, lsl #16
-; CHECK-NEXT: ccmp x12, x9, #0, eq
-; CHECK-NEXT: movk x8, #12345, lsl #32
-; CHECK-NEXT: movk x8, #12849, lsl #48
-; CHECK-NEXT: ccmp x10, x8, #0, eq
+; CHECK-NEXT: movk x15, #12849, lsl #48
+; CHECK-NEXT: eor x14, x14, x9
+; CHECK-NEXT: orr x10, x10, x12
+; CHECK-NEXT: eor x9, x16, x9
+; CHECK-NEXT: eor x15, x17, x15
+; CHECK-NEXT: orr x11, x11, x14
+; CHECK-NEXT: orr x8, x13, x8
+; CHECK-NEXT: orr x9, x9, x15
+; CHECK-NEXT: orr x10, x11, x10
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x8, x10, x8
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%m = tail call i32 @memcmp(ptr %X, ptr @.str, i64 63) nounwind
@@ -2161,22 +2195,30 @@ define i32 @length64(ptr %X, ptr %Y) nounwind {
define i1 @length64_eq(ptr %x, ptr %y) nounwind {
; CHECK-LABEL: length64_eq:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x11, [x1]
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: ldp x12, x13, [x1, #16]
-; CHECK-NEXT: cmp x9, x8
-; CHECK-NEXT: ldp x8, x9, [x0, #16]
-; CHECK-NEXT: ccmp x10, x11, #0, eq
-; CHECK-NEXT: ccmp x8, x12, #0, eq
-; CHECK-NEXT: ldp x8, x11, [x0, #32]
-; CHECK-NEXT: ldp x10, x12, [x1, #32]
-; CHECK-NEXT: ccmp x9, x13, #0, eq
-; CHECK-NEXT: ldp x9, x13, [x1, #48]
-; CHECK-NEXT: ccmp x8, x10, #0, eq
-; CHECK-NEXT: ldp x8, x10, [x0, #48]
-; CHECK-NEXT: ccmp x11, x12, #0, eq
-; CHECK-NEXT: ccmp x8, x9, #0, eq
-; CHECK-NEXT: ccmp x10, x13, #0, eq
+; CHECK-NEXT: ldp x8, x9, [x0]
+; CHECK-NEXT: ldp x10, x11, [x1]
+; CHECK-NEXT: ldp x12, x13, [x0, #16]
+; CHECK-NEXT: ldp x14, x15, [x1, #16]
+; CHECK-NEXT: eor x8, x8, x10
+; CHECK-NEXT: eor x9, x9, x11
+; CHECK-NEXT: ldp x17, x18, [x0, #48]
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: eor x10, x12, x14
+; CHECK-NEXT: ldp x11, x12, [x0, #32]
+; CHECK-NEXT: ldp x14, x16, [x1, #32]
+; CHECK-NEXT: eor x13, x13, x15
+; CHECK-NEXT: ldp x0, x1, [x1, #48]
+; CHECK-NEXT: orr x9, x10, x13
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: eor x11, x11, x14
+; CHECK-NEXT: eor x12, x12, x16
+; CHECK-NEXT: eor x14, x17, x0
+; CHECK-NEXT: eor x15, x18, x1
+; CHECK-NEXT: orr x10, x11, x12
+; CHECK-NEXT: orr x11, x14, x15
+; CHECK-NEXT: orr x9, x10, x11
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, ne
; CHECK-NEXT: ret
%call = tail call i32 @memcmp(ptr %x, ptr %y, i64 64) nounwind
@@ -2333,38 +2375,46 @@ define i1 @length64_gt(ptr %x, ptr %y) nounwind {
define i1 @length64_eq_const(ptr %X) nounwind {
; CHECK-LABEL: length64_eq_const:
; CHECK: // %bb.0:
+; CHECK-NEXT: mov x12, #13620 // =0x3534
+; CHECK-NEXT: ldp x13, x14, [x0, #16]
+; CHECK-NEXT: movk x12, #14134, lsl #16
+; CHECK-NEXT: ldp x16, x17, [x0, #32]
+; CHECK-NEXT: movk x12, #14648, lsl #32
; CHECK-NEXT: mov x8, #12592 // =0x3130
-; CHECK-NEXT: ldp x9, x10, [x0]
-; CHECK-NEXT: movk x8, #13106, lsl #16
-; CHECK-NEXT: ldp x11, x12, [x0, #16]
-; CHECK-NEXT: movk x8, #13620, lsl #32
-; CHECK-NEXT: ldp x13, x14, [x0, #32]
-; CHECK-NEXT: movk x8, #14134, lsl #48
-; CHECK-NEXT: cmp x9, x8
; CHECK-NEXT: mov x9, #14648 // =0x3938
-; CHECK-NEXT: movk x9, #12592, lsl #16
-; CHECK-NEXT: movk x9, #13106, lsl #32
-; CHECK-NEXT: movk x9, #13620, lsl #48
-; CHECK-NEXT: ccmp x10, x9, #0, eq
+; CHECK-NEXT: movk x12, #12592, lsl #48
; CHECK-NEXT: mov x10, #14134 // =0x3736
+; CHECK-NEXT: movk x8, #13106, lsl #16
+; CHECK-NEXT: eor x12, x14, x12
+; CHECK-NEXT: mov x14, #13106 // =0x3332
+; CHECK-NEXT: movk x9, #12592, lsl #16
+; CHECK-NEXT: movk x14, #13620, lsl #16
; CHECK-NEXT: movk x10, #14648, lsl #16
+; CHECK-NEXT: movk x8, #13620, lsl #32
+; CHECK-NEXT: movk x14, #14134, lsl #32
+; CHECK-NEXT: ldp x11, x15, [x0]
+; CHECK-NEXT: movk x14, #14648, lsl #48
+; CHECK-NEXT: movk x9, #13106, lsl #32
; CHECK-NEXT: movk x10, #12592, lsl #32
+; CHECK-NEXT: eor x14, x16, x14
+; CHECK-NEXT: ldp x16, x18, [x0, #48]
+; CHECK-NEXT: movk x8, #14134, lsl #48
+; CHECK-NEXT: movk x9, #13620, lsl #48
; CHECK-NEXT: movk x10, #13106, lsl #48
-; CHECK-NEXT: ccmp x11, x10, #0, eq
-; CHECK-NEXT: mov x11, #13620 // =0x3534
-; CHECK-NEXT: movk x11, #14134, lsl #16
-; CHECK-NEXT: movk x11, #14648, lsl #32
-; CHECK-NEXT: movk x11, #12592, lsl #48
-; CHECK-NEXT: ccmp x12, x11, #0, eq
-; CHECK-NEXT: mov x11, #13106 // =0x3332
-; CHECK-NEXT: movk x11, #13620, lsl #16
-; CHECK-NEXT: movk x11, #14134, lsl #32
-; CHECK-NEXT: movk x11, #14648, lsl #48
-; CHECK-NEXT: ccmp x13, x11, #0, eq
-; CHECK-NEXT: ldp x11, x12, [x0, #48]
-; CHECK-NEXT: ccmp x14, x8, #0, eq
-; CHECK-NEXT: ccmp x11, x9, #0, eq
-; CHECK-NEXT: ccmp x12, x10, #0, eq
+; CHECK-NEXT: eor x11, x11, x8
+; CHECK-NEXT: eor x8, x17, x8
+; CHECK-NEXT: eor x15, x15, x9
+; CHECK-NEXT: eor x13, x13, x10
+; CHECK-NEXT: eor x9, x16, x9
+; CHECK-NEXT: eor x10, x18, x10
+; CHECK-NEXT: orr x11, x11, x15
+; CHECK-NEXT: orr x12, x13, x12
+; CHECK-NEXT: orr x8, x14, x8
+; CHECK-NEXT: orr x9, x9, x10
+; CHECK-NEXT: orr x10, x11, x12
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: orr x8, x10, x8
+; CHECK-NEXT: cmp x8, #0
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%m = tail call i32 @memcmp(ptr %X, ptr @.str, i64 64) nounwind
diff --git a/llvm/test/CodeGen/AArch64/umulo-128-legalisation-lowering.ll b/llvm/test/CodeGen/AArch64/umulo-128-legalisation-lowering.ll
index d7348ea154b24..f9993e53c318a 100644
--- a/llvm/test/CodeGen/AArch64/umulo-128-legalisation-lowering.ll
+++ b/llvm/test/CodeGen/AArch64/umulo-128-legalisation-lowering.ll
@@ -81,9 +81,9 @@ define i128 @__muloti4(i128 %0, i128 %1, ptr nocapture nonnull writeonly align 4
; AARCH-NEXT: mov w9, wzr
; AARCH-NEXT: mul x0, x0, x2
; AARCH-NEXT: .LBB1_3: // %overflow.res
-; AARCH-NEXT: eor x10, x3, #0x8000000000000000
-; AARCH-NEXT: orr x10, x2, x10
-; AARCH-NEXT: cmp x10, #0
+; AARCH-NEXT: mov x10, #-9223372036854775808 // =0x8000000000000000
+; AARCH-NEXT: cmp x3, x10
+; AARCH-NEXT: ccmp x2, #0, #0, eq
; AARCH-NEXT: ccmp x1, #0, #0, eq
; AARCH-NEXT: b.mi .LBB1_5
; AARCH-NEXT: // %bb.4: // %Else2
>From 134e258a2db825effa2487c3304aa1cc8cb2db67 Mon Sep 17 00:00:00 2001
From: clingfei <chenglingfei at foxmail.com>
Date: Mon, 6 Jul 2026 17:44:36 +0800
Subject: [PATCH 8/8] Relax optsize
---
.../Target/AArch64/AArch64ISelLowering.cpp | 32 +++--
llvm/test/CodeGen/AArch64/bcmp.ll | 32 ++---
llvm/test/CodeGen/AArch64/memcmp.ll | 116 ++++++++----------
3 files changed, 83 insertions(+), 97 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index c8837ee5c9e5f..bf27bc94e09a2 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -12219,10 +12219,11 @@ static bool hasCheapXorImmediateThatNeedsCondCmpReg(
// Check whether the continuous comparison sequence.
static bool
-isOrXorChain(SDValue N, SelectionDAG &DAG, unsigned &Num, bool &SawXor,
+isOrXorChain(SDValue N, SelectionDAG &DAG, unsigned &NumLeaves,
+ unsigned &NumXors, bool &SawXor,
bool RequireLegalCmpImmediates,
SmallVectorImpl<std::pair<SDValue, SDValue>> &WorkList) {
- if (Num == MaxXors)
+ if (NumLeaves == MaxXors)
return false;
// Skip the one-use zext
@@ -12234,16 +12235,17 @@ isOrXorChain(SDValue N, SelectionDAG &DAG, unsigned &Num, bool &SawXor,
!hasLegalCmpImmediate(N->getOperand(0), N->getOperand(1)))
return false;
WorkList.push_back(std::make_pair(N->getOperand(0), N->getOperand(1)));
- Num++;
+ NumLeaves++;
+ NumXors++;
SawXor = true;
return true;
}
// All the non-leaf nodes must be OR.
if (N->getOpcode() == ISD::OR && N->hasOneUse())
- return isOrXorChain(N->getOperand(0), DAG, Num, SawXor,
+ return isOrXorChain(N->getOperand(0), DAG, NumLeaves, NumXors, SawXor,
RequireLegalCmpImmediates, WorkList) &&
- isOrXorChain(N->getOperand(1), DAG, Num, SawXor,
+ isOrXorChain(N->getOperand(1), DAG, NumLeaves, NumXors, SawXor,
RequireLegalCmpImmediates, WorkList);
if (N->getOpcode() == ISD::OR)
return false;
@@ -12257,7 +12259,7 @@ isOrXorChain(SDValue N, SelectionDAG &DAG, unsigned &Num, bool &SawXor,
// 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++;
+ NumLeaves++;
return true;
}
@@ -12276,6 +12278,7 @@ static SDValue performOrXorChainCombine(SDNode *N, SelectionDAG &DAG) {
ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(2))->get();
// 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 NumLeaves = 0;
unsigned NumXors = 0;
bool SawXor = false;
bool RequireLegalCmpImmediates = any_of(N->users(), [](SDNode *User) {
@@ -12284,13 +12287,20 @@ static SDValue performOrXorChainCombine(SDNode *N, SelectionDAG &DAG) {
});
if ((Cond == ISD::SETEQ || Cond == ISD::SETNE) && isNullConstant(RHS) &&
LHS->getOpcode() == ISD::OR && LHS->hasOneUse() &&
- isOrXorChain(LHS, DAG, NumXors, SawXor, RequireLegalCmpImmediates,
- WorkList) &&
+ isOrXorChain(LHS, DAG, NumLeaves, NumXors, SawXor,
+ RequireLegalCmpImmediates, WorkList) &&
SawXor) {
// A CCMP sequence serializes the comparisons through NZCV. Keep the
- // transform to short chains where the instruction-count reduction outweighs
- // the longer dependency chain.
- if (WorkList.size() > 5)
+ // default transform to short chains, but account for real XOR leaves: each
+ // one removed is a code-size and front-end win. Under size optimization,
+ // prefer the smaller CCMP form unless another guard rejects it.
+ const Function &F = DAG.getMachineFunction().getFunction();
+ unsigned Limit = 5;
+ if (NumXors >= 6)
+ Limit = 6;
+ if (F.hasOptSize() || F.hasMinSize())
+ Limit = MaxXors;
+ if (WorkList.size() > Limit)
return SDValue();
if (WorkList.size() > 2 &&
diff --git a/llvm/test/CodeGen/AArch64/bcmp.ll b/llvm/test/CodeGen/AArch64/bcmp.ll
index 3379790797f11..c6ea5a5b189b2 100644
--- a/llvm/test/CodeGen/AArch64/bcmp.ll
+++ b/llvm/test/CodeGen/AArch64/bcmp.ll
@@ -359,26 +359,20 @@ define i1 @bcmp38(ptr %a, ptr %b) {
define i1 @bcmp45(ptr %a, ptr %b) {
; CHECK-LABEL: bcmp45:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x9, [x0]
-; CHECK-NEXT: ldr x16, [x1, #32]
-; CHECK-NEXT: ldp x10, x11, [x1]
-; CHECK-NEXT: ldur x17, [x1, #37]
-; CHECK-NEXT: ldp x12, x13, [x0, #16]
-; CHECK-NEXT: ldp x14, x15, [x1, #16]
-; CHECK-NEXT: eor x8, x8, x10
-; CHECK-NEXT: eor x9, x9, x11
+; CHECK-NEXT: ldp x8, x11, [x1]
+; CHECK-NEXT: ldp x9, x10, [x0]
+; CHECK-NEXT: ldp x12, x13, [x1, #16]
+; CHECK-NEXT: cmp x9, x8
+; CHECK-NEXT: ldp x8, x9, [x0, #16]
+; CHECK-NEXT: ccmp x10, x11, #0, eq
; CHECK-NEXT: ldr x10, [x0, #32]
-; CHECK-NEXT: ldur x11, [x0, #37]
-; CHECK-NEXT: orr x8, x8, x9
-; CHECK-NEXT: eor x12, x12, x14
-; CHECK-NEXT: eor x13, x13, x15
-; CHECK-NEXT: eor x10, x10, x16
-; CHECK-NEXT: eor x11, x11, x17
-; CHECK-NEXT: orr x9, x12, x13
-; CHECK-NEXT: orr x10, x10, x11
-; CHECK-NEXT: orr x8, x8, x9
-; CHECK-NEXT: orr x8, x8, x10
-; CHECK-NEXT: cmp x8, #0
+; CHECK-NEXT: ldr x11, [x1, #32]
+; CHECK-NEXT: ccmp x8, x12, #0, eq
+; CHECK-NEXT: ldur x8, [x0, #37]
+; CHECK-NEXT: ldur x12, [x1, #37]
+; CHECK-NEXT: ccmp x9, x13, #0, eq
+; CHECK-NEXT: ccmp x10, x11, #0, eq
+; CHECK-NEXT: ccmp x8, x12, #0, eq
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%cr = call i32 @bcmp(ptr %a, ptr %b, i64 45)
diff --git a/llvm/test/CodeGen/AArch64/memcmp.ll b/llvm/test/CodeGen/AArch64/memcmp.ll
index 92ff1c024a24f..2ab944f63e045 100644
--- a/llvm/test/CodeGen/AArch64/memcmp.ll
+++ b/llvm/test/CodeGen/AArch64/memcmp.ll
@@ -1599,24 +1599,18 @@ define i32 @length48(ptr %X, ptr %Y) nounwind {
define i1 @length48_eq(ptr %x, ptr %y) nounwind {
; CHECK-LABEL: length48_eq:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x9, [x0]
-; CHECK-NEXT: ldp x10, x11, [x1]
-; CHECK-NEXT: ldp x12, x13, [x0, #16]
-; CHECK-NEXT: ldp x14, x15, [x1, #16]
-; CHECK-NEXT: eor x8, x8, x10
-; CHECK-NEXT: ldp x16, x17, [x1, #32]
-; CHECK-NEXT: eor x9, x9, x11
-; CHECK-NEXT: ldp x10, x11, [x0, #32]
-; CHECK-NEXT: eor x12, x12, x14
-; CHECK-NEXT: eor x13, x13, x15
-; CHECK-NEXT: orr x8, x8, x9
-; CHECK-NEXT: orr x9, x12, x13
-; CHECK-NEXT: eor x10, x10, x16
-; CHECK-NEXT: eor x11, x11, x17
-; CHECK-NEXT: orr x8, x8, x9
-; CHECK-NEXT: orr x10, x10, x11
-; CHECK-NEXT: orr x8, x8, x10
-; CHECK-NEXT: cmp x8, #0
+; CHECK-NEXT: ldp x8, x11, [x1]
+; CHECK-NEXT: ldp x9, x10, [x0]
+; CHECK-NEXT: ldp x12, x13, [x1, #16]
+; CHECK-NEXT: cmp x9, x8
+; CHECK-NEXT: ldp x8, x9, [x0, #16]
+; CHECK-NEXT: ccmp x10, x11, #0, eq
+; CHECK-NEXT: ccmp x8, x12, #0, eq
+; CHECK-NEXT: ldp x8, x11, [x0, #32]
+; CHECK-NEXT: ldp x10, x12, [x1, #32]
+; CHECK-NEXT: ccmp x9, x13, #0, eq
+; CHECK-NEXT: ccmp x8, x10, #0, eq
+; CHECK-NEXT: ccmp x11, x12, #0, eq
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%call = tail call i32 @memcmp(ptr %x, ptr %y, i64 48) nounwind
@@ -1745,24 +1739,18 @@ define i1 @length48_gt(ptr %x, ptr %y) nounwind {
define i1 @length48_eq_prefer128(ptr %x, ptr %y) nounwind "prefer-vector-width"="128" {
; CHECK-LABEL: length48_eq_prefer128:
; CHECK: // %bb.0:
-; CHECK-NEXT: ldp x8, x9, [x0]
-; CHECK-NEXT: ldp x10, x11, [x1]
-; CHECK-NEXT: ldp x12, x13, [x0, #16]
-; CHECK-NEXT: ldp x14, x15, [x1, #16]
-; CHECK-NEXT: eor x8, x8, x10
-; CHECK-NEXT: ldp x16, x17, [x1, #32]
-; CHECK-NEXT: eor x9, x9, x11
-; CHECK-NEXT: ldp x10, x11, [x0, #32]
-; CHECK-NEXT: eor x12, x12, x14
-; CHECK-NEXT: eor x13, x13, x15
-; CHECK-NEXT: orr x8, x8, x9
-; CHECK-NEXT: orr x9, x12, x13
-; CHECK-NEXT: eor x10, x10, x16
-; CHECK-NEXT: eor x11, x11, x17
-; CHECK-NEXT: orr x8, x8, x9
-; CHECK-NEXT: orr x10, x10, x11
-; CHECK-NEXT: orr x8, x8, x10
-; CHECK-NEXT: cmp x8, #0
+; CHECK-NEXT: ldp x8, x11, [x1]
+; CHECK-NEXT: ldp x9, x10, [x0]
+; CHECK-NEXT: ldp x12, x13, [x1, #16]
+; CHECK-NEXT: cmp x9, x8
+; CHECK-NEXT: ldp x8, x9, [x0, #16]
+; CHECK-NEXT: ccmp x10, x11, #0, eq
+; CHECK-NEXT: ccmp x8, x12, #0, eq
+; CHECK-NEXT: ldp x8, x11, [x0, #32]
+; CHECK-NEXT: ldp x10, x12, [x1, #32]
+; CHECK-NEXT: ccmp x9, x13, #0, eq
+; CHECK-NEXT: ccmp x8, x10, #0, eq
+; CHECK-NEXT: ccmp x11, x12, #0, eq
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%call = tail call i32 @memcmp(ptr %x, ptr %y, i64 48) nounwind
@@ -1773,41 +1761,35 @@ define i1 @length48_eq_prefer128(ptr %x, ptr %y) nounwind "prefer-vector-width"=
define i1 @length48_eq_const(ptr %X) nounwind {
; CHECK-LABEL: length48_eq_const:
; CHECK: // %bb.0:
-; CHECK-NEXT: mov x12, #14134 // =0x3736
-; CHECK-NEXT: mov x9, #14648 // =0x3938
; CHECK-NEXT: mov x8, #12592 // =0x3130
-; CHECK-NEXT: movk x12, #14648, lsl #16
-; CHECK-NEXT: ldp x14, x15, [x0, #16]
-; CHECK-NEXT: movk x9, #12592, lsl #16
-; CHECK-NEXT: ldp x10, x11, [x0]
-; CHECK-NEXT: movk x12, #12592, lsl #32
-; CHECK-NEXT: movk x9, #13106, lsl #32
-; CHECK-NEXT: mov x13, #13620 // =0x3534
-; CHECK-NEXT: movk x12, #13106, lsl #48
+; CHECK-NEXT: ldp x9, x10, [x0]
; CHECK-NEXT: movk x8, #13106, lsl #16
-; CHECK-NEXT: movk x9, #13620, lsl #48
-; CHECK-NEXT: movk x13, #14134, lsl #16
-; CHECK-NEXT: eor x12, x14, x12
-; CHECK-NEXT: mov x14, #13106 // =0x3332
+; CHECK-NEXT: ldp x11, x12, [x0, #16]
; CHECK-NEXT: movk x8, #13620, lsl #32
-; CHECK-NEXT: movk x13, #14648, lsl #32
-; CHECK-NEXT: eor x9, x11, x9
-; CHECK-NEXT: ldp x11, x16, [x0, #32]
-; CHECK-NEXT: movk x14, #13620, lsl #16
; CHECK-NEXT: movk x8, #14134, lsl #48
-; CHECK-NEXT: movk x13, #12592, lsl #48
-; CHECK-NEXT: movk x14, #14134, lsl #32
-; CHECK-NEXT: movk x14, #14648, lsl #48
-; CHECK-NEXT: eor x13, x15, x13
-; CHECK-NEXT: eor x10, x10, x8
-; CHECK-NEXT: eor x11, x11, x14
-; CHECK-NEXT: eor x8, x16, x8
-; CHECK-NEXT: orr x9, x10, x9
-; CHECK-NEXT: orr x10, x12, x13
-; CHECK-NEXT: orr x8, x11, x8
-; CHECK-NEXT: orr x9, x9, x10
-; CHECK-NEXT: orr x8, x9, x8
-; CHECK-NEXT: cmp x8, #0
+; CHECK-NEXT: cmp x9, x8
+; CHECK-NEXT: mov x9, #14648 // =0x3938
+; CHECK-NEXT: movk x9, #12592, lsl #16
+; CHECK-NEXT: movk x9, #13106, lsl #32
+; CHECK-NEXT: movk x9, #13620, lsl #48
+; CHECK-NEXT: ccmp x10, x9, #0, eq
+; CHECK-NEXT: mov x9, #14134 // =0x3736
+; CHECK-NEXT: movk x9, #14648, lsl #16
+; CHECK-NEXT: movk x9, #12592, lsl #32
+; CHECK-NEXT: movk x9, #13106, lsl #48
+; CHECK-NEXT: ccmp x11, x9, #0, eq
+; CHECK-NEXT: mov x9, #13620 // =0x3534
+; CHECK-NEXT: movk x9, #14134, lsl #16
+; CHECK-NEXT: ldp x10, x11, [x0, #32]
+; CHECK-NEXT: movk x9, #14648, lsl #32
+; CHECK-NEXT: movk x9, #12592, lsl #48
+; CHECK-NEXT: ccmp x12, x9, #0, eq
+; CHECK-NEXT: mov x9, #13106 // =0x3332
+; CHECK-NEXT: movk x9, #13620, lsl #16
+; CHECK-NEXT: movk x9, #14134, lsl #32
+; CHECK-NEXT: movk x9, #14648, lsl #48
+; CHECK-NEXT: ccmp x10, x9, #0, eq
+; CHECK-NEXT: ccmp x11, x8, #0, eq
; CHECK-NEXT: cset w0, ne
; CHECK-NEXT: ret
%m = tail call i32 @memcmp(ptr %X, ptr @.str, i64 48) nounwind
More information about the llvm-commits
mailing list