[llvm] [RISCV] Use SLLI+ADD for a select of constants that differ by a power of 2 in more cases. (PR #217812)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 21:49:23 PDT 2026


https://github.com/topperc created https://github.com/llvm/llvm-project/pull/217812

Previously we restricted to cases that can use ADDI, but we need
to materialize a constant for the czero+add lowering. Using slli
avoids materializing the delta constant.

I'm restricting to cases where we can't fold the comparison into
the czero.

>From ba2ac56b6d0a194938c8ac50410aa67cef6cc131 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 20 Aug 2026 21:26:35 -0700
Subject: [PATCH 1/2] Pre-commit tests

---
 llvm/test/CodeGen/RISCV/select-const.ll | 164 ++++++++++++++++++++++++
 1 file changed, 164 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/select-const.ll b/llvm/test/CodeGen/RISCV/select-const.ll
index 3ef5ddb032554..339a68bcbb8dd 100644
--- a/llvm/test/CodeGen/RISCV/select-const.ll
+++ b/llvm/test/CodeGen/RISCV/select-const.ll
@@ -1427,3 +1427,167 @@ define i32 @select_394_0(i32 signext %x) {
   %cond = select i1 %cmp, i32 394, i32 0
   ret i32 %cond
 }
+
+define i32 @diff_pow2_4392_4388(i32 signext %x) {
+; RV32I-LABEL: diff_pow2_4392_4388:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    li a2, 3
+; RV32I-NEXT:    lui a1, 1
+; RV32I-NEXT:    blt a0, a2, .LBB35_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    addi a0, a1, 292
+; RV32I-NEXT:    ret
+; RV32I-NEXT:  .LBB35_2:
+; RV32I-NEXT:    addi a0, a1, 296
+; RV32I-NEXT:    ret
+;
+; RV32IF-LABEL: diff_pow2_4392_4388:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    li a2, 3
+; RV32IF-NEXT:    lui a1, 1
+; RV32IF-NEXT:    blt a0, a2, .LBB35_2
+; RV32IF-NEXT:  # %bb.1:
+; RV32IF-NEXT:    addi a0, a1, 292
+; RV32IF-NEXT:    ret
+; RV32IF-NEXT:  .LBB35_2:
+; RV32IF-NEXT:    addi a0, a1, 296
+; RV32IF-NEXT:    ret
+;
+; RV32ZICOND-LABEL: diff_pow2_4392_4388:
+; RV32ZICOND:       # %bb.0:
+; RV32ZICOND-NEXT:    li a1, -4
+; RV32ZICOND-NEXT:    slti a0, a0, 3
+; RV32ZICOND-NEXT:    lui a2, 1
+; RV32ZICOND-NEXT:    czero.nez a0, a1, a0
+; RV32ZICOND-NEXT:    addi a1, a2, 296
+; RV32ZICOND-NEXT:    add a0, a0, a1
+; RV32ZICOND-NEXT:    ret
+;
+; RV32IXQCI-LABEL: diff_pow2_4392_4388:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    lui a1, 1
+; RV32IXQCI-NEXT:    addi a2, a1, 296
+; RV32IXQCI-NEXT:    addi a1, a1, 292
+; RV32IXQCI-NEXT:    qc.mvlti a1, a0, 3, a2
+; RV32IXQCI-NEXT:    mv a0, a1
+; RV32IXQCI-NEXT:    ret
+;
+; RV64I-LABEL: diff_pow2_4392_4388:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    li a2, 3
+; RV64I-NEXT:    lui a1, 1
+; RV64I-NEXT:    blt a0, a2, .LBB35_2
+; RV64I-NEXT:  # %bb.1:
+; RV64I-NEXT:    addi a0, a1, 292
+; RV64I-NEXT:    ret
+; RV64I-NEXT:  .LBB35_2:
+; RV64I-NEXT:    addi a0, a1, 296
+; RV64I-NEXT:    ret
+;
+; RV64IFD-LABEL: diff_pow2_4392_4388:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    li a2, 3
+; RV64IFD-NEXT:    lui a1, 1
+; RV64IFD-NEXT:    blt a0, a2, .LBB35_2
+; RV64IFD-NEXT:  # %bb.1:
+; RV64IFD-NEXT:    addi a0, a1, 292
+; RV64IFD-NEXT:    ret
+; RV64IFD-NEXT:  .LBB35_2:
+; RV64IFD-NEXT:    addi a0, a1, 296
+; RV64IFD-NEXT:    ret
+;
+; RV64ZICOND-LABEL: diff_pow2_4392_4388:
+; RV64ZICOND:       # %bb.0:
+; RV64ZICOND-NEXT:    li a1, -4
+; RV64ZICOND-NEXT:    slti a0, a0, 3
+; RV64ZICOND-NEXT:    lui a2, 1
+; RV64ZICOND-NEXT:    czero.nez a0, a1, a0
+; RV64ZICOND-NEXT:    addi a1, a2, 296
+; RV64ZICOND-NEXT:    add a0, a0, a1
+; RV64ZICOND-NEXT:    ret
+  %cmp = icmp slt i32 %x, 3
+  %cond = select i1 %cmp, i32 4392, i32 4388
+  ret i32 %cond
+}
+
+define i32 @diff_pow2_4000_4016(i32 signext %x) {
+; RV32I-LABEL: diff_pow2_4000_4016:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    li a2, 5
+; RV32I-NEXT:    lui a1, 1
+; RV32I-NEXT:    blt a0, a2, .LBB36_2
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    addi a0, a1, -80
+; RV32I-NEXT:    ret
+; RV32I-NEXT:  .LBB36_2:
+; RV32I-NEXT:    addi a0, a1, -96
+; RV32I-NEXT:    ret
+;
+; RV32IF-LABEL: diff_pow2_4000_4016:
+; RV32IF:       # %bb.0:
+; RV32IF-NEXT:    li a2, 5
+; RV32IF-NEXT:    lui a1, 1
+; RV32IF-NEXT:    blt a0, a2, .LBB36_2
+; RV32IF-NEXT:  # %bb.1:
+; RV32IF-NEXT:    addi a0, a1, -80
+; RV32IF-NEXT:    ret
+; RV32IF-NEXT:  .LBB36_2:
+; RV32IF-NEXT:    addi a0, a1, -96
+; RV32IF-NEXT:    ret
+;
+; RV32ZICOND-LABEL: diff_pow2_4000_4016:
+; RV32ZICOND:       # %bb.0:
+; RV32ZICOND-NEXT:    li a1, 16
+; RV32ZICOND-NEXT:    slti a0, a0, 5
+; RV32ZICOND-NEXT:    lui a2, 1
+; RV32ZICOND-NEXT:    czero.nez a0, a1, a0
+; RV32ZICOND-NEXT:    addi a1, a2, -96
+; RV32ZICOND-NEXT:    or a0, a0, a1
+; RV32ZICOND-NEXT:    ret
+;
+; RV32IXQCI-LABEL: diff_pow2_4000_4016:
+; RV32IXQCI:       # %bb.0:
+; RV32IXQCI-NEXT:    lui a1, 1
+; RV32IXQCI-NEXT:    addi a2, a1, -96
+; RV32IXQCI-NEXT:    addi a1, a1, -80
+; RV32IXQCI-NEXT:    qc.mvlti a1, a0, 5, a2
+; RV32IXQCI-NEXT:    mv a0, a1
+; RV32IXQCI-NEXT:    ret
+;
+; RV64I-LABEL: diff_pow2_4000_4016:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    li a2, 5
+; RV64I-NEXT:    lui a1, 1
+; RV64I-NEXT:    blt a0, a2, .LBB36_2
+; RV64I-NEXT:  # %bb.1:
+; RV64I-NEXT:    addi a0, a1, -80
+; RV64I-NEXT:    ret
+; RV64I-NEXT:  .LBB36_2:
+; RV64I-NEXT:    addi a0, a1, -96
+; RV64I-NEXT:    ret
+;
+; RV64IFD-LABEL: diff_pow2_4000_4016:
+; RV64IFD:       # %bb.0:
+; RV64IFD-NEXT:    li a2, 5
+; RV64IFD-NEXT:    lui a1, 1
+; RV64IFD-NEXT:    blt a0, a2, .LBB36_2
+; RV64IFD-NEXT:  # %bb.1:
+; RV64IFD-NEXT:    addi a0, a1, -80
+; RV64IFD-NEXT:    ret
+; RV64IFD-NEXT:  .LBB36_2:
+; RV64IFD-NEXT:    addi a0, a1, -96
+; RV64IFD-NEXT:    ret
+;
+; RV64ZICOND-LABEL: diff_pow2_4000_4016:
+; RV64ZICOND:       # %bb.0:
+; RV64ZICOND-NEXT:    li a1, 16
+; RV64ZICOND-NEXT:    slti a0, a0, 5
+; RV64ZICOND-NEXT:    lui a2, 1
+; RV64ZICOND-NEXT:    czero.nez a0, a1, a0
+; RV64ZICOND-NEXT:    addi a1, a2, -96
+; RV64ZICOND-NEXT:    or a0, a0, a1
+; RV64ZICOND-NEXT:    ret
+  %cmp = icmp slt i32 %x, 5
+  %cond = select i1 %cmp, i32 4000, i32 4016
+  ret i32 %cond
+}

>From febe40680e344019532df16c80c73fb38737fd43 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 20 Aug 2026 21:39:50 -0700
Subject: [PATCH 2/2] [RISCV] Use SLLI+ADD for a select of constants that
 differ by a power of 2 in more cases.

Previously we restricted to cases that can use ADDI, but we need
to materialize a constant for the czero+add lowering. Using slli
avoids materializing the delta constant.

I'm restricting to cases where we can't fold the comparison into
the czero.
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 24 +++++++++++-
 llvm/test/CodeGen/RISCV/select-const.ll     | 42 ++++++++-------------
 2 files changed, 39 insertions(+), 27 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 1db51b19fde2d..4de4655c44389 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -10625,7 +10625,7 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const {
         }
       }
 
