[llvm] da19383 - [RISCV] Fold (X & -4096) == 0 -> (X >> 12) == 0 (#154233)

via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 18 21:24:38 PDT 2025


Author: Craig Topper
Date: 2025-08-18T21:24:35-07:00
New Revision: da19383ae76d8483492cd52c26349ade8bfa4fbe

URL: https://github.com/llvm/llvm-project/commit/da19383ae76d8483492cd52c26349ade8bfa4fbe
DIFF: https://github.com/llvm/llvm-project/commit/da19383ae76d8483492cd52c26349ade8bfa4fbe.diff

LOG: [RISCV] Fold (X & -4096) == 0 -> (X >> 12) == 0 (#154233)

This is a more general form of the recently added isel pattern
(seteq (i64 (and GPR:$rs1, 0x8000000000000000)), 0)
  -> (XORI (i64 (SRLI GPR:$rs1, 63)), 1)

We can use a shift right for any AND mask that is a negated power
of 2. But for every other constant we need to use seqz instead of
xori. I don't think there is a benefit to xori over seqz as neither
are compressible.

We already do this transform from target independent code when the setcc
constant is a non-zero subset of the AND mask that is not a legal icmp
immediate.

I don't believe any of these patterns comparing MSBs to 0 are
canonical according to InstCombine. The canonical form is (X < 4096).
I'm curious if these appear during SelectionDAG and if so, how.

My goal here was just to remove the special case isel patterns.

Added: 
    

Modified: 
    llvm/lib/Target/RISCV/RISCVISelLowering.cpp
    llvm/lib/Target/RISCV/RISCVInstrInfo.td
    llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
    llvm/test/CodeGen/RISCV/bittest.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 8763f0091b8c2..0ced14be7615a 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -16588,8 +16588,10 @@ combineVectorSizedSetCCEquality(EVT VT, SDValue X, SDValue Y, ISD::CondCode CC,
 // (seteq (i64 (sext_inreg (X, i32)), C1')) where C1' is C1 sign extended from
 // bit 31. Same for setne. C1' may be cheaper to materialize and the sext_inreg
 // can become a sext.w instead of a shift pair.
-static SDValue performSETCCCombine(SDNode *N, SelectionDAG &DAG,
+static SDValue performSETCCCombine(SDNode *N,
+                                   TargetLowering::DAGCombinerInfo &DCI,
                                    const RISCVSubtarget &Subtarget) {
+  SelectionDAG &DAG = DCI.DAG;
   SDLoc dl(N);
   SDValue N0 = N->getOperand(0);
   SDValue N1 = N->getOperand(1);
@@ -16605,6 +16607,20 @@ static SDValue performSETCCCombine(SDNode *N, SelectionDAG &DAG,
           combineVectorSizedSetCCEquality(VT, N0, N1, Cond, dl, DAG, Subtarget))
     return V;
 
+  // (X & -4096) == 0 -> (X >> 12) == 0 if the AND constant can't use ANDI.
+  if (DCI.isAfterLegalizeDAG() && isNullConstant(N1) &&
+      N0.getOpcode() == ISD::AND && N0.hasOneUse() &&
+      isa<ConstantSDNode>(N0.getOperand(1))) {
+    const APInt &AndRHSC =
+        cast<ConstantSDNode>(N0.getOperand(1))->getAPIntValue();
+    if (!isInt<12>(AndRHSC.getSExtValue()) && AndRHSC.isNegatedPowerOf2()) {
+      unsigned ShiftBits = AndRHSC.countr_zero();
+      SDValue Shift = DAG.getNode(ISD::SRL, dl, VT, N0.getOperand(0),
+                                  DAG.getConstant(ShiftBits, dl, VT));
+      return DAG.getSetCC(dl, VT, Shift, N1, Cond);
+    }
+  }
+
   if (OpVT != MVT::i64 || !Subtarget.is64Bit())
     return SDValue();
 
@@ -20091,7 +20107,7 @@ SDValue RISCVTargetLowering::PerformDAGCombine(SDNode *N,
     return SDValue();
   }
   case ISD::SETCC:
-    return performSETCCCombine(N, DAG, Subtarget);
+    return performSETCCCombine(N, DCI, Subtarget);
   case ISD::SIGN_EXTEND_INREG:
     return performSIGN_EXTEND_INREGCombine(N, DCI, Subtarget);
   case ISD::ZERO_EXTEND:

diff  --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.td b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
index 8cbdf0ec7fa33..836a2b166feb9 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfo.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.td
@@ -1695,13 +1695,9 @@ multiclass SelectCC_GPR_riirr<DAGOperand valty, DAGOperand imm> {
 }
 
 let Predicates = [IsRV32] in {
-def : Pat<(i32 (seteq (i32 (and GPR:$rs1, 0xffffffff80000000)), 0)),
-          (XORI (i32 (SRLI GPR:$rs1, 31)), 1)>;
 def : Pat<(i32 (setlt (i32 GPR:$rs1), 0)), (SRLI GPR:$rs1, 31)>; // compressible
 }
 let Predicates = [IsRV64] in {
-def : Pat<(i64 (seteq (i64 (and GPR:$rs1, 0x8000000000000000)), 0)),
-          (XORI (i64 (SRLI GPR:$rs1, 63)), 1)>;
 def : Pat<(i64 (seteq (i64 (and GPR:$rs1, 0x0000000080000000)), 0)),
           (XORI (i64 (SRLIW GPR:$rs1, 31)), 1)>;
 def : Pat<(i64 (setlt (i64 GPR:$rs1), 0)), (SRLI GPR:$rs1, 63)>; // compressible

diff  --git a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
index be3de37927564..b16672d3c4a16 100644
--- a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
+++ b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
@@ -1,14 +1,66 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
-; RUN: llc < %s -mtriple=riscv64 | FileCheck %s
+; RUN: llc < %s -mtriple=riscv32 | FileCheck %s --check-prefixes=CHECK,RV32
+; RUN: llc < %s -mtriple=riscv64 | FileCheck %s --check-prefixes=CHECK,RV64
 
-define i1 @src(i64 %x) {
-; CHECK-LABEL: src:
+define i1 @test1(i64 %x) {
+; RV32-LABEL: test1:
+; RV32:       # %bb.0:
+; RV32-NEXT:    slli a2, a1, 2
+; RV32-NEXT:    srli a0, a0, 30
+; RV32-NEXT:    srai a1, a1, 30
+; RV32-NEXT:    or a0, a0, a2
+; RV32-NEXT:    xori a0, a0, -2
+; RV32-NEXT:    not a1, a1
+; RV32-NEXT:    or a0, a0, a1
+; RV32-NEXT:    seqz a0, a0
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: test1:
+; RV64:       # %bb.0:
+; RV64-NEXT:    srai a0, a0, 30
+; RV64-NEXT:    addi a0, a0, 2
+; RV64-NEXT:    seqz a0, a0
+; RV64-NEXT:    ret
+  %a = and i64 %x, -1073741824
+  %b = icmp eq i64 %a, -2147483648
+  ret i1 %b
+}
+
+define i1 @test2(i32 signext %x) {
+; CHECK-LABEL: test2:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    srai a0, a0, 30
-; CHECK-NEXT:    addi a0, a0, 2
+; CHECK-NEXT:    srli a0, a0, 30
 ; CHECK-NEXT:    seqz a0, a0
 ; CHECK-NEXT:    ret
-  %a = and i64 %x, -1073741824
-  %b = icmp eq i64 %a, -2147483648
+  %a = and i32 %x, -1073741824
+  %b = icmp eq i32 %a, 0
+  ret i1 %b
+}
+
+define i1 @test3(i32 signext %x) {
+; CHECK-LABEL: test3:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    srli a0, a0, 29
+; CHECK-NEXT:    snez a0, a0
+; CHECK-NEXT:    ret
+  %a = and i32 %x, -536870912
+  %b = icmp ne i32 %a, 0
+  ret i1 %b
+}
+
+define i1 @test4(i64 %x) {
+; RV32-LABEL: test4:
+; RV32:       # %bb.0:
+; RV32-NEXT:    srli a1, a1, 14
+; RV32-NEXT:    seqz a0, a1
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: test4:
+; RV64:       # %bb.0:
+; RV64-NEXT:    srli a0, a0, 46
+; RV64-NEXT:    seqz a0, a0
+; RV64-NEXT:    ret
+  %a = and i64 %x, -70368744177664
+  %b = icmp eq i64 %a, 0
   ret i1 %b
 }

diff  --git a/llvm/test/CodeGen/RISCV/bittest.ll b/llvm/test/CodeGen/RISCV/bittest.ll
index 95c577f833a37..40e3168d08b27 100644
--- a/llvm/test/CodeGen/RISCV/bittest.ll
+++ b/llvm/test/CodeGen/RISCV/bittest.ll
@@ -3512,7 +3512,7 @@ define i32 @bittest_31_andeq0_i64(i64 %x) {
 ; RV32-LABEL: bittest_31_andeq0_i64:
 ; RV32:       # %bb.0:
 ; RV32-NEXT:    srli a0, a0, 31
-; RV32-NEXT:    xori a0, a0, 1
+; RV32-NEXT:    seqz a0, a0
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: bittest_31_andeq0_i64:
@@ -3530,13 +3530,13 @@ define i32 @bittest_63_andeq0_i64(i64 %x) {
 ; RV32-LABEL: bittest_63_andeq0_i64:
 ; RV32:       # %bb.0:
 ; RV32-NEXT:    srli a1, a1, 31
-; RV32-NEXT:    xori a0, a1, 1
+; RV32-NEXT:    seqz a0, a1
 ; RV32-NEXT:    ret
 ;
 ; RV64-LABEL: bittest_63_andeq0_i64:
 ; RV64:       # %bb.0:
 ; RV64-NEXT:    srli a0, a0, 63
-; RV64-NEXT:    xori a0, a0, 1
+; RV64-NEXT:    seqz a0, a0
 ; RV64-NEXT:    ret
   %and = and i64 %x, 9223372036854775808
   %cmp = icmp eq i64 %and, 0


        


More information about the llvm-commits mailing list