[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