[llvm] [ValueTracking] Treat RISC-V vsetvlimax as a power of two (PR #218831)
Pengcheng Wang via llvm-commits
llvm-commits at lists.llvm.org
Tue Aug 25 20:34:39 PDT 2026
https://github.com/wangpc-pp created https://github.com/llvm/llvm-project/pull/218831
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)
>From 144315a44c2a7830410e3f2c65338bb1e92dbaef Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Wed, 26 Aug 2026 11:13:29 +0800
Subject: [PATCH] [ValueTracking] Treat RISC-V vsetvlimax as a power of two
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)
---
llvm/lib/Analysis/ValueTracking.cpp | 4 ++
.../RISCV/riscv-vsetvlimax-ispow2.ll | 63 +++++++++++++++++++
2 files changed, 67 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-ispow2.ll
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) }
More information about the llvm-commits
mailing list