[llvm] [AArch64] optimize lowering for icmp on i128 when RHS is an immediate (PR #181822)
Cheng Lingfei via llvm-commits
llvm-commits at lists.llvm.org
Thu Jun 11 06:50:07 PDT 2026
https://github.com/clingfei updated https://github.com/llvm/llvm-project/pull/181822
>From 3caeb6658972cbd04ebd67842908df102bc3aebc Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Tue, 17 Feb 2026 21:22:02 +0800
Subject: [PATCH 1/4] [AArch64] optimize lowering for icmp on i128 when RHS is
an immediate
---
.../Target/AArch64/AArch64ISelLowering.cpp | 118 ++++++++++++++++++
.../CodeGen/AArch64/i128-imm-compare-ccmp.ll | 61 +++++++++
llvm/test/CodeGen/AArch64/isinf.ll | 10 +-
3 files changed, 184 insertions(+), 5 deletions(-)
create mode 100644 llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index 66c22db0491d1..0880e5ce210f5 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27145,6 +27145,121 @@ performVecReduceBitwiseCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
return SDValue();
}
+static bool splitI128ValueToI64Halves(SDValue V, SelectionDAG &DAG,
+ const SDLoc &DL, SDValue &Lo,
+ SDValue &Hi) {
+ EVT VT = V.getValueType();
+ if (!VT.isInteger() || VT.getFixedSizeInBits() != 128)
+ return false;
+ Lo = DAG.getNode(ISD::TRUNCATE, DL, MVT::i64, V);
+ Hi = DAG.getNode(
+ ISD::TRUNCATE, DL, MVT::i64,
+ DAG.getNode(ISD::SRL, DL, VT, V, DAG.getShiftAmountConstant(64, VT, DL)));
+ return true;
+}
+
+static SDValue
+performExpandedI128CmpCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
+ SelectionDAG &DAG, ISD::CondCode CCCode) {
+ SDValue NewLHS = N->getOperand(0);
+ SDValue NewRHS = N->getOperand(1);
+ if (N->hasOneUse()) {
+ auto User = N->user_begin();
+ if ((User)->getOpcode() == ISD::BRCOND)
+ return SDValue();
+ }
+ // Keep the dedicated shift+cmp-zero combine opportunities for wide integers.
+ // Canonicalizing these into split compares here tends to introduce an extra
+ // EXTR on AArch64 (see icmp-shift-opt.ll).
+ if ((CCCode == ISD::SETEQ || CCCode == ISD::SETNE) &&
+ isNullConstant(NewRHS) &&
+ (NewLHS.getOpcode() == ISD::SRL || NewLHS.getOpcode() == ISD::SHL))
+ return SDValue();
+
+ if (NewLHS.getNumOperands() != 2)
+ return SDValue();
+ if (NewLHS.getValueType().isScalableVT() ||
+ NewRHS.getValueType().isScalableVT() || NewLHS.getNumOperands() != 2 ||
+ !(NewLHS.getValueType().isInteger() &&
+ NewLHS.getValueSizeInBits() == 128) ||
+ !(NewRHS.getValueType().isInteger() &&
+ NewRHS.getValueSizeInBits() == 128))
+ return SDValue();
+ ConstantSDNode *ConstLHS = dyn_cast<ConstantSDNode>(NewLHS.getNode());
+ ConstantSDNode *ConstRHS = dyn_cast<ConstantSDNode>(NewRHS.getNode());
+ if (ConstLHS || !ConstRHS)
+ return SDValue();
+
+ SDValue LHSLo = NewLHS.getOperand(0);
+ SDValue LHSHi = NewLHS.getOperand(1);
+ SDValue RHSLo =
+ DAG.getConstant(ConstRHS->getAPIntValue().trunc(64), SDLoc(N), MVT::i64);
+ SDValue RHSHi = DAG.getConstant(ConstRHS->getAPIntValue().lshr(64).trunc(64),
+ SDLoc(N), MVT::i64);
+ if (!splitI128ValueToI64Halves(NewLHS, DAG, SDLoc(N), LHSLo, LHSHi)) {
+ return SDValue();
+ }
+
+ if (CCCode == ISD::SETEQ || CCCode == ISD::SETNE) {
+ SDValue LoCmpF =
+ DAG.FoldSetCC(N->getValueType(0), LHSLo, RHSLo, CCCode, SDLoc(N));
+ SDValue HiCmpF =
+ DAG.FoldSetCC(N->getValueType(0), LHSHi, RHSHi, CCCode, SDLoc(N));
+ auto IsConstBool = [](SDValue V) {
+ return isa_and_nonnull<ConstantSDNode>(V.getNode());
+ };
+ if (IsConstBool(LoCmpF) || IsConstBool(HiCmpF))
+ return SDValue();
+
+ SDValue LoCmp =
+ DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
+ SDValue HiCmp =
+ DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, CCCode);
+ unsigned Opcode = (CCCode == ISD::SETEQ) ? ISD::AND : ISD::OR;
+ return DAG.getNode(Opcode, SDLoc(N), LoCmp.getValueType(), LoCmp, HiCmp);
+ }
+
+ if (CCCode == ISD::SETUGT || CCCode == ISD::SETUGE) {
+ // x > K <=> (xhi > khi) || (xhi==khi && xlo > klo)
+ // x >= K <=> (xhi > khi) || (xhi==khi && xlo >= klo)
+ SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
+
+ ISD::CondCode LoCC = (CCCode == ISD::SETUGT) ? ISD::SETULE : ISD::SETULT;
+
+ SDValue HiEq =
+ DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, ZeroHi, ISD::SETEQ);
+ SDValue LoCmp =
+ DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, LoCC);
+ SDValue Tree =
+ DAG.getNode(ISD::AND, SDLoc(N), N->getValueType(0), HiEq, LoCmp);
+
+ SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
+ return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETEQ);
+ }
+
+ if (CCCode == ISD::SETULT || CCCode == ISD::SETULE) {
+ // x < K <=> (xhi < khi) || (xhi==khi && xlo < klo)
+ // x <= K <=> (xhi < khi) || (xhi==khi && xlo <= klo)
+ ISD::CondCode Opcode = ISD::SETULT;
+
+ SDValue HiCmp =
+ DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, Opcode);
+ SDValue HiEq =
+ DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, ISD::SETEQ);
+ SDValue LoCmp =
+ DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
+
+ SDValue LoAnd =
+ DAG.getNode(ISD::AND, SDLoc(N), HiEq.getValueType(), HiEq, LoCmp);
+ SDValue Tree =
+ DAG.getNode(ISD::OR, SDLoc(N), N->getValueType(0), HiCmp, LoAnd);
+
+ SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
+ return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETNE);
+ }
+ return SDValue();
+}
+
static SDValue performSETCCCombine(SDNode *N,
TargetLowering::DAGCombinerInfo &DCI,
SelectionDAG &DAG) {
@@ -27225,6 +27340,9 @@ static SDValue performSETCCCombine(SDNode *N,
SplatLHSVal.isOne())
return DAG.getSetCC(DL, VT, DAG.getConstant(0, DL, CmpVT), RHS, ISD::SETGE);
+ if (SDValue V = performExpandedI128CmpCombine(N, DCI, DAG, Cond))
+ return V;
+
return SDValue();
}
diff --git a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
new file mode 100644
index 0000000000000..7422cbbfd19a0
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
@@ -0,0 +1,61 @@
+; RUN: llc -mtriple=aarch64-linux-gnu -O3 -o - < %s | FileCheck %s
+
+define i1 @eq_imm(i128 %x) {
+; CHECK-LABEL: eq_imm:
+; CHECK: cmp x0, #5
+; CHECK-NEXT: ccmp x1, #0, #0, eq
+; CHECK-NEXT: cset w0, eq
+; CHECK-NEXT: ret
+ %cmp = icmp eq i128 %x, 5
+ ret i1 %cmp
+}
+
+define i1 @ne_imm(i128 %x) {
+; CHECK-LABEL: ne_imm:
+; CHECK: cmp x0, #5
+; CHECK-NEXT: ccmp x1, #0, #0, eq
+; CHECK-NEXT: cset w0, ne
+; CHECK-NEXT: ret
+ %cmp = icmp ne i128 %x, 5
+ ret i1 %cmp
+}
+
+define i1 @ult_imm(i128 %x) {
+; CHECK-LABEL: ult_imm:
+; CHECK: cmp x1, #0
+; CHECK-NEXT: ccmp x0, #5, #2, eq
+; CHECK-NEXT: cset w0, {{cc|lo}}
+; CHECK-NEXT: ret
+ %cmp = icmp ult i128 %x, 5
+ ret i1 %cmp
+}
+
+define i1 @ule_imm(i128 %x) {
+; CHECK-LABEL: ule_imm:
+; CHECK: cmp x1, #0
+; CHECK-NEXT: ccmp x0, #6, #2, eq
+; CHECK-NEXT: cset w0, {{cc|lo}}
+; CHECK-NEXT: ret
+ %cmp = icmp ule i128 %x, 5
+ ret i1 %cmp
+}
+
+define i1 @ugt_imm(i128 %x) {
+; CHECK-LABEL: ugt_imm:
+; CHECK: cmp x1, #0
+; CHECK-NEXT: ccmp x0, #5, #2, eq
+; CHECK-NEXT: cset w0, hi
+; CHECK-NEXT: ret
+ %cmp = icmp ugt i128 %x, 5
+ ret i1 %cmp
+}
+
+define i1 @uge_imm(i128 %x) {
+; CHECK-LABEL: uge_imm:
+; CHECK: cmp x1, #0
+; CHECK-NEXT: ccmp x0, #4, #2, eq
+; CHECK-NEXT: cset w0, hi
+; CHECK-NEXT: ret
+ %cmp = icmp uge i128 %x, 5
+ ret i1 %cmp
+}
\ No newline at end of file
diff --git a/llvm/test/CodeGen/AArch64/isinf.ll b/llvm/test/CodeGen/AArch64/isinf.ll
index e8bbaf96395f0..59e1ab44f16b4 100644
--- a/llvm/test/CodeGen/AArch64/isinf.ll
+++ b/llvm/test/CodeGen/AArch64/isinf.ll
@@ -58,11 +58,11 @@ define i32 @replace_isinf_call_f128(fp128 %x) {
; CHECK: // %bb.0:
; CHECK-NEXT: str q0, [sp, #-16]!
; CHECK-NEXT: .cfi_def_cfa_offset 16
-; CHECK-NEXT: ldp x9, x8, [sp], #16
-; CHECK-NEXT: and x8, x8, #0x7fffffffffffffff
-; CHECK-NEXT: eor x8, x8, #0x7fff000000000000
-; CHECK-NEXT: orr x8, x9, x8
-; CHECK-NEXT: cmp x8, #0
+; CHECK-NEXT: mov x8, #-562949953421312
+; CHECK-NEXT: ldp x10, x9, [sp], #16
+; CHECK-NEXT: lsl x9, x9, #1
+; CHECK-NEXT: cmp x10, #0
+; CHECK-NEXT: ccmp x8, x9, #0, eq
; CHECK-NEXT: cset w0, eq
; CHECK-NEXT: ret
%abs = tail call fp128 @llvm.fabs.f128(fp128 %x)
>From bf60f9f64421d695b97c73be30d5c71bfbf0d5e4 Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Fri, 10 Apr 2026 23:30:38 +0800
Subject: [PATCH 2/4] fix
---
llvm/lib/Target/AArch64/AArch64ISelLowering.cpp | 8 +++++++-
llvm/test/CodeGen/AArch64/isinf.ll | 14 +++++++-------
2 files changed, 14 insertions(+), 8 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index c9f6e0d651251..305d22859d51d 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -27769,8 +27769,14 @@ performExpandedI128CmpCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
if (CCCode == ISD::SETUGT || CCCode == ISD::SETUGE) {
// x > K <=> (xhi > khi) || (xhi==khi && xlo > klo)
// x >= K <=> (xhi > khi) || (xhi==khi && xlo >= klo)
- SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
+ // This specialized form relies on the immediate living entirely in the low
+ // 64 bits. When the high half is non-zero, reducing the compare to a
+ // high==0 test is invalid and can miscompile widened expressions such as
+ // (shl i128 X, 1) > C.
+ if (!isNullConstant(RHSHi))
+ return SDValue();
+ SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
ISD::CondCode LoCC = (CCCode == ISD::SETUGT) ? ISD::SETULE : ISD::SETULT;
SDValue HiEq =
diff --git a/llvm/test/CodeGen/AArch64/isinf.ll b/llvm/test/CodeGen/AArch64/isinf.ll
index f3283d2cf7ec2..92d53d7086e46 100644
--- a/llvm/test/CodeGen/AArch64/isinf.ll
+++ b/llvm/test/CodeGen/AArch64/isinf.ll
@@ -80,14 +80,14 @@ define i32 @replace_isinf_call_f64(double %x) {
define i32 @replace_isinf_call_f128(fp128 %x) {
; CHECK-SD-LABEL: replace_isinf_call_f128:
; CHECK-SD: // %bb.0:
-; CHECK-SD-NEXT: str q0, [sp, #-16]!
+; CHECK-SD-NEXT: str q0, [sp, #-16]!
; CHECK-SD-NEXT: .cfi_def_cfa_offset 16
-; CHECK-SD-NEXT: ldp x9, x8, [sp], #16
-; CHECK-SD-NEXT: and x8, x8, #0x7fffffffffffffff
-; CHECK-SD-NEXT: eor x8, x8, #0x7fff000000000000
-; CHECK-SD-NEXT: orr x8, x9, x8
-; CHECK-SD-NEXT: cmp x8, #0
-; CHECK-SD-NEXT: cset w0, eq
+; CHECK-SD-NEXT: mov x8, #-562949953421312 // =0xfffe000000000000
+; CHECK-SD-NEXT: ldp x10, x9, [sp], #16
+; CHECK-SD-NEXT: lsl x9, x9, #1
+; CHECK-SD-NEXT: cmp x10, #0
+; CHECK-SD-NEXT: ccmp x8, x9, #0, eq
+; CHECK-SD-NEXT: cset w0, eq
; CHECK-SD-NEXT: ret
;
; CHECK-GI-LABEL: replace_isinf_call_f128:
>From 25d8e9f018ae8558682bbbc1ad9bbe588c5a1a1e Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Thu, 7 May 2026 23:45:29 +0800
Subject: [PATCH 3/4] Optimize at later stage
---
.../Target/AArch64/AArch64ISelLowering.cpp | 266 +++++++++---------
.../CodeGen/AArch64/i128-imm-compare-ccmp.ll | 116 +++++++-
2 files changed, 242 insertions(+), 140 deletions(-)
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index fb95b6e2cb67c..071149ef46585 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1155,6 +1155,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
// Try and combine setcc with csel
setTargetDAGCombine(ISD::SETCC);
+ setTargetDAGCombine(ISD::SETCCCARRY);
setTargetDAGCombine(ISD::INTRINSIC_WO_CHAIN);
@@ -11863,9 +11864,62 @@ SDValue AArch64TargetLowering::LowerBitreverse(SDValue Op,
}
// Check whether the continuous comparison sequence.
+static bool hasLegalCmpImmediateOperand(SDValue V) {
+ ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
+ return !C || isLegalCmpImmed(C->getAPIntValue());
+}
+
+static bool hasLegalCmpImmediate(SDValue LHS, SDValue RHS) {
+ return hasLegalCmpImmediateOperand(LHS) && hasLegalCmpImmediateOperand(RHS);
+}
+
+static bool isLegalCondCmpImmediate(const APInt &Imm) {
+ if (Imm.isNegative())
+ return Imm.sgt(-32);
+ return Imm.getLimitedValue(32) <= 31;
+}
+
+static bool hasLegalCondCmpImmediateOperand(SDValue V) {
+ ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
+ return !C || isLegalCondCmpImmediate(C->getAPIntValue());
+}
+
+static bool hasLegalCondCmpImmediate(SDValue LHS, SDValue RHS) {
+ return hasLegalCondCmpImmediateOperand(LHS) &&
+ hasLegalCondCmpImmediateOperand(RHS);
+}
+
+static bool isLegalXorImmediate(const APInt &Imm) {
+ unsigned BitWidth = Imm.getBitWidth() <= 32 ? 32 : 64;
+ if (Imm.getBitWidth() > BitWidth)
+ return false;
+ return AArch64_AM::isLogicalImmediate(Imm.getZExtValue(), BitWidth);
+}
+
+static bool hasCheapXorImmediateThatNeedsCondCmpRegOperand(SDValue V) {
+ ConstantSDNode *C = dyn_cast<ConstantSDNode>(V);
+ if (!C)
+ return false;
+
+ const APInt &Imm = C->getAPIntValue();
+ return !isLegalCondCmpImmediate(Imm) && isLegalXorImmediate(Imm);
+}
+
+static bool hasCheapXorImmediateThatNeedsCondCmpReg(
+ const std::pair<SDValue, SDValue> &Pair) {
+ return hasCheapXorImmediateThatNeedsCondCmpRegOperand(Pair.first) ||
+ hasCheapXorImmediateThatNeedsCondCmpRegOperand(Pair.second);
+}
+
+static bool preferAsFirstCmp(const std::pair<SDValue, SDValue> &Pair) {
+ return hasLegalCmpImmediate(Pair.first, Pair.second) &&
+ !hasLegalCondCmpImmediate(Pair.first, Pair.second);
+}
+
static bool
-isOrXorChain(SDValue N, unsigned &Num,
- SmallVector<std::pair<SDValue, SDValue>, 16> &WorkList) {
+isOrXorChain(SDValue N, SelectionDAG &DAG, unsigned &Num, bool &SawXor,
+ bool RequireLegalCmpImmediates,
+ SmallVectorImpl<std::pair<SDValue, SDValue>> &WorkList) {
if (Num == MaxXors)
return false;
@@ -11873,21 +11927,36 @@ isOrXorChain(SDValue N, unsigned &Num,
if (N->getOpcode() == ISD::ZERO_EXTEND && N->hasOneUse())
N = N->getOperand(0);
- // The leaf node must be XOR
if (N->getOpcode() == ISD::XOR) {
+ if (RequireLegalCmpImmediates &&
+ !hasLegalCmpImmediate(N->getOperand(0), N->getOperand(1)))
+ return false;
WorkList.push_back(std::make_pair(N->getOperand(0), N->getOperand(1)));
Num++;
+ SawXor = true;
return true;
}
// All the non-leaf nodes must be OR.
- if (N->getOpcode() != ISD::OR || !N->hasOneUse())
+ if (N->getOpcode() == ISD::OR && N->hasOneUse())
+ return isOrXorChain(N->getOperand(0), DAG, Num, SawXor,
+ RequireLegalCmpImmediates, WorkList) &&
+ isOrXorChain(N->getOperand(1), DAG, Num, SawXor,
+ RequireLegalCmpImmediates, WorkList);
+ if (N->getOpcode() == ISD::OR)
return false;
- if (isOrXorChain(N->getOperand(0), Num, WorkList) &&
- isOrXorChain(N->getOperand(1), Num, WorkList))
- return true;
- return false;
+ EVT VT = N.getValueType();
+ if (!VT.isScalarInteger())
+ return false;
+
+ // A xor with zero may have been folded away before this combine sees it.
+ // Treat such leaves as comparisons with zero so the original OR/XOR form and
+ // type-legalized wide integer equality compares converge to the same SETCC
+ // tree.
+ WorkList.push_back(std::make_pair(N, DAG.getConstant(0, SDLoc(N), VT)));
+ Num++;
+ return true;
}
// Transform chains of ORs and XORs, which usually outlined by memcmp/bmp.
@@ -11906,9 +11975,26 @@ static SDValue performOrXorChainCombine(SDNode *N, SelectionDAG &DAG) {
// Try to express conjunction "cmp 0 (or (xor A0 A1) (xor B0 B1))" as:
// sub A0, A1; ccmp B0, B1, 0, eq; cmp inv(Cond) flag
unsigned NumXors = 0;
+ bool SawXor = false;
+ bool RequireLegalCmpImmediates =
+ any_of(N->users(), [](SDNode *User) {
+ return User->getOpcode() == ISD::BRCOND ||
+ User->getOpcode() == AArch64ISD::BRCOND;
+ });
if ((Cond == ISD::SETEQ || Cond == ISD::SETNE) && isNullConstant(RHS) &&
LHS->getOpcode() == ISD::OR && LHS->hasOneUse() &&
- isOrXorChain(LHS, NumXors, WorkList)) {
+ isOrXorChain(LHS, DAG, NumXors, SawXor, RequireLegalCmpImmediates,
+ WorkList) &&
+ SawXor) {
+ if (WorkList.size() > 2 &&
+ any_of(WorkList, hasCheapXorImmediateThatNeedsCondCmpReg))
+ return SDValue();
+
+ SmallVector<std::pair<SDValue, SDValue>, 16>::iterator First =
+ find_if(WorkList, preferAsFirstCmp);
+ if (First != WorkList.end())
+ std::iter_swap(WorkList.begin(), First);
+
SDValue XOR0, XOR1;
std::tie(XOR0, XOR1) = WorkList[0];
unsigned LogicOp = (Cond == ISD::SETEQ) ? ISD::AND : ISD::OR;
@@ -27384,127 +27470,6 @@ performVecReduceBitwiseCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
return SDValue();
}
-static bool splitI128ValueToI64Halves(SDValue V, SelectionDAG &DAG,
- const SDLoc &DL, SDValue &Lo,
- SDValue &Hi) {
- EVT VT = V.getValueType();
- if (!VT.isInteger() || VT.getFixedSizeInBits() != 128)
- return false;
- Lo = DAG.getNode(ISD::TRUNCATE, DL, MVT::i64, V);
- Hi = DAG.getNode(
- ISD::TRUNCATE, DL, MVT::i64,
- DAG.getNode(ISD::SRL, DL, VT, V, DAG.getShiftAmountConstant(64, VT, DL)));
- return true;
-}
-
-static SDValue
-performExpandedI128CmpCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
- SelectionDAG &DAG, ISD::CondCode CCCode) {
- SDValue NewLHS = N->getOperand(0);
- SDValue NewRHS = N->getOperand(1);
- if (N->hasOneUse()) {
- auto User = N->user_begin();
- if ((User)->getOpcode() == ISD::BRCOND)
- return SDValue();
- }
- // Keep the dedicated shift+cmp-zero combine opportunities for wide integers.
- // Canonicalizing these into split compares here tends to introduce an extra
- // EXTR on AArch64 (see icmp-shift-opt.ll).
- if ((CCCode == ISD::SETEQ || CCCode == ISD::SETNE) &&
- isNullConstant(NewRHS) &&
- (NewLHS.getOpcode() == ISD::SRL || NewLHS.getOpcode() == ISD::SHL))
- return SDValue();
-
- if (NewLHS.getNumOperands() != 2)
- return SDValue();
- if (NewLHS.getValueType().isScalableVT() ||
- NewRHS.getValueType().isScalableVT() || NewLHS.getNumOperands() != 2 ||
- !(NewLHS.getValueType().isInteger() &&
- NewLHS.getValueSizeInBits() == 128) ||
- !(NewRHS.getValueType().isInteger() &&
- NewRHS.getValueSizeInBits() == 128))
- return SDValue();
- ConstantSDNode *ConstLHS = dyn_cast<ConstantSDNode>(NewLHS.getNode());
- ConstantSDNode *ConstRHS = dyn_cast<ConstantSDNode>(NewRHS.getNode());
- if (ConstLHS || !ConstRHS)
- return SDValue();
-
- SDValue LHSLo = NewLHS.getOperand(0);
- SDValue LHSHi = NewLHS.getOperand(1);
- SDValue RHSLo =
- DAG.getConstant(ConstRHS->getAPIntValue().trunc(64), SDLoc(N), MVT::i64);
- SDValue RHSHi = DAG.getConstant(ConstRHS->getAPIntValue().lshr(64).trunc(64),
- SDLoc(N), MVT::i64);
- if (!splitI128ValueToI64Halves(NewLHS, DAG, SDLoc(N), LHSLo, LHSHi)) {
- return SDValue();
- }
-
- if (CCCode == ISD::SETEQ || CCCode == ISD::SETNE) {
- SDValue LoCmpF =
- DAG.FoldSetCC(N->getValueType(0), LHSLo, RHSLo, CCCode, SDLoc(N));
- SDValue HiCmpF =
- DAG.FoldSetCC(N->getValueType(0), LHSHi, RHSHi, CCCode, SDLoc(N));
- auto IsConstBool = [](SDValue V) {
- return isa_and_nonnull<ConstantSDNode>(V.getNode());
- };
- if (IsConstBool(LoCmpF) || IsConstBool(HiCmpF))
- return SDValue();
-
- SDValue LoCmp =
- DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
- SDValue HiCmp =
- DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, CCCode);
- unsigned Opcode = (CCCode == ISD::SETEQ) ? ISD::AND : ISD::OR;
- return DAG.getNode(Opcode, SDLoc(N), LoCmp.getValueType(), LoCmp, HiCmp);
- }
-
- if (CCCode == ISD::SETUGT || CCCode == ISD::SETUGE) {
- // x > K <=> (xhi > khi) || (xhi==khi && xlo > klo)
- // x >= K <=> (xhi > khi) || (xhi==khi && xlo >= klo)
- // This specialized form relies on the immediate living entirely in the low
- // 64 bits. When the high half is non-zero, reducing the compare to a
- // high==0 test is invalid and can miscompile widened expressions such as
- // (shl i128 X, 1) > C.
- if (!isNullConstant(RHSHi))
- return SDValue();
-
- SDValue ZeroHi = DAG.getConstant(0, SDLoc(N), LHSHi.getValueType());
- ISD::CondCode LoCC = (CCCode == ISD::SETUGT) ? ISD::SETULE : ISD::SETULT;
-
- SDValue HiEq =
- DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, ZeroHi, ISD::SETEQ);
- SDValue LoCmp =
- DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, LoCC);
- SDValue Tree =
- DAG.getNode(ISD::AND, SDLoc(N), N->getValueType(0), HiEq, LoCmp);
-
- SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
- return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETEQ);
- }
-
- if (CCCode == ISD::SETULT || CCCode == ISD::SETULE) {
- // x < K <=> (xhi < khi) || (xhi==khi && xlo < klo)
- // x <= K <=> (xhi < khi) || (xhi==khi && xlo <= klo)
- ISD::CondCode Opcode = ISD::SETULT;
-
- SDValue HiCmp =
- DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, Opcode);
- SDValue HiEq =
- DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSHi, RHSHi, ISD::SETEQ);
- SDValue LoCmp =
- DAG.getSetCC(SDLoc(N), N->getValueType(0), LHSLo, RHSLo, CCCode);
-
- SDValue LoAnd =
- DAG.getNode(ISD::AND, SDLoc(N), HiEq.getValueType(), HiEq, LoCmp);
- SDValue Tree =
- DAG.getNode(ISD::OR, SDLoc(N), N->getValueType(0), HiCmp, LoAnd);
-
- SDValue Zero = DAG.getConstant(0, SDLoc(N), N->getValueType(0));
- return DAG.getSetCC(SDLoc(N), N->getValueType(0), Tree, Zero, ISD::SETNE);
- }
- return SDValue();
-}
-
static SDValue performSETCCCombine(SDNode *N,
TargetLowering::DAGCombinerInfo &DCI,
SelectionDAG &DAG) {
@@ -27585,12 +27550,45 @@ static SDValue performSETCCCombine(SDNode *N,
SplatLHSVal.isOne())
return DAG.getSetCC(DL, VT, DAG.getConstant(0, DL, CmpVT), RHS, ISD::SETGE);
- if (SDValue V = performExpandedI128CmpCombine(N, DCI, DAG, Cond))
- return V;
-
return SDValue();
}
+static SDValue performSETCCCARRYCombine(SDNode *N, SelectionDAG &DAG) {
+ assert(N->getOpcode() == ISD::SETCCCARRY && "Unexpected opcode!");
+
+ // Rebuild narrow high/low compares from type-legalized wide unsigned compares
+ // so the existing CCMP conjunction/disjunction lowering can handle them.
+ SDValue HiLHS = N->getOperand(0);
+ SDValue HiRHS = N->getOperand(1);
+ SDValue Carry = N->getOperand(2);
+ ISD::CondCode Cond = cast<CondCodeSDNode>(N->getOperand(3))->get();
+ if (Cond != ISD::SETULT || Carry.getOpcode() != ISD::USUBO ||
+ Carry.getResNo() != 1)
+ return SDValue();
+
+ if (!isNullConstant(HiLHS) && !isNullConstant(HiRHS))
+ return SDValue();
+
+ SDValue LoLHS = Carry.getOperand(0);
+ SDValue LoRHS = Carry.getOperand(1);
+ if (!isa<ConstantSDNode>(LoLHS) && !isa<ConstantSDNode>(LoRHS))
+ return SDValue();
+
+ EVT VT = N->getValueType(0);
+ SDLoc DL(N);
+
+ SDValue LoCmp = DAG.getSetCC(DL, VT, LoLHS, LoRHS, ISD::SETULT);
+ if (isNullConstant(HiRHS)) {
+ SDValue HiEq = DAG.getSetCC(DL, VT, HiLHS, HiRHS, ISD::SETEQ);
+ return DAG.getNode(ISD::AND, DL, VT, HiEq, LoCmp);
+ }
+
+ SDValue HiNe = DAG.getSetCC(DL, VT, HiRHS,
+ DAG.getConstant(0, DL, HiRHS.getValueType()),
+ ISD::SETNE);
+ return DAG.getNode(ISD::OR, DL, VT, HiNe, LoCmp);
+}
+
// Replace a flag-setting operator (eg ANDS) with the generic version
// (eg AND) if the flag is unused.
static SDValue performFlagSettingCombine(SDNode *N,
@@ -29421,6 +29419,8 @@ SDValue AArch64TargetLowering::PerformDAGCombine(SDNode *N,
return performVSelectCombine(N, DCI, Subtarget);
case ISD::SETCC:
return performSETCCCombine(N, DCI, DAG);
+ case ISD::SETCCCARRY:
+ return performSETCCCARRYCombine(N, DAG);
case ISD::LOAD:
return performLOADCombine(N, DCI, DAG, Subtarget);
case ISD::STORE:
diff --git a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
index 7422cbbfd19a0..eb414dbad3858 100644
--- a/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
+++ b/llvm/test/CodeGen/AArch64/i128-imm-compare-ccmp.ll
@@ -1,5 +1,7 @@
; RUN: llc -mtriple=aarch64-linux-gnu -O3 -o - < %s | FileCheck %s
+declare void @foo()
+
define i1 @eq_imm(i128 %x) {
; CHECK-LABEL: eq_imm:
; CHECK: cmp x0, #5
@@ -20,11 +22,111 @@ define i1 @ne_imm(i128 %x) {
ret i1 %cmp
}
+define i1 @eq_or_xor_zero_hi(i64 %lo, i64 %hi) {
+; CHECK-LABEL: eq_or_xor_zero_hi:
+; CHECK: cmp x0, #5
+; CHECK-NEXT: ccmp x1, #0, #0, eq
+; CHECK-NEXT: cset w0, eq
+; CHECK-NEXT: ret
+ %xorlo = xor i64 %lo, 5
+ %or = or i64 %xorlo, %hi
+ %cmp = icmp eq i64 %or, 0
+ ret i1 %cmp
+}
+
+define i1 @ne_or_xor_zero_hi(i64 %lo, i64 %hi) {
+; CHECK-LABEL: ne_or_xor_zero_hi:
+; CHECK: cmp x0, #5
+; CHECK-NEXT: ccmp x1, #0, #0, eq
+; CHECK-NEXT: cset w0, ne
+; CHECK-NEXT: ret
+ %xorlo = xor i64 %lo, 5
+ %or = or i64 %xorlo, %hi
+ %cmp = icmp ne i64 %or, 0
+ ret i1 %cmp
+}
+
+define void @eq_or_xor_large_cmp_imm_branch(i64 %lo, i64 %hi) {
+; CHECK-LABEL: eq_or_xor_large_cmp_imm_branch:
+; CHECK: eor x8, x1, #0x8000000000000000
+; CHECK-NEXT: orr x8, x0, x8
+; CHECK-NEXT: cbz x8, .LBB4_2
+ %xor = xor i64 %hi, -9223372036854775808
+ %or = or i64 %lo, %xor
+ %cmp = icmp eq i64 %or, 0
+ br i1 %cmp, label %true, label %false
+
+true:
+ call void @foo()
+ ret void
+
+false:
+ ret void
+}
+
+define void @eq_or_xor_large_condcmp_imm_branch(i64 %a, i64 %b, i64 %c, i64 %d) {
+; CHECK-LABEL: eq_or_xor_large_condcmp_imm_branch:
+; CHECK: eor x8, x3, #0xff
+; CHECK-NEXT: orr x9, x0, x1
+; CHECK-NEXT: orr x8, x2, x8
+; CHECK-NEXT: orr x8, x9, x8
+; CHECK-NEXT: cbz x8, .LBB5_2
+ %xor = xor i64 %d, 255
+ %or0 = or i64 %a, %b
+ %or1 = or i64 %c, %xor
+ %or = or i64 %or0, %or1
+ %cmp = icmp eq i64 %or, 0
+ br i1 %cmp, label %true, label %false
+
+true:
+ call void @foo()
+ ret void
+
+false:
+ ret void
+}
+
+define i1 @eq_or_xor_two_condcmp_imm_reordered(i64 %zero, i64 %value) {
+; CHECK-LABEL: eq_or_xor_two_condcmp_imm_reordered:
+; CHECK: cmp x1, #255
+; CHECK-NEXT: ccmp x0, #0, #0, eq
+; CHECK-NEXT: cset w0, eq
+; CHECK-NEXT: ret
+ %xor = xor i64 %value, 255
+ %or = or i64 %zero, %xor
+ %cmp = icmp eq i64 %or, 0
+ ret i1 %cmp
+}
+
+define void @eq_or_xor_multiple_large_condcmp_imm_branch(i64 %a, i64 %b, i64 %c, i64 %d) {
+; CHECK-LABEL: eq_or_xor_multiple_large_condcmp_imm_branch:
+; CHECK: eor x8, x1, #0xff
+; CHECK-NEXT: eor x9, x3, #0xffff
+; CHECK-NEXT: orr x8, x0, x8
+; CHECK-NEXT: orr x9, x2, x9
+; CHECK-NEXT: orr x8, x8, x9
+; CHECK-NEXT: cbz x8, .LBB7_2
+ %xorb = xor i64 %b, 255
+ %xord = xor i64 %d, 65535
+ %or0 = or i64 %a, %xorb
+ %or1 = or i64 %c, %xord
+ %or = or i64 %or0, %or1
+ %cmp = icmp eq i64 %or, 0
+ br i1 %cmp, label %true, label %false
+
+true:
+ call void @foo()
+ ret void
+
+false:
+ ret void
+}
+
define i1 @ult_imm(i128 %x) {
; CHECK-LABEL: ult_imm:
; CHECK: cmp x1, #0
; CHECK-NEXT: ccmp x0, #5, #2, eq
-; CHECK-NEXT: cset w0, {{cc|lo}}
+; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%cmp = icmp ult i128 %x, 5
ret i1 %cmp
@@ -34,7 +136,7 @@ define i1 @ule_imm(i128 %x) {
; CHECK-LABEL: ule_imm:
; CHECK: cmp x1, #0
; CHECK-NEXT: ccmp x0, #6, #2, eq
-; CHECK-NEXT: cset w0, {{cc|lo}}
+; CHECK-NEXT: cset w0, lo
; CHECK-NEXT: ret
%cmp = icmp ule i128 %x, 5
ret i1 %cmp
@@ -44,7 +146,7 @@ define i1 @ugt_imm(i128 %x) {
; CHECK-LABEL: ugt_imm:
; CHECK: cmp x1, #0
; CHECK-NEXT: ccmp x0, #5, #2, eq
-; CHECK-NEXT: cset w0, hi
+; CHECK-NEXT: cset w0, hi
; CHECK-NEXT: ret
%cmp = icmp ugt i128 %x, 5
ret i1 %cmp
@@ -52,10 +154,10 @@ define i1 @ugt_imm(i128 %x) {
define i1 @uge_imm(i128 %x) {
; CHECK-LABEL: uge_imm:
-; CHECK: cmp x1, #0
-; CHECK-NEXT: ccmp x0, #4, #2, eq
-; CHECK-NEXT: cset w0, hi
+; CHECK: cmp x1, #0
+; CHECK-NEXT: ccmp x0, #4, #2, eq
+; CHECK-NEXT: cset w0, hi
; CHECK-NEXT: ret
%cmp = icmp uge i128 %x, 5
ret i1 %cmp
-}
\ No newline at end of file
+}
>From 0f4540caa2276630798744601306d2072d73fa92 Mon Sep 17 00:00:00 2001
From: clingfei <1599101385 at qq.com>
Date: Fri, 8 May 2026 09:52:28 +0800
Subject: [PATCH 4/4] update
---
.../Target/AArch64/AArch64ISelLowering.cpp | 124 ++++++---------
.../CodeGen/AArch64/i128-imm-compare-ccmp.ll | 142 ++++++++++++------
2 files changed, 138 insertions(+), 128 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
}
More information about the llvm-commits
mailing list