[llvm] [RISCV] Teach getIntImmCostInst about (X & -(1 << C1) & 0xffffffff) == C2 << C1 (PR #160163)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Tue Sep 23 08:34:53 PDT 2025


https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/160163

>From a639a0b7d217c307bc9504cbbe2d5776b159bbba Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Mon, 22 Sep 2025 10:44:44 -0700
Subject: [PATCH 1/3] Pre-commit tests

---
 llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll | 121 +++++++++++++++++++++
 1 file changed, 121 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
index 55b0d1f0bf7be..898bb2bc69050 100644
--- a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
+++ b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
@@ -155,3 +155,124 @@ define i1 @test9(i64 %x) {
   %b = icmp eq i64 %a, u0x08000000
   ret i1 %b
 }
+
+; Make sure the and constant doesn't get converted to an opaque constant by
+; ConstantHoisting. If it's an opaque constant, we'll have addi -16 and addi 15.
+define i64 @test10(i64 %0) #0 {
+; RV32-LABEL: test10:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    addi a0, a0, -16
+; RV32-NEXT:    addi a0, a0, 15
+; RV32-NEXT:    andi a0, a0, -16
+; RV32-NEXT:    snez a0, a0
+; RV32-NEXT:    li a1, 0
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: test10:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    addi a0, a0, -16
+; RV64-NEXT:    addi a0, a0, 15
+; RV64-NEXT:    sraiw a0, a0, 4
+; RV64-NEXT:    snez a0, a0
+; RV64-NEXT:    ret
+entry:
+  %1 = add nuw nsw i64 %0, u0xffffffff
+  %2 = and i64 %1, u0xfffffff0
+  %3 = icmp ne i64 %2, 0
+  %4 = zext i1 %3 to i64
+  ret i64 %4
+}
+
+; Make sure the and constant doesn't get converted to an opaque constant by
+; ConstantHoisting. If it's an opaque constant, we'll have addi -16 and addi 15.
+define i64 @test11(i64 %0) #0 {
+; RV32-LABEL: test11:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    addi a0, a0, -16
+; RV32-NEXT:    addi a0, a0, 15
+; RV32-NEXT:    srai a0, a0, 4
+; RV32-NEXT:    addi a0, a0, 1621
+; RV32-NEXT:    seqz a0, a0
+; RV32-NEXT:    li a1, 0
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: test11:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    addi a0, a0, -16
+; RV64-NEXT:    addi a0, a0, 15
+; RV64-NEXT:    sraiw a0, a0, 4
+; RV64-NEXT:    addi a0, a0, 1621
+; RV64-NEXT:    seqz a0, a0
+; RV64-NEXT:    ret
+entry:
+  %1 = add nuw nsw i64 %0, u0xffffffff
+  %2 = and i64 %1, u0xfffffff0
+  %3 = icmp eq i64 %2, u0xffff9ab0
+  %4 = zext i1 %3 to i64
+  ret i64 %4
+}
+
+; Make sure the and constant doesn't get converted to an opaque constant by
+; ConstantHoisting. If it's an opaque constant we'll end up with constant
+; materialization sequences on RV64.
+define i64 @test12(i64 %0) #0 {
+; RV32-LABEL: test12:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    addi a0, a0, -3
+; RV32-NEXT:    seqz a0, a0
+; RV32-NEXT:    li a1, 0
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: test12:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    li a1, 1
+; RV64-NEXT:    slli a1, a1, 32
+; RV64-NEXT:    addi a2, a1, -16
+; RV64-NEXT:    add a0, a0, a2
+; RV64-NEXT:    addi a2, a2, 15
+; RV64-NEXT:    and a0, a0, a2
+; RV64-NEXT:    addi a1, a1, -13
+; RV64-NEXT:    xor a0, a0, a1
+; RV64-NEXT:    seqz a0, a0
+; RV64-NEXT:    ret
+entry:
+  %1 = add nuw nsw i64 %0, u0xfffffff0
+  %2 = and i64 %1, u0xffffffff
+  %3 = icmp eq i64 %2, u0xfffffff3
+  %4 = zext i1 %3 to i64
+  ret i64 %4
+}
+
+; Make sure the and constant doesn't get converted to an opaque constant by
+; ConstantHoisting.
+define i64 @test13(i64 %0) #0 {
+; RV32-LABEL: test13:
+; RV32:       # %bb.0: # %entry
+; RV32-NEXT:    lui a1, 524288
+; RV32-NEXT:    addi a1, a1, 15
+; RV32-NEXT:    add a0, a0, a1
+; RV32-NEXT:    addi a1, a1, -15
+; RV32-NEXT:    and a0, a0, a1
+; RV32-NEXT:    seqz a0, a0
+; RV32-NEXT:    li a1, 0
+; RV32-NEXT:    ret
+;
+; RV64-LABEL: test13:
+; RV64:       # %bb.0: # %entry
+; RV64-NEXT:    lui a1, 524288
+; RV64-NEXT:    addi a1, a1, -15
+; RV64-NEXT:    sub a0, a0, a1
+; RV64-NEXT:    li a1, 1
+; RV64-NEXT:    slli a1, a1, 31
+; RV64-NEXT:    addi a1, a1, 15
+; RV64-NEXT:    addi a1, a1, -15
+; RV64-NEXT:    and a0, a0, a1
+; RV64-NEXT:    seqz a0, a0
+; RV64-NEXT:    ret
+entry:
+  %1 = add nuw nsw i64 %0, u0x8000000f
+  %2 = and i64 %1, u0x80000000
+  %3 = icmp eq i64 %2, 0
+  %4 = zext i1 %3 to i64
+  ret i64 %4
+}

