From 59ca14958b7aba892e16a864a02516e12d24a3fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 3 Dec 2025 14:46:53 +0100 Subject: [PATCH 1/5] [SPIR-V] fix variable with inline SC added to CB Before this commit, non-static global variables with a inline SPIR-V ext_storage_class attribute were added to the default constant buffer global struct. This is an issue as the point of a custom storage class is to create globals such as acceleration structures. Fixes #7742 --- tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 7 +++++- .../spv.intrinsicStorageClass.hlsl | 17 ++++++++------ .../inline-spirv/spv.raytracing.hlsl | 22 +++++++++++++++++++ 3 files changed, 38 insertions(+), 8 deletions(-) create mode 100644 tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 98051f5844..236d0651b3 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -330,6 +330,10 @@ bool shouldSkipInStructLayout(const Decl *decl) { return true; } + if (decl->hasAttr()) { + return true; + } + // External visibility if (const auto *declDecl = dyn_cast(decl)) if (!declDecl->hasExternalFormalLinkage()) @@ -1183,6 +1187,7 @@ SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var) { SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var, QualType type) { const bool isGroupShared = var->hasAttr(); + const bool isInlineSpirv = var->hasAttr(); const bool isACSBuffer = isAppendStructuredBuffer(type) || isConsumeStructuredBuffer(type); const bool isRWSBuffer = isRWStructuredBuffer(type); @@ -1190,7 +1195,7 @@ SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var, const auto rule = getLayoutRuleForExternVar(type, spirvOptions); const auto loc = var->getLocation(); - if (!isGroupShared && !isResourceType(type) && + if (!isGroupShared && !isResourceType(type) && !isInlineSpirv && !isResourceOnlyStructure(type)) { // We currently cannot support global structures that contain both resources diff --git a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicStorageClass.hlsl b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicStorageClass.hlsl index bc2e6b94b0..762af25d77 100644 --- a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicStorageClass.hlsl +++ b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.intrinsicStorageClass.hlsl @@ -1,15 +1,18 @@ // RUN: %dxc -T ps_6_0 -E main -spirv -Vd -fcgl %s -spirv | FileCheck %s -//CHECK: [[payloadTy:%[a-zA-Z0-9_]+]] = OpTypeStruct %v4float -//CHECK-NEXT: [[payloadTyPtr:%[a-zA-Z0-9_]+]] = OpTypePointer RayPayloadKHR [[payloadTy]] -//CHECK: [[crossTy:%[a-zA-Z0-9_]+]] = OpTypePointer CrossWorkgroup %int -//CHECK: {{%[a-zA-Z0-9_]+}} = OpVariable [[payloadTyPtr]] RayPayloadKHR -//CHECK: {{%[a-zA-Z0-9_]+}} = OpVariable [[crossTy]] CrossWorkgroup - +[[vk::ext_extension("SPV_KHR_ray_tracing")]] +[[vk::ext_capability(/* RayTracingKHR */ 4479)]] [[vk::ext_storage_class(/*RayPayloadKHR*/5338)]] float4 payload; +// CHECK-DAG: [[ptr_payload_v4:%[a-zA-Z0-9_]+]] = OpTypePointer RayPayloadKHR %v4float +// CHECK-DAG: %payload = OpVariable [[ptr_payload_v4]] RayPayloadKHR int main() : SV_Target0 { - [[vk::ext_storage_class(/* CrossWorkgroup */ 5)]] int foo = 3; + + [[vk::ext_storage_class(/* CrossWorkgroup */ 5)]] + int foo = 3; +// CHECK-DAG: [[ptr_cw_int:%[a-zA-Z0-9_]+]] = OpTypePointer CrossWorkgroup %int +// CHECK-DAG: %foo = OpVariable [[ptr_cw_int]] CrossWorkgroup + return foo; } diff --git a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl new file mode 100644 index 0000000000..f510738602 --- /dev/null +++ b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl @@ -0,0 +1,22 @@ +// RUN: %dxc %s -T cs_6_8 -spirv -fspv-target-env=vulkan1.3 -E main -O0 | Filecheck %s + +// CHECK-DAG: OpCapability RuntimeDescriptorArray +// CHECK-DAG: OpCapability RayQueryKHR + +// CHECK-DAG: OpDecorate %MyScene DescriptorSet 1 +// CHECK-DAG: OpDecorate %MyScene Binding 3 + +using A = vk::SpirvOpaqueType; +// CHECK: %[[name:[^ ]+]] = OpTypeAccelerationStructureKHR + +using RA [[vk::ext_capability(/* RuntimeDescriptorArray */ 5302)]] = vk::SpirvOpaqueType; +// CHECK: %[[rarr:[^ ]+]] = OpTypeRuntimeArray %[[name]] + +// CHECK: %[[ptr:[^ ]+]] = OpTypePointer UniformConstant %[[rarr]] +// CHECK: %MyScene = OpVariable %[[ptr]] UniformConstant +[[vk::binding(3, 1)]] +[[vk::ext_storage_class(0)]] +RA MyScene; + +[numthreads(1, 1, 1)] +void main() {} From 77f1f426f108e67c4a594fe110d9810b2b8de97e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 3 Dec 2025 14:52:42 +0100 Subject: [PATCH 2/5] change variable name --- tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 236d0651b3..136b864288 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1187,7 +1187,7 @@ SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var) { SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var, QualType type) { const bool isGroupShared = var->hasAttr(); - const bool isInlineSpirv = var->hasAttr(); + const bool hasInlineSpirvSC = var->hasAttr(); const bool isACSBuffer = isAppendStructuredBuffer(type) || isConsumeStructuredBuffer(type); const bool isRWSBuffer = isRWStructuredBuffer(type); @@ -1195,7 +1195,7 @@ SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var, const auto rule = getLayoutRuleForExternVar(type, spirvOptions); const auto loc = var->getLocation(); - if (!isGroupShared && !isResourceType(type) && !isInlineSpirv && + if (!isGroupShared && !isResourceType(type) && !hasInlineSpirvSC && !isResourceOnlyStructure(type)) { // We currently cannot support global structures that contain both resources From 562d7f9e559f5c0b28c5e571a586918a45b8a02f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 3 Dec 2025 14:54:37 +0100 Subject: [PATCH 3/5] fix FileCheck spelling --- tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl index f510738602..0d6c19904c 100644 --- a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl +++ b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.raytracing.hlsl @@ -1,4 +1,4 @@ -// RUN: %dxc %s -T cs_6_8 -spirv -fspv-target-env=vulkan1.3 -E main -O0 | Filecheck %s +// RUN: %dxc %s -T cs_6_8 -spirv -fspv-target-env=vulkan1.3 -E main -O0 | FileCheck %s // CHECK-DAG: OpCapability RuntimeDescriptorArray // CHECK-DAG: OpCapability RayQueryKHR From 9550de97540a8dbae0d34e9815a0aaeb0e5ea2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 3 Dec 2025 15:14:36 +0100 Subject: [PATCH 4/5] [SPIR-V] Normalize inline SPIR-V for builtins Inline SPIR-V provides mutliple attributes with overlapping capabilities: - vk::builtin - vk::ext_input_builtin - vk::ext_output_builtin - vk::storage_class - vk::decorate The issue is that what should be syntactic sugar was not implemented as such: ext_input_builtin should be sugar for decorate + storage_class. This meant codegen has different paths depending on the attributes used, even if they should in the end have the same effect. As expected, those paths have different behaviors, and some are buggy. This commits adds a new step in sema: attribute normalization. The target idea is to `desugar` some attributes into a more basic format, but it requires large changes in CG. As of now, this commits normalizes a bit the attributes in sema, but might lift decoration+storage into ext_input_builtin. Properly doing de-sugar will require additional changes, especially around parameters & builtin through decorations which works only because codegen checks are not complete. --- .../clang/Basic/DiagnosticSemaKinds.td | 13 ++ tools/clang/include/clang/Sema/SemaHLSL.h | 2 + tools/clang/lib/SPIRV/DeclResultIdMapper.cpp | 4 + tools/clang/lib/Sema/SemaDeclAttr.cpp | 5 + tools/clang/lib/Sema/SemaHLSL.cpp | 160 ++++++++++++++++++ .../spv.inline.builtin.both.error.hlsl | 9 - .../spv.inline.builtin.bad.storage.hlsl | 9 + .../spv.inline.builtin.both.error.hlsl | 9 + .../spv.inline.builtin.location.hlsl | 10 ++ .../spv.inline.builtin.missing.hlsl | 8 + .../spv.inline.builtin.multiple.error.hlsl | 10 ++ .../spv.inline.builtin.multiple.input.hlsl | 9 + ....inline.builtin.multiple.input.manual.hlsl | 11 ++ ...iltin.multiple.input.storage.conflict.hlsl | 9 + ...inline.builtin.multiple.input.storage.hlsl | 10 ++ ...nline.builtin.multiple.input.storage2.hlsl | 10 ++ .../spv.inline.builtin.multiple.output.hlsl | 9 + ...inline.builtin.multiple.output.manual.hlsl | 11 ++ ...ltin.multiple.output.storage.conflict.hlsl | 10 ++ ...nline.builtin.multiple.output.storage.hlsl | 10 ++ ...line.builtin.multiple.output.storage2.hlsl | 10 ++ .../spv.inline.builtin.multiple.same.hlsl | 12 ++ .../spv.inline.location.error.hlsl | 8 + .../spv.inline.location.error2.hlsl | 8 + .../spv.inline.location.error3.hlsl | 8 + .../inline-spirv/spv.inline.location.hlsl | 9 + .../spv.inline.location.multiple.hlsl | 10 ++ 27 files changed, 384 insertions(+), 9 deletions(-) delete mode 100644 tools/clang/test/CodeGenSPIRV/inline-spirv/spv.inline.builtin.both.error.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.bad.storage.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.both.error.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.location.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.missing.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.error.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.manual.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.conflict.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage2.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.manual.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.conflict.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage2.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.same.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error2.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error3.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.hlsl create mode 100644 tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.multiple.hlsl diff --git a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td index 34a2195cbc..04f9788190 100644 --- a/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/tools/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8066,6 +8066,19 @@ def err_hlsl_vk_static_pointer_cast_type: Error< "vk::static_pointer_cast() content type must be base class of argument's content type">; def warn_spirv_node_shaders_experimental : Warning< "SPIR-V implementation of node shaders is experimental and subject to change">; + +def err_hlsl_spv_inline_builtin_redefinition + : Error<"%0 cannot be used on a variable already associated with the " + "built-in %1">; +def err_hlsl_spv_inline_builtin_incompatible + : Error<"%0 incompatible with the BuiltIn %1">; +def err_hlsl_spv_inline_location_redefinition + : Error<"invalid %0 : target already associated with another location %1">; +def err_hlsl_spv_inline_invalid_decoration_single_operand_missing + : Error<"decoration %0 requires 1 operand">; +def err_hlsl_spv_inline_storage_class_redefinition + : Error<"%0 cannot be used on a variable already associated to another " + "storage class %1">; // SPIRV Change Ends let CategoryName = "OpenMP Issue" in { diff --git a/tools/clang/include/clang/Sema/SemaHLSL.h b/tools/clang/include/clang/Sema/SemaHLSL.h index 80ce8ddd7d..f52d6686b7 100644 --- a/tools/clang/include/clang/Sema/SemaHLSL.h +++ b/tools/clang/include/clang/Sema/SemaHLSL.h @@ -139,6 +139,8 @@ GetBestViableFunction(clang::Sema &S, clang::SourceLocation Loc, bool ShouldSkipNRVO(clang::Sema &sema, clang::QualType returnType, clang::VarDecl *VD, clang::FunctionDecl *FD); +void NormalizeInlineSPIRVAttributes(clang::Sema &S, clang::Decl *D); + /// Processes an attribute for a declaration. /// Sema with context. /// Annotated declaration. diff --git a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp index 136b864288..54a55dc481 100644 --- a/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp +++ b/tools/clang/lib/SPIRV/DeclResultIdMapper.cpp @@ -1298,6 +1298,10 @@ SpirvVariable *DeclResultIdMapper::createExternVar(const VarDecl *var, return varInstr; const auto *bindingAttr = var->getAttr(); + + if (var->hasAttr() && !bindingAttr) + return varInstr; + resourceVars.emplace_back(varInstr, var, loc, getResourceBinding(var), bindingAttr, var->getAttr()); diff --git a/tools/clang/lib/Sema/SemaDeclAttr.cpp b/tools/clang/lib/Sema/SemaDeclAttr.cpp index 085874a0ed..f5029a2fa9 100644 --- a/tools/clang/lib/Sema/SemaDeclAttr.cpp +++ b/tools/clang/lib/Sema/SemaDeclAttr.cpp @@ -5326,6 +5326,11 @@ void Sema::ProcessDeclAttributes(Scope *S, Decl *D, const Declarator &PD) { D->addAttr(*i); } // HLSL Change Ends + + // SPIR-V Change Starts + if (LangOpts.HLSL) + hlsl::NormalizeInlineSPIRVAttributes(*this, D); + // SPIR-V Change Ends } /// Is the given declaration allowed to use a forbidden type? diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index e9c8c90a2d..a157da3a89 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -48,10 +48,16 @@ #include "llvm/ADT/StringRef.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" + +// Enables functions like spv::BuiltInToString() +#define SPV_ENABLE_UTILITY_CODE +#include "spirv/unified1/spirv.hpp11" + #include #include #include #include +#include enum ArBasicKind { AR_BASIC_BOOL, @@ -14593,6 +14599,160 @@ void ValidateDispatchGridValues(DiagnosticsEngine &Diags, << A.getName() << A.getRange(); } +void hlsl::NormalizeInlineSPIRVAttributes(Sema &S, Decl *D) { + // Collecting the values that can be set across multiple attributes. + std::optional> StorageClass; + std::optional> Location; + std::optional> BuiltIn; + + // Vector collecting all the other attributes. + clang::AttrVec NewAttrs; + + for (auto *A : D->attrs()) { + if (auto *DA = dyn_cast(A)) { + spv::Decoration Decoration = spv::Decoration(DA->getDecorate()); + if (Decoration == spv::Decoration::Location) { + if (DA->literals_size() != 1) { + S.Diags.Report( + A->getLocation(), + diag:: + err_hlsl_spv_inline_invalid_decoration_single_operand_missing) + << "Location"; + return; + } + unsigned Index = *DA->literals_begin(); + if (Location.has_value() && Location->first != Index) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_location_redefinition) + << DA << Location->first; + return; + } + Location = {Index, DA->getLocation()}; + } else if (Decoration == spv::Decoration::BuiltIn) { + if (DA->literals_size() != 1) { + S.Diags.Report( + A->getLocation(), + diag:: + err_hlsl_spv_inline_invalid_decoration_single_operand_missing) + << "BuiltIn"; + return; + } + spv::BuiltIn ID = spv::BuiltIn(*DA->literals_begin()); + if (BuiltIn.has_value() && ID != BuiltIn->first) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_builtin_redefinition) + << DA << spv::BuiltInToString(BuiltIn->first); + return; + } + BuiltIn = {ID, DA->getLocation()}; + } else { + NewAttrs.push_back(A); + } + } else if (auto *SCA = dyn_cast(A)) { + spv::StorageClass SC = spv::StorageClass(SCA->getStclass()); + if (StorageClass.has_value() && StorageClass->first != SC) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_storage_class_redefinition) + << SCA << spv::StorageClassToString(StorageClass->first); + return; + } + StorageClass = {SC, SCA->getLocation()}; + } else if (auto *LA = dyn_cast(A)) { + if (Location.has_value() && + Location->first != static_cast(LA->getNumber())) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_location_redefinition) + << LA << Location->first; + return; + } + Location = {LA->getNumber(), LA->getLocation()}; + } else if (auto *BA = dyn_cast(A)) { + if (StorageClass.has_value() && + StorageClass->first != spv::StorageClass::Output) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_storage_class_redefinition) + << BA << spv::StorageClassToString(StorageClass->first); + return; + } + StorageClass = {spv::StorageClass::Output, BA->getLocation()}; + + spv::BuiltIn ID = spv::BuiltIn(BA->getBuiltInID()); + if (BuiltIn.has_value() && ID != BuiltIn->first) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_builtin_redefinition) + << BA << spv::BuiltInToString(BuiltIn->first); + return; + } + BuiltIn = {ID, BA->getLocation()}; + + } else if (auto *BA = dyn_cast(A)) { + if (StorageClass.has_value() && + StorageClass->first != spv::StorageClass::Input) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_storage_class_redefinition) + << BA << spv::StorageClassToString(StorageClass->first); + return; + } + StorageClass = {spv::StorageClass::Input, BA->getLocation()}; + + spv::BuiltIn ID = spv::BuiltIn(BA->getBuiltInID()); + if (BuiltIn.has_value() && ID != BuiltIn->first) { + S.Diags.Report(A->getLocation(), + diag::err_hlsl_spv_inline_builtin_redefinition) + << BA << spv::BuiltInToString(BuiltIn->first); + return; + } + BuiltIn = {ID, BA->getLocation()}; + } else { + NewAttrs.push_back(A); + } + } + + if (BuiltIn.has_value()) { + // Location should not be set if a BuiltIn is requested. + if (Location.has_value()) { + S.Diags.Report(D->getLocation(), + diag::err_hlsl_spv_inline_builtin_incompatible) + << "Location" << spv::BuiltInToString(BuiltIn->first); + return; + } + + // BuiltIn requires either Input or Output storage class. + if (StorageClass.has_value() && + StorageClass->first == spv::StorageClass::Input) + NewAttrs.push_back(new (S.Context) VKExtBuiltinInputAttr( + BuiltIn->second, S.Context, static_cast(BuiltIn->first), + 0)); + else if (StorageClass.has_value() && + StorageClass->first == spv::StorageClass::Output) + NewAttrs.push_back(new (S.Context) VKExtBuiltinOutputAttr( + BuiltIn->second, S.Context, static_cast(BuiltIn->first), + 0)); + else if (isa(D) || isa(D)) { + unsigned BuiltInID = static_cast(BuiltIn->first); + NewAttrs.push_back(new (S.Context) VKDecorateExtAttr( + BuiltIn->second, S.Context, /* BuiltIn */ 11, &BuiltInID, 1, 0)); + } else + S.Diags.Report(D->getLocation(), + diag::err_hlsl_spv_inline_builtin_incompatible) + << std::string("StorageClass ") + + spv::StorageClassToString(StorageClass->first) + << spv::BuiltInToString(BuiltIn->first); + } else { + if (Location.has_value()) + NewAttrs.push_back(new (S.Context) VKLocationAttr( + Location->second, S.Context, static_cast(Location->first), + 0)); + if (StorageClass.has_value()) + NewAttrs.push_back(new (S.Context) VKStorageClassExtAttr( + StorageClass->second, S.Context, + static_cast(StorageClass->first), 0)); + } + + D->dropAttrs(); + D->setAttrs(NewAttrs); +} + void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A, bool &Handled) { DXASSERT_NOMSG(D != nullptr); diff --git a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.inline.builtin.both.error.hlsl b/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.inline.builtin.both.error.hlsl deleted file mode 100644 index f7983678db..0000000000 --- a/tools/clang/test/CodeGenSPIRV/inline-spirv/spv.inline.builtin.both.error.hlsl +++ /dev/null @@ -1,9 +0,0 @@ -// RUN: not %dxc -T ps_6_0 -E main -fcgl %s -spirv 2>&1 | FileCheck %s - -// CHECK: error: vk::ext_builtin_input cannot be used together with vk::ext_builtin_output -[[vk::ext_builtin_input(/* NumWorkgroups */ 24)]] -[[vk::ext_builtin_output(/* NumWorkgroups */ 24)]] -static uint3 invalid; - -void main() { -} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.bad.storage.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.bad.storage.hlsl new file mode 100644 index 0000000000..4abd608074 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.bad.storage.hlsl @@ -0,0 +1,9 @@ +// RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify + +[[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] +[[vk::ext_storage_class(/* UniformConstant */ 0)]] +// expected-error@+1{{StorageClass UniformConstant incompatible with the BuiltIn WorkgroupId}} +static uint3 input; + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.both.error.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.both.error.hlsl new file mode 100644 index 0000000000..89d2b873cc --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.both.error.hlsl @@ -0,0 +1,9 @@ +// RUN: %dxc -T ps_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{'ext_builtin_input' cannot be used on a variable already associated to another storage class Output}} +[[vk::ext_builtin_input(/* NumWorkgroups */ 24)]] +[[vk::ext_builtin_output(/* NumWorkgroups */ 24)]] +static uint3 invalid; + +void main() { +} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.location.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.location.hlsl new file mode 100644 index 0000000000..3db779ed1e --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.location.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify + +[[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] +[[vk::ext_builtin_input(/* WorkgroupId */ 26)]] +[[vk::ext_decorate(/* Location */ 30, 0)]] +// expected-error@+1{{Location incompatible with the BuiltIn WorkgroupId}} +static uint3 input; + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.missing.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.missing.hlsl new file mode 100644 index 0000000000..caa0fcb91f --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.missing.hlsl @@ -0,0 +1,8 @@ +// RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{decoration BuiltIn requires 1 operand}} +[[vk::ext_decorate(/* BuiltIn */ 11)]] +static uint3 input; + +[numthreads(1, 1, 1)] +void main() {} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.error.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.error.hlsl new file mode 100644 index 0000000000..6d9e4b00fd --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.error.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{'ext_decorate' cannot be used on a variable already associated with the built-in LocalInvocationId}} +[[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] +[[vk::ext_decorate(/* BuiltIn */ 11, /* LocalInvocationId */ 27)]] +[[vk::ext_storage_class(/* Input */ 1)]] +static uint3 invalid; + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.hlsl new file mode 100644 index 0000000000..b156c4ff09 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.hlsl @@ -0,0 +1,9 @@ +// RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{'ext_builtin_input' cannot be used on a variable already associated with the built-in WorkgroupId}} +[[vk::ext_builtin_input(/* NumWorkgroups */ 24)]] +[[vk::ext_builtin_input(/* WorkgroupId */ 26)]] +static uint3 invalid; + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.manual.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.manual.hlsl new file mode 100644 index 0000000000..215b1ef590 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.manual.hlsl @@ -0,0 +1,11 @@ +// RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] +[[vk::ext_builtin_input(/* WorkgroupId */ 26)]] +static uint3 input; + +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector' static +// CHECK-NEXT: VKExtBuiltinInputAttr 0x{{.+}} 26 + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.conflict.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.conflict.hlsl new file mode 100644 index 0000000000..f6fd2bbfea --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.conflict.hlsl @@ -0,0 +1,9 @@ +// RUN: %dxc -T cs_6_9 -E main -spirv %s -verify + +// expected-error@+1{{'ext_storage_class' cannot be used on a variable already associated to another storage class Input}} +[[vk::ext_storage_class(/* Output */ 3)]] +[[vk::ext_builtin_input(/* WorkgroupId */ 26)]] +static uint3 input; + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.hlsl new file mode 100644 index 0000000000..cacba6c564 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_storage_class(/* Input */ 1)]] +[[vk::ext_builtin_input(/* WorkgroupId */ 26)]] +static uint3 input; +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector' static +// CHECK-NEXT: VKExtBuiltinInputAttr 0x{{.+}} 26 + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage2.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage2.hlsl new file mode 100644 index 0000000000..958bac4dec --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.input.storage2.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_builtin_input(/* WorkgroupId */ 26)]] +[[vk::ext_storage_class(/* Input */ 1)]] +static uint3 input; +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector' static +// CHECK-NEXT: VKExtBuiltinInputAttr 0x{{.+}} 26 + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.hlsl new file mode 100644 index 0000000000..087ead35d0 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.hlsl @@ -0,0 +1,9 @@ +// RUN: %dxc -T cs_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{'ext_builtin_output' cannot be used on a variable already associated with the built-in WorkgroupId}} +[[vk::ext_builtin_output(/* NumWorkgroups */ 24)]] +[[vk::ext_builtin_output(/* WorkgroupId */ 26)]] +static uint3 invalid; + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.manual.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.manual.hlsl new file mode 100644 index 0000000000..8d380b2b50 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.manual.hlsl @@ -0,0 +1,11 @@ +// RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] +[[vk::ext_builtin_output(/* WorkgroupId */ 26)]] +static uint3 input; + +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector' static +// CHECK-NEXT: VKExtBuiltinOutputAttr 0x{{.+}} 26 + +[numthreads(1, 1, 1)] +void main() { } diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.conflict.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.conflict.hlsl new file mode 100644 index 0000000000..02884ae65f --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.conflict.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T vs_6_9 -E main -spirv %s -verify + +// expected-error@+1{{'ext_storage_class' cannot be used on a variable already associated to another storage class Output}} +[[vk::ext_storage_class(/* Input */ 1)]] +[[vk::ext_builtin_output(/* Position */ 0)]] +static float4 output; + +void main() { +} + diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.hlsl new file mode 100644 index 0000000000..6ff9dfb552 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T vs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_storage_class(/* Output */ 3)]] +[[vk::ext_builtin_output(/* Position */ 0)]] +static float4 output; +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:15 output 'float4':'vector' static +// CHECK-NEXT: VKExtBuiltinOutputAttr 0x{{.+}} 0 + +void main() { +} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage2.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage2.hlsl new file mode 100644 index 0000000000..2ed27bfc10 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.output.storage2.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T vs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_builtin_output(/* Position */ 0)]] +[[vk::ext_storage_class(/* Output */ 3)]] +static float4 output; +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:15 output 'float4':'vector' static +// CHECK-NEXT: VKExtBuiltinOutputAttr 0x{{.+}} 0 + +void main() { +} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.same.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.same.hlsl new file mode 100644 index 0000000000..709b9d5322 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.builtin.multiple.same.hlsl @@ -0,0 +1,12 @@ +// RUN: %dxc -T cs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] +[[vk::ext_decorate(/* BuiltIn */ 11, /* WorkgroupId */ 26)]] +[[vk::ext_storage_class(/* Input */ 1)]] +static uint3 input; + +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:14 input 'uint3':'vector' static +// CHECK-NEXT: VKExtBuiltinInputAttr 0x{{.+}} 26 + +[numthreads(1, 1, 1)] +void main() {} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error.hlsl new file mode 100644 index 0000000000..caf17f07fb --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error.hlsl @@ -0,0 +1,8 @@ +// RUN: %dxc -T vs_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{invalid 'ext_decorate' : target already associated with another location 1}} +[[vk::ext_decorate(/* Location */ 30, 0)]] +[[vk::ext_decorate(/* Location */ 30, 1)]] +static float4 output; + +void main() {} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error2.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error2.hlsl new file mode 100644 index 0000000000..acb9820f75 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error2.hlsl @@ -0,0 +1,8 @@ +// RUN: %dxc -T vs_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{'location' attribute only applies to functions, parameters, and fields}} +[[vk::location(0)]] +[[vk::ext_decorate(/* Location */ 30, 1)]] +static float4 output; + +void main() {} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error3.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error3.hlsl new file mode 100644 index 0000000000..a236db2ab8 --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.error3.hlsl @@ -0,0 +1,8 @@ +// RUN: %dxc -T vs_6_0 -E main -fcgl %s -spirv -verify + +// expected-error@+1{{decoration Location requires 1 operand}} +[[vk::ext_decorate(/* Location */ 30)]] +[[vk::ext_decorate(/* Location */ 30, 1)]] +static float4 output; + +void main() {} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.hlsl new file mode 100644 index 0000000000..2e9e2393fa --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.hlsl @@ -0,0 +1,9 @@ +// RUN: %dxc -T vs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_decorate(/* Location */ 30, 0)]] +static float4 output; + +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:15 output 'float4':'vector' static +// CHECK-NEXT: VKLocationAttr 0x{{.+}} 0 + +void main() {} diff --git a/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.multiple.hlsl b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.multiple.hlsl new file mode 100644 index 0000000000..85c43f1ddb --- /dev/null +++ b/tools/clang/test/SemaHLSL/inline-spirv/spv.inline.location.multiple.hlsl @@ -0,0 +1,10 @@ +// RUN: %dxc -T vs_6_9 -E main -ast-dump -spirv %s | FileCheck %s + +[[vk::ext_decorate(/* Location */ 30, 1)]] +[[vk::ext_decorate(/* Location */ 30, 1)]] +static float4 output; + +// CHECK: VarDecl 0x{{.+}} <{{.+}}> col:15 output 'float4':'vector' static +// CHECK-NEXT: VKLocationAttr 0x{{.+}} 1 + +void main() {} From 1d3312c8cc7ab23a7745278d92bdcb57fbc5d6aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Fri, 5 Dec 2025 15:15:56 +0100 Subject: [PATCH 5/5] add guards for DXIL-only build --- tools/clang/lib/Sema/SemaHLSL.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tools/clang/lib/Sema/SemaHLSL.cpp b/tools/clang/lib/Sema/SemaHLSL.cpp index a157da3a89..e4c6ff068d 100644 --- a/tools/clang/lib/Sema/SemaHLSL.cpp +++ b/tools/clang/lib/Sema/SemaHLSL.cpp @@ -49,9 +49,11 @@ #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/raw_ostream.h" +#ifdef ENABLE_SPIRV_CODEGEN // Enables functions like spv::BuiltInToString() #define SPV_ENABLE_UTILITY_CODE #include "spirv/unified1/spirv.hpp11" +#endif #include #include @@ -14600,6 +14602,7 @@ void ValidateDispatchGridValues(DiagnosticsEngine &Diags, } void hlsl::NormalizeInlineSPIRVAttributes(Sema &S, Decl *D) { +#ifdef ENABLE_SPIRV_CODEGEN // Collecting the values that can be set across multiple attributes. std::optional> StorageClass; std::optional> Location; @@ -14751,6 +14754,7 @@ void hlsl::NormalizeInlineSPIRVAttributes(Sema &S, Decl *D) { D->dropAttrs(); D->setAttrs(NewAttrs); +#endif // ENABLE_SPIRV_CODEGEN } void hlsl::HandleDeclAttributeForHLSL(Sema &S, Decl *D, const AttributeList &A,