[llvm] [RISCV] Track the value range of vsetvlimax (PR #218313)
Pengcheng Wang via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 24 02:31:48 PDT 2026
https://github.com/wangpc-pp updated https://github.com/llvm/llvm-project/pull/218313
>From 27ff836ccb0185239b88ffb0ce37afd0f750e959 Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Fri, 21 Aug 2026 17:58:56 +0800
Subject: [PATCH 1/4] [RISCV] Precommit tests for vsetvlimax ranges
---
.../RISCV/riscv-vsetvlimax-range.ll | 123 ++++++++++++++++++
1 file changed, 123 insertions(+)
create mode 100644 llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll
diff --git a/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll
new file mode 100644
index 0000000000000..bc863dcf9a8b5
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll
@@ -0,0 +1,123 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=instcombine -S | FileCheck %s
+
+define i1 @e8m1_min_i64() #0 {
+; CHECK-LABEL: define i1 @e8m1_min_i64(
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 15
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+ %cmp = icmp uge i64 %vl, 16
+ ret i1 %cmp
+}
+
+define i1 @e8m1_max_i64() #0 {
+; CHECK-LABEL: define i1 @e8m1_max_i64(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i64 [[VL]], 8193
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+ %cmp = icmp ule i64 %vl, 8192
+ ret i1 %cmp
+}
+
+define i1 @e32mf2_min_i32() #0 {
+; CHECK-LABEL: define i1 @e32mf2_min_i32(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: [[VL:%.*]] = call i32 @llvm.riscv.vsetvlimax.i32(i32 2, i32 7)
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[VL]], 1
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i32 @llvm.riscv.vsetvlimax.i32(i32 2, i32 7)
+ %cmp = icmp uge i32 %vl, 2
+ ret i1 %cmp
+}
+
+define i1 @e8m8_min_i64() #0 {
+; CHECK-LABEL: define i1 @e8m8_min_i64(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 127
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
+ %cmp = icmp uge i64 %vl, 128
+ ret i1 %cmp
+}
+
+define i1 @e8m8_max_i64() #0 {
+; CHECK-LABEL: define i1 @e8m8_max_i64(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i64 [[VL]], 65537
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
+ %cmp = icmp ule i64 %vl, 65536
+ ret i1 %cmp
+}
+
+define i1 @fixed_e16m2_i64() #1 {
+; CHECK-LABEL: define i1 @fixed_e16m2_i64(
+; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 1, i64 1)
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[VL]], 32
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 1, i64 1)
+ %cmp = icmp eq i64 %vl, 32
+ ret i1 %cmp
+}
+
+define i1 @no_vscale_range_i64() {
+; CHECK-LABEL: define i1 @no_vscale_range_i64() {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 3
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+ %cmp = icmp uge i64 %vl, 4
+ ret i1 %cmp
+}
+
+define i1 @no_vscale_range_e8m8_i64() {
+; CHECK-LABEL: define i1 @no_vscale_range_e8m8_i64() {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 31
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
+ %cmp = icmp uge i64 %vl, 32
+ ret i1 %cmp
+}
+
+define i1 @e64mf8_zero_i64() #0 {
+; CHECK-LABEL: define i1 @e64mf8_zero_i64(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 3, i64 5)
+; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[VL]], 0
+; CHECK-NEXT: ret i1 [[CMP]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 3, i64 5)
+ %cmp = icmp eq i64 %vl, 0
+ ret i1 %cmp
+}
+
+define i64 @power_of_two_i64() #0 {
+; CHECK-LABEL: define i64 @power_of_two_i64(
+; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+; CHECK-NEXT: [[COUNT:%.*]] = call range(i64 0, 15) i64 @llvm.ctpop.i64(i64 [[VL]])
+; CHECK-NEXT: ret i64 [[COUNT]]
+;
+ %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
+ %count = call i64 @llvm.ctpop.i64(i64 %vl)
+ ret i64 %count
+}
+
+attributes #0 = { vscale_range(2,1024) }
+attributes #1 = { vscale_range(4,4) }
>From f11367b23a5cb43dd122baafb4ea415b2abb3cfa Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Fri, 21 Aug 2026 18:00:51 +0800
Subject: [PATCH 2/4] [RISCV] Track the value range of vsetvlimax
---
llvm/lib/Analysis/ValueTracking.cpp | 57 ++++++++++++++++---
.../RISCV/riscv-vsetvlimax-range.ll | 52 ++++++-----------
2 files changed, 66 insertions(+), 43 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index f4ed5e07038da..d20e229096822 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -87,6 +87,38 @@
using namespace llvm;
using namespace llvm::PatternMatch;
+// Return the architectural range for VLMAX. RISC-V defines VLEN in the range
+// [32, 65536], while vscale_range can provide tighter bounds for VLEN >= 64.
+static ConstantRange getRISCVVSetVLMaxRange(const IntrinsicInst &II,
+ unsigned ArgOffset = 0) {
+ unsigned Width = II.getType()->getScalarSizeInBits();
+ constexpr unsigned MinVLen = 32;
+ constexpr unsigned MaxVLen = 65536;
+ ConstantRange VLenRange(APInt(Width, MinVLen), APInt(Width, MaxVLen + 1));
+ if (II.getFunction() &&
+ II.getFunction()->hasFnAttribute(Attribute::VScaleRange)) {
+ ConstantRange VScaleRange = getVScaleRange(II.getFunction(), Width);
+ VScaleRange = VScaleRange.intersectWith(ConstantRange(
+ APInt(Width, 1),
+ APInt(Width, MaxVLen / RISCV::RVVBitsPerBlock + 1)));
+ VLenRange = VScaleRange.multiply(
+ ConstantRange(APInt(Width, RISCV::RVVBitsPerBlock)));
+ }
+
+ auto *VSEW = dyn_cast<ConstantInt>(II.getArgOperand(ArgOffset));
+ auto *VLMULArg = dyn_cast<ConstantInt>(II.getArgOperand(ArgOffset + 1));
+ // These are immarg operands, but keep this helper conservative for malformed
+ // IR rather than asserting while performing generic value analysis.
+ if (!VSEW || !VLMULArg || VSEW->getZExtValue() > 3 ||
+ VLMULArg->getZExtValue() > 7 || VLMULArg->getZExtValue() == 4)
+ return ConstantRange::getFull(Width);
+
+ unsigned SEW = RISCVVType::decodeVSEW(VSEW->getZExtValue());
+ auto VLMUL = static_cast<RISCVVType::VLMUL>(VLMULArg->getZExtValue());
+ unsigned Ratio = RISCVVType::getSEWLMULRatio(SEW, VLMUL);
+ return VLenRange.udiv(ConstantRange(APInt(Width, Ratio)));
+}
+
// Controls the number of uses of the value searched for possible
// dominating comparisons.
static cl::opt<unsigned> DomConditionsMaxUses("dom-conditions-max-uses",
@@ -2275,20 +2307,21 @@ static void computeKnownBitsFromOperator(const Operator *I,
case Intrinsic::riscv_vsetvli:
case Intrinsic::riscv_vsetvlimax: {
bool HasAVL = II->getIntrinsicID() == Intrinsic::riscv_vsetvli;
- const ConstantRange Range = getVScaleRange(II->getFunction(), BitWidth);
- uint64_t SEW = RISCVVType::decodeVSEW(
- cast<ConstantInt>(II->getArgOperand(HasAVL))->getZExtValue());
- RISCVVType::VLMUL VLMUL = static_cast<RISCVVType::VLMUL>(
- cast<ConstantInt>(II->getArgOperand(1 + HasAVL))->getZExtValue());
- uint64_t MaxVLEN =
- Range.getUnsignedMax().getZExtValue() * RISCV::RVVBitsPerBlock;
- uint64_t MaxVL = MaxVLEN / RISCVVType::getSEWLMULRatio(SEW, VLMUL);
+ ConstantRange Range = getRISCVVSetVLMaxRange(*II, HasAVL);
+ if (Range.isFullSet())
+ break;
+ uint64_t MaxVL = Range.getUnsignedMax().getZExtValue();
// Result of vsetvli must be not larger than AVL.
if (HasAVL)
if (auto *CI = dyn_cast<ConstantInt>(II->getArgOperand(0)))
MaxVL = std::min(MaxVL, CI->getZExtValue());
+ if (MaxVL == 0) {
+ Known.setAllZero();
+ break;
+ }
+
unsigned KnownZeroFirstBit = Log2_32(MaxVL) + 1;
if (BitWidth > KnownZeroFirstBit)
Known.Zero.setBitsFrom(KnownZeroFirstBit);
@@ -2871,6 +2904,12 @@ 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:
+ // VLEN and LMUL are powers of two, and SEW is a power of two.
+ if (ConstantRange Range = getRISCVVSetVLMaxRange(*II);
+ !Range.isFullSet())
+ return OrZero || !Range.contains(APInt(Range.getBitWidth(), 0));
+ return false;
default:
break;
}
@@ -10390,6 +10429,8 @@ static ConstantRange getRangeForIntrinsic(const IntrinsicInst &II,
unsigned Width = II.getType()->getScalarSizeInBits();
const APInt *C;
switch (II.getIntrinsicID()) {
+ case Intrinsic::riscv_vsetvlimax:
+ return getRISCVVSetVLMaxRange(II);
case Intrinsic::ctlz:
case Intrinsic::cttz: {
APInt Upper(Width, Width);
diff --git a/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll
index bc863dcf9a8b5..52d7dac8f6c53 100644
--- a/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll
+++ b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvlimax-range.ll
@@ -3,10 +3,8 @@
define i1 @e8m1_min_i64() #0 {
; CHECK-LABEL: define i1 @e8m1_min_i64(
-; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 15
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-SAME: ) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: ret i1 true
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
%cmp = icmp uge i64 %vl, 16
@@ -15,10 +13,8 @@ define i1 @e8m1_min_i64() #0 {
define i1 @e8m1_max_i64() #0 {
; CHECK-LABEL: define i1 @e8m1_max_i64(
-; CHECK-SAME: ) #[[ATTR1]] {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i64 [[VL]], 8193
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT: ret i1 true
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
%cmp = icmp ule i64 %vl, 8192
@@ -27,10 +23,8 @@ define i1 @e8m1_max_i64() #0 {
define i1 @e32mf2_min_i32() #0 {
; CHECK-LABEL: define i1 @e32mf2_min_i32(
-; CHECK-SAME: ) #[[ATTR1]] {
-; CHECK-NEXT: [[VL:%.*]] = call i32 @llvm.riscv.vsetvlimax.i32(i32 2, i32 7)
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[VL]], 1
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT: ret i1 true
;
%vl = call i32 @llvm.riscv.vsetvlimax.i32(i32 2, i32 7)
%cmp = icmp uge i32 %vl, 2
@@ -39,10 +33,8 @@ define i1 @e32mf2_min_i32() #0 {
define i1 @e8m8_min_i64() #0 {
; CHECK-LABEL: define i1 @e8m8_min_i64(
-; CHECK-SAME: ) #[[ATTR1]] {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 127
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT: ret i1 true
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
%cmp = icmp uge i64 %vl, 128
@@ -51,10 +43,8 @@ define i1 @e8m8_min_i64() #0 {
define i1 @e8m8_max_i64() #0 {
; CHECK-LABEL: define i1 @e8m8_max_i64(
-; CHECK-SAME: ) #[[ATTR1]] {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i64 [[VL]], 65537
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT: ret i1 true
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
%cmp = icmp ule i64 %vl, 65536
@@ -63,10 +53,8 @@ define i1 @e8m8_max_i64() #0 {
define i1 @fixed_e16m2_i64() #1 {
; CHECK-LABEL: define i1 @fixed_e16m2_i64(
-; CHECK-SAME: ) #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 1, i64 1)
-; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[VL]], 32
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-SAME: ) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT: ret i1 true
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 1, i64 1)
%cmp = icmp eq i64 %vl, 32
@@ -75,9 +63,7 @@ define i1 @fixed_e16m2_i64() #1 {
define i1 @no_vscale_range_i64() {
; CHECK-LABEL: define i1 @no_vscale_range_i64() {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 3
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
%cmp = icmp uge i64 %vl, 4
@@ -86,9 +72,7 @@ define i1 @no_vscale_range_i64() {
define i1 @no_vscale_range_e8m8_i64() {
; CHECK-LABEL: define i1 @no_vscale_range_e8m8_i64() {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
-; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i64 [[VL]], 31
-; CHECK-NEXT: ret i1 [[CMP]]
+; CHECK-NEXT: ret i1 true
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 3)
%cmp = icmp uge i64 %vl, 32
@@ -97,7 +81,7 @@ define i1 @no_vscale_range_e8m8_i64() {
define i1 @e64mf8_zero_i64() #0 {
; CHECK-LABEL: define i1 @e64mf8_zero_i64(
-; CHECK-SAME: ) #[[ATTR1]] {
+; CHECK-SAME: ) #[[ATTR0]] {
; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 3, i64 5)
; CHECK-NEXT: [[CMP:%.*]] = icmp eq i64 [[VL]], 0
; CHECK-NEXT: ret i1 [[CMP]]
@@ -109,10 +93,8 @@ define i1 @e64mf8_zero_i64() #0 {
define i64 @power_of_two_i64() #0 {
; CHECK-LABEL: define i64 @power_of_two_i64(
-; CHECK-SAME: ) #[[ATTR1]] {
-; CHECK-NEXT: [[VL:%.*]] = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
-; CHECK-NEXT: [[COUNT:%.*]] = call range(i64 0, 15) i64 @llvm.ctpop.i64(i64 [[VL]])
-; CHECK-NEXT: ret i64 [[COUNT]]
+; CHECK-SAME: ) #[[ATTR0]] {
+; CHECK-NEXT: ret i64 1
;
%vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 0)
%count = call i64 @llvm.ctpop.i64(i64 %vl)
>From 0488fa2db2edd68c2b95d28e415c22ee71a472fc Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Mon, 24 Aug 2026 12:34:39 +0800
Subject: [PATCH 3/4] clang-format
---
llvm/lib/Analysis/ValueTracking.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index d20e229096822..299c9f4c32cfb 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -99,8 +99,7 @@ static ConstantRange getRISCVVSetVLMaxRange(const IntrinsicInst &II,
II.getFunction()->hasFnAttribute(Attribute::VScaleRange)) {
ConstantRange VScaleRange = getVScaleRange(II.getFunction(), Width);
VScaleRange = VScaleRange.intersectWith(ConstantRange(
- APInt(Width, 1),
- APInt(Width, MaxVLen / RISCV::RVVBitsPerBlock + 1)));
+ APInt(Width, 1), APInt(Width, MaxVLen / RISCV::RVVBitsPerBlock + 1)));
VLenRange = VScaleRange.multiply(
ConstantRange(APInt(Width, RISCV::RVVBitsPerBlock)));
}
>From f273366c7e3d595ebabb83f493b17f8ff09c07b5 Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Mon, 24 Aug 2026 17:31:26 +0800
Subject: [PATCH 4/4] Address comments
---
llvm/lib/Analysis/ValueTracking.cpp | 78 +++++++++----------
.../RISCV/riscv-vsetvli-knownbits.ll | 26 ++-----
2 files changed, 45 insertions(+), 59 deletions(-)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index 299c9f4c32cfb..cbbe28cbd4aa2 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -87,10 +87,13 @@
using namespace llvm;
using namespace llvm::PatternMatch;
-// Return the architectural range for VLMAX. RISC-V defines VLEN in the range
-// [32, 65536], while vscale_range can provide tighter bounds for VLEN >= 64.
-static ConstantRange getRISCVVSetVLMaxRange(const IntrinsicInst &II,
- unsigned ArgOffset = 0) {
+// Return the value range for VLMAX produced by a vsetvli/vsetvlimax intrinsic.
+// RISC-V defines VLEN in the range [32, 65536], while vscale_range can provide
+// tighter bounds for VLEN >= 64. For vsetvli the result is additionally bounded
+// by a constant AVL operand.
+static ConstantRange getRISCVVSetVLMaxRange(const IntrinsicInst &II) {
+ bool HasAVL = II.getIntrinsicID() == Intrinsic::riscv_vsetvli;
+ unsigned Offset = HasAVL ? 1 : 0;
unsigned Width = II.getType()->getScalarSizeInBits();
constexpr unsigned MinVLen = 32;
constexpr unsigned MaxVLen = 65536;
@@ -104,18 +107,28 @@ static ConstantRange getRISCVVSetVLMaxRange(const IntrinsicInst &II,
ConstantRange(APInt(Width, RISCV::RVVBitsPerBlock)));
}
- auto *VSEW = dyn_cast<ConstantInt>(II.getArgOperand(ArgOffset));
- auto *VLMULArg = dyn_cast<ConstantInt>(II.getArgOperand(ArgOffset + 1));
- // These are immarg operands, but keep this helper conservative for malformed
- // IR rather than asserting while performing generic value analysis.
- if (!VSEW || !VLMULArg || VSEW->getZExtValue() > 3 ||
- VLMULArg->getZExtValue() > 7 || VLMULArg->getZExtValue() == 4)
- return ConstantRange::getFull(Width);
+ uint64_t VSEW = cast<ConstantInt>(II.getArgOperand(Offset))->getZExtValue();
+ uint64_t VLMULVal =
+ cast<ConstantInt>(II.getArgOperand(Offset + 1))->getZExtValue();
+ assert(VSEW <= 3 && VLMULVal <= 7 && VLMULVal != RISCVVType::LMUL_RESERVED &&
+ "Unexpected VSEW/VLMUL for vsetvli/vsetvlimax intrinsic");
- unsigned SEW = RISCVVType::decodeVSEW(VSEW->getZExtValue());
- auto VLMUL = static_cast<RISCVVType::VLMUL>(VLMULArg->getZExtValue());
+ unsigned SEW = RISCVVType::decodeVSEW(VSEW);
+ auto VLMUL = static_cast<RISCVVType::VLMUL>(VLMULVal);
unsigned Ratio = RISCVVType::getSEWLMULRatio(SEW, VLMUL);
- return VLenRange.udiv(ConstantRange(APInt(Width, Ratio)));
+ ConstantRange VLRange = VLenRange.udiv(ConstantRange(APInt(Width, Ratio)));
+
+ // The result of vsetvli is no larger than the AVL operand. When the AVL is
+ // a constant we can compute a tighter bound, otherwise the result may be as
+ // small as zero.
+ if (HasAVL) {
+ if (auto *AVL = dyn_cast<ConstantInt>(II.getArgOperand(0)))
+ VLRange = VLRange.umin(ConstantRange(AVL->getValue()));
+ else
+ VLRange = VLRange.unionWith(ConstantRange(APInt::getZero(Width)));
+ }
+
+ return VLRange;
}
// Controls the number of uses of the value searched for possible
@@ -2304,28 +2317,9 @@ static void computeKnownBitsFromOperator(const Operator *I,
break;
}
case Intrinsic::riscv_vsetvli:
- case Intrinsic::riscv_vsetvlimax: {
- bool HasAVL = II->getIntrinsicID() == Intrinsic::riscv_vsetvli;
- ConstantRange Range = getRISCVVSetVLMaxRange(*II, HasAVL);
- if (Range.isFullSet())
- break;
- uint64_t MaxVL = Range.getUnsignedMax().getZExtValue();
-
- // Result of vsetvli must be not larger than AVL.
- if (HasAVL)
- if (auto *CI = dyn_cast<ConstantInt>(II->getArgOperand(0)))
- MaxVL = std::min(MaxVL, CI->getZExtValue());
-
- if (MaxVL == 0) {
- Known.setAllZero();
- break;
- }
-
- unsigned KnownZeroFirstBit = Log2_32(MaxVL) + 1;
- if (BitWidth > KnownZeroFirstBit)
- Known.Zero.setBitsFrom(KnownZeroFirstBit);
+ case Intrinsic::riscv_vsetvlimax:
+ Known = Known.unionWith(getRISCVVSetVLMaxRange(*II).toKnownBits());
break;
- }
case Intrinsic::amdgcn_mbcnt_hi:
case Intrinsic::amdgcn_mbcnt_lo: {
// Wave64 mbcnt_lo returns at most 32 + src1. Otherwise these return at
@@ -2903,12 +2897,13 @@ 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:
- // VLEN and LMUL are powers of two, and SEW is a power of two.
- if (ConstantRange Range = getRISCVVSetVLMaxRange(*II);
- !Range.isFullSet())
- return OrZero || !Range.contains(APInt(Range.getBitWidth(), 0));
- return false;
+ case Intrinsic::riscv_vsetvlimax: {
+ // VLMAX is VLEN * LMUL / SEW, which is always a power of two. It can
+ // still be zero for a fractional LMUL with a large SEW and small VLEN
+ // (e.g. e64mf8), so consult the range to rule out zero unless OrZero.
+ ConstantRange Range = getRISCVVSetVLMaxRange(*II);
+ return OrZero || !Range.contains(APInt(Range.getBitWidth(), 0));
+ }
default:
break;
}
@@ -10428,6 +10423,7 @@ static ConstantRange getRangeForIntrinsic(const IntrinsicInst &II,
unsigned Width = II.getType()->getScalarSizeInBits();
const APInt *C;
switch (II.getIntrinsicID()) {
+ case Intrinsic::riscv_vsetvli:
case Intrinsic::riscv_vsetvlimax:
return getRISCVVSetVLMaxRange(II);
case Intrinsic::ctlz:
diff --git a/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvli-knownbits.ll b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvli-knownbits.ll
index 6e0acfd685116..aeb9ad7d94e05 100644
--- a/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvli-knownbits.ll
+++ b/llvm/test/Transforms/InstCombine/RISCV/riscv-vsetvli-knownbits.ll
@@ -7,8 +7,7 @@ declare i64 @llvm.riscv.vsetvli.i64(i64, i64, i64)
define i32 @vsetvli_i32() nounwind #0 {
; CHECK-LABEL: @vsetvli_i32(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.vsetvli.i32(i32 1, i32 1, i32 1)
-; CHECK-NEXT: ret i32 [[TMP0]]
+; CHECK-NEXT: ret i32 1
;
entry:
%0 = call i32 @llvm.riscv.vsetvli.i32(i32 1, i32 1, i32 1)
@@ -19,8 +18,7 @@ entry:
define i64 @vsetvli_sext_i64() nounwind #0 {
; CHECK-LABEL: @vsetvli_sext_i64(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
-; CHECK-NEXT: ret i64 [[TMP0]]
+; CHECK-NEXT: ret i64 1
;
entry:
%0 = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
@@ -32,8 +30,7 @@ entry:
define i64 @vsetvli_zext_i64() nounwind #0 {
; CHECK-LABEL: @vsetvli_zext_i64(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
-; CHECK-NEXT: ret i64 [[TMP0]]
+; CHECK-NEXT: ret i64 1
;
entry:
%0 = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
@@ -44,9 +41,7 @@ entry:
define signext i32 @vsetvl_sext() nounwind #0 {
; CHECK-LABEL: @vsetvl_sext(
-; CHECK-NEXT: [[A:%.*]] = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
-; CHECK-NEXT: [[B:%.*]] = trunc nuw nsw i64 [[A]] to i32
-; CHECK-NEXT: ret i32 [[B]]
+; CHECK-NEXT: ret i32 1
;
%a = call i64 @llvm.riscv.vsetvli(i64 1, i64 1, i64 1)
%b = trunc i64 %a to i32
@@ -55,9 +50,7 @@ define signext i32 @vsetvl_sext() nounwind #0 {
define zeroext i32 @vsetvl_zext() nounwind #0 {
; CHECK-LABEL: @vsetvl_zext(
-; CHECK-NEXT: [[A:%.*]] = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
-; CHECK-NEXT: [[B:%.*]] = trunc nuw nsw i64 [[A]] to i32
-; CHECK-NEXT: ret i32 [[B]]
+; CHECK-NEXT: ret i32 1
;
%a = call i64 @llvm.riscv.vsetvli(i64 1, i64 1, i64 1)
%b = trunc i64 %a to i32
@@ -67,8 +60,7 @@ define zeroext i32 @vsetvl_zext() nounwind #0 {
define i32 @vsetvli_and17_i32() nounwind #0 {
; CHECK-LABEL: @vsetvli_and17_i32(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.vsetvli.i32(i32 1, i32 1, i32 1)
-; CHECK-NEXT: ret i32 [[TMP0]]
+; CHECK-NEXT: ret i32 1
;
entry:
%0 = call i32 @llvm.riscv.vsetvli.i32(i32 1, i32 1, i32 1)
@@ -79,8 +71,7 @@ entry:
define i64 @vsetvli_and17_i64() nounwind #0 {
; CHECK-LABEL: @vsetvli_and17_i64(
; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
-; CHECK-NEXT: ret i64 [[TMP0]]
+; CHECK-NEXT: ret i64 1
;
entry:
%0 = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 1, i64 1)
@@ -111,8 +102,7 @@ define i64 @vsetvl_e8m1_and13bits(i64 %avl) nounwind #0 {
define i64 @vsetvl_e8m1_constant_avl() nounwind #0 {
; CHECK-LABEL: @vsetvl_e8m1_constant_avl(
-; CHECK-NEXT: [[A:%.*]] = call i64 @llvm.riscv.vsetvli.i64(i64 1, i64 0, i64 0)
-; CHECK-NEXT: ret i64 [[A]]
+; CHECK-NEXT: ret i64 1
;
%a = call i64 @llvm.riscv.vsetvli(i64 1, i64 0, i64 0)
%b = and i64 %a, 1
More information about the llvm-commits
mailing list