>From 78dddfa65a254f91b720925264ab2362bd8dad54 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Mon, 22 Sep 2025 10:41:47 -0700
Subject: [PATCH 2/3] [RISCV] Teach getIntImmCostInst about X & -(1 << C1) &
 0xffffffff) == C2 << C1

We can rewrite this to (sraiw X, C1) == C2 so the AND immediate is
free.

This fixes the opaque constant case mentioned in #157416.
---
 .../Target/RISCV/RISCVTargetTransformInfo.cpp | 38 +++++++++++++++++++
 llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll    | 31 ++++-----------
 2 files changed, 46 insertions(+), 23 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
index a06faa414a2ef..73edfbf28f3d2 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
@@ -166,6 +166,41 @@ static bool canUseShiftPair(Instruction *Inst, const APInt &Imm) {
   return false;
 }
 
+// If this is a 64-bit AND with a mask of the form -(1 << C) in the lower 32
+// bits and the only user is an equality comparison, we might be able to use a
+// sraiw instead. This avoids the need to materialize the AND constant.
+static bool canUseSRAIWCmp(Instruction *Inst, const APInt &Imm) {
+  if (!Inst->hasOneUse())
+    return false;
+
+  // Look for equality comparison.
+  auto *Cmp = dyn_cast<ICmpInst>(*Inst->user_begin());
+  if (!Cmp || !Cmp->isEquality())
+    return false;
+
+  // Right hand side of comparison should be a constant.
+  auto *C = dyn_cast<ConstantInt>(Cmp->getOperand(1));
+  if (!C)
+    return false;
+
+  uint64_t Mask = Imm.getZExtValue();
+
+  // Mask should be of the form -(1 << C) in the lower 32 bits.
+  if (!isUInt<32>(Mask) || !isPowerOf2_32(-uint32_t(Mask)))
+    return false;
+
+  // Comparison constant should be a subset of Mask.
+  uint64_t CmpC = C->getZExtValue();
+  if ((CmpC & Mask) != CmpC)
+    return false;
+
+  // We'll need to sign extend the comparison constant and shift it right. Make
+  // sure the new constant can use addi/xori+seqz/snez.
+  unsigned ShiftBits = llvm::countr_zero(Mask);
+  int64_t NewCmpC = SignExtend64<32>(CmpC) >> ShiftBits;
+  return NewCmpC >= -2048 && NewCmpC <= 2048;
+}
+
 InstructionCost RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx,
                                                 const APInt &Imm, Type *Ty,
                                                 TTI::TargetCostKind CostKind,
@@ -223,6 +258,9 @@ InstructionCost RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx,
     if (Inst && Idx == 1 && Imm.getBitWidth() <= ST->getXLen() &&
         canUseShiftPair(Inst, Imm))
       return TTI::TCC_Free;
+    if (Inst && Idx == 1 && Imm.getBitWidth() == 64 &&
+        canUseSRAIWCmp(Inst, Imm))
+      return TTI::TCC_Free;
     Takes12BitImm = true;
     break;
   case Instruction::Add:
