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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
