[llvm] [ARM] Fold AND and Or cases with CSET and CMOV (PR #194667)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 28 09:21:45 PDT 2026
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-backend-arm
Author: LumioseSil (LumioseSil)
<details>
<summary>Changes</summary>
---
Full diff: https://github.com/llvm/llvm-project/pull/194667.diff
3 Files Affected:
- (modified) llvm/lib/Target/ARM/ARMISelLowering.cpp (+55)
- (modified) llvm/lib/Target/ARM/ARMInstrThumb2.td (+3)
- (modified) llvm/test/CodeGen/Thumb2/csel-andor-onebit.ll (+8-12)
``````````diff
diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp
index 17d57c1a24407..b2242bed62b20 100644
--- a/llvm/lib/Target/ARM/ARMISelLowering.cpp
+++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp
@@ -14370,6 +14370,52 @@ static SDValue CombineANDShift(SDNode *N,
return SDValue();
}
+// Return true if V is a boolean set-like value:
+// CMOV(0, 1, CC, Flags) or CSINC(0, 0, CC, Flags).
+// In both cases, C == 1 when CC is true, 0 otherwise.
+static bool getSetLikeCondAndFlags(SDValue V, SDValue &Cond, SDValue &Flags) {
+ if (V.getOpcode() == ARMISD::CMOV && isNullConstant(V.getOperand(0)) &&
+ isOneConstant(V.getOperand(1))) {
+ Cond = V.getOperand(2);
+ Flags = V.getOperand(3);
+ return true;
+ }
+ if (V.getOpcode() == ARMISD::CSINC && isNullConstant(V.getOperand(0)) &&
+ isNullConstant(V.getOperand(1))) {
+ Cond = V.getOperand(2);
+ Flags = V.getOperand(3);
+ return true;
+ }
+ return false;
+}
+
+// Given a tree of and/or(setlike(cc0), setlike(cc1)), fold to a CMOV chain
+// without materializing the intermediate and/or:
+//
+// OR(CSET cc0 cmp0, CSET cc1 cmp1) -> CMOV(CSET cc1 cmp1, 1, cc0, cmp0)
+// AND(CSET cc0 cmp0, CSET cc1 cmp1) -> CMOV(0, CSET cc1 cmp1, cc0, cmp0)
+static SDValue performANDORCMOVCombine(SDNode *N, SelectionDAG &DAG) {
+ EVT VT = N->getValueType(0);
+ if (VT != MVT::i32)
+ return SDValue();
+
+ SDValue Set0 = N->getOperand(0);
+ SDValue Set1 = N->getOperand(1);
+ SDValue Cond0, Flags0;
+ if (!getSetLikeCondAndFlags(Set0, Cond0, Flags0))
+ return SDValue();
+ SDValue UnusedCond, UnusedFlags;
+ if (!getSetLikeCondAndFlags(Set1, UnusedCond, UnusedFlags))
+ return SDValue();
+
+ SDLoc DL(N);
+ SDValue Zero = DAG.getConstant(0, DL, VT);
+ SDValue One = DAG.getConstant(1, DL, VT);
+ if (N->getOpcode() == ISD::OR)
+ return DAG.getNode(ARMISD::CMOV, DL, VT, Set1, One, Cond0, Flags0);
+ return DAG.getNode(ARMISD::CMOV, DL, VT, Zero, Set1, Cond0, Flags0);
+}
+
static SDValue PerformANDCombine(SDNode *N,
TargetLowering::DAGCombinerInfo &DCI,
const ARMSubtarget *Subtarget) {
@@ -14383,6 +14429,12 @@ static SDValue PerformANDCombine(SDNode *N,
VT == MVT::v4i1 || VT == MVT::v8i1 || VT == MVT::v16i1)
return SDValue();
+ // Akin to AArch64's ANDS handling, only do this when the AND value itself is
+ // dead, to avoid perturbing value-producing uses just for flag consumers.
+ if (!N->hasAnyUseOfValue(0))
+ if (SDValue Result = performANDORCMOVCombine(N, DAG))
+ return Result;
+
APInt SplatBits, SplatUndef;
unsigned SplatBitSize;
bool HasAnyUndefs;
@@ -14722,6 +14774,9 @@ static SDValue PerformORCombine(SDNode *N, TargetLowering::DAGCombinerInfo &DCI,
if (!DAG.getTargetLoweringInfo().isTypeLegal(VT))
return SDValue();
+ if (SDValue Result = performANDORCMOVCombine(N, DAG))
+ return Result;
+
if (Subtarget->hasMVEIntegerOps() && (VT == MVT::v2i1 || VT == MVT::v4i1 ||
VT == MVT::v8i1 || VT == MVT::v16i1))
return PerformORCombine_i1(N, DAG, Subtarget);
diff --git a/llvm/lib/Target/ARM/ARMInstrThumb2.td b/llvm/lib/Target/ARM/ARMInstrThumb2.td
index e79adb370798e..fbe70a0f664d2 100644
--- a/llvm/lib/Target/ARM/ARMInstrThumb2.td
+++ b/llvm/lib/Target/ARM/ARMInstrThumb2.td
@@ -5828,6 +5828,9 @@ let Predicates = [HasV8_1MMainline] in {
def : T2Pat<(and (topbitsallzero32:$Rn),
(ARMcsinc_su (i32 0), (i32 0), imm:$cc, CPSR)),
(t2CSEL ZR, $Rn, imm:$cc)>;
+ def : T2Pat<(or (topbitsallzero32:$Rn),
+ (ARMcsinc_su (i32 0), (i32 0), imm:$cc, CPSR)),
+ (t2CSINC $Rn, ZR, imm:$cc)>;
}
// CS aliases.
diff --git a/llvm/test/CodeGen/Thumb2/csel-andor-onebit.ll b/llvm/test/CodeGen/Thumb2/csel-andor-onebit.ll
index 5219c74e35817..a9b3a2124afa5 100644
--- a/llvm/test/CodeGen/Thumb2/csel-andor-onebit.ll
+++ b/llvm/test/CodeGen/Thumb2/csel-andor-onebit.ll
@@ -109,10 +109,9 @@ define i32 @andi32_sgt(i8 %x, i8 %y) {
define i64 @ori64i32_eq(i64 %x, i32 %y) {
; CHECK-LABEL: ori64i32_eq:
; CHECK: @ %bb.0:
-; CHECK-NEXT: cmp r2, #0
; CHECK-NEXT: and r0, r0, #1
-; CHECK-NEXT: cset r1, eq
-; CHECK-NEXT: orrs r0, r1
+; CHECK-NEXT: cmp r2, #0
+; CHECK-NEXT: csinc r0, r0, zr, ne
; CHECK-NEXT: movs r1, #0
; CHECK-NEXT: bx lr
%xa = and i64 %x, 1
@@ -127,8 +126,7 @@ define i64 @ori64i64_eq(i64 %x, i64 %y) {
; CHECK: @ %bb.0:
; CHECK-NEXT: orrs.w r1, r2, r3
; CHECK-NEXT: and r0, r0, #1
-; CHECK-NEXT: cset r1, eq
-; CHECK-NEXT: orrs r0, r1
+; CHECK-NEXT: csinc r0, r0, zr, ne
; CHECK-NEXT: movs r1, #0
; CHECK-NEXT: bx lr
%xa = and i64 %x, 1
@@ -141,10 +139,9 @@ define i64 @ori64i64_eq(i64 %x, i64 %y) {
define i64 @ori64_eq_c(i64 %x, i32 %y) {
; CHECK-LABEL: ori64_eq_c:
; CHECK: @ %bb.0:
-; CHECK-NEXT: cmp r2, #0
; CHECK-NEXT: and r0, r0, #1
-; CHECK-NEXT: cset r1, eq
-; CHECK-NEXT: orrs r0, r1
+; CHECK-NEXT: cmp r2, #0
+; CHECK-NEXT: csinc r0, r0, zr, ne
; CHECK-NEXT: movs r1, #0
; CHECK-NEXT: bx lr
%xa = and i64 %x, 1
@@ -178,11 +175,10 @@ define i32 @t5(i32 %f.0, i32 %call) {
; CHECK-NEXT: cmp r1, #0
; CHECK-NEXT: cset r1, ne
; CHECK-NEXT: cmp r0, #13
-; CHECK-NEXT: cset r0, eq
-; CHECK-NEXT: and.w r2, r0, r1
+; CHECK-NEXT: csel r0, zr, r1, ne
+; CHECK-NEXT: csinc r1, r1, zr, eq
+; CHECK-NEXT: eor r1, r1, #1
; CHECK-NEXT: orrs r0, r1
-; CHECK-NEXT: eor r0, r0, #1
-; CHECK-NEXT: orrs r0, r2
; CHECK-NEXT: bx lr
entry:
%tobool1.i = icmp ne i32 %call, 0
``````````
</details>
https://github.com/llvm/llvm-project/pull/194667
More information about the llvm-commits
mailing list