https://github.com/s-perron created https://github.com/llvm/llvm-project/pull/143180
The vk::constant_id attribute is used to indicate that a global const variable represents a specialization constant in SPIR-V. This PR adds this attribute to clang. The documetation for the attribute is [here](https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#specialization-constants). The strategy is to to modify the initializer to get the value of a specialize constant, and make the variable itself static. >From f62c588033f28126ac7f755c53a83d0540f8ac27 Mon Sep 17 00:00:00 2001 From: Steven Perron <stevenper...@google.com> Date: Fri, 30 May 2025 12:32:21 -0400 Subject: [PATCH] [HLSL][SPIRV] Add vk::constant_id attribute. The vk::constant_id attribute is used to indicate that a global const variable represents a specialization constant in SPIR-V. This PR adds this attribute to clang. The documetation for the attribute is [here](https://github.com/microsoft/DirectXShaderCompiler/blob/main/docs/SPIR-V.rst#specialization-constants). The strategy is to to modify the initializer to get the value of a specialize constant, and make the variable itself static. --- clang/include/clang/Basic/Attr.td | 8 + clang/include/clang/Basic/AttrDocs.td | 15 ++ clang/include/clang/Basic/Builtins.td | 13 ++ .../clang/Basic/DiagnosticSemaKinds.td | 15 ++ clang/include/clang/Sema/SemaHLSL.h | 5 +- clang/lib/Basic/Attributes.cpp | 3 +- clang/lib/CodeGen/CGHLSLBuiltins.cpp | 17 ++ clang/lib/Sema/SemaDecl.cpp | 14 ++ clang/lib/Sema/SemaDeclAttr.cpp | 3 + clang/lib/Sema/SemaHLSL.cpp | 133 ++++++++++- .../test/AST/HLSL/vk.spec-constant.usage.hlsl | 130 +++++++++++ .../SpirvType.alignment.hlsl | 0 .../SpirvType.hlsl | 0 .../vk-features/vk.spec-constant.hlsl | 210 ++++++++++++++++++ .../test/SemaHLSL/vk.spec-constant.error.hlsl | 37 +++ llvm/include/llvm/IR/IntrinsicsSPIRV.td | 4 + 16 files changed, 604 insertions(+), 3 deletions(-) create mode 100644 clang/test/AST/HLSL/vk.spec-constant.usage.hlsl rename clang/test/CodeGenHLSL/{inline-spirv => vk-features}/SpirvType.alignment.hlsl (100%) rename clang/test/CodeGenHLSL/{inline-spirv => vk-features}/SpirvType.hlsl (100%) create mode 100644 clang/test/CodeGenHLSL/vk-features/vk.spec-constant.hlsl create mode 100644 clang/test/SemaHLSL/vk.spec-constant.error.hlsl diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index f889e41c8699f..1d26af9d28daa 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -4993,6 +4993,14 @@ def HLSLVkExtBuiltinInput : InheritableAttr { let Documentation = [HLSLVkExtBuiltinInputDocs]; } +def HLSLVkConstantId : InheritableAttr { + let Spellings = [CXX11<"vk", "constant_id">]; + let Args = [IntArgument<"Id">]; + let Subjects = SubjectList<[Var]>; + let LangOpts = [HLSL]; + let Documentation = [VkConstantIdDocs]; +} + def RandomizeLayout : InheritableAttr { let Spellings = [GCC<"randomize_layout">]; let Subjects = SubjectList<[Record]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index a16218f038518..d5a617c4769fd 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8247,6 +8247,21 @@ and https://microsoft.github.io/hlsl-specs/proposals/0013-wave-size-range.html }]; } +def VkConstantIdDocs : Documentation { + let Category = DocCatFunction; + let Content = [{ +The ``vk::constant_id`` attribute specify the id for a SPIR-V specialization +constant. The attribute applies to const global scalar variables. The variable must be initialized with a C++11 constexpr. +In SPIR-V, the +variable will be replaced with an `OpSpecConstant` with the given id. +The syntax is: + +.. code-block:: text + + ``[[vk::constant_id(<Id>)]] const T Name = <Init>`` +}]; +} + def RootSignatureDocs : Documentation { let Category = DocCatFunction; let Content = [{ diff --git a/clang/include/clang/Basic/Builtins.td b/clang/include/clang/Basic/Builtins.td index b15cde05410ab..4db6ca3b36ff9 100644 --- a/clang/include/clang/Basic/Builtins.td +++ b/clang/include/clang/Basic/Builtins.td @@ -5059,6 +5059,19 @@ def HLSLGroupMemoryBarrierWithGroupSync: LangBuiltin<"HLSL_LANG"> { let Prototype = "void()"; } +class HLSLScalarTemplate + : Template<["bool", "char", "short", "int", "long long int", + "unsigned short", "unsigned int", "unsigned long long int", + "__fp16", "float", "double"], + ["_bool", "_char", "_short", "_int", "_longlong", "_ushort", + "_uint", "_ulonglong", "_half", "_float", "_double"]>; + +def HLSLGetSpirvSpecConstant : LangBuiltin<"HLSL_LANG">, HLSLScalarTemplate { + let Spellings = ["__builtin_get_spirv_spec_constant"]; + let Attributes = [NoThrow, Const, Pure]; + let Prototype = "T(unsigned int, T)"; +} + // Builtins for XRay. def XRayCustomEvent : Builtin { let Spellings = ["__xray_customevent"]; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8ba3bb099d741..624c0963ae366 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -12904,6 +12904,21 @@ def err_spirv_enum_not_int : Error< def err_spirv_enum_not_valid : Error< "invalid value for %select{storage class}0 argument">; +def err_specialization_const_lit_init + : Error<"variable with 'vk::constant_id' attribute cannot have an " + "initializer that is not a constexpr">; +def err_specialization_const_is_not_externally_visible + : Error<"variable with 'vk::constant_id' attribute must be externally " + "visible">; +def err_specialization_const_missing_initializer + : Error< + "variable with 'vk::constant_id' attribute must have an initializer">; +def err_specialization_const_missing_const + : Error<"variable with 'vk::constant_id' attribute must be const">; +def err_specialization_const_is_not_int_or_float + : Error<"variable with 'vk::constant_id' attribute must be an enum, bool, " + "integer, or floating point value">; + // errors of expect.with.probability def err_probability_not_constant_float : Error< "probability argument to __builtin_expect_with_probability must be constant " diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 66d09f49680be..099d9c35684e8 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -98,6 +98,8 @@ class SemaHLSL : public SemaBase { HLSLWaveSizeAttr *mergeWaveSizeAttr(Decl *D, const AttributeCommonInfo &AL, int Min, int Max, int Preferred, int SpelledArgsCount); + HLSLVkConstantIdAttr * + mergeVkConstantIdAttr(Decl *D, const AttributeCommonInfo &AL, int Id); HLSLShaderAttr *mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL, llvm::Triple::EnvironmentType ShaderType); HLSLParamModifierAttr * @@ -122,6 +124,7 @@ class SemaHLSL : public SemaBase { void handleRootSignatureAttr(Decl *D, const ParsedAttr &AL); void handleNumThreadsAttr(Decl *D, const ParsedAttr &AL); void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL); + void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL); void handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL); void handleSV_GroupThreadIDAttr(Decl *D, const ParsedAttr &AL); void handleSV_GroupIDAttr(Decl *D, const ParsedAttr &AL); @@ -156,7 +159,7 @@ class SemaHLSL : public SemaBase { QualType getInoutParameterType(QualType Ty); bool transformInitList(const InitializedEntity &Entity, InitListExpr *Init); - + bool handleInitialization(VarDecl *VDecl, Expr *&Init); void deduceAddressSpace(VarDecl *Decl); private: diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp index 905046685934b..5f74365f0ed00 100644 --- a/clang/lib/Basic/Attributes.cpp +++ b/clang/lib/Basic/Attributes.cpp @@ -213,7 +213,8 @@ getScopeFromNormalizedScopeName(StringRef ScopeName) { .Case("vk", AttributeCommonInfo::Scope::VK) .Case("msvc", AttributeCommonInfo::Scope::MSVC) .Case("omp", AttributeCommonInfo::Scope::OMP) - .Case("riscv", AttributeCommonInfo::Scope::RISCV); + .Case("riscv", AttributeCommonInfo::Scope::RISCV) + .Case("vk", AttributeCommonInfo::Scope::HLSL); } unsigned AttributeCommonInfo::calculateAttributeSpellingListIndex() const { diff --git a/clang/lib/CodeGen/CGHLSLBuiltins.cpp b/clang/lib/CodeGen/CGHLSLBuiltins.cpp index 10dd9fd04eb9e..3f199e1a3895f 100644 --- a/clang/lib/CodeGen/CGHLSLBuiltins.cpp +++ b/clang/lib/CodeGen/CGHLSLBuiltins.cpp @@ -769,6 +769,23 @@ Value *CodeGenFunction::EmitHLSLBuiltinExpr(unsigned BuiltinID, return EmitRuntimeCall( Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID)); } + case Builtin::BI__builtin_get_spirv_spec_constant_bool: + case Builtin::BI__builtin_get_spirv_spec_constant_short: + case Builtin::BI__builtin_get_spirv_spec_constant_ushort: + case Builtin::BI__builtin_get_spirv_spec_constant_int: + case Builtin::BI__builtin_get_spirv_spec_constant_uint: + case Builtin::BI__builtin_get_spirv_spec_constant_longlong: + case Builtin::BI__builtin_get_spirv_spec_constant_ulonglong: + case Builtin::BI__builtin_get_spirv_spec_constant_half: + case Builtin::BI__builtin_get_spirv_spec_constant_float: + case Builtin::BI__builtin_get_spirv_spec_constant_double: { + assert(CGM.getTarget().getTriple().isSPIRV() && "SPIR-V only"); + Intrinsic::ID ID = Intrinsic::spv_get_specialization_constant; + llvm::Type *T = CGM.getTypes().ConvertType(E->getType()); + auto F = Intrinsic::getOrInsertDeclaration(&CGM.getModule(), ID, {T}); + return EmitRuntimeCall( + F, {EmitScalarExpr(E->getArg(0)), EmitScalarExpr(E->getArg(1))}); + } } return nullptr; } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 60e911b9fecc0..c6ef680e77fa1 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -2889,6 +2889,8 @@ static bool mergeDeclAttribute(Sema &S, NamedDecl *D, NewAttr = S.HLSL().mergeWaveSizeAttr(D, *WS, WS->getMin(), WS->getMax(), WS->getPreferred(), WS->getSpelledArgsCount()); + else if (const auto *CI = dyn_cast<HLSLVkConstantIdAttr>(Attr)) + NewAttr = S.HLSL().mergeVkConstantIdAttr(D, *CI, CI->getId()); else if (const auto *SA = dyn_cast<HLSLShaderAttr>(Attr)) NewAttr = S.HLSL().mergeShaderAttr(D, *SA, SA->getType()); else if (isa<SuppressAttr>(Attr)) @@ -13755,6 +13757,10 @@ void Sema::AddInitializerToDecl(Decl *RealDecl, Expr *Init, bool DirectInit) { return; } + if (getLangOpts().HLSL) + if (!HLSL().handleInitialization(VDecl, Init)) + return; + // Get the decls type and save a reference for later, since // CheckInitializerTypes may change it. QualType DclT = VDecl->getType(), SavT = DclT; @@ -14215,6 +14221,14 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) { } } + // HLSL variable with the `vk::constant_id` attribute must be initialized. + if (!Var->isInvalidDecl() && Var->hasAttr<HLSLVkConstantIdAttr>()) { + Diag(Var->getLocation(), + diag::err_specialization_const_missing_initializer); + Var->setInvalidDecl(); + return; + } + if (!Var->isInvalidDecl() && RealDecl->hasAttr<LoaderUninitializedAttr>()) { if (Var->getStorageClass() == SC_Extern) { Diag(Var->getLocation(), diag::err_loader_uninitialized_extern_decl) diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index da0e3265767d8..e49bdda1a402b 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7560,6 +7560,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_HLSLVkExtBuiltinInput: S.HLSL().handleVkExtBuiltinInputAttr(D, AL); break; + case ParsedAttr::AT_HLSLVkConstantId: + S.HLSL().handleVkConstantIdAttr(D, AL); + break; case ParsedAttr::AT_HLSLSV_GroupThreadID: S.HLSL().handleSV_GroupThreadIDAttr(D, AL); break; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 9065cc5a1d4a5..9b63250680351 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -119,6 +119,40 @@ static ResourceClass getResourceClass(RegisterType RT) { llvm_unreachable("unexpected RegisterType value"); } +static Builtin::ID getSpecConstBuiltinId(QualType Type) { + const auto *BT = dyn_cast<BuiltinType>(Type); + if (!BT) { + if (!Type->isEnumeralType()) + return Builtin::NotBuiltin; + return Builtin::BI__builtin_get_spirv_spec_constant_int; + } + + switch (BT->getKind()) { + case BuiltinType::Bool: + return Builtin::BI__builtin_get_spirv_spec_constant_bool; + case BuiltinType::Short: + return Builtin::BI__builtin_get_spirv_spec_constant_short; + case BuiltinType::Int: + return Builtin::BI__builtin_get_spirv_spec_constant_int; + case BuiltinType::LongLong: + return Builtin::BI__builtin_get_spirv_spec_constant_longlong; + case BuiltinType::UShort: + return Builtin::BI__builtin_get_spirv_spec_constant_ushort; + case BuiltinType::UInt: + return Builtin::BI__builtin_get_spirv_spec_constant_uint; + case BuiltinType::ULongLong: + return Builtin::BI__builtin_get_spirv_spec_constant_ulonglong; + case BuiltinType::Half: + return Builtin::BI__builtin_get_spirv_spec_constant_half; + case BuiltinType::Float: + return Builtin::BI__builtin_get_spirv_spec_constant_float; + case BuiltinType::Double: + return Builtin::BI__builtin_get_spirv_spec_constant_double; + default: + return Builtin::NotBuiltin; + } +} + DeclBindingInfo *ResourceBindings::addDeclBindingInfo(const VarDecl *VD, ResourceClass ResClass) { assert(getDeclBindingInfo(VD, ResClass) == nullptr && @@ -607,6 +641,54 @@ HLSLWaveSizeAttr *SemaHLSL::mergeWaveSizeAttr(Decl *D, return Result; } +HLSLVkConstantIdAttr * +SemaHLSL::mergeVkConstantIdAttr(Decl *D, const AttributeCommonInfo &AL, + int Id) { + + auto &TargetInfo = getASTContext().getTargetInfo(); + if (TargetInfo.getTriple().getArch() != llvm::Triple::spirv) { + Diag(AL.getLoc(), diag::warn_attribute_ignored) << AL; + return nullptr; + } + + auto *VD = cast<VarDecl>(D); + + if (getSpecConstBuiltinId(VD->getType()) == Builtin::NotBuiltin) { + Diag(VD->getLocation(), diag::err_specialization_const_is_not_int_or_float); + return nullptr; + } + + if (VD->getStorageClass() != StorageClass::SC_None && + VD->getStorageClass() != StorageClass::SC_Extern) { + Diag(VD->getLocation(), + diag::err_specialization_const_is_not_externally_visible); + return nullptr; + } + + if (VD->isLocalVarDecl()) { + Diag(VD->getLocation(), + diag::err_specialization_const_is_not_externally_visible); + return nullptr; + } + + if (!VD->getType().isConstQualified()) { + Diag(VD->getLocation(), diag::err_specialization_const_missing_const); + return nullptr; + } + + if (HLSLVkConstantIdAttr *CI = D->getAttr<HLSLVkConstantIdAttr>()) { + if (CI->getId() != Id) { + Diag(CI->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL; + Diag(AL.getLoc(), diag::note_conflicting_attribute); + } + return nullptr; + } + + HLSLVkConstantIdAttr *Result = + ::new (getASTContext()) HLSLVkConstantIdAttr(getASTContext(), AL, Id); + return Result; +} + HLSLShaderAttr * SemaHLSL::mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL, llvm::Triple::EnvironmentType ShaderType) { @@ -1125,6 +1207,15 @@ void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) { HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID)); } +void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) { + uint32_t Id; + if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Id)) + return; + HLSLVkConstantIdAttr *NewAttr = mergeVkConstantIdAttr(D, AL, Id); + if (NewAttr) + D->addAttr(NewAttr); +} + bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) { const auto *VT = T->getAs<VectorType>(); @@ -3154,6 +3245,7 @@ static bool IsDefaultBufferConstantDecl(VarDecl *VD) { return VD->getDeclContext()->isTranslationUnit() && QT.getAddressSpace() == LangAS::Default && VD->getStorageClass() != SC_Static && + !VD->hasAttr<HLSLVkConstantIdAttr>() && !isInvalidConstantBufferLeafElementType(QT.getTypePtr()); } @@ -3221,7 +3313,8 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) { const Type *VarType = VD->getType().getTypePtr(); while (VarType->isArrayType()) VarType = VarType->getArrayElementTypeNoTypeQual(); - if (VarType->isHLSLResourceRecord()) { + if (VarType->isHLSLResourceRecord() || + VD->hasAttr<HLSLVkConstantIdAttr>()) { // Make the variable for resources static. The global externally visible // storage is accessed through the handle, which is a member. The variable // itself is not externally visible. @@ -3644,3 +3737,41 @@ bool SemaHLSL::transformInitList(const InitializedEntity &Entity, Init->updateInit(Ctx, I, NewInit->getInit(I)); return true; } + +bool SemaHLSL::handleInitialization(VarDecl *VDecl, Expr *&Init) { + const HLSLVkConstantIdAttr *ConstIdAttr = + VDecl->getAttr<HLSLVkConstantIdAttr>(); + if (!ConstIdAttr) + return true; + + ASTContext &Context = SemaRef.getASTContext(); + + APValue InitValue; + if (!Init->isCXX11ConstantExpr(Context, &InitValue)) { + Diag(VDecl->getLocation(), diag::err_specialization_const_lit_init); + VDecl->setInvalidDecl(); + return false; + } + + Builtin::ID BID = getSpecConstBuiltinId(VDecl->getType()); + + // Argument 1: The ID from the attribute + int ConstantID = ConstIdAttr->getId(); + llvm::APInt IDVal(Context.getIntWidth(Context.IntTy), ConstantID); + Expr *IdExpr = IntegerLiteral::Create(Context, IDVal, Context.IntTy, + ConstIdAttr->getLocation()); + + SmallVector<Expr *, 2> Args = {IdExpr, Init}; + Expr *C = SemaRef.BuildBuiltinCallExpr(Init->getExprLoc(), BID, Args); + if (C->getType()->getCanonicalTypeUnqualified() != + VDecl->getType()->getCanonicalTypeUnqualified()) { + C = SemaRef + .BuildCStyleCastExpr(SourceLocation(), + Context.getTrivialTypeSourceInfo( + Init->getType(), Init->getExprLoc()), + SourceLocation(), C) + .get(); + } + Init = C; + return true; +} diff --git a/clang/test/AST/HLSL/vk.spec-constant.usage.hlsl b/clang/test/AST/HLSL/vk.spec-constant.usage.hlsl new file mode 100644 index 0000000000000..c0955c1ea7b43 --- /dev/null +++ b/clang/test/AST/HLSL/vk.spec-constant.usage.hlsl @@ -0,0 +1,130 @@ +// RUN: %clang_cc1 -finclude-default-header -triple spirv-unknown-vulkan-compute -x hlsl -ast-dump -o - %s | FileCheck %s + +// CHECK: VarDecl {{.*}} bool_const 'const hlsl_private bool' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'bool' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'bool (*)(unsigned int, bool) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'bool (unsigned int, bool) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_bool' 'bool (unsigned int, bool) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1 +// CHECK-NEXT: CXXBoolLiteralExpr {{.*}} 'bool' true +[[vk::constant_id(1)]] +const bool bool_const = true; + +// CHECK: VarDecl {{.*}} short_const 'const hlsl_private short' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'short' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'short (*)(unsigned int, short) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'short (unsigned int, short) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_short' 'short (unsigned int, short) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 2 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'short' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 +[[vk::constant_id(2)]] +const short short_const = 4; + +// CHECK: VarDecl {{.*}} int_const 'const hlsl_private int' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'int' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(unsigned int, int) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'int (unsigned int, int) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_int' 'int (unsigned int, int) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 3 +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +[[vk::constant_id(3)]] +const int int_const = 5; + +// CHECK: VarDecl {{.*}} long_const 'const hlsl_private long long' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'long long' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'long long (*)(unsigned int, long long) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'long long (unsigned int, long long) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_longlong' 'long long (unsigned int, long long) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 4 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'long long' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 8 +[[vk::constant_id(4)]] +const long long long_const = 8; + +// CHECK: VarDecl {{.*}} ushort_const 'const hlsl_private unsigned short' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'unsigned short' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned short (*)(unsigned int, unsigned short) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned short (unsigned int, unsigned short) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_ushort' 'unsigned short (unsigned int, unsigned short) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 5 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned short' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 10 +[[vk::constant_id(5)]] +const unsigned short ushort_const = 10; + +// CHECK: VarDecl {{.*}} uint_const 'const hlsl_private unsigned int' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'unsigned int' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int (*)(unsigned int, unsigned int) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned int (unsigned int, unsigned int) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_uint' 'unsigned int (unsigned int, unsigned int) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 6 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 12 +[[vk::constant_id(6)]] +const unsigned int uint_const = 12; + + +// CHECK: VarDecl {{.*}} ulong_const 'const hlsl_private unsigned long long' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'unsigned long long' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned long long (*)(unsigned int, unsigned long long) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'unsigned long long (unsigned int, unsigned long long) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_ulonglong' 'unsigned long long (unsigned int, unsigned long long) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 7 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned long long' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 25 +[[vk::constant_id(7)]] +const unsigned long long ulong_const = 25; + +// CHECK: VarDecl {{.*}} half_const 'const hlsl_private half' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'half' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half (*)(unsigned int, half) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'half (unsigned int, half) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_half' 'half (unsigned int, half) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 8 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'half' <FloatingCast> +// CHECK-NEXT: FloatingLiteral {{.*}} 'float' 4.040000e+01 +[[vk::constant_id(8)]] +const half half_const = 40.4; + +// CHECK: VarDecl {{.*}} float_const 'const hlsl_private float' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'float' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float (*)(unsigned int, float) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'float (unsigned int, float) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_float' 'float (unsigned int, float) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 8 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'float' <IntegralToFloating> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 50 +[[vk::constant_id(8)]] +const float float_const = 50; + +// CHECK: VarDecl {{.*}} double_const 'const hlsl_private double' static cinit +// CHECK-NEXT: CallExpr {{.*}} 'double' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double (*)(unsigned int, double) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'double (unsigned int, double) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_double' 'double (unsigned int, double) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 9 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'double' <IntegralToFloating> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 100 +[[vk::constant_id(9)]] +const double double_const = 100; + +// CHECK: VarDecl {{.*}} enum_const 'const hlsl_private E' static cinit +// CHECK-NEXT: CStyleCastExpr {{.*}} 'E' <IntegralCast> +// CHECK-NEXT: CallExpr {{.*}} 'int' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int (*)(unsigned int, int) noexcept' <FunctionToPointerDecay> +// CHECK-NEXT: DeclRefExpr {{.*}} 'int (unsigned int, int) noexcept' lvalue Function {{.*}} '__builtin_get_spirv_spec_constant_int' 'int (unsigned int, int) noexcept' +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'unsigned int' <IntegralCast> +// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 10 +// CHECK-NEXT: ImplicitCastExpr {{.*}} 'int' <IntegralCast> +// CHECK-NEXT: DeclRefExpr {{.*}} 'E' EnumConstant {{.*}} 'e2' 'E' +enum E { + e0 = 10, + e1 = 20, + e2 = 30 +}; + +[[vk::constant_id(10)]] +const E enum_const = e2; + +// CHECK-NOT: CXXRecordDecl {{.*}} implicit struct __cblayout_$Globals definition diff --git a/clang/test/CodeGenHLSL/inline-spirv/SpirvType.alignment.hlsl b/clang/test/CodeGenHLSL/vk-features/SpirvType.alignment.hlsl similarity index 100% rename from clang/test/CodeGenHLSL/inline-spirv/SpirvType.alignment.hlsl rename to clang/test/CodeGenHLSL/vk-features/SpirvType.alignment.hlsl diff --git a/clang/test/CodeGenHLSL/inline-spirv/SpirvType.hlsl b/clang/test/CodeGenHLSL/vk-features/SpirvType.hlsl similarity index 100% rename from clang/test/CodeGenHLSL/inline-spirv/SpirvType.hlsl rename to clang/test/CodeGenHLSL/vk-features/SpirvType.hlsl diff --git a/clang/test/CodeGenHLSL/vk-features/vk.spec-constant.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.spec-constant.hlsl new file mode 100644 index 0000000000000..95a3968083f62 --- /dev/null +++ b/clang/test/CodeGenHLSL/vk-features/vk.spec-constant.hlsl @@ -0,0 +1,210 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --check-globals all --include-generated-funcs --version 5 +// RUN: %clang_cc1 -finclude-default-header -x hlsl -triple \ +// RUN: spirv-unknown-vulkan-compute %s -emit-llvm -disable-llvm-passes \ +// RUN: -o - | FileCheck %s + +[[vk::constant_id(1)]] +const bool bool_const = true; + +[[vk::constant_id(2)]] +const short short_const = 4; + +[[vk::constant_id(3)]] +const int int_const = 5; + +[[vk::constant_id(4)]] +const long long long_const = 8; + +[[vk::constant_id(5)]] +const unsigned short ushort_const = 10; + +[[vk::constant_id(6)]] +const unsigned int uint_const = 12; + +[[vk::constant_id(7)]] +const unsigned long long ulong_const = 25; + +[[vk::constant_id(8)]] +const half half_const = 40.4; + +[[vk::constant_id(8)]] +const float float_const = 50.5; + +[[vk::constant_id(9)]] +const double double_const = 100.2; + +enum E { + e0 = 10, + e1 = 20, + e2 = 30 +}; + +[[vk::constant_id(10)]] +const E enum_const = e2; + +[numthreads(1,1,1)] +void main() { + bool b = bool_const; + short s = short_const; + int i = int_const; + long long l = long_const; + unsigned short us = ushort_const; + unsigned int ui = uint_const; + unsigned long long ul = ulong_const; + half h = half_const; + float f = float_const; + double d = double_const; + E e = enum_const; +} +//. +// CHECK: @_ZL10bool_const = internal addrspace(10) global i32 0, align 4 +// CHECK: @_ZL11short_const = internal addrspace(10) global i16 0, align 2 +// CHECK: @_ZL9int_const = internal addrspace(10) global i32 0, align 4 +// CHECK: @_ZL10long_const = internal addrspace(10) global i64 0, align 8 +// CHECK: @_ZL12ushort_const = internal addrspace(10) global i16 0, align 2 +// CHECK: @_ZL10uint_const = internal addrspace(10) global i32 0, align 4 +// CHECK: @_ZL11ulong_const = internal addrspace(10) global i64 0, align 8 +// CHECK: @_ZL10half_const = internal addrspace(10) global float 0.000000e+00, align 4 +// CHECK: @_ZL11float_const = internal addrspace(10) global float 0.000000e+00, align 4 +// CHECK: @_ZL12double_const = internal addrspace(10) global double 0.000000e+00, align 8 +// CHECK: @_ZL10enum_const = internal addrspace(10) global i32 0, align 4 +//. +// CHECK-LABEL: define internal spir_func void @_Z4mainv( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[B:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[S:%.*]] = alloca i16, align 2 +// CHECK-NEXT: [[I:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[L:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[US:%.*]] = alloca i16, align 2 +// CHECK-NEXT: [[UI:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[UL:%.*]] = alloca i64, align 8 +// CHECK-NEXT: [[H:%.*]] = alloca float, align 4 +// CHECK-NEXT: [[F:%.*]] = alloca float, align 4 +// CHECK-NEXT: [[D:%.*]] = alloca double, align 8 +// CHECK-NEXT: [[E:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr addrspace(10) @_ZL10bool_const, align 4 +// CHECK-NEXT: [[LOADEDV:%.*]] = trunc i32 [[TMP1]] to i1 +// CHECK-NEXT: [[STOREDV:%.*]] = zext i1 [[LOADEDV]] to i32 +// CHECK-NEXT: store i32 [[STOREDV]], ptr [[B]], align 4 +// CHECK-NEXT: [[TMP2:%.*]] = load i16, ptr addrspace(10) @_ZL11short_const, align 2 +// CHECK-NEXT: store i16 [[TMP2]], ptr [[S]], align 2 +// CHECK-NEXT: [[TMP3:%.*]] = load i32, ptr addrspace(10) @_ZL9int_const, align 4 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[I]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = load i64, ptr addrspace(10) @_ZL10long_const, align 8 +// CHECK-NEXT: store i64 [[TMP4]], ptr [[L]], align 8 +// CHECK-NEXT: [[TMP5:%.*]] = load i16, ptr addrspace(10) @_ZL12ushort_const, align 2 +// CHECK-NEXT: store i16 [[TMP5]], ptr [[US]], align 2 +// CHECK-NEXT: [[TMP6:%.*]] = load i32, ptr addrspace(10) @_ZL10uint_const, align 4 +// CHECK-NEXT: store i32 [[TMP6]], ptr [[UI]], align 4 +// CHECK-NEXT: [[TMP7:%.*]] = load i64, ptr addrspace(10) @_ZL11ulong_const, align 8 +// CHECK-NEXT: store i64 [[TMP7]], ptr [[UL]], align 8 +// CHECK-NEXT: [[TMP8:%.*]] = load float, ptr addrspace(10) @_ZL10half_const, align 4 +// CHECK-NEXT: store float [[TMP8]], ptr [[H]], align 4 +// CHECK-NEXT: [[TMP9:%.*]] = load float, ptr addrspace(10) @_ZL11float_const, align 4 +// CHECK-NEXT: store float [[TMP9]], ptr [[F]], align 4 +// CHECK-NEXT: [[TMP10:%.*]] = load double, ptr addrspace(10) @_ZL12double_const, align 8 +// CHECK-NEXT: store double [[TMP10]], ptr [[D]], align 8 +// CHECK-NEXT: [[TMP11:%.*]] = load i32, ptr addrspace(10) @_ZL10enum_const, align 4 +// CHECK-NEXT: store i32 [[TMP11]], ptr [[E]], align 4 +// CHECK-NEXT: ret void +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init( +// CHECK-SAME: ) #[[ATTR3:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i1 @llvm.spv.get.specialization.constant.i1(i32 1, i1 true) +// CHECK-NEXT: [[STOREDV:%.*]] = zext i1 [[TMP1]] to i32 +// CHECK-NEXT: store i32 [[STOREDV]], ptr addrspace(10) @_ZL10bool_const, align 4 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.1( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i16 @llvm.spv.get.specialization.constant.i16(i32 2, i16 4) +// CHECK-NEXT: store i16 [[TMP1]], ptr addrspace(10) @_ZL11short_const, align 2 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.2( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i32 @llvm.spv.get.specialization.constant.i32(i32 3, i32 5) +// CHECK-NEXT: store i32 [[TMP1]], ptr addrspace(10) @_ZL9int_const, align 4 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.3( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i64 @llvm.spv.get.specialization.constant.i64(i32 4, i64 8) +// CHECK-NEXT: store i64 [[TMP1]], ptr addrspace(10) @_ZL10long_const, align 8 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.4( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i16 @llvm.spv.get.specialization.constant.i16(i32 5, i16 10) +// CHECK-NEXT: store i16 [[TMP1]], ptr addrspace(10) @_ZL12ushort_const, align 2 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.5( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i32 @llvm.spv.get.specialization.constant.i32(i32 6, i32 12) +// CHECK-NEXT: store i32 [[TMP1]], ptr addrspace(10) @_ZL10uint_const, align 4 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.6( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i64 @llvm.spv.get.specialization.constant.i64(i32 7, i64 25) +// CHECK-NEXT: store i64 [[TMP1]], ptr addrspace(10) @_ZL11ulong_const, align 8 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.7( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call reassoc nnan ninf nsz arcp afn spir_func float @llvm.spv.get.specialization.constant.f32(i32 8, float 0x4044333340000000) +// CHECK-NEXT: store float [[TMP1]], ptr addrspace(10) @_ZL10half_const, align 4 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.8( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call reassoc nnan ninf nsz arcp afn spir_func float @llvm.spv.get.specialization.constant.f32(i32 8, float 5.050000e+01) +// CHECK-NEXT: store float [[TMP1]], ptr addrspace(10) @_ZL11float_const, align 4 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.9( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call reassoc nnan ninf nsz arcp afn spir_func double @llvm.spv.get.specialization.constant.f64(i32 9, double 0x40590CCCC0000000) +// CHECK-NEXT: store double [[TMP1]], ptr addrspace(10) @_ZL12double_const, align 8 +// CHECK-NEXT: ret void +// +// +// CHECK-LABEL: define internal spir_func void @__cxx_global_var_init.10( +// CHECK-SAME: ) #[[ATTR3]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[TMP0:%.*]] = call token @llvm.experimental.convergence.entry() +// CHECK-NEXT: [[TMP1:%.*]] = call spir_func i32 @llvm.spv.get.specialization.constant.i32(i32 10, i32 30) +// CHECK-NEXT: store i32 [[TMP1]], ptr addrspace(10) @_ZL10enum_const, align 4 +// CHECK-NEXT: ret void diff --git a/clang/test/SemaHLSL/vk.spec-constant.error.hlsl b/clang/test/SemaHLSL/vk.spec-constant.error.hlsl new file mode 100644 index 0000000000000..ed4f69a4b5987 --- /dev/null +++ b/clang/test/SemaHLSL/vk.spec-constant.error.hlsl @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -finclude-default-header -triple spirv-pc-vulkan1.3-compute -verify %s +// RUN: %clang_cc1 -finclude-default-header -triple dxil-pc-shadermodel6.8-compute -verify %s + +#ifndef __spirv__ +// expected-warning@+2{{'constant_id' attribute ignored}} +#endif +[[vk::constant_id(0)]] +const bool sc0 = true; + +#ifdef __spirv__ +// expected-error@+2{{variable with 'vk::constant_id' attribute cannot have an initializer that is not a constexpr}} +[[vk::constant_id(1)]] +const bool sc1 = sc0; // error + +// expected-error@+2{{variable with 'vk::constant_id' attribute must be externally visible}} +[[vk::constant_id(2)]] +static const bool sc2 = false; // error + +// expected-error@+2{{variable with 'vk::constant_id' attribute must have an initializer}} +[[vk::constant_id(3)]] +const bool sc3; // error + +// expected-error@+2{{variable with 'vk::constant_id' attribute must be const}} +[[vk::constant_id(4)]] +bool sc4 = false; // error + +// expected-error@+2{{variable with 'vk::constant_id' attribute must be an enum, bool, integer, or floating point value}} +[[vk::constant_id(5)]] +const int2 sc5 = {0,0}; // error + +[numthreads(1,1,1)] +void main() { + // expected-error@+2{{variable with 'vk::constant_id' attribute must be externally visible}} + [[vk::constant_id(6)]] + const bool sc6 = false; // error +} +#endif diff --git a/llvm/include/llvm/IR/IntrinsicsSPIRV.td b/llvm/include/llvm/IR/IntrinsicsSPIRV.td index 8d984d6ce58df..01f335a2ad2b9 100644 --- a/llvm/include/llvm/IR/IntrinsicsSPIRV.td +++ b/llvm/include/llvm/IR/IntrinsicsSPIRV.td @@ -137,6 +137,10 @@ let TargetPrefix = "spv" in { : DefaultAttrsIntrinsic<[llvm_anyptr_ty], [llvm_any_ty, llvm_i32_ty], [IntrNoMem]>; + def int_spv_get_specialization_constant + : DefaultAttrsIntrinsic<[llvm_any_ty], [llvm_i32_ty, LLVMMatchType<0>], + [IntrNoMem, IntrWillReturn]>; + // Read a value from the image buffer. It does not translate directly to a // single OpImageRead because the result type is not necessarily a 4 element // vector. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits