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 98051f5844..54a55dc481 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 hasInlineSpirvSC = 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) && !hasInlineSpirvSC &&
!isResourceOnlyStructure(type)) {
// We currently cannot support global structures that contain both resources
@@ -1293,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..e4c6ff068d 100644
--- a/tools/clang/lib/Sema/SemaHLSL.cpp
+++ b/tools/clang/lib/Sema/SemaHLSL.cpp
@@ -48,10 +48,18 @@
#include "llvm/ADT/StringRef.h"
#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
#include
#include
+#include
enum ArBasicKind {
AR_BASIC_BOOL,
@@ -14593,6 +14601,162 @@ void ValidateDispatchGridValues(DiagnosticsEngine &Diags,
<< A.getName() << A.getRange();
}
+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;
+ 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);
+#endif // ENABLE_SPIRV_CODEGEN
+}
+
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/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..0d6c19904c
--- /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* OpTypeAccelerationStructureKHR */ 5341>;
+// CHECK: %[[name:[^ ]+]] = OpTypeAccelerationStructureKHR
+
+using RA [[vk::ext_capability(/* RuntimeDescriptorArray */ 5302)]] = vk::SpirvOpaqueType* OpTypeRuntimeArray */ 29, A>;
+// 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() {}
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() {}