-
Notifications
You must be signed in to change notification settings - Fork 15.2k
[HLSL][SPIR-V] Implement vk::push_constant #166793
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
clang/lib/Sema/SemaHLSL.cpp
Outdated
| !VD->hasAttr<HLSLVkConstantIdAttr>() && | ||
| !VD->hasAttr<HLSLVkPushConstantAttr>() && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is where we should be ignoring the vk::push_constant attribuute if we are not targeting spir-v.
We might need to fix up the vk:constant_id attribute as well, but that is not your problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Right, and I should also add a test targeting DXIL but using vk::push_constant
8576183 to
d326ece
Compare
Implements initial support for vk::push_constant. As is, this allows handling simple push constants, but has one main issue: layout can be incorrect. The old fix would be to use target specific types, but this is actively being reworked on for cbuffers (llvm#147352). So for now, this part is marked as XFAIL.
d326ece to
fb3dec4
Compare
|
@llvm/pr-subscribers-clang-codegen @llvm/pr-subscribers-clang Author: Nathan Gauër (Keenuts) ChangesImplements initial support for vk::push_constant. The old fix would be to use target specific types, but this is Patch is 27.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/166793.diff 33 Files Affected:
diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h
index 48e4a1c61fe02..7280b8fc923d2 100644
--- a/clang/include/clang/Basic/AddressSpaces.h
+++ b/clang/include/clang/Basic/AddressSpaces.h
@@ -62,6 +62,7 @@ enum class LangAS : unsigned {
hlsl_private,
hlsl_device,
hlsl_input,
+ hlsl_push_constant,
// Wasm specific address spaces.
wasm_funcref,
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8dfe4bc08c48e..e00765a57cb23 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5146,6 +5146,14 @@ def HLSLVkExtBuiltinInput : InheritableAttr {
let Documentation = [HLSLVkExtBuiltinInputDocs];
}
+def HLSLVkPushConstant : InheritableAttr {
+ let Spellings = [CXX11<"vk", "push_constant">];
+ let Args = [];
+ let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkPushConstantDocs];
+}
+
def HLSLVkConstantId : InheritableAttr {
let Spellings = [CXX11<"vk", "constant_id">];
let Args = [IntArgument<"Id">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 4813191d2d602..3938c624c9d0c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8777,6 +8777,11 @@ https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md
}];
}
+def HLSLVkPushConstantDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{ FIXME }];
+}
+
def AnnotateTypeDocs : Documentation {
let Category = DocCatType;
let Heading = "annotate_type";
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a6e60fe4692ee..c9d128d77c90f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13183,6 +13183,9 @@ def err_hlsl_attr_invalid_type : Error<
"attribute %0 only applies to a field or parameter of type '%1'">;
def err_hlsl_attr_invalid_ast_node : Error<
"attribute %0 only applies to %1">;
+def err_hlsl_attr_incompatible
+ : Error<"%0 attribute is not compatible with %1 attribute">;
+
def err_hlsl_entry_shader_attr_mismatch : Error<
"%0 attribute on entry function does not match the target profile">;
def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numthreads attribute cannot exceed %1">;
@@ -13294,6 +13297,9 @@ def err_hlsl_incomplete_resource_array_in_function_param: Error<
def err_hlsl_assign_to_global_resource: Error<
"assignment to global resource variable %0 is not allowed">;
+def err_hlsl_push_constant_unique
+ : Error<"cannot have more than one push constant block">;
+
// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Basic/HLSLRuntime.h b/clang/include/clang/Basic/HLSLRuntime.h
index 03166805daa6a..f6a1cf9636467 100644
--- a/clang/include/clang/Basic/HLSLRuntime.h
+++ b/clang/include/clang/Basic/HLSLRuntime.h
@@ -14,6 +14,7 @@
#ifndef CLANG_BASIC_HLSLRUNTIME_H
#define CLANG_BASIC_HLSLRUNTIME_H
+#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/LangOptions.h"
#include <cstdint>
@@ -30,6 +31,10 @@ getStageFromEnvironment(const llvm::Triple::EnvironmentType &E) {
return static_cast<ShaderStage>(Pipeline);
}
+constexpr bool isInitializedByPipeline(LangAS AS) {
+ return AS == LangAS::hlsl_input || AS == LangAS::hlsl_push_constant;
+}
+
#define ENUM_COMPARE_ASSERT(Value) \
static_assert( \
getStageFromEnvironment(llvm::Triple::Value) == ShaderStage::Value, \
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 86da323892f98..2fcac237eba1c 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -190,6 +190,7 @@ class SemaHLSL : public SemaBase {
void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL);
bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
QualType ProcessResourceTypeAttributes(QualType Wrapped);
@@ -239,6 +240,8 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ bool HasDeclaredAPushConstant = false;
+
struct SemanticInfo {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4548af17e37f2..53082bcf78f6a 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -101,6 +101,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
(A == LangAS::Default && B == LangAS::hlsl_private) ||
(A == LangAS::Default && B == LangAS::hlsl_device) ||
(A == LangAS::Default && B == LangAS::hlsl_input) ||
+ (A == LangAS::Default && B == LangAS::hlsl_push_constant) ||
// Conversions from target specific address spaces may be legal
// depending on the target information.
Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index c18b2eafc722c..8448dd3748e28 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2749,6 +2749,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) {
return "hlsl_device";
case LangAS::hlsl_input:
return "hlsl_input";
+ case LangAS::hlsl_push_constant:
+ return "hlsl_push_constant";
case LangAS::wasm_funcref:
return "__funcref";
default:
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index ffaf98bf9c366..92ca7a66a9593 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -52,6 +52,7 @@ static const LangASMap FakeAddrSpaceMap = {
15, // hlsl_private
16, // hlsl_device
17, // hlsl_input
+ 18, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 1a7aa658e9d87..8e8d8e6ae86b5 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -48,6 +48,7 @@ static const unsigned ARM64AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp
index d4d696b8456b6..993a73a89c9e9 100644
--- a/clang/lib/Basic/Targets/AMDGPU.cpp
+++ b/clang/lib/Basic/Targets/AMDGPU.cpp
@@ -63,6 +63,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
@@ -91,6 +92,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
} // namespace targets
} // namespace clang
diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h
index a21a593365773..c0799a6f7610f 100644
--- a/clang/lib/Basic/Targets/DirectX.h
+++ b/clang/lib/Basic/Targets/DirectX.h
@@ -46,6 +46,7 @@ static const unsigned DirectXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h
index f5c8396f398aa..6338a4f2f9036 100644
--- a/clang/lib/Basic/Targets/NVPTX.h
+++ b/clang/lib/Basic/Targets/NVPTX.h
@@ -50,6 +50,7 @@ static const unsigned NVPTXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index 22b2799518dd0..94449231efb94 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -51,6 +51,7 @@ static const unsigned SPIRDefIsPrivMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
@@ -87,6 +88,7 @@ static const unsigned SPIRDefIsGenMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index 4e15d5af1cde6..4ce515b31a001 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -46,6 +46,7 @@ static const unsigned ZOSAddressMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
0 // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h
index 005cab9819472..161025378c471 100644
--- a/clang/lib/Basic/Targets/TCE.h
+++ b/clang/lib/Basic/Targets/TCE.h
@@ -55,6 +55,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 4de6ce6bb5a21..c8065843aeb42 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -46,6 +46,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index e7da2622e78b5..7b88ac70e234f 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -50,6 +50,7 @@ static const unsigned X86AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 3eeb1718e455a..c3b536c7a267f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6049,9 +6049,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
getCUDARuntime().handleVarRegistration(D, *GV);
}
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input) {
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D))) {
// HLSL Input variables are considered to be set by the driver/pipeline, but
- // only visible to a single thread/wave.
+ // only visible to a single thread/wave. Push constants are also externally
+ // initialized, but constant, hence cross-wave visibility is not relevant.
GV->setExternallyInitialized(true);
} else {
GV->setInitializer(Init);
@@ -6102,10 +6104,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
!D->hasAttr<ConstInitAttr>())
Linkage = llvm::GlobalValue::InternalLinkage;
- // HLSL variables in the input address space maps like memory-mapped
- // variables. Even if they are 'static', they are externally initialized and
- // read/write by the hardware/driver/pipeline.
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input)
+ // HLSL variables in the input or push-constant address space maps are like
+ // memory-mapped variables. Even if they are 'static', they are externally
+ // initialized and read/write by the hardware/driver/pipeline.
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D)))
Linkage = llvm::GlobalValue::ExternalLinkage;
GV->setLinkage(Linkage);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 25b89d65847ad..5c29bf5e77414 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -30,6 +30,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticComment.h"
+#include "clang/Basic/HLSLRuntime.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -14559,10 +14560,10 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
if (getLangOpts().HLSL && HLSL().ActOnUninitializedVarDecl(Var))
return;
- // HLSL input variables are expected to be externally initialized, even
- // when marked `static`.
+ // HLSL input & push-constant variables are expected to be externally
+ // initialized, even when marked `static`.
if (getLangOpts().HLSL &&
- Var->getType().getAddressSpace() == LangAS::hlsl_input)
+ hlsl::isInitializedByPipeline(Var->getType().getAddressSpace()))
return;
// C++03 [dcl.init]p9:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..0396155bd6a9d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7614,6 +7614,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLVkExtBuiltinInput:
S.HLSL().handleVkExtBuiltinInputAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkPushConstant:
+ S.HLSL().handleVkPushConstantAttr(D, AL);
+ break;
case ParsedAttr::AT_HLSLVkConstantId:
S.HLSL().handleVkConstantIdAttr(D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2b9b3abbd5360..1831584c88697 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1667,6 +1667,11 @@ void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID));
}
+void SemaHLSL::handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL) {
+ D->addAttr(::new (getASTContext())
+ HLSLVkPushConstantAttr(getASTContext(), AL));
+}
+
void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) {
uint32_t Id;
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Id))
@@ -3837,12 +3842,15 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
return Ty;
}
-static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
+static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) {
+ bool IsVulkan =
+ Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan;
+ bool IsVKPushConstant = IsVulkan && VD->hasAttr<HLSLVkPushConstantAttr>();
QualType QT = VD->getType();
return VD->getDeclContext()->isTranslationUnit() &&
QT.getAddressSpace() == LangAS::Default &&
VD->getStorageClass() != SC_Static &&
- !VD->hasAttr<HLSLVkConstantIdAttr>() &&
+ !VD->hasAttr<HLSLVkConstantIdAttr>() && !IsVKPushConstant &&
!isInvalidConstantBufferLeafElementType(QT.getTypePtr());
}
@@ -3863,6 +3871,19 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
return;
}
+ bool IsVulkan = getASTContext().getTargetInfo().getTriple().getOS() ==
+ llvm::Triple::Vulkan;
+ if (IsVulkan && Decl->hasAttr<HLSLVkPushConstantAttr>()) {
+ if (HasDeclaredAPushConstant)
+ SemaRef.Diag(Decl->getLocation(), diag::err_hlsl_push_constant_unique);
+
+ LangAS ImplAS = LangAS::hlsl_push_constant;
+ Type = SemaRef.getASTContext().getAddrSpaceQualType(Type, ImplAS);
+ Decl->setType(Type);
+ HasDeclaredAPushConstant = true;
+ return;
+ }
+
if (Type->isSamplerT() || Type->isVoidType())
return;
@@ -3895,7 +3916,7 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
// Global variables outside a cbuffer block that are not a resource, static,
// groupshared, or an empty array or struct belong to the default constant
// buffer $Globals (to be created at the end of the translation unit).
- if (IsDefaultBufferConstantDecl(VD)) {
+ if (IsDefaultBufferConstantDecl(getASTContext(), VD)) {
// update address space to hlsl_constant
QualType NewTy = getASTContext().getAddrSpaceQualType(
VD->getType(), LangAS::hlsl_constant);
@@ -4196,8 +4217,11 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
bool HasBinding = false;
for (Attr *A : VD->attrs()) {
- if (isa<HLSLVkBindingAttr>(A))
+ if (isa<HLSLVkBindingAttr>(A)) {
HasBinding = true;
+ if (auto PA = VD->getAttr<HLSLVkPushConstantAttr>())
+ Diag(PA->getLoc(), diag::err_hlsl_attr_incompatible) << A << PA;
+ }
HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
if (!RBA || !RBA->hasRegisterSlot())
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
new file mode 100644
index 0000000000000..412ec4dffc572
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+struct S {
+ uint32_t a : 1;
+ uint32_t b : 1;
+};
+// CHECK: %struct.S = type { i8 }
+
+[[vk::push_constant]] S buffer;
+// CHECK: @buffer = external hidden addrspace(13) externally_initialized global %struct.S, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ uint32_t v = buffer.b;
+// CHECK: %bf.load = load i8, ptr addrspace(13) @buffer, align 1
+// CHECK: %bf.lshr = lshr i8 %bf.load, 1
+// CHECK: %bf.clear = and i8 %bf.lshr, 1
+// CHECK: %bf.cast = zext i8 %bf.clear to i32
+// CHECK: store i32 %bf.cast
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
new file mode 100644
index 0000000000000..2b2e9d09c7ab0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+[[vk::push_constant]]
+struct {
+ int a;
+ float b;
+ float3 c;
+}
+PushConstants;
+
+// CHECK: %struct.anon = type <{ i32, float, <3 x float> }>
+// CHECK: @PushConstants = external hidden addrspace(13) externally_initialized global %struct.anon, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ float tmp = PushConstants.b;
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
new file mode 100644
index 0000000000000..6b58decfa5188
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -verify
+
+struct S {
+ float f;
+};
+
+// expected-error@+1 {{'vk::binding' attribute is not compatible with 'vk::push_constant' attribute}}
+[[vk::push_constant, vk::binding(5)]]
+S pcs;
+
+[numthreads(1, 1, 1)]
+void main() {
+}
diff...
[truncated]
|
|
@llvm/pr-subscribers-hlsl Author: Nathan Gauër (Keenuts) ChangesImplements initial support for vk::push_constant. The old fix would be to use target specific types, but this is Patch is 27.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/166793.diff 33 Files Affected:
diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h
index 48e4a1c61fe02..7280b8fc923d2 100644
--- a/clang/include/clang/Basic/AddressSpaces.h
+++ b/clang/include/clang/Basic/AddressSpaces.h
@@ -62,6 +62,7 @@ enum class LangAS : unsigned {
hlsl_private,
hlsl_device,
hlsl_input,
+ hlsl_push_constant,
// Wasm specific address spaces.
wasm_funcref,
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8dfe4bc08c48e..e00765a57cb23 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5146,6 +5146,14 @@ def HLSLVkExtBuiltinInput : InheritableAttr {
let Documentation = [HLSLVkExtBuiltinInputDocs];
}
+def HLSLVkPushConstant : InheritableAttr {
+ let Spellings = [CXX11<"vk", "push_constant">];
+ let Args = [];
+ let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkPushConstantDocs];
+}
+
def HLSLVkConstantId : InheritableAttr {
let Spellings = [CXX11<"vk", "constant_id">];
let Args = [IntArgument<"Id">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 4813191d2d602..3938c624c9d0c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8777,6 +8777,11 @@ https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md
}];
}
+def HLSLVkPushConstantDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{ FIXME }];
+}
+
def AnnotateTypeDocs : Documentation {
let Category = DocCatType;
let Heading = "annotate_type";
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a6e60fe4692ee..c9d128d77c90f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13183,6 +13183,9 @@ def err_hlsl_attr_invalid_type : Error<
"attribute %0 only applies to a field or parameter of type '%1'">;
def err_hlsl_attr_invalid_ast_node : Error<
"attribute %0 only applies to %1">;
+def err_hlsl_attr_incompatible
+ : Error<"%0 attribute is not compatible with %1 attribute">;
+
def err_hlsl_entry_shader_attr_mismatch : Error<
"%0 attribute on entry function does not match the target profile">;
def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numthreads attribute cannot exceed %1">;
@@ -13294,6 +13297,9 @@ def err_hlsl_incomplete_resource_array_in_function_param: Error<
def err_hlsl_assign_to_global_resource: Error<
"assignment to global resource variable %0 is not allowed">;
+def err_hlsl_push_constant_unique
+ : Error<"cannot have more than one push constant block">;
+
// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Basic/HLSLRuntime.h b/clang/include/clang/Basic/HLSLRuntime.h
index 03166805daa6a..f6a1cf9636467 100644
--- a/clang/include/clang/Basic/HLSLRuntime.h
+++ b/clang/include/clang/Basic/HLSLRuntime.h
@@ -14,6 +14,7 @@
#ifndef CLANG_BASIC_HLSLRUNTIME_H
#define CLANG_BASIC_HLSLRUNTIME_H
+#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/LangOptions.h"
#include <cstdint>
@@ -30,6 +31,10 @@ getStageFromEnvironment(const llvm::Triple::EnvironmentType &E) {
return static_cast<ShaderStage>(Pipeline);
}
+constexpr bool isInitializedByPipeline(LangAS AS) {
+ return AS == LangAS::hlsl_input || AS == LangAS::hlsl_push_constant;
+}
+
#define ENUM_COMPARE_ASSERT(Value) \
static_assert( \
getStageFromEnvironment(llvm::Triple::Value) == ShaderStage::Value, \
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 86da323892f98..2fcac237eba1c 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -190,6 +190,7 @@ class SemaHLSL : public SemaBase {
void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL);
bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
QualType ProcessResourceTypeAttributes(QualType Wrapped);
@@ -239,6 +240,8 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ bool HasDeclaredAPushConstant = false;
+
struct SemanticInfo {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4548af17e37f2..53082bcf78f6a 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -101,6 +101,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
(A == LangAS::Default && B == LangAS::hlsl_private) ||
(A == LangAS::Default && B == LangAS::hlsl_device) ||
(A == LangAS::Default && B == LangAS::hlsl_input) ||
+ (A == LangAS::Default && B == LangAS::hlsl_push_constant) ||
// Conversions from target specific address spaces may be legal
// depending on the target information.
Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index c18b2eafc722c..8448dd3748e28 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2749,6 +2749,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) {
return "hlsl_device";
case LangAS::hlsl_input:
return "hlsl_input";
+ case LangAS::hlsl_push_constant:
+ return "hlsl_push_constant";
case LangAS::wasm_funcref:
return "__funcref";
default:
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index ffaf98bf9c366..92ca7a66a9593 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -52,6 +52,7 @@ static const LangASMap FakeAddrSpaceMap = {
15, // hlsl_private
16, // hlsl_device
17, // hlsl_input
+ 18, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 1a7aa658e9d87..8e8d8e6ae86b5 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -48,6 +48,7 @@ static const unsigned ARM64AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp
index d4d696b8456b6..993a73a89c9e9 100644
--- a/clang/lib/Basic/Targets/AMDGPU.cpp
+++ b/clang/lib/Basic/Targets/AMDGPU.cpp
@@ -63,6 +63,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
@@ -91,6 +92,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
} // namespace targets
} // namespace clang
diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h
index a21a593365773..c0799a6f7610f 100644
--- a/clang/lib/Basic/Targets/DirectX.h
+++ b/clang/lib/Basic/Targets/DirectX.h
@@ -46,6 +46,7 @@ static const unsigned DirectXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h
index f5c8396f398aa..6338a4f2f9036 100644
--- a/clang/lib/Basic/Targets/NVPTX.h
+++ b/clang/lib/Basic/Targets/NVPTX.h
@@ -50,6 +50,7 @@ static const unsigned NVPTXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index 22b2799518dd0..94449231efb94 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -51,6 +51,7 @@ static const unsigned SPIRDefIsPrivMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
@@ -87,6 +88,7 @@ static const unsigned SPIRDefIsGenMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index 4e15d5af1cde6..4ce515b31a001 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -46,6 +46,7 @@ static const unsigned ZOSAddressMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
0 // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h
index 005cab9819472..161025378c471 100644
--- a/clang/lib/Basic/Targets/TCE.h
+++ b/clang/lib/Basic/Targets/TCE.h
@@ -55,6 +55,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 4de6ce6bb5a21..c8065843aeb42 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -46,6 +46,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index e7da2622e78b5..7b88ac70e234f 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -50,6 +50,7 @@ static const unsigned X86AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 3eeb1718e455a..c3b536c7a267f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6049,9 +6049,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
getCUDARuntime().handleVarRegistration(D, *GV);
}
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input) {
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D))) {
// HLSL Input variables are considered to be set by the driver/pipeline, but
- // only visible to a single thread/wave.
+ // only visible to a single thread/wave. Push constants are also externally
+ // initialized, but constant, hence cross-wave visibility is not relevant.
GV->setExternallyInitialized(true);
} else {
GV->setInitializer(Init);
@@ -6102,10 +6104,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
!D->hasAttr<ConstInitAttr>())
Linkage = llvm::GlobalValue::InternalLinkage;
- // HLSL variables in the input address space maps like memory-mapped
- // variables. Even if they are 'static', they are externally initialized and
- // read/write by the hardware/driver/pipeline.
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input)
+ // HLSL variables in the input or push-constant address space maps are like
+ // memory-mapped variables. Even if they are 'static', they are externally
+ // initialized and read/write by the hardware/driver/pipeline.
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D)))
Linkage = llvm::GlobalValue::ExternalLinkage;
GV->setLinkage(Linkage);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 25b89d65847ad..5c29bf5e77414 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -30,6 +30,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticComment.h"
+#include "clang/Basic/HLSLRuntime.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -14559,10 +14560,10 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
if (getLangOpts().HLSL && HLSL().ActOnUninitializedVarDecl(Var))
return;
- // HLSL input variables are expected to be externally initialized, even
- // when marked `static`.
+ // HLSL input & push-constant variables are expected to be externally
+ // initialized, even when marked `static`.
if (getLangOpts().HLSL &&
- Var->getType().getAddressSpace() == LangAS::hlsl_input)
+ hlsl::isInitializedByPipeline(Var->getType().getAddressSpace()))
return;
// C++03 [dcl.init]p9:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..0396155bd6a9d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7614,6 +7614,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLVkExtBuiltinInput:
S.HLSL().handleVkExtBuiltinInputAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkPushConstant:
+ S.HLSL().handleVkPushConstantAttr(D, AL);
+ break;
case ParsedAttr::AT_HLSLVkConstantId:
S.HLSL().handleVkConstantIdAttr(D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2b9b3abbd5360..1831584c88697 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1667,6 +1667,11 @@ void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID));
}
+void SemaHLSL::handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL) {
+ D->addAttr(::new (getASTContext())
+ HLSLVkPushConstantAttr(getASTContext(), AL));
+}
+
void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) {
uint32_t Id;
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Id))
@@ -3837,12 +3842,15 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
return Ty;
}
-static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
+static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) {
+ bool IsVulkan =
+ Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan;
+ bool IsVKPushConstant = IsVulkan && VD->hasAttr<HLSLVkPushConstantAttr>();
QualType QT = VD->getType();
return VD->getDeclContext()->isTranslationUnit() &&
QT.getAddressSpace() == LangAS::Default &&
VD->getStorageClass() != SC_Static &&
- !VD->hasAttr<HLSLVkConstantIdAttr>() &&
+ !VD->hasAttr<HLSLVkConstantIdAttr>() && !IsVKPushConstant &&
!isInvalidConstantBufferLeafElementType(QT.getTypePtr());
}
@@ -3863,6 +3871,19 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
return;
}
+ bool IsVulkan = getASTContext().getTargetInfo().getTriple().getOS() ==
+ llvm::Triple::Vulkan;
+ if (IsVulkan && Decl->hasAttr<HLSLVkPushConstantAttr>()) {
+ if (HasDeclaredAPushConstant)
+ SemaRef.Diag(Decl->getLocation(), diag::err_hlsl_push_constant_unique);
+
+ LangAS ImplAS = LangAS::hlsl_push_constant;
+ Type = SemaRef.getASTContext().getAddrSpaceQualType(Type, ImplAS);
+ Decl->setType(Type);
+ HasDeclaredAPushConstant = true;
+ return;
+ }
+
if (Type->isSamplerT() || Type->isVoidType())
return;
@@ -3895,7 +3916,7 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
// Global variables outside a cbuffer block that are not a resource, static,
// groupshared, or an empty array or struct belong to the default constant
// buffer $Globals (to be created at the end of the translation unit).
- if (IsDefaultBufferConstantDecl(VD)) {
+ if (IsDefaultBufferConstantDecl(getASTContext(), VD)) {
// update address space to hlsl_constant
QualType NewTy = getASTContext().getAddrSpaceQualType(
VD->getType(), LangAS::hlsl_constant);
@@ -4196,8 +4217,11 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
bool HasBinding = false;
for (Attr *A : VD->attrs()) {
- if (isa<HLSLVkBindingAttr>(A))
+ if (isa<HLSLVkBindingAttr>(A)) {
HasBinding = true;
+ if (auto PA = VD->getAttr<HLSLVkPushConstantAttr>())
+ Diag(PA->getLoc(), diag::err_hlsl_attr_incompatible) << A << PA;
+ }
HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
if (!RBA || !RBA->hasRegisterSlot())
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
new file mode 100644
index 0000000000000..412ec4dffc572
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+struct S {
+ uint32_t a : 1;
+ uint32_t b : 1;
+};
+// CHECK: %struct.S = type { i8 }
+
+[[vk::push_constant]] S buffer;
+// CHECK: @buffer = external hidden addrspace(13) externally_initialized global %struct.S, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ uint32_t v = buffer.b;
+// CHECK: %bf.load = load i8, ptr addrspace(13) @buffer, align 1
+// CHECK: %bf.lshr = lshr i8 %bf.load, 1
+// CHECK: %bf.clear = and i8 %bf.lshr, 1
+// CHECK: %bf.cast = zext i8 %bf.clear to i32
+// CHECK: store i32 %bf.cast
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
new file mode 100644
index 0000000000000..2b2e9d09c7ab0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+[[vk::push_constant]]
+struct {
+ int a;
+ float b;
+ float3 c;
+}
+PushConstants;
+
+// CHECK: %struct.anon = type <{ i32, float, <3 x float> }>
+// CHECK: @PushConstants = external hidden addrspace(13) externally_initialized global %struct.anon, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ float tmp = PushConstants.b;
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
new file mode 100644
index 0000000000000..6b58decfa5188
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -verify
+
+struct S {
+ float f;
+};
+
+// expected-error@+1 {{'vk::binding' attribute is not compatible with 'vk::push_constant' attribute}}
+[[vk::push_constant, vk::binding(5)]]
+S pcs;
+
+[numthreads(1, 1, 1)]
+void main() {
+}
diff...
[truncated]
|
|
@llvm/pr-subscribers-backend-systemz Author: Nathan Gauër (Keenuts) ChangesImplements initial support for vk::push_constant. The old fix would be to use target specific types, but this is Patch is 27.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/166793.diff 33 Files Affected:
diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h
index 48e4a1c61fe02..7280b8fc923d2 100644
--- a/clang/include/clang/Basic/AddressSpaces.h
+++ b/clang/include/clang/Basic/AddressSpaces.h
@@ -62,6 +62,7 @@ enum class LangAS : unsigned {
hlsl_private,
hlsl_device,
hlsl_input,
+ hlsl_push_constant,
// Wasm specific address spaces.
wasm_funcref,
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8dfe4bc08c48e..e00765a57cb23 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5146,6 +5146,14 @@ def HLSLVkExtBuiltinInput : InheritableAttr {
let Documentation = [HLSLVkExtBuiltinInputDocs];
}
+def HLSLVkPushConstant : InheritableAttr {
+ let Spellings = [CXX11<"vk", "push_constant">];
+ let Args = [];
+ let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkPushConstantDocs];
+}
+
def HLSLVkConstantId : InheritableAttr {
let Spellings = [CXX11<"vk", "constant_id">];
let Args = [IntArgument<"Id">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 4813191d2d602..3938c624c9d0c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8777,6 +8777,11 @@ https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md
}];
}
+def HLSLVkPushConstantDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{ FIXME }];
+}
+
def AnnotateTypeDocs : Documentation {
let Category = DocCatType;
let Heading = "annotate_type";
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a6e60fe4692ee..c9d128d77c90f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13183,6 +13183,9 @@ def err_hlsl_attr_invalid_type : Error<
"attribute %0 only applies to a field or parameter of type '%1'">;
def err_hlsl_attr_invalid_ast_node : Error<
"attribute %0 only applies to %1">;
+def err_hlsl_attr_incompatible
+ : Error<"%0 attribute is not compatible with %1 attribute">;
+
def err_hlsl_entry_shader_attr_mismatch : Error<
"%0 attribute on entry function does not match the target profile">;
def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numthreads attribute cannot exceed %1">;
@@ -13294,6 +13297,9 @@ def err_hlsl_incomplete_resource_array_in_function_param: Error<
def err_hlsl_assign_to_global_resource: Error<
"assignment to global resource variable %0 is not allowed">;
+def err_hlsl_push_constant_unique
+ : Error<"cannot have more than one push constant block">;
+
// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Basic/HLSLRuntime.h b/clang/include/clang/Basic/HLSLRuntime.h
index 03166805daa6a..f6a1cf9636467 100644
--- a/clang/include/clang/Basic/HLSLRuntime.h
+++ b/clang/include/clang/Basic/HLSLRuntime.h
@@ -14,6 +14,7 @@
#ifndef CLANG_BASIC_HLSLRUNTIME_H
#define CLANG_BASIC_HLSLRUNTIME_H
+#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/LangOptions.h"
#include <cstdint>
@@ -30,6 +31,10 @@ getStageFromEnvironment(const llvm::Triple::EnvironmentType &E) {
return static_cast<ShaderStage>(Pipeline);
}
+constexpr bool isInitializedByPipeline(LangAS AS) {
+ return AS == LangAS::hlsl_input || AS == LangAS::hlsl_push_constant;
+}
+
#define ENUM_COMPARE_ASSERT(Value) \
static_assert( \
getStageFromEnvironment(llvm::Triple::Value) == ShaderStage::Value, \
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 86da323892f98..2fcac237eba1c 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -190,6 +190,7 @@ class SemaHLSL : public SemaBase {
void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL);
bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
QualType ProcessResourceTypeAttributes(QualType Wrapped);
@@ -239,6 +240,8 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ bool HasDeclaredAPushConstant = false;
+
struct SemanticInfo {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4548af17e37f2..53082bcf78f6a 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -101,6 +101,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
(A == LangAS::Default && B == LangAS::hlsl_private) ||
(A == LangAS::Default && B == LangAS::hlsl_device) ||
(A == LangAS::Default && B == LangAS::hlsl_input) ||
+ (A == LangAS::Default && B == LangAS::hlsl_push_constant) ||
// Conversions from target specific address spaces may be legal
// depending on the target information.
Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index c18b2eafc722c..8448dd3748e28 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2749,6 +2749,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) {
return "hlsl_device";
case LangAS::hlsl_input:
return "hlsl_input";
+ case LangAS::hlsl_push_constant:
+ return "hlsl_push_constant";
case LangAS::wasm_funcref:
return "__funcref";
default:
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index ffaf98bf9c366..92ca7a66a9593 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -52,6 +52,7 @@ static const LangASMap FakeAddrSpaceMap = {
15, // hlsl_private
16, // hlsl_device
17, // hlsl_input
+ 18, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 1a7aa658e9d87..8e8d8e6ae86b5 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -48,6 +48,7 @@ static const unsigned ARM64AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp
index d4d696b8456b6..993a73a89c9e9 100644
--- a/clang/lib/Basic/Targets/AMDGPU.cpp
+++ b/clang/lib/Basic/Targets/AMDGPU.cpp
@@ -63,6 +63,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
@@ -91,6 +92,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
} // namespace targets
} // namespace clang
diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h
index a21a593365773..c0799a6f7610f 100644
--- a/clang/lib/Basic/Targets/DirectX.h
+++ b/clang/lib/Basic/Targets/DirectX.h
@@ -46,6 +46,7 @@ static const unsigned DirectXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h
index f5c8396f398aa..6338a4f2f9036 100644
--- a/clang/lib/Basic/Targets/NVPTX.h
+++ b/clang/lib/Basic/Targets/NVPTX.h
@@ -50,6 +50,7 @@ static const unsigned NVPTXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index 22b2799518dd0..94449231efb94 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -51,6 +51,7 @@ static const unsigned SPIRDefIsPrivMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
@@ -87,6 +88,7 @@ static const unsigned SPIRDefIsGenMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index 4e15d5af1cde6..4ce515b31a001 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -46,6 +46,7 @@ static const unsigned ZOSAddressMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
0 // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h
index 005cab9819472..161025378c471 100644
--- a/clang/lib/Basic/Targets/TCE.h
+++ b/clang/lib/Basic/Targets/TCE.h
@@ -55,6 +55,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 4de6ce6bb5a21..c8065843aeb42 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -46,6 +46,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index e7da2622e78b5..7b88ac70e234f 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -50,6 +50,7 @@ static const unsigned X86AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 3eeb1718e455a..c3b536c7a267f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6049,9 +6049,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
getCUDARuntime().handleVarRegistration(D, *GV);
}
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input) {
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D))) {
// HLSL Input variables are considered to be set by the driver/pipeline, but
- // only visible to a single thread/wave.
+ // only visible to a single thread/wave. Push constants are also externally
+ // initialized, but constant, hence cross-wave visibility is not relevant.
GV->setExternallyInitialized(true);
} else {
GV->setInitializer(Init);
@@ -6102,10 +6104,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
!D->hasAttr<ConstInitAttr>())
Linkage = llvm::GlobalValue::InternalLinkage;
- // HLSL variables in the input address space maps like memory-mapped
- // variables. Even if they are 'static', they are externally initialized and
- // read/write by the hardware/driver/pipeline.
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input)
+ // HLSL variables in the input or push-constant address space maps are like
+ // memory-mapped variables. Even if they are 'static', they are externally
+ // initialized and read/write by the hardware/driver/pipeline.
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D)))
Linkage = llvm::GlobalValue::ExternalLinkage;
GV->setLinkage(Linkage);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 25b89d65847ad..5c29bf5e77414 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -30,6 +30,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticComment.h"
+#include "clang/Basic/HLSLRuntime.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -14559,10 +14560,10 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
if (getLangOpts().HLSL && HLSL().ActOnUninitializedVarDecl(Var))
return;
- // HLSL input variables are expected to be externally initialized, even
- // when marked `static`.
+ // HLSL input & push-constant variables are expected to be externally
+ // initialized, even when marked `static`.
if (getLangOpts().HLSL &&
- Var->getType().getAddressSpace() == LangAS::hlsl_input)
+ hlsl::isInitializedByPipeline(Var->getType().getAddressSpace()))
return;
// C++03 [dcl.init]p9:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..0396155bd6a9d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7614,6 +7614,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLVkExtBuiltinInput:
S.HLSL().handleVkExtBuiltinInputAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkPushConstant:
+ S.HLSL().handleVkPushConstantAttr(D, AL);
+ break;
case ParsedAttr::AT_HLSLVkConstantId:
S.HLSL().handleVkConstantIdAttr(D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2b9b3abbd5360..1831584c88697 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1667,6 +1667,11 @@ void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID));
}
+void SemaHLSL::handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL) {
+ D->addAttr(::new (getASTContext())
+ HLSLVkPushConstantAttr(getASTContext(), AL));
+}
+
void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) {
uint32_t Id;
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Id))
@@ -3837,12 +3842,15 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
return Ty;
}
-static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
+static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) {
+ bool IsVulkan =
+ Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan;
+ bool IsVKPushConstant = IsVulkan && VD->hasAttr<HLSLVkPushConstantAttr>();
QualType QT = VD->getType();
return VD->getDeclContext()->isTranslationUnit() &&
QT.getAddressSpace() == LangAS::Default &&
VD->getStorageClass() != SC_Static &&
- !VD->hasAttr<HLSLVkConstantIdAttr>() &&
+ !VD->hasAttr<HLSLVkConstantIdAttr>() && !IsVKPushConstant &&
!isInvalidConstantBufferLeafElementType(QT.getTypePtr());
}
@@ -3863,6 +3871,19 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
return;
}
+ bool IsVulkan = getASTContext().getTargetInfo().getTriple().getOS() ==
+ llvm::Triple::Vulkan;
+ if (IsVulkan && Decl->hasAttr<HLSLVkPushConstantAttr>()) {
+ if (HasDeclaredAPushConstant)
+ SemaRef.Diag(Decl->getLocation(), diag::err_hlsl_push_constant_unique);
+
+ LangAS ImplAS = LangAS::hlsl_push_constant;
+ Type = SemaRef.getASTContext().getAddrSpaceQualType(Type, ImplAS);
+ Decl->setType(Type);
+ HasDeclaredAPushConstant = true;
+ return;
+ }
+
if (Type->isSamplerT() || Type->isVoidType())
return;
@@ -3895,7 +3916,7 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
// Global variables outside a cbuffer block that are not a resource, static,
// groupshared, or an empty array or struct belong to the default constant
// buffer $Globals (to be created at the end of the translation unit).
- if (IsDefaultBufferConstantDecl(VD)) {
+ if (IsDefaultBufferConstantDecl(getASTContext(), VD)) {
// update address space to hlsl_constant
QualType NewTy = getASTContext().getAddrSpaceQualType(
VD->getType(), LangAS::hlsl_constant);
@@ -4196,8 +4217,11 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
bool HasBinding = false;
for (Attr *A : VD->attrs()) {
- if (isa<HLSLVkBindingAttr>(A))
+ if (isa<HLSLVkBindingAttr>(A)) {
HasBinding = true;
+ if (auto PA = VD->getAttr<HLSLVkPushConstantAttr>())
+ Diag(PA->getLoc(), diag::err_hlsl_attr_incompatible) << A << PA;
+ }
HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
if (!RBA || !RBA->hasRegisterSlot())
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
new file mode 100644
index 0000000000000..412ec4dffc572
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+struct S {
+ uint32_t a : 1;
+ uint32_t b : 1;
+};
+// CHECK: %struct.S = type { i8 }
+
+[[vk::push_constant]] S buffer;
+// CHECK: @buffer = external hidden addrspace(13) externally_initialized global %struct.S, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ uint32_t v = buffer.b;
+// CHECK: %bf.load = load i8, ptr addrspace(13) @buffer, align 1
+// CHECK: %bf.lshr = lshr i8 %bf.load, 1
+// CHECK: %bf.clear = and i8 %bf.lshr, 1
+// CHECK: %bf.cast = zext i8 %bf.clear to i32
+// CHECK: store i32 %bf.cast
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
new file mode 100644
index 0000000000000..2b2e9d09c7ab0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+[[vk::push_constant]]
+struct {
+ int a;
+ float b;
+ float3 c;
+}
+PushConstants;
+
+// CHECK: %struct.anon = type <{ i32, float, <3 x float> }>
+// CHECK: @PushConstants = external hidden addrspace(13) externally_initialized global %struct.anon, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ float tmp = PushConstants.b;
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
new file mode 100644
index 0000000000000..6b58decfa5188
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -verify
+
+struct S {
+ float f;
+};
+
+// expected-error@+1 {{'vk::binding' attribute is not compatible with 'vk::push_constant' attribute}}
+[[vk::push_constant, vk::binding(5)]]
+S pcs;
+
+[numthreads(1, 1, 1)]
+void main() {
+}
diff...
[truncated]
|
|
@llvm/pr-subscribers-backend-amdgpu Author: Nathan Gauër (Keenuts) ChangesImplements initial support for vk::push_constant. The old fix would be to use target specific types, but this is Patch is 27.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/166793.diff 33 Files Affected:
diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h
index 48e4a1c61fe02..7280b8fc923d2 100644
--- a/clang/include/clang/Basic/AddressSpaces.h
+++ b/clang/include/clang/Basic/AddressSpaces.h
@@ -62,6 +62,7 @@ enum class LangAS : unsigned {
hlsl_private,
hlsl_device,
hlsl_input,
+ hlsl_push_constant,
// Wasm specific address spaces.
wasm_funcref,
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8dfe4bc08c48e..e00765a57cb23 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5146,6 +5146,14 @@ def HLSLVkExtBuiltinInput : InheritableAttr {
let Documentation = [HLSLVkExtBuiltinInputDocs];
}
+def HLSLVkPushConstant : InheritableAttr {
+ let Spellings = [CXX11<"vk", "push_constant">];
+ let Args = [];
+ let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkPushConstantDocs];
+}
+
def HLSLVkConstantId : InheritableAttr {
let Spellings = [CXX11<"vk", "constant_id">];
let Args = [IntArgument<"Id">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 4813191d2d602..3938c624c9d0c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8777,6 +8777,11 @@ https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md
}];
}
+def HLSLVkPushConstantDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{ FIXME }];
+}
+
def AnnotateTypeDocs : Documentation {
let Category = DocCatType;
let Heading = "annotate_type";
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a6e60fe4692ee..c9d128d77c90f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13183,6 +13183,9 @@ def err_hlsl_attr_invalid_type : Error<
"attribute %0 only applies to a field or parameter of type '%1'">;
def err_hlsl_attr_invalid_ast_node : Error<
"attribute %0 only applies to %1">;
+def err_hlsl_attr_incompatible
+ : Error<"%0 attribute is not compatible with %1 attribute">;
+
def err_hlsl_entry_shader_attr_mismatch : Error<
"%0 attribute on entry function does not match the target profile">;
def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numthreads attribute cannot exceed %1">;
@@ -13294,6 +13297,9 @@ def err_hlsl_incomplete_resource_array_in_function_param: Error<
def err_hlsl_assign_to_global_resource: Error<
"assignment to global resource variable %0 is not allowed">;
+def err_hlsl_push_constant_unique
+ : Error<"cannot have more than one push constant block">;
+
// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Basic/HLSLRuntime.h b/clang/include/clang/Basic/HLSLRuntime.h
index 03166805daa6a..f6a1cf9636467 100644
--- a/clang/include/clang/Basic/HLSLRuntime.h
+++ b/clang/include/clang/Basic/HLSLRuntime.h
@@ -14,6 +14,7 @@
#ifndef CLANG_BASIC_HLSLRUNTIME_H
#define CLANG_BASIC_HLSLRUNTIME_H
+#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/LangOptions.h"
#include <cstdint>
@@ -30,6 +31,10 @@ getStageFromEnvironment(const llvm::Triple::EnvironmentType &E) {
return static_cast<ShaderStage>(Pipeline);
}
+constexpr bool isInitializedByPipeline(LangAS AS) {
+ return AS == LangAS::hlsl_input || AS == LangAS::hlsl_push_constant;
+}
+
#define ENUM_COMPARE_ASSERT(Value) \
static_assert( \
getStageFromEnvironment(llvm::Triple::Value) == ShaderStage::Value, \
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 86da323892f98..2fcac237eba1c 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -190,6 +190,7 @@ class SemaHLSL : public SemaBase {
void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL);
bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
QualType ProcessResourceTypeAttributes(QualType Wrapped);
@@ -239,6 +240,8 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ bool HasDeclaredAPushConstant = false;
+
struct SemanticInfo {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4548af17e37f2..53082bcf78f6a 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -101,6 +101,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
(A == LangAS::Default && B == LangAS::hlsl_private) ||
(A == LangAS::Default && B == LangAS::hlsl_device) ||
(A == LangAS::Default && B == LangAS::hlsl_input) ||
+ (A == LangAS::Default && B == LangAS::hlsl_push_constant) ||
// Conversions from target specific address spaces may be legal
// depending on the target information.
Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index c18b2eafc722c..8448dd3748e28 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2749,6 +2749,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) {
return "hlsl_device";
case LangAS::hlsl_input:
return "hlsl_input";
+ case LangAS::hlsl_push_constant:
+ return "hlsl_push_constant";
case LangAS::wasm_funcref:
return "__funcref";
default:
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index ffaf98bf9c366..92ca7a66a9593 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -52,6 +52,7 @@ static const LangASMap FakeAddrSpaceMap = {
15, // hlsl_private
16, // hlsl_device
17, // hlsl_input
+ 18, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 1a7aa658e9d87..8e8d8e6ae86b5 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -48,6 +48,7 @@ static const unsigned ARM64AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp
index d4d696b8456b6..993a73a89c9e9 100644
--- a/clang/lib/Basic/Targets/AMDGPU.cpp
+++ b/clang/lib/Basic/Targets/AMDGPU.cpp
@@ -63,6 +63,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
@@ -91,6 +92,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
} // namespace targets
} // namespace clang
diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h
index a21a593365773..c0799a6f7610f 100644
--- a/clang/lib/Basic/Targets/DirectX.h
+++ b/clang/lib/Basic/Targets/DirectX.h
@@ -46,6 +46,7 @@ static const unsigned DirectXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h
index f5c8396f398aa..6338a4f2f9036 100644
--- a/clang/lib/Basic/Targets/NVPTX.h
+++ b/clang/lib/Basic/Targets/NVPTX.h
@@ -50,6 +50,7 @@ static const unsigned NVPTXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index 22b2799518dd0..94449231efb94 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -51,6 +51,7 @@ static const unsigned SPIRDefIsPrivMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
@@ -87,6 +88,7 @@ static const unsigned SPIRDefIsGenMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index 4e15d5af1cde6..4ce515b31a001 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -46,6 +46,7 @@ static const unsigned ZOSAddressMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
0 // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h
index 005cab9819472..161025378c471 100644
--- a/clang/lib/Basic/Targets/TCE.h
+++ b/clang/lib/Basic/Targets/TCE.h
@@ -55,6 +55,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 4de6ce6bb5a21..c8065843aeb42 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -46,6 +46,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index e7da2622e78b5..7b88ac70e234f 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -50,6 +50,7 @@ static const unsigned X86AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 3eeb1718e455a..c3b536c7a267f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6049,9 +6049,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
getCUDARuntime().handleVarRegistration(D, *GV);
}
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input) {
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D))) {
// HLSL Input variables are considered to be set by the driver/pipeline, but
- // only visible to a single thread/wave.
+ // only visible to a single thread/wave. Push constants are also externally
+ // initialized, but constant, hence cross-wave visibility is not relevant.
GV->setExternallyInitialized(true);
} else {
GV->setInitializer(Init);
@@ -6102,10 +6104,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
!D->hasAttr<ConstInitAttr>())
Linkage = llvm::GlobalValue::InternalLinkage;
- // HLSL variables in the input address space maps like memory-mapped
- // variables. Even if they are 'static', they are externally initialized and
- // read/write by the hardware/driver/pipeline.
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input)
+ // HLSL variables in the input or push-constant address space maps are like
+ // memory-mapped variables. Even if they are 'static', they are externally
+ // initialized and read/write by the hardware/driver/pipeline.
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D)))
Linkage = llvm::GlobalValue::ExternalLinkage;
GV->setLinkage(Linkage);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 25b89d65847ad..5c29bf5e77414 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -30,6 +30,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticComment.h"
+#include "clang/Basic/HLSLRuntime.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -14559,10 +14560,10 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
if (getLangOpts().HLSL && HLSL().ActOnUninitializedVarDecl(Var))
return;
- // HLSL input variables are expected to be externally initialized, even
- // when marked `static`.
+ // HLSL input & push-constant variables are expected to be externally
+ // initialized, even when marked `static`.
if (getLangOpts().HLSL &&
- Var->getType().getAddressSpace() == LangAS::hlsl_input)
+ hlsl::isInitializedByPipeline(Var->getType().getAddressSpace()))
return;
// C++03 [dcl.init]p9:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..0396155bd6a9d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7614,6 +7614,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLVkExtBuiltinInput:
S.HLSL().handleVkExtBuiltinInputAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkPushConstant:
+ S.HLSL().handleVkPushConstantAttr(D, AL);
+ break;
case ParsedAttr::AT_HLSLVkConstantId:
S.HLSL().handleVkConstantIdAttr(D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2b9b3abbd5360..1831584c88697 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1667,6 +1667,11 @@ void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID));
}
+void SemaHLSL::handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL) {
+ D->addAttr(::new (getASTContext())
+ HLSLVkPushConstantAttr(getASTContext(), AL));
+}
+
void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) {
uint32_t Id;
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Id))
@@ -3837,12 +3842,15 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
return Ty;
}
-static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
+static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) {
+ bool IsVulkan =
+ Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan;
+ bool IsVKPushConstant = IsVulkan && VD->hasAttr<HLSLVkPushConstantAttr>();
QualType QT = VD->getType();
return VD->getDeclContext()->isTranslationUnit() &&
QT.getAddressSpace() == LangAS::Default &&
VD->getStorageClass() != SC_Static &&
- !VD->hasAttr<HLSLVkConstantIdAttr>() &&
+ !VD->hasAttr<HLSLVkConstantIdAttr>() && !IsVKPushConstant &&
!isInvalidConstantBufferLeafElementType(QT.getTypePtr());
}
@@ -3863,6 +3871,19 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
return;
}
+ bool IsVulkan = getASTContext().getTargetInfo().getTriple().getOS() ==
+ llvm::Triple::Vulkan;
+ if (IsVulkan && Decl->hasAttr<HLSLVkPushConstantAttr>()) {
+ if (HasDeclaredAPushConstant)
+ SemaRef.Diag(Decl->getLocation(), diag::err_hlsl_push_constant_unique);
+
+ LangAS ImplAS = LangAS::hlsl_push_constant;
+ Type = SemaRef.getASTContext().getAddrSpaceQualType(Type, ImplAS);
+ Decl->setType(Type);
+ HasDeclaredAPushConstant = true;
+ return;
+ }
+
if (Type->isSamplerT() || Type->isVoidType())
return;
@@ -3895,7 +3916,7 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
// Global variables outside a cbuffer block that are not a resource, static,
// groupshared, or an empty array or struct belong to the default constant
// buffer $Globals (to be created at the end of the translation unit).
- if (IsDefaultBufferConstantDecl(VD)) {
+ if (IsDefaultBufferConstantDecl(getASTContext(), VD)) {
// update address space to hlsl_constant
QualType NewTy = getASTContext().getAddrSpaceQualType(
VD->getType(), LangAS::hlsl_constant);
@@ -4196,8 +4217,11 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
bool HasBinding = false;
for (Attr *A : VD->attrs()) {
- if (isa<HLSLVkBindingAttr>(A))
+ if (isa<HLSLVkBindingAttr>(A)) {
HasBinding = true;
+ if (auto PA = VD->getAttr<HLSLVkPushConstantAttr>())
+ Diag(PA->getLoc(), diag::err_hlsl_attr_incompatible) << A << PA;
+ }
HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
if (!RBA || !RBA->hasRegisterSlot())
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
new file mode 100644
index 0000000000000..412ec4dffc572
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+struct S {
+ uint32_t a : 1;
+ uint32_t b : 1;
+};
+// CHECK: %struct.S = type { i8 }
+
+[[vk::push_constant]] S buffer;
+// CHECK: @buffer = external hidden addrspace(13) externally_initialized global %struct.S, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ uint32_t v = buffer.b;
+// CHECK: %bf.load = load i8, ptr addrspace(13) @buffer, align 1
+// CHECK: %bf.lshr = lshr i8 %bf.load, 1
+// CHECK: %bf.clear = and i8 %bf.lshr, 1
+// CHECK: %bf.cast = zext i8 %bf.clear to i32
+// CHECK: store i32 %bf.cast
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
new file mode 100644
index 0000000000000..2b2e9d09c7ab0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+[[vk::push_constant]]
+struct {
+ int a;
+ float b;
+ float3 c;
+}
+PushConstants;
+
+// CHECK: %struct.anon = type <{ i32, float, <3 x float> }>
+// CHECK: @PushConstants = external hidden addrspace(13) externally_initialized global %struct.anon, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ float tmp = PushConstants.b;
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
new file mode 100644
index 0000000000000..6b58decfa5188
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -verify
+
+struct S {
+ float f;
+};
+
+// expected-error@+1 {{'vk::binding' attribute is not compatible with 'vk::push_constant' attribute}}
+[[vk::push_constant, vk::binding(5)]]
+S pcs;
+
+[numthreads(1, 1, 1)]
+void main() {
+}
diff...
[truncated]
|
|
@llvm/pr-subscribers-backend-directx Author: Nathan Gauër (Keenuts) ChangesImplements initial support for vk::push_constant. The old fix would be to use target specific types, but this is Patch is 27.93 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/166793.diff 33 Files Affected:
diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h
index 48e4a1c61fe02..7280b8fc923d2 100644
--- a/clang/include/clang/Basic/AddressSpaces.h
+++ b/clang/include/clang/Basic/AddressSpaces.h
@@ -62,6 +62,7 @@ enum class LangAS : unsigned {
hlsl_private,
hlsl_device,
hlsl_input,
+ hlsl_push_constant,
// Wasm specific address spaces.
wasm_funcref,
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8dfe4bc08c48e..e00765a57cb23 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5146,6 +5146,14 @@ def HLSLVkExtBuiltinInput : InheritableAttr {
let Documentation = [HLSLVkExtBuiltinInputDocs];
}
+def HLSLVkPushConstant : InheritableAttr {
+ let Spellings = [CXX11<"vk", "push_constant">];
+ let Args = [];
+ let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkPushConstantDocs];
+}
+
def HLSLVkConstantId : InheritableAttr {
let Spellings = [CXX11<"vk", "constant_id">];
let Args = [IntArgument<"Id">];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index 4813191d2d602..3938c624c9d0c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8777,6 +8777,11 @@ https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md
}];
}
+def HLSLVkPushConstantDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{ FIXME }];
+}
+
def AnnotateTypeDocs : Documentation {
let Category = DocCatType;
let Heading = "annotate_type";
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index a6e60fe4692ee..c9d128d77c90f 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13183,6 +13183,9 @@ def err_hlsl_attr_invalid_type : Error<
"attribute %0 only applies to a field or parameter of type '%1'">;
def err_hlsl_attr_invalid_ast_node : Error<
"attribute %0 only applies to %1">;
+def err_hlsl_attr_incompatible
+ : Error<"%0 attribute is not compatible with %1 attribute">;
+
def err_hlsl_entry_shader_attr_mismatch : Error<
"%0 attribute on entry function does not match the target profile">;
def err_hlsl_numthreads_argument_oor : Error<"argument '%select{X|Y|Z}0' to numthreads attribute cannot exceed %1">;
@@ -13294,6 +13297,9 @@ def err_hlsl_incomplete_resource_array_in_function_param: Error<
def err_hlsl_assign_to_global_resource: Error<
"assignment to global resource variable %0 is not allowed">;
+def err_hlsl_push_constant_unique
+ : Error<"cannot have more than one push constant block">;
+
// Layout randomization diagnostics.
def err_non_designated_init_used : Error<
"a randomized struct can only be initialized with a designated initializer">;
diff --git a/clang/include/clang/Basic/HLSLRuntime.h b/clang/include/clang/Basic/HLSLRuntime.h
index 03166805daa6a..f6a1cf9636467 100644
--- a/clang/include/clang/Basic/HLSLRuntime.h
+++ b/clang/include/clang/Basic/HLSLRuntime.h
@@ -14,6 +14,7 @@
#ifndef CLANG_BASIC_HLSLRUNTIME_H
#define CLANG_BASIC_HLSLRUNTIME_H
+#include "clang/Basic/AddressSpaces.h"
#include "clang/Basic/LangOptions.h"
#include <cstdint>
@@ -30,6 +31,10 @@ getStageFromEnvironment(const llvm::Triple::EnvironmentType &E) {
return static_cast<ShaderStage>(Pipeline);
}
+constexpr bool isInitializedByPipeline(LangAS AS) {
+ return AS == LangAS::hlsl_input || AS == LangAS::hlsl_push_constant;
+}
+
#define ENUM_COMPARE_ASSERT(Value) \
static_assert( \
getStageFromEnvironment(llvm::Triple::Value) == ShaderStage::Value, \
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 86da323892f98..2fcac237eba1c 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -190,6 +190,7 @@ class SemaHLSL : public SemaBase {
void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL);
bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
QualType ProcessResourceTypeAttributes(QualType Wrapped);
@@ -239,6 +240,8 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ bool HasDeclaredAPushConstant = false;
+
struct SemanticInfo {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp
index 4548af17e37f2..53082bcf78f6a 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -101,6 +101,7 @@ bool Qualifiers::isTargetAddressSpaceSupersetOf(LangAS A, LangAS B,
(A == LangAS::Default && B == LangAS::hlsl_private) ||
(A == LangAS::Default && B == LangAS::hlsl_device) ||
(A == LangAS::Default && B == LangAS::hlsl_input) ||
+ (A == LangAS::Default && B == LangAS::hlsl_push_constant) ||
// Conversions from target specific address spaces may be legal
// depending on the target information.
Ctx.getTargetInfo().isAddressSpaceSupersetOf(A, B);
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index c18b2eafc722c..8448dd3748e28 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2749,6 +2749,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) {
return "hlsl_device";
case LangAS::hlsl_input:
return "hlsl_input";
+ case LangAS::hlsl_push_constant:
+ return "hlsl_push_constant";
case LangAS::wasm_funcref:
return "__funcref";
default:
diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp
index ffaf98bf9c366..92ca7a66a9593 100644
--- a/clang/lib/Basic/TargetInfo.cpp
+++ b/clang/lib/Basic/TargetInfo.cpp
@@ -52,6 +52,7 @@ static const LangASMap FakeAddrSpaceMap = {
15, // hlsl_private
16, // hlsl_device
17, // hlsl_input
+ 18, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h
index 1a7aa658e9d87..8e8d8e6ae86b5 100644
--- a/clang/lib/Basic/Targets/AArch64.h
+++ b/clang/lib/Basic/Targets/AArch64.h
@@ -48,6 +48,7 @@ static const unsigned ARM64AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/AMDGPU.cpp b/clang/lib/Basic/Targets/AMDGPU.cpp
index d4d696b8456b6..993a73a89c9e9 100644
--- a/clang/lib/Basic/Targets/AMDGPU.cpp
+++ b/clang/lib/Basic/Targets/AMDGPU.cpp
@@ -63,6 +63,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsGenMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
@@ -91,6 +92,7 @@ const LangASMap AMDGPUTargetInfo::AMDGPUDefIsPrivMap = {
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_private
llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_device
llvm::AMDGPUAS::PRIVATE_ADDRESS, // hlsl_input
+ llvm::AMDGPUAS::GLOBAL_ADDRESS, // hlsl_push_constant
};
} // namespace targets
} // namespace clang
diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h
index a21a593365773..c0799a6f7610f 100644
--- a/clang/lib/Basic/Targets/DirectX.h
+++ b/clang/lib/Basic/Targets/DirectX.h
@@ -46,6 +46,7 @@ static const unsigned DirectXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h
index f5c8396f398aa..6338a4f2f9036 100644
--- a/clang/lib/Basic/Targets/NVPTX.h
+++ b/clang/lib/Basic/Targets/NVPTX.h
@@ -50,6 +50,7 @@ static const unsigned NVPTXAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h
index 22b2799518dd0..94449231efb94 100644
--- a/clang/lib/Basic/Targets/SPIR.h
+++ b/clang/lib/Basic/Targets/SPIR.h
@@ -51,6 +51,7 @@ static const unsigned SPIRDefIsPrivMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
@@ -87,6 +88,7 @@ static const unsigned SPIRDefIsGenMap[] = {
10, // hlsl_private
11, // hlsl_device
7, // hlsl_input
+ 13, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/SystemZ.h b/clang/lib/Basic/Targets/SystemZ.h
index 4e15d5af1cde6..4ce515b31a001 100644
--- a/clang/lib/Basic/Targets/SystemZ.h
+++ b/clang/lib/Basic/Targets/SystemZ.h
@@ -46,6 +46,7 @@ static const unsigned ZOSAddressMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
0 // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h
index 005cab9819472..161025378c471 100644
--- a/clang/lib/Basic/Targets/TCE.h
+++ b/clang/lib/Basic/Targets/TCE.h
@@ -55,6 +55,7 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h
index 4de6ce6bb5a21..c8065843aeb42 100644
--- a/clang/lib/Basic/Targets/WebAssembly.h
+++ b/clang/lib/Basic/Targets/WebAssembly.h
@@ -46,6 +46,7 @@ static const unsigned WebAssemblyAddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
20, // wasm_funcref
};
diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h
index e7da2622e78b5..7b88ac70e234f 100644
--- a/clang/lib/Basic/Targets/X86.h
+++ b/clang/lib/Basic/Targets/X86.h
@@ -50,6 +50,7 @@ static const unsigned X86AddrSpaceMap[] = {
0, // hlsl_private
0, // hlsl_device
0, // hlsl_input
+ 0, // hlsl_push_constant
// Wasm address space values for this target are dummy values,
// as it is only enabled for Wasm targets.
20, // wasm_funcref
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index 3eeb1718e455a..c3b536c7a267f 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -6049,9 +6049,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
getCUDARuntime().handleVarRegistration(D, *GV);
}
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input) {
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D))) {
// HLSL Input variables are considered to be set by the driver/pipeline, but
- // only visible to a single thread/wave.
+ // only visible to a single thread/wave. Push constants are also externally
+ // initialized, but constant, hence cross-wave visibility is not relevant.
GV->setExternallyInitialized(true);
} else {
GV->setInitializer(Init);
@@ -6102,10 +6104,11 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
!D->hasAttr<ConstInitAttr>())
Linkage = llvm::GlobalValue::InternalLinkage;
- // HLSL variables in the input address space maps like memory-mapped
- // variables. Even if they are 'static', they are externally initialized and
- // read/write by the hardware/driver/pipeline.
- if (LangOpts.HLSL && GetGlobalVarAddressSpace(D) == LangAS::hlsl_input)
+ // HLSL variables in the input or push-constant address space maps are like
+ // memory-mapped variables. Even if they are 'static', they are externally
+ // initialized and read/write by the hardware/driver/pipeline.
+ if (LangOpts.HLSL &&
+ hlsl::isInitializedByPipeline(GetGlobalVarAddressSpace(D)))
Linkage = llvm::GlobalValue::ExternalLinkage;
GV->setLinkage(Linkage);
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 25b89d65847ad..5c29bf5e77414 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -30,6 +30,7 @@
#include "clang/AST/Type.h"
#include "clang/Basic/Builtins.h"
#include "clang/Basic/DiagnosticComment.h"
+#include "clang/Basic/HLSLRuntime.h"
#include "clang/Basic/PartialDiagnostic.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -14559,10 +14560,10 @@ void Sema::ActOnUninitializedDecl(Decl *RealDecl) {
if (getLangOpts().HLSL && HLSL().ActOnUninitializedVarDecl(Var))
return;
- // HLSL input variables are expected to be externally initialized, even
- // when marked `static`.
+ // HLSL input & push-constant variables are expected to be externally
+ // initialized, even when marked `static`.
if (getLangOpts().HLSL &&
- Var->getType().getAddressSpace() == LangAS::hlsl_input)
+ hlsl::isInitializedByPipeline(Var->getType().getAddressSpace()))
return;
// C++03 [dcl.init]p9:
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index a9e7b44ac9d73..0396155bd6a9d 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7614,6 +7614,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLVkExtBuiltinInput:
S.HLSL().handleVkExtBuiltinInputAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkPushConstant:
+ S.HLSL().handleVkPushConstantAttr(D, AL);
+ break;
case ParsedAttr::AT_HLSLVkConstantId:
S.HLSL().handleVkConstantIdAttr(D, AL);
break;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 2b9b3abbd5360..1831584c88697 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -1667,6 +1667,11 @@ void SemaHLSL::handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkExtBuiltinInputAttr(getASTContext(), AL, ID));
}
+void SemaHLSL::handleVkPushConstantAttr(Decl *D, const ParsedAttr &AL) {
+ D->addAttr(::new (getASTContext())
+ HLSLVkPushConstantAttr(getASTContext(), AL));
+}
+
void SemaHLSL::handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL) {
uint32_t Id;
if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Id))
@@ -3837,12 +3842,15 @@ QualType SemaHLSL::getInoutParameterType(QualType Ty) {
return Ty;
}
-static bool IsDefaultBufferConstantDecl(VarDecl *VD) {
+static bool IsDefaultBufferConstantDecl(const ASTContext &Ctx, VarDecl *VD) {
+ bool IsVulkan =
+ Ctx.getTargetInfo().getTriple().getOS() == llvm::Triple::Vulkan;
+ bool IsVKPushConstant = IsVulkan && VD->hasAttr<HLSLVkPushConstantAttr>();
QualType QT = VD->getType();
return VD->getDeclContext()->isTranslationUnit() &&
QT.getAddressSpace() == LangAS::Default &&
VD->getStorageClass() != SC_Static &&
- !VD->hasAttr<HLSLVkConstantIdAttr>() &&
+ !VD->hasAttr<HLSLVkConstantIdAttr>() && !IsVKPushConstant &&
!isInvalidConstantBufferLeafElementType(QT.getTypePtr());
}
@@ -3863,6 +3871,19 @@ void SemaHLSL::deduceAddressSpace(VarDecl *Decl) {
return;
}
+ bool IsVulkan = getASTContext().getTargetInfo().getTriple().getOS() ==
+ llvm::Triple::Vulkan;
+ if (IsVulkan && Decl->hasAttr<HLSLVkPushConstantAttr>()) {
+ if (HasDeclaredAPushConstant)
+ SemaRef.Diag(Decl->getLocation(), diag::err_hlsl_push_constant_unique);
+
+ LangAS ImplAS = LangAS::hlsl_push_constant;
+ Type = SemaRef.getASTContext().getAddrSpaceQualType(Type, ImplAS);
+ Decl->setType(Type);
+ HasDeclaredAPushConstant = true;
+ return;
+ }
+
if (Type->isSamplerT() || Type->isVoidType())
return;
@@ -3895,7 +3916,7 @@ void SemaHLSL::ActOnVariableDeclarator(VarDecl *VD) {
// Global variables outside a cbuffer block that are not a resource, static,
// groupshared, or an empty array or struct belong to the default constant
// buffer $Globals (to be created at the end of the translation unit).
- if (IsDefaultBufferConstantDecl(VD)) {
+ if (IsDefaultBufferConstantDecl(getASTContext(), VD)) {
// update address space to hlsl_constant
QualType NewTy = getASTContext().getAddrSpaceQualType(
VD->getType(), LangAS::hlsl_constant);
@@ -4196,8 +4217,11 @@ void SemaHLSL::processExplicitBindingsOnDecl(VarDecl *VD) {
bool HasBinding = false;
for (Attr *A : VD->attrs()) {
- if (isa<HLSLVkBindingAttr>(A))
+ if (isa<HLSLVkBindingAttr>(A)) {
HasBinding = true;
+ if (auto PA = VD->getAttr<HLSLVkPushConstantAttr>())
+ Diag(PA->getLoc(), diag::err_hlsl_attr_incompatible) << A << PA;
+ }
HLSLResourceBindingAttr *RBA = dyn_cast<HLSLResourceBindingAttr>(A);
if (!RBA || !RBA->hasRegisterSlot())
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
new file mode 100644
index 0000000000000..412ec4dffc572
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.access.bitfield.hlsl
@@ -0,0 +1,20 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+struct S {
+ uint32_t a : 1;
+ uint32_t b : 1;
+};
+// CHECK: %struct.S = type { i8 }
+
+[[vk::push_constant]] S buffer;
+// CHECK: @buffer = external hidden addrspace(13) externally_initialized global %struct.S, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ uint32_t v = buffer.b;
+// CHECK: %bf.load = load i8, ptr addrspace(13) @buffer, align 1
+// CHECK: %bf.lshr = lshr i8 %bf.load, 1
+// CHECK: %bf.clear = and i8 %bf.lshr, 1
+// CHECK: %bf.cast = zext i8 %bf.clear to i32
+// CHECK: store i32 %bf.cast
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
new file mode 100644
index 0000000000000..2b2e9d09c7ab0
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.anon-struct.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan-compute -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+[[vk::push_constant]]
+struct {
+ int a;
+ float b;
+ float3 c;
+}
+PushConstants;
+
+// CHECK: %struct.anon = type <{ i32, float, <3 x float> }>
+// CHECK: @PushConstants = external hidden addrspace(13) externally_initialized global %struct.anon, align 1
+
+[numthreads(1, 1, 1)]
+void main() {
+ float tmp = PushConstants.b;
+}
diff --git a/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
new file mode 100644
index 0000000000000..6b58decfa5188
--- /dev/null
+++ b/clang/test/CodeGenHLSL/vk-features/vk.pushconstant.invalid.hlsl
@@ -0,0 +1,13 @@
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan-compute -x hlsl -emit-llvm -disable-llvm-passes -o - -hlsl-entry main %s -verify
+
+struct S {
+ float f;
+};
+
+// expected-error@+1 {{'vk::binding' attribute is not compatible with 'vk::push_constant' attribute}}
+[[vk::push_constant, vk::binding(5)]]
+S pcs;
+
+[numthreads(1, 1, 1)]
+void main() {
+}
diff...
[truncated]
|
s-perron
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM except I would like to see a test that checks that the AST is adding the attribute correctly.
Also, add a test for DXIL. Make sure it properly ignores the attribute.
Adds proposal to implement VK push constant support in Clang. E2E implementation draft: llvm/llvm-project#166793
🐧 Linux x64 Test Results
Failed Tests(click on a test name to see its output) LLVMLLVM.CodeGen/SPIRV/vk-pushconstant-access.llIf these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the |
|
|
||
| def HLSLVkPushConstantDocs : Documentation { | ||
| let Category = DocCatVariable; | ||
| let Content = [{ FIXME }]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this a fix me? Feels like we should have the documentaion for this feature.
|
LGTM once you fix the ci test failures and update documentation in tablegen. |
Implements initial support for vk::push_constant.
As is, this allows handling simple push constants, but has one
main issue: layout can be incorrect.
The old fix would be to use target specific types, but this is
actively being reworked on for cbuffers (#147352). So for now, this
part is marked as XFAIL.