[llvm] [ValueTracking] Treat RISC-V vsetvlimax as a power of two (PR #218831)

via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 20:35:18 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-risc-v

Author: Pengcheng Wang (wangpc-pp)

<details>
<summary>Changes</summary>

VLMAX = VLEN * LMUL / SEW is always a non-zero power of two for any valid
vtype, so llvm.riscv.vsetvlimax is a power of two regardless of SEW/LMUL or
whether a vscale_range is present. Report this in isKnownToBeAPowerOfTwo so
that consumers such as ctpop and the x & (x - 1) idiom can fold.

Assisted-by: TRAE CLI (Opus 4.8)


---
Full diff: https://github.com/llvm/llvm-project/pull/218831.diff


2 Files Affected:

- (modified) llvm/lib/Analysis/ValueTracking.cpp (+4) 
- (added) llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-ispow2.ll (+63) 


``````````diff
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index f4ed5e07038da..181e294eb020d 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -2871,6 +2871,10 @@ bool llvm::isKnownToBeAPowerOfTwo(const Value *V, bool OrZero,
         if (II->getArgOperand(0) == II->getArgOperand(1))
           return isKnownToBeAPowerOfTwo(II->getArgOperand(0), OrZero, Q, Depth);
         break;
+      case Intrinsic::riscv_vsetvlimax:
+        // VLMAX is VLEN * LMUL / SEW, which is always a non-zero power of two
+        // for any valid vtype, so it is a power of two regardless of OrZero.
+        return true;
       default:
         break;
       }
diff --git a/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-ispow2.ll b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-ispow2.ll
new file mode 100644
index 0000000000000..9fb91f04eaa0e
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-ispow2.ll
@@ -0,0 +1,63 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+; VLMAX = VLEN * LMUL / SEW is always a non-zero power of two for any valid
+; vtype, so llvm.riscv.vsetvlimax is known to be a power of two irrespective of
+; SEW/LMUL or whether a vscale_range is present.
+
+; ctpop of a power of two is 1.
+define i64 @ctpop_e8m1_i64() #0 {
+; CHECK-LABEL: define i64 @ctpop_e8m1_i64(
+; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    ret i64 1
+;
+  %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+  %n = call i64 @llvm.ctpop.i64(i64 %vl)
+  ret i64 %n
+}
+
+; The result holds without a vscale_range too.
+define i64 @ctpop_e8m1_no_vscale_range_i64() {
+; CHECK-LABEL: define i64 @ctpop_e8m1_no_vscale_range_i64() {
+; CHECK-NEXT:    ret i64 1
+;
+  %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+  %n = call i64 @llvm.ctpop.i64(i64 %vl)
+  ret i64 %n
+}
+
+; A fractional LMUL with a large SEW (smallest VLMAX) is still a power of two.
+define i64 @ctpop_e64mf8_i64() {
+; CHECK-LABEL: define i64 @ctpop_e64mf8_i64() {
+; CHECK-NEXT:    ret i64 1
+;
+  %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 3, i64 5)
+  %n = call i64 @llvm.ctpop.i64(i64 %vl)
+  ret i64 %n
+}
+
+; i32 result.
+define i32 @ctpop_e32mf2_i32() #0 {
+; CHECK-LABEL: define i32 @ctpop_e32mf2_i32(
+; CHECK-SAME: ) #[[ATTR2]] {
+; CHECK-NEXT:    ret i32 1
+;
+  %vl = call i32 @llvm.riscv.vsetvlimax.i32(i32 2, i32 7)
+  %n = call i32 @llvm.ctpop.i32(i32 %vl)
+  ret i32 %n
+}
+
+; The x & (x - 1) == 0 power-of-two idiom folds to true.
+define i1 @and_xminus1_is_zero_i64() #0 {
+; CHECK-LABEL: define i1 @and_xminus1_is_zero_i64(
+; CHECK-SAME: ) #[[ATTR2]] {
+; CHECK-NEXT:    ret i1 true
+;
+  %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+  %sub = sub i64 %vl, 1
+  %and = and i64 %vl, %sub
+  %cmp = icmp eq i64 %and, 0
+  ret i1 %cmp
+}
+
+attributes #0 = { vscale_range(2,1024) }

``````````

</details>


https://github.com/llvm/llvm-project/pull/218831


More information about the llvm-commits mailing list