diff --git a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
index 898bb2bc69050..2a46a59e90535 100644
--- a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
+++ b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll
@@ -161,8 +161,7 @@ define i1 @test9(i64 %x) {
 define i64 @test10(i64 %0) #0 {
 ; RV32-LABEL: test10:
 ; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    addi a0, a0, -16
-; RV32-NEXT:    addi a0, a0, 15
+; RV32-NEXT:    addi a0, a0, -1
 ; RV32-NEXT:    andi a0, a0, -16
 ; RV32-NEXT:    snez a0, a0
 ; RV32-NEXT:    li a1, 0
@@ -170,8 +169,7 @@ define i64 @test10(i64 %0) #0 {
 ;
 ; RV64-LABEL: test10:
 ; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    addi a0, a0, -16
-; RV64-NEXT:    addi a0, a0, 15
+; RV64-NEXT:    addi a0, a0, -1
 ; RV64-NEXT:    sraiw a0, a0, 4
 ; RV64-NEXT:    snez a0, a0
 ; RV64-NEXT:    ret
@@ -188,8 +186,7 @@ entry:
 define i64 @test11(i64 %0) #0 {
 ; RV32-LABEL: test11:
 ; RV32:       # %bb.0: # %entry
-; RV32-NEXT:    addi a0, a0, -16
-; RV32-NEXT:    addi a0, a0, 15
+; RV32-NEXT:    addi a0, a0, -1
 ; RV32-NEXT:    srai a0, a0, 4
 ; RV32-NEXT:    addi a0, a0, 1621
 ; RV32-NEXT:    seqz a0, a0
@@ -198,8 +195,7 @@ define i64 @test11(i64 %0) #0 {
 ;
 ; RV64-LABEL: test11:
 ; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    addi a0, a0, -16
-; RV64-NEXT:    addi a0, a0, 15
+; RV64-NEXT:    addi a0, a0, -1
 ; RV64-NEXT:    sraiw a0, a0, 4
 ; RV64-NEXT:    addi a0, a0, 1621
 ; RV64-NEXT:    seqz a0, a0
@@ -225,14 +221,8 @@ define i64 @test12(i64 %0) #0 {
 ;
 ; RV64-LABEL: test12:
 ; RV64:       # %bb.0: # %entry
-; RV64-NEXT:    li a1, 1
-; RV64-NEXT:    slli a1, a1, 32
-; RV64-NEXT:    addi a2, a1, -16
-; RV64-NEXT:    add a0, a0, a2
-; RV64-NEXT:    addi a2, a2, 15
-; RV64-NEXT:    and a0, a0, a2
-; RV64-NEXT:    addi a1, a1, -13
-; RV64-NEXT:    xor a0, a0, a1
+; RV64-NEXT:    addiw a0, a0, -16
+; RV64-NEXT:    addi a0, a0, 13
 ; RV64-NEXT:    seqz a0, a0
 ; RV64-NEXT:    ret
 entry:
@@ -251,8 +241,7 @@ define i64 @test13(i64 %0) #0 {
 ; RV32-NEXT:    lui a1, 524288
 ; RV32-NEXT:    addi a1, a1, 15
 ; RV32-NEXT:    add a0, a0, a1
-; RV32-NEXT:    addi a1, a1, -15
-; RV32-NEXT:    and a0, a0, a1
+; RV32-NEXT:    srli a0, a0, 31
 ; RV32-NEXT:    seqz a0, a0
 ; RV32-NEXT:    li a1, 0
 ; RV32-NEXT:    ret
@@ -262,11 +251,7 @@ define i64 @test13(i64 %0) #0 {
 ; RV64-NEXT:    lui a1, 524288
 ; RV64-NEXT:    addi a1, a1, -15
 ; RV64-NEXT:    sub a0, a0, a1
-; RV64-NEXT:    li a1, 1
-; RV64-NEXT:    slli a1, a1, 31
-; RV64-NEXT:    addi a1, a1, 15
-; RV64-NEXT:    addi a1, a1, -15
-; RV64-NEXT:    and a0, a0, a1
+; RV64-NEXT:    sraiw a0, a0, 31
 ; RV64-NEXT:    seqz a0, a0
 ; RV64-NEXT:    ret
 entry:

>From 82f426c00d022cb309ee8e349b5519aacf240ac8 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Tue, 23 Sep 2025 08:34:35 -0700
Subject: [PATCH 3/3] fixup! Update comment

---
 llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
index 73edfbf28f3d2..aef5175518e65 100644
--- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
+++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp
@@ -166,10 +166,11 @@ static bool canUseShiftPair(Instruction *Inst, const APInt &Imm) {
   return false;
 }
 
-// If this is a 64-bit AND with a mask of the form -(1 << C) in the lower 32
-// bits and the only user is an equality comparison, we might be able to use a
-// sraiw instead. This avoids the need to materialize the AND constant.
-static bool canUseSRAIWCmp(Instruction *Inst, const APInt &Imm) {
+// If this is i64 AND is part of (X & -(1 << C1) & 0xffffffff) == C2 << C1),
+// DAGCombiner can convert this to (sraiw X, C1) == sext(C2) for RV64. On RV32,
+// the type will be split so only the lower 32 bits need to be compared using
+// (srai/srli X, C) == C2.
+static bool canUseShiftCmp(Instruction *Inst, const APInt &Imm) {
   if (!Inst->hasOneUse())
     return false;
 



More information about the llvm-commits mailing list