-      // Use SHL/ADDI (and possible XORI) to avoid having to materialize
+      // Use SLLI/ADDI (and possible XORI) to avoid having to materialize
       // a constant in register
       if ((TrueVal - FalseVal).isPowerOf2() && FalseVal.isSignedIntN(12)) {
         SDValue Log2 = DAG.getConstant((TrueVal - FalseVal).logBase2(), DL, VT);
@@ -10639,6 +10639,28 @@ SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const {
         return DAG.getNode(ISD::ADD, DL, VT, BitDiff, TrueV);
       }
 
+      // If we can't use ADDI, it might still be profitable to use SLLI/ADD or
+      // SLLI/SUB. We need to materialize a large constant for the czero+add
+      // sequence below anyway. Using SLLI avoids materializing the delta.
+      // Don't do this if the condition is an equality comparison since czero
+      // allows us to fold part of the compare.
+      if ((TrueVal - FalseVal).isPowerOf2() &&
+          !(CondV.getOpcode() == ISD::SETCC &&
+            ISD::isIntEqualitySetCC(
+                cast<CondCodeSDNode>(CondV.getOperand(2))->get()))) {
+        SDValue Log2 = DAG.getConstant((TrueVal - FalseVal).logBase2(), DL, VT);
+        SDValue BitDiff = DAG.getNode(ISD::SHL, DL, VT, CondV, Log2);
+        return DAG.getNode(ISD::ADD, DL, VT, FalseV, BitDiff);
+      }
+      if ((FalseVal - TrueVal).isPowerOf2() && !FalseVal.isSignedIntN(12) &&
+          !(CondV.getOpcode() == ISD::SETCC &&
+            ISD::isIntEqualitySetCC(
+                cast<CondCodeSDNode>(CondV.getOperand(2))->get()))) {
+        SDValue Log2 = DAG.getConstant((FalseVal - TrueVal).logBase2(), DL, VT);
+        SDValue BitDiff = DAG.getNode(ISD::SHL, DL, VT, CondV, Log2);
+        return DAG.getNode(ISD::SUB, DL, VT, FalseV, BitDiff);
+      }
+
       auto getCost = [&](const APInt &Delta, const APInt &Addend) {
         const int DeltaCost = RISCVMatInt::getIntMatCost(
             Delta, Subtarget.getXLen(), Subtarget, /*CompressionCost=*/true);
diff --git a/llvm/test/CodeGen/RISCV/select-const.ll b/llvm/test/CodeGen/RISCV/select-const.ll
index 339a68bcbb8dd..20f1c4afa6f93 100644
--- a/llvm/test/CodeGen/RISCV/select-const.ll
+++ b/llvm/test/CodeGen/RISCV/select-const.ll
@@ -169,10 +169,9 @@ define float @select_const_fp(i1 zeroext %a) nounwind {
 ;
 ; RV32ZICOND-LABEL: select_const_fp:
 ; RV32ZICOND:       # %bb.0:
-; RV32ZICOND-NEXT:    lui a1, 1024
-; RV32ZICOND-NEXT:    czero.nez a0, a1, a0
-; RV32ZICOND-NEXT:    lui a1, 263168
-; RV32ZICOND-NEXT:    add a0, a0, a1
+; RV32ZICOND-NEXT:    lui a1, 264192
+; RV32ZICOND-NEXT:    slli a0, a0, 22
+; RV32ZICOND-NEXT:    sub a0, a1, a0
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV32IXQCI-LABEL: select_const_fp:
@@ -210,10 +209,9 @@ define float @select_const_fp(i1 zeroext %a) nounwind {
 ;
 ; RV64ZICOND-LABEL: select_const_fp:
 ; RV64ZICOND:       # %bb.0:
-; RV64ZICOND-NEXT:    lui a1, 1024
-; RV64ZICOND-NEXT:    czero.nez a0, a1, a0
-; RV64ZICOND-NEXT:    lui a1, 263168
-; RV64ZICOND-NEXT:    add a0, a0, a1
+; RV64ZICOND-NEXT:    lui a1, 264192
+; RV64ZICOND-NEXT:    slli a0, a0, 22
+; RV64ZICOND-NEXT:    sub a0, a1, a0
 ; RV64ZICOND-NEXT:    ret
   %1 = select i1 %a, float 3.0, float 4.0
   ret float %1
@@ -1455,11 +1453,10 @@ define i32 @diff_pow2_4392_4388(i32 signext %x) {
 ;
 ; RV32ZICOND-LABEL: diff_pow2_4392_4388:
 ; RV32ZICOND:       # %bb.0:
-; RV32ZICOND-NEXT:    li a1, -4
+; RV32ZICOND-NEXT:    lui a1, 1
 ; RV32ZICOND-NEXT:    slti a0, a0, 3
-; RV32ZICOND-NEXT:    lui a2, 1
-; RV32ZICOND-NEXT:    czero.nez a0, a1, a0
-; RV32ZICOND-NEXT:    addi a1, a2, 296
+; RV32ZICOND-NEXT:    slli a0, a0, 2
+; RV32ZICOND-NEXT:    addi a1, a1, 292
 ; RV32ZICOND-NEXT:    add a0, a0, a1
 ; RV32ZICOND-NEXT:    ret
 ;
@@ -1498,11 +1495,10 @@ define i32 @diff_pow2_4392_4388(i32 signext %x) {
 ;
 ; RV64ZICOND-LABEL: diff_pow2_4392_4388:
 ; RV64ZICOND:       # %bb.0:
-; RV64ZICOND-NEXT:    li a1, -4
+; RV64ZICOND-NEXT:    lui a1, 1
 ; RV64ZICOND-NEXT:    slti a0, a0, 3
-; RV64ZICOND-NEXT:    lui a2, 1
-; RV64ZICOND-NEXT:    czero.nez a0, a1, a0
-; RV64ZICOND-NEXT:    addi a1, a2, 296
+; RV64ZICOND-NEXT:    slli a0, a0, 2
+; RV64ZICOND-NEXT:    addi a1, a1, 292
 ; RV64ZICOND-NEXT:    add a0, a0, a1
 ; RV64ZICOND-NEXT:    ret
   %cmp = icmp slt i32 %x, 3
@@ -1537,12 +1533,9 @@ define i32 @diff_pow2_4000_4016(i32 signext %x) {
 ;
 ; RV32ZICOND-LABEL: diff_pow2_4000_4016:
 ; RV32ZICOND:       # %bb.0:
-; RV32ZICOND-NEXT:    li a1, 16
 ; RV32ZICOND-NEXT:    slti a0, a0, 5
-; RV32ZICOND-NEXT:    lui a2, 1
-; RV32ZICOND-NEXT:    czero.nez a0, a1, a0
-; RV32ZICOND-NEXT:    addi a1, a2, -96
-; RV32ZICOND-NEXT:    or a0, a0, a1
+; RV32ZICOND-NEXT:    xori a0, a0, 251
+; RV32ZICOND-NEXT:    slli a0, a0, 4
 ; RV32ZICOND-NEXT:    ret
 ;
 ; RV32IXQCI-LABEL: diff_pow2_4000_4016:
@@ -1580,12 +1573,9 @@ define i32 @diff_pow2_4000_4016(i32 signext %x) {
 ;
 ; RV64ZICOND-LABEL: diff_pow2_4000_4016:
 ; RV64ZICOND:       # %bb.0:
-; RV64ZICOND-NEXT:    li a1, 16
 ; RV64ZICOND-NEXT:    slti a0, a0, 5
-; RV64ZICOND-NEXT:    lui a2, 1
-; RV64ZICOND-NEXT:    czero.nez a0, a1, a0
-; RV64ZICOND-NEXT:    addi a1, a2, -96
-; RV64ZICOND-NEXT:    or a0, a0, a1
+; RV64ZICOND-NEXT:    xori a0, a0, 251
+; RV64ZICOND-NEXT:    slli a0, a0, 4
 ; RV64ZICOND-NEXT:    ret
   %cmp = icmp slt i32 %x, 5
   %cond = select i1 %cmp, i32 4000, i32 4016



More information about the llvm-commits mailing list