[clang] [llvm] [RISCV][LLVM][Clang] Add experimental `Zvvm` config intrinsics (PR #203774)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jun 14 08:58:36 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-ir
@llvm/pr-subscribers-clang-codegen
Author: Kiva (imkiva)
<details>
<summary>Changes</summary>
Adds initial LLVM and Clang support for the Zvvm/IME configuration APIs:
- Adds Clang builtins/macros for __riscv_ime_vlen(), __riscv_ime_lambda(), and __riscv_vsetlambda().
- Adds LLVM intrinsics for implementation geometry queries, selected vtype.lambda readback, and nonzero
lambda write/readback.
This does not add full VSETVLI high-field tracking or matrix operation intrinsics/codegen
---
Patch is 84.05 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/203774.diff
14 Files Affected:
- (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+2)
- (modified) clang/include/clang/Basic/riscv_vector.td (+28)
- (modified) clang/lib/CodeGen/TargetBuiltins/RISCV.cpp (+71)
- (modified) clang/lib/Sema/SemaRISCV.cpp (+35)
- (added) clang/test/CodeGen/RISCV/rvv-intrinsics-handcrafted/ime-config.c (+158)
- (added) clang/test/Sema/riscv-ime-vsetlambda.c (+38)
- (modified) llvm/include/llvm/IR/IntrinsicsRISCV.td (+25)
- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+282)
- (modified) llvm/lib/Target/RISCV/RISCVInstrInfoZvvm.td (+28)
- (modified) llvm/lib/Target/RISCV/RISCVSystemOperands.td (+1-1)
- (added) llvm/test/CodeGen/RISCV/ime-config-intrinsics-invalid-rv32.ll (+34)
- (added) llvm/test/CodeGen/RISCV/ime-config-intrinsics-invalid-rv64.ll (+34)
- (added) llvm/test/CodeGen/RISCV/ime-config-intrinsics-rv32.ll (+705)
- (added) llvm/test/CodeGen/RISCV/ime-config-intrinsics-rv64.ll (+735)
``````````diff
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a3b575b7ee63a..f9e6ba99377cc 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13601,6 +13601,8 @@ def err_riscv_builtin_requires_extension : Error<
"builtin requires%select{| at least one of the following extensions}0: %1">;
def err_riscv_builtin_invalid_lmul : Error<
"LMUL argument must be in the range [0,3] or [5,7]">;
+def err_riscv_builtin_invalid_ime_lambda : Error<
+ "constant argument to RISC-V IME vsetlambda builtin must be 0 or a power of two in the range [1, 64]">;
def err_riscv_type_requires_extension : Error<
"RISC-V type %0 requires the '%1' extension">;
def err_riscv_attribute_interrupt_requires_extension : Error<
diff --git a/clang/include/clang/Basic/riscv_vector.td b/clang/include/clang/Basic/riscv_vector.td
index c5ce8b7ae8fc1..f3651a9265e0e 100644
--- a/clang/include/clang/Basic/riscv_vector.td
+++ b/clang/include/clang/Basic/riscv_vector.td
@@ -2162,3 +2162,31 @@ let UnMaskedPolicyScheme = HasPassthruOperand in {
defm vpairo : RVVOutBuiltinSet<"vpairo", "csil", [["vv", "Uv", "UvUvUv"]]>;
}
}
+
+//===----------------------------------------------------------------------===//
+// Zvvm - Integrated Matrix Extension configuration builtins.
+//===----------------------------------------------------------------------===//
+
+let HeaderCode =
+[{
+#define __riscv_ime_vlen() __builtin_rvv_ime_vlen()
+#define __riscv_ime_lambda() __builtin_rvv_ime_lambda()
+#define __riscv_vsetlambda(lambda) __builtin_rvv_vsetlambda((size_t)(lambda))
+}] in
+def ime_config_macro: RVVHeader;
+
+let HasBuiltinAlias = false, HasVL = false, HasMasked = false,
+ UnMaskedPolicyScheme = NonePolicy, MaskedPolicyScheme = NonePolicy,
+ Log2LMUL = [0], RequiredFeatures = ["zvvmm"],
+ ManualCodegen = [{
+ return emitRVVIMEBuiltin(this, E, ReturnValue, ResultType, ID, Ops,
+ PolicyAttrs, IsMasked);
+ }] in
+{
+ let IRName = "ime_vlen" in
+ def ime_vlen : RVVBuiltin<"", "z", "i">;
+ let IRName = "ime_lambda" in
+ def ime_lambda : RVVBuiltin<"", "z", "i">;
+ let IRName = "ime_vsetlambda_nonzero" in
+ def vsetlambda : RVVBuiltin<"", "zz", "i">;
+}
diff --git a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
index 3bf7dd07d54d3..1b6e0b52c5dd8 100644
--- a/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
+++ b/clang/lib/CodeGen/TargetBuiltins/RISCV.cpp
@@ -308,6 +308,77 @@ emitRVVVsetvliBuiltin(CodeGenFunction *CGF, const CallExpr *E,
return Builder.CreateCall(F, Ops, "");
}
+static LLVM_ATTRIBUTE_NOINLINE Value *
+emitRVVIMEBuiltin(CodeGenFunction *CGF, const CallExpr *E,
+ ReturnValueSlot ReturnValue, llvm::Type *ResultType,
+ Intrinsic::ID ID, SmallVectorImpl<Value *> &Ops,
+ int PolicyAttrs, bool IsMasked) {
+ auto &Builder = CGF->Builder;
+ auto &CGM = CGF->CGM;
+
+ switch (ID) {
+ case Intrinsic::riscv_ime_vlen:
+ case Intrinsic::riscv_ime_lambda: {
+ assert(Ops.empty() && "unexpected IME geometry operands");
+ llvm::Function *F = CGM.getIntrinsic(ID, {ResultType});
+ return Builder.CreateCall(F);
+ }
+ case Intrinsic::riscv_ime_vsetlambda_nonzero: {
+ assert(Ops.size() == 1 && "unexpected vsetlambda arity");
+ Value *Req = Ops[0];
+
+ if (auto *C = dyn_cast<llvm::ConstantInt>(Req)) {
+ if (C->isZero()) {
+ llvm::Function *ReadF =
+ CGM.getIntrinsic(Intrinsic::riscv_ime_readlambda, {ResultType});
+ return Builder.CreateCall(ReadF);
+ }
+
+ llvm::Function *SetF = CGM.getIntrinsic(
+ Intrinsic::riscv_ime_vsetlambda_nonzero, {ResultType});
+ return Builder.CreateCall(SetF, {Req});
+ }
+
+ // Runtime value. The IME API defines requested_lambda == 0 as a read-only
+ // selected-lambda query, so emit real control flow instead of an
+ // unconditional vsetvl guarded only by a selected vtype value.
+ llvm::Function *Fn = Builder.GetInsertBlock()->getParent();
+ llvm::BasicBlock *ReadBB =
+ CGF->createBasicBlock("ime.vsetlambda.read", Fn);
+ llvm::BasicBlock *SetBB =
+ CGF->createBasicBlock("ime.vsetlambda.set", Fn);
+ llvm::BasicBlock *ContBB =
+ CGF->createBasicBlock("ime.vsetlambda.cont", Fn);
+
+ Value *IsZero =
+ Builder.CreateICmpEQ(Req, llvm::ConstantInt::get(ResultType, 0));
+ Builder.CreateCondBr(IsZero, ReadBB, SetBB);
+
+ Builder.SetInsertPoint(ReadBB);
+ llvm::Function *ReadF =
+ CGM.getIntrinsic(Intrinsic::riscv_ime_readlambda, {ResultType});
+ Value *ReadVal = Builder.CreateCall(ReadF);
+ Builder.CreateBr(ContBB);
+ ReadBB = Builder.GetInsertBlock();
+
+ Builder.SetInsertPoint(SetBB);
+ llvm::Function *SetF = CGM.getIntrinsic(
+ Intrinsic::riscv_ime_vsetlambda_nonzero, {ResultType});
+ Value *SetVal = Builder.CreateCall(SetF, {Req});
+ Builder.CreateBr(ContBB);
+ SetBB = Builder.GetInsertBlock();
+
+ Builder.SetInsertPoint(ContBB);
+ llvm::PHINode *Phi = Builder.CreatePHI(ResultType, 2);
+ Phi->addIncoming(ReadVal, ReadBB);
+ Phi->addIncoming(SetVal, SetBB);
+ return Phi;
+ }
+ default:
+ llvm_unreachable("unexpected IME builtin");
+ }
+}
+
static LLVM_ATTRIBUTE_NOINLINE Value *
emitRVVVSEMaskBuiltin(CodeGenFunction *CGF, const CallExpr *E,
ReturnValueSlot ReturnValue, llvm::Type *ResultType,
diff --git a/clang/lib/Sema/SemaRISCV.cpp b/clang/lib/Sema/SemaRISCV.cpp
index 9647a7d913744..095e1cc5e808c 100644
--- a/clang/lib/Sema/SemaRISCV.cpp
+++ b/clang/lib/Sema/SemaRISCV.cpp
@@ -26,6 +26,7 @@
#include "clang/Sema/Sema.h"
#include "clang/Support/RISCVVIntrinsicUtils.h"
#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/MathExtras.h"
#include "llvm/TargetParser/RISCVISAInfo.h"
#include "llvm/TargetParser/RISCVTargetParser.h"
#include <optional>
@@ -677,11 +678,45 @@ bool SemaRISCV::CheckBuiltinFunctionCall(const TargetInfo &TI,
return SemaRef.BuiltinConstantArgRange(TheCall, SEWOffset, 0, 3) ||
CheckLMUL(TheCall, LMULOffset);
};
+
+ auto CheckIMEVSetLambda = [&]() -> bool {
+ assert(TheCall->getNumArgs() == 1 && "unexpected vsetlambda arity");
+
+ Expr *Arg = TheCall->getArg(0);
+ if (Arg->isTypeDependent() || Arg->isValueDependent())
+ return false;
+ Expr *DiagArg = Arg->IgnoreParenCasts();
+
+ Expr::EvalResult Eval;
+ Expr *EvalArg = DiagArg;
+ // Prefer evaluating the user source expression before the macro-introduced
+ // (size_t) cast. This catches constants that would otherwise wrap into a
+ // valid size_t value on RV32, e.g. 0x100000004ULL -> 4.
+ if (!EvalArg->EvaluateAsInt(Eval, Context, Expr::SE_NoSideEffects))
+ return false;
+
+ llvm::APSInt Val = Eval.Val.getInt();
+ if (Val.isSigned() && Val.isNegative())
+ return Diag(DiagArg->getBeginLoc(),
+ diag::err_riscv_builtin_invalid_ime_lambda)
+ << DiagArg->getSourceRange();
+
+ uint64_t U = Val.getLimitedValue(65);
+ if (U != 0 && (U > 64 || !llvm::isPowerOf2_64(U)))
+ return Diag(DiagArg->getBeginLoc(),
+ diag::err_riscv_builtin_invalid_ime_lambda)
+ << DiagArg->getSourceRange();
+
+ return false;
+ };
+
switch (BuiltinID) {
case RISCVVector::BI__builtin_rvv_vsetvli:
return CheckVSetVL(1, 2);
case RISCVVector::BI__builtin_rvv_vsetvlimax:
return CheckVSetVL(0, 1);
+ case RISCVVector::BI__builtin_rvv_vsetlambda:
+ return CheckIMEVSetLambda();
case RISCVVector::BI__builtin_rvv_sf_vsettnt:
case RISCVVector::BI__builtin_rvv_sf_vsettm:
case RISCVVector::BI__builtin_rvv_sf_vsettn:
diff --git a/clang/test/CodeGen/RISCV/rvv-intrinsics-handcrafted/ime-config.c b/clang/test/CodeGen/RISCV/rvv-intrinsics-handcrafted/ime-config.c
new file mode 100644
index 0000000000000..d69a91fae7297
--- /dev/null
+++ b/clang/test/CodeGen/RISCV/rvv-intrinsics-handcrafted/ime-config.c
@@ -0,0 +1,158 @@
+// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv32 -target-feature +v \
+// RUN: -target-feature +experimental-zvvmm -disable-O0-optnone \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=RV32 %s
+// RUN: %clang_cc1 -triple riscv64 -target-feature +v \
+// RUN: -target-feature +experimental-zvvmm -disable-O0-optnone \
+// RUN: -emit-llvm -o - %s | FileCheck --check-prefix=RV64 %s
+
+#include <stddef.h>
+#include <riscv_vector.h>
+
+// RV32-LABEL: define dso_local i32 @test_ime_vlen(
+// RV32-SAME: ) #[[ATTR0:[0-9]+]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.ime.vlen.i32()
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local i64 @test_ime_vlen(
+// RV64-SAME: ) #[[ATTR0:[0-9]+]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.ime.vlen.i64()
+// RV64-NEXT: ret i64 [[TMP0]]
+//
+size_t test_ime_vlen(void) {
+ return __riscv_ime_vlen();
+}
+
+// RV32-LABEL: define dso_local i32 @test_ime_lambda(
+// RV32-SAME: ) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.ime.lambda.i32()
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local i64 @test_ime_lambda(
+// RV64-SAME: ) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.ime.lambda.i64()
+// RV64-NEXT: ret i64 [[TMP0]]
+//
+size_t test_ime_lambda(void) {
+ return __riscv_ime_lambda();
+}
+
+// RV32-LABEL: define dso_local i32 @test_vsetlambda(
+// RV32-SAME: ) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.ime.vsetlambda.nonzero.i32(i32 4)
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local i64 @test_vsetlambda(
+// RV64-SAME: ) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.ime.vsetlambda.nonzero.i64(i64 4)
+// RV64-NEXT: ret i64 [[TMP0]]
+//
+size_t test_vsetlambda(void) {
+ return __riscv_vsetlambda(4);
+}
+
+// RV32-LABEL: define dso_local i32 @test_vsetlambda_zero(
+// RV32-SAME: ) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.ime.readlambda.i32()
+// RV32-NEXT: ret i32 [[TMP0]]
+//
+// RV64-LABEL: define dso_local i64 @test_vsetlambda_zero(
+// RV64-SAME: ) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.ime.readlambda.i64()
+// RV64-NEXT: ret i64 [[TMP0]]
+//
+size_t test_vsetlambda_zero(void) {
+ return __riscv_vsetlambda(0);
+}
+
+// RV32-LABEL: define dso_local i32 @test_vsetlambda_runtime(
+// RV32-SAME: i32 noundef [[X:%.*]]) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4
+// RV32-NEXT: store i32 [[X]], ptr [[X_ADDR]], align 4
+// RV32-NEXT: [[TMP0:%.*]] = load i32, ptr [[X_ADDR]], align 4
+// RV32-NEXT: [[TMP1:%.*]] = icmp eq i32 [[TMP0]], 0
+// RV32-NEXT: br i1 [[TMP1]], label %[[IME_VSETLAMBDA_READ:.*]], label %[[IME_VSETLAMBDA_SET:.*]]
+// RV32: [[IME_VSETLAMBDA_READ]]:
+// RV32-NEXT: [[TMP2:%.*]] = call i32 @llvm.riscv.ime.readlambda.i32()
+// RV32-NEXT: br label %[[IME_VSETLAMBDA_CONT:.*]]
+// RV32: [[IME_VSETLAMBDA_SET]]:
+// RV32-NEXT: [[TMP3:%.*]] = call i32 @llvm.riscv.ime.vsetlambda.nonzero.i32(i32 [[TMP0]])
+// RV32-NEXT: br label %[[IME_VSETLAMBDA_CONT]]
+// RV32: [[IME_VSETLAMBDA_CONT]]:
+// RV32-NEXT: [[TMP4:%.*]] = phi i32 [ [[TMP2]], %[[IME_VSETLAMBDA_READ]] ], [ [[TMP3]], %[[IME_VSETLAMBDA_SET]] ]
+// RV32-NEXT: ret i32 [[TMP4]]
+//
+// RV64-LABEL: define dso_local i64 @test_vsetlambda_runtime(
+// RV64-SAME: i64 noundef [[X:%.*]]) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[X_ADDR:%.*]] = alloca i64, align 8
+// RV64-NEXT: store i64 [[X]], ptr [[X_ADDR]], align 8
+// RV64-NEXT: [[TMP0:%.*]] = load i64, ptr [[X_ADDR]], align 8
+// RV64-NEXT: [[TMP1:%.*]] = icmp eq i64 [[TMP0]], 0
+// RV64-NEXT: br i1 [[TMP1]], label %[[IME_VSETLAMBDA_READ:.*]], label %[[IME_VSETLAMBDA_SET:.*]]
+// RV64: [[IME_VSETLAMBDA_READ]]:
+// RV64-NEXT: [[TMP2:%.*]] = call i64 @llvm.riscv.ime.readlambda.i64()
+// RV64-NEXT: br label %[[IME_VSETLAMBDA_CONT:.*]]
+// RV64: [[IME_VSETLAMBDA_SET]]:
+// RV64-NEXT: [[TMP3:%.*]] = call i64 @llvm.riscv.ime.vsetlambda.nonzero.i64(i64 [[TMP0]])
+// RV64-NEXT: br label %[[IME_VSETLAMBDA_CONT]]
+// RV64: [[IME_VSETLAMBDA_CONT]]:
+// RV64-NEXT: [[TMP4:%.*]] = phi i64 [ [[TMP2]], %[[IME_VSETLAMBDA_READ]] ], [ [[TMP3]], %[[IME_VSETLAMBDA_SET]] ]
+// RV64-NEXT: ret i64 [[TMP4]]
+//
+size_t test_vsetlambda_runtime(size_t x) {
+ return __riscv_vsetlambda(x);
+}
+
+// RV32-LABEL: define dso_local i32 @test_vsetlambda_save_restore(
+// RV32-SAME: ) #[[ATTR0]] {
+// RV32-NEXT: [[ENTRY:.*:]]
+// RV32-NEXT: [[SAVED:%.*]] = alloca i32, align 4
+// RV32-NEXT: [[TMP0:%.*]] = call i32 @llvm.riscv.ime.readlambda.i32()
+// RV32-NEXT: store i32 [[TMP0]], ptr [[SAVED]], align 4
+// RV32-NEXT: [[TMP1:%.*]] = load i32, ptr [[SAVED]], align 4
+// RV32-NEXT: [[TMP2:%.*]] = icmp eq i32 [[TMP1]], 0
+// RV32-NEXT: br i1 [[TMP2]], label %[[IME_VSETLAMBDA_READ:.*]], label %[[IME_VSETLAMBDA_SET:.*]]
+// RV32: [[IME_VSETLAMBDA_READ]]:
+// RV32-NEXT: [[TMP3:%.*]] = call i32 @llvm.riscv.ime.readlambda.i32()
+// RV32-NEXT: br label %[[IME_VSETLAMBDA_CONT:.*]]
+// RV32: [[IME_VSETLAMBDA_SET]]:
+// RV32-NEXT: [[TMP4:%.*]] = call i32 @llvm.riscv.ime.vsetlambda.nonzero.i32(i32 [[TMP1]])
+// RV32-NEXT: br label %[[IME_VSETLAMBDA_CONT]]
+// RV32: [[IME_VSETLAMBDA_CONT]]:
+// RV32-NEXT: [[TMP5:%.*]] = phi i32 [ [[TMP3]], %[[IME_VSETLAMBDA_READ]] ], [ [[TMP4]], %[[IME_VSETLAMBDA_SET]] ]
+// RV32-NEXT: ret i32 [[TMP5]]
+//
+// RV64-LABEL: define dso_local i64 @test_vsetlambda_save_restore(
+// RV64-SAME: ) #[[ATTR0]] {
+// RV64-NEXT: [[ENTRY:.*:]]
+// RV64-NEXT: [[SAVED:%.*]] = alloca i64, align 8
+// RV64-NEXT: [[TMP0:%.*]] = call i64 @llvm.riscv.ime.readlambda.i64()
+// RV64-NEXT: store i64 [[TMP0]], ptr [[SAVED]], align 8
+// RV64-NEXT: [[TMP1:%.*]] = load i64, ptr [[SAVED]], align 8
+// RV64-NEXT: [[TMP2:%.*]] = icmp eq i64 [[TMP1]], 0
+// RV64-NEXT: br i1 [[TMP2]], label %[[IME_VSETLAMBDA_READ:.*]], label %[[IME_VSETLAMBDA_SET:.*]]
+// RV64: [[IME_VSETLAMBDA_READ]]:
+// RV64-NEXT: [[TMP3:%.*]] = call i64 @llvm.riscv.ime.readlambda.i64()
+// RV64-NEXT: br label %[[IME_VSETLAMBDA_CONT:.*]]
+// RV64: [[IME_VSETLAMBDA_SET]]:
+// RV64-NEXT: [[TMP4:%.*]] = call i64 @llvm.riscv.ime.vsetlambda.nonzero.i64(i64 [[TMP1]])
+// RV64-NEXT: br label %[[IME_VSETLAMBDA_CONT]]
+// RV64: [[IME_VSETLAMBDA_CONT]]:
+// RV64-NEXT: [[TMP5:%.*]] = phi i64 [ [[TMP3]], %[[IME_VSETLAMBDA_READ]] ], [ [[TMP4]], %[[IME_VSETLAMBDA_SET]] ]
+// RV64-NEXT: ret i64 [[TMP5]]
+//
+size_t test_vsetlambda_save_restore(void) {
+ size_t saved = __riscv_vsetlambda(0);
+ return __riscv_vsetlambda(saved);
+}
diff --git a/clang/test/Sema/riscv-ime-vsetlambda.c b/clang/test/Sema/riscv-ime-vsetlambda.c
new file mode 100644
index 0000000000000..0c41cdc491de2
--- /dev/null
+++ b/clang/test/Sema/riscv-ime-vsetlambda.c
@@ -0,0 +1,38 @@
+// REQUIRES: riscv-registered-target
+// RUN: %clang_cc1 -triple riscv32 -target-feature +v \
+// RUN: -target-feature +experimental-zvvmm -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple riscv64 -target-feature +v \
+// RUN: -target-feature +experimental-zvvmm -fsyntax-only -verify %s
+
+#include <stddef.h>
+#include <riscv_vector.h>
+
+void ok(void) {
+ __riscv_vsetlambda(0);
+ __riscv_vsetlambda(1);
+ __riscv_vsetlambda(2);
+ __riscv_vsetlambda(4);
+ __riscv_vsetlambda(8);
+ __riscv_vsetlambda(16);
+ __riscv_vsetlambda(32);
+ __riscv_vsetlambda(64);
+}
+
+void bad_value(void) {
+ __riscv_vsetlambda(3); // expected-error {{constant argument to RISC-V IME vsetlambda builtin must be 0 or a power of two in the range [1, 64]}}
+ __riscv_vsetlambda(128); // expected-error {{constant argument to RISC-V IME vsetlambda builtin must be 0 or a power of two in the range [1, 64]}}
+ __riscv_vsetlambda(-1); // expected-error {{constant argument to RISC-V IME vsetlambda builtin must be 0 or a power of two in the range [1, 64]}}
+}
+
+void ok_runtime(size_t x) {
+ __riscv_vsetlambda(x);
+ __riscv_vsetlambda(x++);
+}
+
+void bad_wrap(void) {
+ __riscv_vsetlambda(0x100000004ULL); // expected-error {{constant argument to RISC-V IME vsetlambda builtin must be 0 or a power of two in the range [1, 64]}}
+ __riscv_vsetlambda(-4294967292LL); // expected-error {{constant argument to RISC-V IME vsetlambda builtin must be 0 or a power of two in the range [1, 64]}}
+#if __SIZEOF_POINTER__ == 8
+ __riscv_vsetlambda(((__int128)1) << 70); // expected-error {{constant argument to RISC-V IME vsetlambda builtin must be 0 or a power of two in the range [1, 64]}}
+#endif
+}
diff --git a/llvm/include/llvm/IR/IntrinsicsRISCV.td b/llvm/include/llvm/IR/IntrinsicsRISCV.td
index f53f752c25c30..8d809ad80f133 100644
--- a/llvm/include/llvm/IR/IntrinsicsRISCV.td
+++ b/llvm/include/llvm/IR/IntrinsicsRISCV.td
@@ -2063,6 +2063,31 @@ let TargetPrefix = "riscv" in {
defm vfncvt_sat_f_f_q_alt : RISCVConversionRoundingMode;
} // TargetPrefix = "riscv"
+//===----------------------------------------------------------------------===//
+// Zvvm - Integrated Matrix Extension
+//
+// These intrinsics expose IME configuration queries and vtype.lambda control.
+// They use llvm_anyint_ty for consistency with RVV configuration intrinsics,
+// but the only supported type is XLen.
+let TargetPrefix = "riscv" in {
+ // Implementation geometry helpers.
+ def int_riscv_ime_vlen : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>;
+ def int_riscv_ime_lambda : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem]>;
+
+ // Current selected vtype.lambda readback. This is not a memory operation,
+ // but keep it conservative until LLVM IR has a first-class vtype state model.
+ def int_riscv_ime_readlambda
+ : Intrinsic<[llvm_anyint_ty], [], [IntrNoMem, IntrHasSideEffects]>;
+
+ // Write a nonzero requested lambda and return the established lambda.
+ // Source-level contract: the argument is a positive power of two in
+ // {1,2,4,8,16,32,64}. Clang emits a separate readlambda path for
+ // requested_lambda == 0 before calling this primitive.
+ def int_riscv_ime_vsetlambda_nonzero
+ : Intrinsic<[llvm_anyint_ty], [LLVMMatchType<0>],
+ [IntrNoMem, IntrHasSideEffects]>;
+} // TargetPrefix = "riscv"
+
// Vendor extensions
//===----------------------------------------------------------------------===//
include "llvm/IR/IntrinsicsRISCVXTHead.td"
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 753901d71baca..3e3d384ce76e5 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -11567,6 +11567,279 @@ static SDValue lowerGetVectorLength(SDNode *N, SelectionDAG &DAG,
return DAG.getNode(ISD::TRUNCATE, DL, N->getValueType(0), Res);
}
+static unsigned getIMELambdaShift(const RISCVSubtarget &Subtarget) {
+ return Subtarget.getXLen() - 4;
+}
+
+static uint64_t getIMELambdaFieldMask(const RISCVSubtarget &Subtarget) {
+ return UINT64_C(7) << getIMELambdaShift(Subtarget);
+}
+
+static uint64_t getIMEClearLambdaMask(const RISCVSubtarget &Subtarget) {
+ uint64_t Mask = ~getIMELambdaFieldMask(Subtarget);
+ if (!Subtarget.is64Bit())
+ Mask = static_cast<uint32_t>(Mask);
+ return Mask;
+}
+
+static bool isValidIMELambdaValue(uint64_t Value) {
+ return Value != 0 && Value <= 64 && isPowerOf2_64(Value);
+}
+
+// The IME implementation lambda is derived from implementation VLEN using the
+// representative shape from the spec:
+//
+// VLEN = 64 * lambda^2
+//
+// For a known VLEN in bits this gives:
+//
+// log2(lambda) = (log2(VLEN) - log2(64)) / 2
+// = (log2(VLEN) - 6) / 2
+//
+// Values below VLEN=64 produce lambda=1. The selected vtype.lambda encoding
+// has seven non-zero values, so the maxi...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/203774
More information about the cfe-commits
mailing list