[llvm] [RISCV][Verifier] Check operands and result type of RISC-V vsetvli/vsetvlimax (PR #218594)

Pengcheng Wang via llvm-commits llvm-commits at lists.llvm.org
Tue Aug 25 03:09:45 PDT 2026


https://github.com/wangpc-pp updated https://github.com/llvm/llvm-project/pull/218594

>From 68c943e4d0b414a0cfe9d42a8fb8eeefc9092b1b Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Tue, 25 Aug 2026 14:11:40 +0800
Subject: [PATCH 1/2] [RISCV][Verifier] Check operands and result type of
 RISC-V vsetvli/vsetvlimax

The result of llvm.riscv.vsetvli/vsetvlimax models VLMAX (or a VL bounded
by it) and is only defined for XLen, so the result type must be i32 or i64.
Narrower types cannot represent the architectural VLMAX range of [1, 65536]
that value analyses rely on.

Also check that the VSEW and VLMUL immediate operands encode a valid
SEW/LMUL pair (VSEW <= 3, VLMUL != reserved), so that consumers can assume
a well-formed vtype.

Assisted-by: TRAE CLI (Opus 4.8)
---
 llvm/lib/IR/Verifier.cpp            | 23 +++++++++++++++++++++++
 llvm/test/Verifier/RISCV/vsetvli.ll | 29 +++++++++++++++++++++++++++++
 2 files changed, 52 insertions(+)
 create mode 100644 llvm/test/Verifier/RISCV/vsetvli.ll

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index 9cef4b05f19e5..a7300abdb5662 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -99,6 +99,7 @@
 #include "llvm/IR/IntrinsicsAArch64.h"
 #include "llvm/IR/IntrinsicsARM.h"
 #include "llvm/IR/IntrinsicsNVPTX.h"
+#include "llvm/IR/IntrinsicsRISCV.h"
 #include "llvm/IR/IntrinsicsWebAssembly.h"
 #include "llvm/IR/LLVMContext.h"
 #include "llvm/IR/MemoryModelRelaxationAnnotations.h"
@@ -126,6 +127,7 @@
 #include "llvm/Support/ModRef.h"
 #include "llvm/Support/TimeProfiler.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/TargetParser/RISCVTargetParser.h"
 #include "llvm/TargetParser/Triple.h"
 #include "llvm/Transforms/Coroutines/CoroInstr.h"
 #include <algorithm>
@@ -6976,6 +6978,27 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
           Call);
     break;
   }
+  case Intrinsic::riscv_vsetvli:
+  case Intrinsic::riscv_vsetvlimax: {
+    // The result models VLMAX (or a VL bounded by it) and is only defined for
+    // XLen (i32/i64). Narrower types cannot represent the architectural VLMAX
+    // range of [1, 65536], which value analyses rely on.
+    unsigned BitWidth = Call.getType()->getScalarSizeInBits();
+    Check(BitWidth == 32 || BitWidth == 64,
+          "llvm.riscv.vsetvli/vsetvlimax result must be i32 or i64", &Call);
+
+    // VSEW and VLMUL select the vtype and must encode a valid SEW/LMUL pair.
+    bool HasAVL = ID == Intrinsic::riscv_vsetvli;
+    unsigned Offset = HasAVL ? 1 : 0;
+    uint64_t VSEW =
+        cast<ConstantInt>(Call.getArgOperand(Offset))->getZExtValue();
+    uint64_t VLMUL =
+        cast<ConstantInt>(Call.getArgOperand(Offset + 1))->getZExtValue();
+    Check(VSEW <= 3, "llvm.riscv.vsetvli/vsetvlimax VSEW must be 0-3", &Call);
+    Check(VLMUL <= 7 && VLMUL != RISCVVType::LMUL_RESERVED,
+          "llvm.riscv.vsetvli/vsetvlimax VLMUL is reserved", &Call);
+    break;
+  }
   case Intrinsic::callbr_landingpad: {
     const auto *CBR = dyn_cast<CallBrInst>(Call.getOperand(0));
     Check(CBR, "intrinstic requires callbr operand", &Call);
diff --git a/llvm/test/Verifier/RISCV/vsetvli.ll b/llvm/test/Verifier/RISCV/vsetvli.ll
new file mode 100644
index 0000000000000..eec9ceb7e8e14
--- /dev/null
+++ b/llvm/test/Verifier/RISCV/vsetvli.ll
@@ -0,0 +1,29 @@
+; RUN: not opt -passes=verify -S < %s 2>&1 | FileCheck %s
+
+declare i16 @llvm.riscv.vsetvlimax.i16(i16, i16)
+declare i64 @llvm.riscv.vsetvlimax.i64(i64, i64)
+declare i64 @llvm.riscv.vsetvli.i64(i64, i64, i64)
+
+; CHECK: llvm.riscv.vsetvli/vsetvlimax result must be i32 or i64
+define i16 @narrow_result() {
+  %vl = call i16 @llvm.riscv.vsetvlimax.i16(i16 0, i16 3)
+  ret i16 %vl
+}
+
+; CHECK: llvm.riscv.vsetvli/vsetvlimax VSEW must be 0-3
+define i64 @bad_vsew() {
+  %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 5, i64 0)
+  ret i64 %vl
+}
+
+; CHECK: llvm.riscv.vsetvli/vsetvlimax VLMUL is reserved
+define i64 @reserved_vlmul() {
+  %vl = call i64 @llvm.riscv.vsetvlimax.i64(i64 0, i64 4)
+  ret i64 %vl
+}
+
+; CHECK: llvm.riscv.vsetvli/vsetvlimax VSEW must be 0-3
+define i64 @vsetvli_bad_vsew(i64 %avl) {
+  %vl = call i64 @llvm.riscv.vsetvli.i64(i64 %avl, i64 7, i64 0)
+  ret i64 %vl
+}

>From 5d505c809ab48791373fa6e959eca541a7b89324 Mon Sep 17 00:00:00 2001
From: Pengcheng Wang <wangpengcheng.pp at bytedance.com>
Date: Tue, 25 Aug 2026 18:09:23 +0800
Subject: [PATCH 2/2] Use isIntegerTy and remove declares

---
 llvm/lib/IR/Verifier.cpp            | 3 +--
 llvm/test/Verifier/RISCV/vsetvli.ll | 4 ----
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp
index a7300abdb5662..7b78acc5aba56 100644
--- a/llvm/lib/IR/Verifier.cpp
+++ b/llvm/lib/IR/Verifier.cpp
@@ -6983,8 +6983,7 @@ void Verifier::visitIntrinsicCall(Intrinsic::ID ID, CallBase &Call) {
     // The result models VLMAX (or a VL bounded by it) and is only defined for
     // XLen (i32/i64). Narrower types cannot represent the architectural VLMAX
     // range of [1, 65536], which value analyses rely on.
-    unsigned BitWidth = Call.getType()->getScalarSizeInBits();
-    Check(BitWidth == 32 || BitWidth == 64,
+    Check(Call.getType()->isIntegerTy(32) || Call.getType()->isIntegerTy(64),
           "llvm.riscv.vsetvli/vsetvlimax result must be i32 or i64", &Call);
 
     // VSEW and VLMUL select the vtype and must encode a valid SEW/LMUL pair.
diff --git a/llvm/test/Verifier/RISCV/vsetvli.ll b/llvm/test/Verifier/RISCV/vsetvli.ll
index eec9ceb7e8e14..82c13a4416c67 100644
--- a/llvm/test/Verifier/RISCV/vsetvli.ll
+++ b/llvm/test/Verifier/RISCV/vsetvli.ll
@@ -1,9 +1,5 @@
 ; RUN: not opt -passes=verify -S < %s 2>&1 | FileCheck %s
 
-declare i16 @llvm.riscv.vsetvlimax.i16(i16, i16)
-declare i64 @llvm.riscv.vsetvlimax.i64(i64, i64)
-declare i64 @llvm.riscv.vsetvli.i64(i64, i64, i64)
-
 ; CHECK: llvm.riscv.vsetvli/vsetvlimax result must be i32 or i64
 define i16 @narrow_result() {
   %vl = call i16 @llvm.riscv.vsetvlimax.i16(i16 0, i16 3)



More information about the llvm-commits mailing list