-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[HLSL][SPIR-V] Implement vk::location for inputs #169479
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
This PR adds the support for the SV_Target semantic and improved the diagnostics when the stage is correct, but the direction is disallowed.
This commit adds the support for vk::location attribute, focusing on input semantics.
|
@llvm/pr-subscribers-hlsl Author: Nathan Gauër (Keenuts) ChangesThis commit adds the support for vk::location attribute which can be applied to input and output variables. As in/inout parameters are not supported yet, vk::location on such parameters is not tested. As implemented in DXC, vk::location has the following rules:
Patch is 33.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169479.diff 21 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8e5f7ef0bb82d..9e7dc200bc6cf 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5172,6 +5172,14 @@ def HLSLVkConstantId : InheritableAttr {
let Documentation = [VkConstantIdDocs];
}
+def HLSLVkLocation : HLSLAnnotationAttr {
+ let Spellings = [CXX11<"vk", "location">];
+ let Args = [IntArgument<"Location">];
+ let Subjects = SubjectList<[ParmVar, Field, Function], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkLocationDocs];
+}
+
def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
let Subjects = SubjectList<[Record]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index c1b1510f363d4..fa365da3ed9aa 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8981,6 +8981,18 @@ The descriptor set is optional and defaults to 0 if not provided.
}];
}
+def HLSLVkLocationDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{
+Attribute used for specifying the location number for the stage input/output
+variables. Allowed on function parameters, function returns, and struct
+fields. This parameter has no effect when used outside of an entrypoint
+parameter/parameter field/return value.
+
+This attribute maps to the 'Location' SPIR-V decoration.
+ }];
+}
+
def WebAssemblyFuncrefDocs : Documentation {
let Category = DocCatType;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 53aa86a7dabde..04812bd78d9b4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13231,6 +13231,12 @@ def err_hlsl_semantic_indexing_not_supported
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
+def err_hlsl_semantic_unsupported_iotype_for_stage
+ : Error<"semantic %0 is unsupported in %2 shaders as %1, requires one of "
+ "the following: %3">;
+def err_hlsl_semantic_partial_explicit_indexing
+ : Error<"partial explicit stage input location assignment via "
+ "vk::location(X) unsupported">;
def warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric types}0">, InGroup<LegacyConstantRegisterBinding>;
def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 15edb7e77a22b..cac89f6e51cae 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -134,9 +134,6 @@ class SemaHLSL : public SemaBase {
void CheckEntryPoint(FunctionDecl *FD);
bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr,
SourceLocation Loc);
- void DiagnoseAttrStageMismatch(
- const Attr *A, llvm::Triple::EnvironmentType Stage,
- std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
QualType LHSType, QualType RHSType,
@@ -171,6 +168,7 @@ class SemaHLSL : public SemaBase {
void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL);
void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL);
void handleVkBindingAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkLocationAttr(Decl *D, const ParsedAttr &AL);
void handlePackOffsetAttr(Decl *D, const ParsedAttr &AL);
void handleShaderAttr(Decl *D, const ParsedAttr &AL);
void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL);
@@ -243,6 +241,19 @@ class SemaHLSL : public SemaBase {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
};
+ std::optional<bool> InputUsesExplicitVkLocations = std::nullopt;
+ std::optional<bool> OutputUsesExplicitVkLocations = std::nullopt;
+
+ enum IOType {
+ In = 0b01,
+ Out = 0b10,
+ InOut = 0b11,
+ };
+
+ struct SemanticStageInfo {
+ llvm::Triple::EnvironmentType Stage;
+ IOType AllowedIOTypesMask;
+ };
private:
void collectResourceBindingsOnVarDecl(VarDecl *D);
@@ -269,6 +280,14 @@ class SemaHLSL : public SemaBase {
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
+ void diagnoseAttrStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage,
+ std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
+
+ void diagnoseSemanticStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ std::initializer_list<SemanticStageInfo> AllowedStages);
+
uint32_t getNextImplicitBindingOrderID() {
return ImplicitBindingNextOrderID++;
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index f5c07fe2e33ff..913d80b7e5c25 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -582,20 +582,22 @@ static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M,
return B.CreateLoad(Ty, GV);
}
-llvm::Value *
-CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
- HLSLAppliedSemanticAttr *Semantic,
- std::optional<unsigned> Index) {
+llvm::Value *CGHLSLRuntime::emitSPIRVUserSemanticLoad(
+ llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
+ HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
unsigned Location = SPIRVLastAssignedInputSemanticLocation;
+ if (auto *L = Decl->getAttr<HLSLVkLocationAttr>())
+ Location = L->getLocation();
// DXC completely ignores the semantic/index pair. Location are assigned from
// the first semantic to the last.
llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Type);
unsigned ElementCount = AT ? AT->getNumElements() : 1;
SPIRVLastAssignedInputSemanticLocation += ElementCount;
+
return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location,
VariableName.str());
}
@@ -616,10 +618,14 @@ static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M,
void CGHLSLRuntime::emitSPIRVUserSemanticStore(
llvm::IRBuilder<> &B, llvm::Value *Source,
- HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
+ const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic,
+ std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
+
unsigned Location = SPIRVLastAssignedOutputSemanticLocation;
+ if (auto *L = Decl->getAttr<HLSLVkLocationAttr>())
+ Location = L->getLocation();
// DXC completely ignores the semantic/index pair. Location are assigned from
// the first semantic to the last.
@@ -671,7 +677,7 @@ llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
if (CGM.getTarget().getTriple().isSPIRV())
- return emitSPIRVUserSemanticLoad(B, Type, Semantic, Index);
+ return emitSPIRVUserSemanticLoad(B, Type, Decl, Semantic, Index);
if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticLoad(B, Type, Semantic, Index);
@@ -684,7 +690,7 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index) {
if (CGM.getTarget().getTriple().isSPIRV())
- return emitSPIRVUserSemanticStore(B, Source, Semantic, Index);
+ return emitSPIRVUserSemanticStore(B, Source, Decl, Semantic, Index);
if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticStore(B, Source, Semantic, Index);
@@ -783,6 +789,11 @@ void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source,
}
}
+ if (SemanticName == "SV_TARGET") {
+ emitUserSemanticStore(B, Source, Decl, Semantic, Index);
+ return;
+ }
+
llvm_unreachable(
"Store hasn't been implemented yet for this system semantic. FIXME");
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c883282a8d9c8..d057add2db222 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -278,6 +278,7 @@ class CGHLSLRuntime {
HLSLResourceBindingAttr *RBA);
llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+ const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
llvm::Value *emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
@@ -289,6 +290,7 @@ class CGHLSLRuntime {
std::optional<unsigned> Index);
void emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
+ const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
void emitDXILUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e3af5023c74d0..c9d1ee76a2e52 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7703,6 +7703,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLUnparsedSemantic:
S.HLSL().handleSemanticAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkLocation:
+ S.HLSL().handleVkLocationAttr(D, AL);
+ break;
case ParsedAttr::AT_AbiTag:
handleAbiTagAttr(S, D, AL);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ecab3946b58c7..2a69703d0d1e9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -771,6 +771,22 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
}
}
+static bool isPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD,
+ HLSLAppliedSemanticAttr *Semantic) {
+ if (AstContext.getTargetInfo().getTriple().getOS() != llvm::Triple::Vulkan)
+ return false;
+
+ const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
+ assert(ShaderAttr && "Entry point has no shader attribute");
+ llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
+ auto SemanticName = Semantic->getSemanticName().upper();
+
+ if (ST == llvm::Triple::Pixel && SemanticName == "SV_POSITION")
+ return true;
+
+ return false;
+}
+
bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
DeclaratorDecl *OutputDecl,
DeclaratorDecl *D,
@@ -800,6 +816,22 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
unsigned Location = ActiveSemantic.Index.value_or(0);
+ if (!isPipelineBuiltin(getASTContext(), FD, A)) {
+ bool HasVkLocation = false;
+ if (auto *A = D->getAttr<HLSLVkLocationAttr>()) {
+ HasVkLocation = true;
+ Location = A->getLocation();
+ }
+
+ auto &UsesExplicitVkLocations =
+ IsInput ? InputUsesExplicitVkLocations : OutputUsesExplicitVkLocations;
+ if (UsesExplicitVkLocations.value_or(HasVkLocation) != HasVkLocation) {
+ Diag(D->getLocation(), diag::err_hlsl_semantic_partial_explicit_indexing);
+ return false;
+ }
+ UsesExplicitVkLocations = HasVkLocation;
+ }
+
const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
unsigned ElementCount = AT ? AT->getZExtSize() : 1;
ActiveSemantic.Index = Location + ElementCount;
@@ -873,14 +905,14 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
case llvm::Triple::Miss:
case llvm::Triple::Callable:
if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) {
- DiagnoseAttrStageMismatch(NT, ST,
+ diagnoseAttrStageMismatch(NT, ST,
{llvm::Triple::Compute,
llvm::Triple::Amplification,
llvm::Triple::Mesh});
FD->setInvalidDecl();
}
if (const auto *WS = FD->getAttr<HLSLWaveSizeAttr>()) {
- DiagnoseAttrStageMismatch(WS, ST,
+ diagnoseAttrStageMismatch(WS, ST,
{llvm::Triple::Compute,
llvm::Triple::Amplification,
llvm::Triple::Mesh});
@@ -954,7 +986,8 @@ void SemaHLSL::checkSemanticAnnotation(
SemanticName == "SV_GROUPID") {
if (ST != llvm::Triple::Compute)
- DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Compute});
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Compute, IOType::In}});
if (SemanticAttr->getSemanticIndex() != 0) {
std::string PrettyName =
@@ -969,10 +1002,15 @@ void SemaHLSL::checkSemanticAnnotation(
if (SemanticName == "SV_POSITION") {
// SV_Position can be an input or output in vertex shaders,
// but only an input in pixel shaders.
- if (ST == llvm::Triple::Vertex || (ST == llvm::Triple::Pixel && IsInput))
- return;
- DiagnoseAttrStageMismatch(SemanticAttr, ST,
- {llvm::Triple::Pixel, llvm::Triple::Vertex});
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Vertex, IOType::InOut},
+ {llvm::Triple::Pixel, IOType::In}});
+ return;
+ }
+
+ if (SemanticName == "SV_TARGET") {
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Pixel, IOType::Out}});
return;
}
@@ -982,7 +1020,7 @@ void SemaHLSL::checkSemanticAnnotation(
llvm_unreachable("Unknown SemanticAttr");
}
-void SemaHLSL::DiagnoseAttrStageMismatch(
+void SemaHLSL::diagnoseAttrStageMismatch(
const Attr *A, llvm::Triple::EnvironmentType Stage,
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages) {
SmallVector<StringRef, 8> StageStrings;
@@ -996,6 +1034,50 @@ void SemaHLSL::DiagnoseAttrStageMismatch(
<< (AllowedStages.size() != 1) << join(StageStrings, ", ");
}
+void SemaHLSL::diagnoseSemanticStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ std::initializer_list<SemanticStageInfo> Allowed) {
+
+ for (auto &Case : Allowed) {
+ if (Case.Stage != Stage)
+ continue;
+
+ if (IsInput && Case.AllowedIOTypesMask & IOType::In)
+ return;
+ if (!IsInput && Case.AllowedIOTypesMask & IOType::Out)
+ return;
+
+ SmallVector<std::string, 8> ValidCases;
+ llvm::transform(
+ Allowed, std::back_inserter(ValidCases), [](SemanticStageInfo Case) {
+ SmallVector<std::string, 2> ValidType;
+ if (Case.AllowedIOTypesMask & IOType::In)
+ ValidType.push_back("input");
+ if (Case.AllowedIOTypesMask & IOType::Out)
+ ValidType.push_back("output");
+ return std::string(
+ HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage)) +
+ " " + join(ValidType, "/");
+ });
+ Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
+ << A->getAttrName() << (IsInput ? "input" : "output")
+ << llvm::Triple::getEnvironmentTypeName(Case.Stage)
+ << join(ValidCases, ", ");
+ return;
+ }
+
+ SmallVector<StringRef, 8> StageStrings;
+ llvm::transform(
+ Allowed, std::back_inserter(StageStrings), [](SemanticStageInfo Case) {
+ return StringRef(
+ HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage));
+ });
+
+ Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage)
+ << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
+ << (Allowed.size() != 1) << join(StageStrings, ", ");
+}
+
template <CastKind Kind>
static void castVector(Sema &S, ExprResult &E, QualType &Ty, unsigned Sz) {
if (const auto *VTy = Ty->getAs<VectorType>())
@@ -1707,6 +1789,15 @@ void SemaHLSL::handleVkBindingAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkBindingAttr(getASTContext(), AL, Binding, Set));
}
+void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) {
+ uint32_t Location;
+ if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Location))
+ return;
+
+ D->addAttr(::new (getASTContext())
+ HLSLVkLocationAttr(getASTContext(), AL, Location));
+}
+
bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) {
const auto *VT = T->getAs<VectorType>();
@@ -1797,6 +1888,16 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
return;
}
+ if (SemanticName == "SV_TARGET") {
+ const auto *VT = ValueType->getAs<VectorType>();
+ if (!ValueType->hasFloatingRepresentation() ||
+ (VT && VT->getNumElements() > 4))
+ Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
+ << AL << "float/float1/float2/float3/float4";
+ D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
+ return;
+ }
+
Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
}
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
new file mode 100644
index 0000000000000..4dc622a1eb6bb
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-DXIL
+
+// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]]
+
+// CHECK: define void @main() {{.*}} {
+float4 main(float4 p : SV_Position) : SV_Target {
+ // CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#]])
+ // CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 16
+
+ // CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0)
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ return p;
+}
+
+// CHECK-SPIRV-DAG: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK-SPIRV-DAG: ![[#MD_3]] = !{i32 30, i32 0}
+// | `-> Location index
+// `-> SPIR-V decoration 'Location'
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
new file mode 100644
index 0000000000000..c5d86637fb4ea
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
+
+// CHECK-SPIRV: @SV_Positi...
[truncated]
|
|
@llvm/pr-subscribers-clang-codegen Author: Nathan Gauër (Keenuts) ChangesThis commit adds the support for vk::location attribute which can be applied to input and output variables. As in/inout parameters are not supported yet, vk::location on such parameters is not tested. As implemented in DXC, vk::location has the following rules:
Patch is 33.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169479.diff 21 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8e5f7ef0bb82d..9e7dc200bc6cf 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5172,6 +5172,14 @@ def HLSLVkConstantId : InheritableAttr {
let Documentation = [VkConstantIdDocs];
}
+def HLSLVkLocation : HLSLAnnotationAttr {
+ let Spellings = [CXX11<"vk", "location">];
+ let Args = [IntArgument<"Location">];
+ let Subjects = SubjectList<[ParmVar, Field, Function], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkLocationDocs];
+}
+
def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
let Subjects = SubjectList<[Record]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index c1b1510f363d4..fa365da3ed9aa 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8981,6 +8981,18 @@ The descriptor set is optional and defaults to 0 if not provided.
}];
}
+def HLSLVkLocationDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{
+Attribute used for specifying the location number for the stage input/output
+variables. Allowed on function parameters, function returns, and struct
+fields. This parameter has no effect when used outside of an entrypoint
+parameter/parameter field/return value.
+
+This attribute maps to the 'Location' SPIR-V decoration.
+ }];
+}
+
def WebAssemblyFuncrefDocs : Documentation {
let Category = DocCatType;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 53aa86a7dabde..04812bd78d9b4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13231,6 +13231,12 @@ def err_hlsl_semantic_indexing_not_supported
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
+def err_hlsl_semantic_unsupported_iotype_for_stage
+ : Error<"semantic %0 is unsupported in %2 shaders as %1, requires one of "
+ "the following: %3">;
+def err_hlsl_semantic_partial_explicit_indexing
+ : Error<"partial explicit stage input location assignment via "
+ "vk::location(X) unsupported">;
def warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric types}0">, InGroup<LegacyConstantRegisterBinding>;
def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 15edb7e77a22b..cac89f6e51cae 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -134,9 +134,6 @@ class SemaHLSL : public SemaBase {
void CheckEntryPoint(FunctionDecl *FD);
bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr,
SourceLocation Loc);
- void DiagnoseAttrStageMismatch(
- const Attr *A, llvm::Triple::EnvironmentType Stage,
- std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
QualType LHSType, QualType RHSType,
@@ -171,6 +168,7 @@ class SemaHLSL : public SemaBase {
void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL);
void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL);
void handleVkBindingAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkLocationAttr(Decl *D, const ParsedAttr &AL);
void handlePackOffsetAttr(Decl *D, const ParsedAttr &AL);
void handleShaderAttr(Decl *D, const ParsedAttr &AL);
void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL);
@@ -243,6 +241,19 @@ class SemaHLSL : public SemaBase {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
};
+ std::optional<bool> InputUsesExplicitVkLocations = std::nullopt;
+ std::optional<bool> OutputUsesExplicitVkLocations = std::nullopt;
+
+ enum IOType {
+ In = 0b01,
+ Out = 0b10,
+ InOut = 0b11,
+ };
+
+ struct SemanticStageInfo {
+ llvm::Triple::EnvironmentType Stage;
+ IOType AllowedIOTypesMask;
+ };
private:
void collectResourceBindingsOnVarDecl(VarDecl *D);
@@ -269,6 +280,14 @@ class SemaHLSL : public SemaBase {
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
+ void diagnoseAttrStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage,
+ std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
+
+ void diagnoseSemanticStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ std::initializer_list<SemanticStageInfo> AllowedStages);
+
uint32_t getNextImplicitBindingOrderID() {
return ImplicitBindingNextOrderID++;
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index f5c07fe2e33ff..913d80b7e5c25 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -582,20 +582,22 @@ static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M,
return B.CreateLoad(Ty, GV);
}
-llvm::Value *
-CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
- HLSLAppliedSemanticAttr *Semantic,
- std::optional<unsigned> Index) {
+llvm::Value *CGHLSLRuntime::emitSPIRVUserSemanticLoad(
+ llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
+ HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
unsigned Location = SPIRVLastAssignedInputSemanticLocation;
+ if (auto *L = Decl->getAttr<HLSLVkLocationAttr>())
+ Location = L->getLocation();
// DXC completely ignores the semantic/index pair. Location are assigned from
// the first semantic to the last.
llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Type);
unsigned ElementCount = AT ? AT->getNumElements() : 1;
SPIRVLastAssignedInputSemanticLocation += ElementCount;
+
return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location,
VariableName.str());
}
@@ -616,10 +618,14 @@ static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M,
void CGHLSLRuntime::emitSPIRVUserSemanticStore(
llvm::IRBuilder<> &B, llvm::Value *Source,
- HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
+ const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic,
+ std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
+
unsigned Location = SPIRVLastAssignedOutputSemanticLocation;
+ if (auto *L = Decl->getAttr<HLSLVkLocationAttr>())
+ Location = L->getLocation();
// DXC completely ignores the semantic/index pair. Location are assigned from
// the first semantic to the last.
@@ -671,7 +677,7 @@ llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
if (CGM.getTarget().getTriple().isSPIRV())
- return emitSPIRVUserSemanticLoad(B, Type, Semantic, Index);
+ return emitSPIRVUserSemanticLoad(B, Type, Decl, Semantic, Index);
if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticLoad(B, Type, Semantic, Index);
@@ -684,7 +690,7 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index) {
if (CGM.getTarget().getTriple().isSPIRV())
- return emitSPIRVUserSemanticStore(B, Source, Semantic, Index);
+ return emitSPIRVUserSemanticStore(B, Source, Decl, Semantic, Index);
if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticStore(B, Source, Semantic, Index);
@@ -783,6 +789,11 @@ void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source,
}
}
+ if (SemanticName == "SV_TARGET") {
+ emitUserSemanticStore(B, Source, Decl, Semantic, Index);
+ return;
+ }
+
llvm_unreachable(
"Store hasn't been implemented yet for this system semantic. FIXME");
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c883282a8d9c8..d057add2db222 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -278,6 +278,7 @@ class CGHLSLRuntime {
HLSLResourceBindingAttr *RBA);
llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+ const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
llvm::Value *emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
@@ -289,6 +290,7 @@ class CGHLSLRuntime {
std::optional<unsigned> Index);
void emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
+ const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
void emitDXILUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e3af5023c74d0..c9d1ee76a2e52 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7703,6 +7703,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLUnparsedSemantic:
S.HLSL().handleSemanticAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkLocation:
+ S.HLSL().handleVkLocationAttr(D, AL);
+ break;
case ParsedAttr::AT_AbiTag:
handleAbiTagAttr(S, D, AL);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ecab3946b58c7..2a69703d0d1e9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -771,6 +771,22 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
}
}
+static bool isPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD,
+ HLSLAppliedSemanticAttr *Semantic) {
+ if (AstContext.getTargetInfo().getTriple().getOS() != llvm::Triple::Vulkan)
+ return false;
+
+ const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
+ assert(ShaderAttr && "Entry point has no shader attribute");
+ llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
+ auto SemanticName = Semantic->getSemanticName().upper();
+
+ if (ST == llvm::Triple::Pixel && SemanticName == "SV_POSITION")
+ return true;
+
+ return false;
+}
+
bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
DeclaratorDecl *OutputDecl,
DeclaratorDecl *D,
@@ -800,6 +816,22 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
unsigned Location = ActiveSemantic.Index.value_or(0);
+ if (!isPipelineBuiltin(getASTContext(), FD, A)) {
+ bool HasVkLocation = false;
+ if (auto *A = D->getAttr<HLSLVkLocationAttr>()) {
+ HasVkLocation = true;
+ Location = A->getLocation();
+ }
+
+ auto &UsesExplicitVkLocations =
+ IsInput ? InputUsesExplicitVkLocations : OutputUsesExplicitVkLocations;
+ if (UsesExplicitVkLocations.value_or(HasVkLocation) != HasVkLocation) {
+ Diag(D->getLocation(), diag::err_hlsl_semantic_partial_explicit_indexing);
+ return false;
+ }
+ UsesExplicitVkLocations = HasVkLocation;
+ }
+
const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
unsigned ElementCount = AT ? AT->getZExtSize() : 1;
ActiveSemantic.Index = Location + ElementCount;
@@ -873,14 +905,14 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
case llvm::Triple::Miss:
case llvm::Triple::Callable:
if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) {
- DiagnoseAttrStageMismatch(NT, ST,
+ diagnoseAttrStageMismatch(NT, ST,
{llvm::Triple::Compute,
llvm::Triple::Amplification,
llvm::Triple::Mesh});
FD->setInvalidDecl();
}
if (const auto *WS = FD->getAttr<HLSLWaveSizeAttr>()) {
- DiagnoseAttrStageMismatch(WS, ST,
+ diagnoseAttrStageMismatch(WS, ST,
{llvm::Triple::Compute,
llvm::Triple::Amplification,
llvm::Triple::Mesh});
@@ -954,7 +986,8 @@ void SemaHLSL::checkSemanticAnnotation(
SemanticName == "SV_GROUPID") {
if (ST != llvm::Triple::Compute)
- DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Compute});
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Compute, IOType::In}});
if (SemanticAttr->getSemanticIndex() != 0) {
std::string PrettyName =
@@ -969,10 +1002,15 @@ void SemaHLSL::checkSemanticAnnotation(
if (SemanticName == "SV_POSITION") {
// SV_Position can be an input or output in vertex shaders,
// but only an input in pixel shaders.
- if (ST == llvm::Triple::Vertex || (ST == llvm::Triple::Pixel && IsInput))
- return;
- DiagnoseAttrStageMismatch(SemanticAttr, ST,
- {llvm::Triple::Pixel, llvm::Triple::Vertex});
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Vertex, IOType::InOut},
+ {llvm::Triple::Pixel, IOType::In}});
+ return;
+ }
+
+ if (SemanticName == "SV_TARGET") {
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Pixel, IOType::Out}});
return;
}
@@ -982,7 +1020,7 @@ void SemaHLSL::checkSemanticAnnotation(
llvm_unreachable("Unknown SemanticAttr");
}
-void SemaHLSL::DiagnoseAttrStageMismatch(
+void SemaHLSL::diagnoseAttrStageMismatch(
const Attr *A, llvm::Triple::EnvironmentType Stage,
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages) {
SmallVector<StringRef, 8> StageStrings;
@@ -996,6 +1034,50 @@ void SemaHLSL::DiagnoseAttrStageMismatch(
<< (AllowedStages.size() != 1) << join(StageStrings, ", ");
}
+void SemaHLSL::diagnoseSemanticStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ std::initializer_list<SemanticStageInfo> Allowed) {
+
+ for (auto &Case : Allowed) {
+ if (Case.Stage != Stage)
+ continue;
+
+ if (IsInput && Case.AllowedIOTypesMask & IOType::In)
+ return;
+ if (!IsInput && Case.AllowedIOTypesMask & IOType::Out)
+ return;
+
+ SmallVector<std::string, 8> ValidCases;
+ llvm::transform(
+ Allowed, std::back_inserter(ValidCases), [](SemanticStageInfo Case) {
+ SmallVector<std::string, 2> ValidType;
+ if (Case.AllowedIOTypesMask & IOType::In)
+ ValidType.push_back("input");
+ if (Case.AllowedIOTypesMask & IOType::Out)
+ ValidType.push_back("output");
+ return std::string(
+ HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage)) +
+ " " + join(ValidType, "/");
+ });
+ Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
+ << A->getAttrName() << (IsInput ? "input" : "output")
+ << llvm::Triple::getEnvironmentTypeName(Case.Stage)
+ << join(ValidCases, ", ");
+ return;
+ }
+
+ SmallVector<StringRef, 8> StageStrings;
+ llvm::transform(
+ Allowed, std::back_inserter(StageStrings), [](SemanticStageInfo Case) {
+ return StringRef(
+ HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage));
+ });
+
+ Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage)
+ << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
+ << (Allowed.size() != 1) << join(StageStrings, ", ");
+}
+
template <CastKind Kind>
static void castVector(Sema &S, ExprResult &E, QualType &Ty, unsigned Sz) {
if (const auto *VTy = Ty->getAs<VectorType>())
@@ -1707,6 +1789,15 @@ void SemaHLSL::handleVkBindingAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkBindingAttr(getASTContext(), AL, Binding, Set));
}
+void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) {
+ uint32_t Location;
+ if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Location))
+ return;
+
+ D->addAttr(::new (getASTContext())
+ HLSLVkLocationAttr(getASTContext(), AL, Location));
+}
+
bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) {
const auto *VT = T->getAs<VectorType>();
@@ -1797,6 +1888,16 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
return;
}
+ if (SemanticName == "SV_TARGET") {
+ const auto *VT = ValueType->getAs<VectorType>();
+ if (!ValueType->hasFloatingRepresentation() ||
+ (VT && VT->getNumElements() > 4))
+ Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
+ << AL << "float/float1/float2/float3/float4";
+ D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
+ return;
+ }
+
Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
}
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
new file mode 100644
index 0000000000000..4dc622a1eb6bb
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-DXIL
+
+// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]]
+
+// CHECK: define void @main() {{.*}} {
+float4 main(float4 p : SV_Position) : SV_Target {
+ // CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#]])
+ // CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 16
+
+ // CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0)
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ return p;
+}
+
+// CHECK-SPIRV-DAG: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK-SPIRV-DAG: ![[#MD_3]] = !{i32 30, i32 0}
+// | `-> Location index
+// `-> SPIR-V decoration 'Location'
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
new file mode 100644
index 0000000000000..c5d86637fb4ea
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
+
+// CHECK-SPIRV: @SV_Positi...
[truncated]
|
|
@llvm/pr-subscribers-backend-spir-v Author: Nathan Gauër (Keenuts) ChangesThis commit adds the support for vk::location attribute which can be applied to input and output variables. As in/inout parameters are not supported yet, vk::location on such parameters is not tested. As implemented in DXC, vk::location has the following rules:
Patch is 33.07 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/169479.diff 21 Files Affected:
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 8e5f7ef0bb82d..9e7dc200bc6cf 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5172,6 +5172,14 @@ def HLSLVkConstantId : InheritableAttr {
let Documentation = [VkConstantIdDocs];
}
+def HLSLVkLocation : HLSLAnnotationAttr {
+ let Spellings = [CXX11<"vk", "location">];
+ let Args = [IntArgument<"Location">];
+ let Subjects = SubjectList<[ParmVar, Field, Function], ErrorDiag>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLVkLocationDocs];
+}
+
def RandomizeLayout : InheritableAttr {
let Spellings = [GCC<"randomize_layout">];
let Subjects = SubjectList<[Record]>;
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index c1b1510f363d4..fa365da3ed9aa 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8981,6 +8981,18 @@ The descriptor set is optional and defaults to 0 if not provided.
}];
}
+def HLSLVkLocationDocs : Documentation {
+ let Category = DocCatVariable;
+ let Content = [{
+Attribute used for specifying the location number for the stage input/output
+variables. Allowed on function parameters, function returns, and struct
+fields. This parameter has no effect when used outside of an entrypoint
+parameter/parameter field/return value.
+
+This attribute maps to the 'Location' SPIR-V decoration.
+ }];
+}
+
def WebAssemblyFuncrefDocs : Documentation {
let Category = DocCatType;
let Content = [{
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 53aa86a7dabde..04812bd78d9b4 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13231,6 +13231,12 @@ def err_hlsl_semantic_indexing_not_supported
def err_hlsl_init_priority_unsupported : Error<
"initializer priorities are not supported in HLSL">;
def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
+def err_hlsl_semantic_unsupported_iotype_for_stage
+ : Error<"semantic %0 is unsupported in %2 shaders as %1, requires one of "
+ "the following: %3">;
+def err_hlsl_semantic_partial_explicit_indexing
+ : Error<"partial explicit stage input location assignment via "
+ "vk::location(X) unsupported">;
def warn_hlsl_user_defined_type_missing_member: Warning<"binding type '%select{t|u|b|s|c}0' only applies to types containing %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric types}0">, InGroup<LegacyConstantRegisterBinding>;
def err_hlsl_binding_type_mismatch: Error<"binding type '%select{t|u|b|s|c}0' only applies to %select{SRV resources|UAV resources|constant buffer resources|sampler state|numeric variables in the global scope}0">;
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 15edb7e77a22b..cac89f6e51cae 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -134,9 +134,6 @@ class SemaHLSL : public SemaBase {
void CheckEntryPoint(FunctionDecl *FD);
bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr,
SourceLocation Loc);
- void DiagnoseAttrStageMismatch(
- const Attr *A, llvm::Triple::EnvironmentType Stage,
- std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS,
QualType LHSType, QualType RHSType,
@@ -171,6 +168,7 @@ class SemaHLSL : public SemaBase {
void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL);
void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL);
void handleVkBindingAttr(Decl *D, const ParsedAttr &AL);
+ void handleVkLocationAttr(Decl *D, const ParsedAttr &AL);
void handlePackOffsetAttr(Decl *D, const ParsedAttr &AL);
void handleShaderAttr(Decl *D, const ParsedAttr &AL);
void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL);
@@ -243,6 +241,19 @@ class SemaHLSL : public SemaBase {
HLSLParsedSemanticAttr *Semantic;
std::optional<uint32_t> Index;
};
+ std::optional<bool> InputUsesExplicitVkLocations = std::nullopt;
+ std::optional<bool> OutputUsesExplicitVkLocations = std::nullopt;
+
+ enum IOType {
+ In = 0b01,
+ Out = 0b10,
+ InOut = 0b11,
+ };
+
+ struct SemanticStageInfo {
+ llvm::Triple::EnvironmentType Stage;
+ IOType AllowedIOTypesMask;
+ };
private:
void collectResourceBindingsOnVarDecl(VarDecl *D);
@@ -269,6 +280,14 @@ class SemaHLSL : public SemaBase {
void diagnoseAvailabilityViolations(TranslationUnitDecl *TU);
+ void diagnoseAttrStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage,
+ std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
+
+ void diagnoseSemanticStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ std::initializer_list<SemanticStageInfo> AllowedStages);
+
uint32_t getNextImplicitBindingOrderID() {
return ImplicitBindingNextOrderID++;
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index f5c07fe2e33ff..913d80b7e5c25 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -582,20 +582,22 @@ static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M,
return B.CreateLoad(Ty, GV);
}
-llvm::Value *
-CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
- HLSLAppliedSemanticAttr *Semantic,
- std::optional<unsigned> Index) {
+llvm::Value *CGHLSLRuntime::emitSPIRVUserSemanticLoad(
+ llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
+ HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
unsigned Location = SPIRVLastAssignedInputSemanticLocation;
+ if (auto *L = Decl->getAttr<HLSLVkLocationAttr>())
+ Location = L->getLocation();
// DXC completely ignores the semantic/index pair. Location are assigned from
// the first semantic to the last.
llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Type);
unsigned ElementCount = AT ? AT->getNumElements() : 1;
SPIRVLastAssignedInputSemanticLocation += ElementCount;
+
return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location,
VariableName.str());
}
@@ -616,10 +618,14 @@ static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M,
void CGHLSLRuntime::emitSPIRVUserSemanticStore(
llvm::IRBuilder<> &B, llvm::Value *Source,
- HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
+ const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic,
+ std::optional<unsigned> Index) {
Twine BaseName = Twine(Semantic->getAttrName()->getName());
Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
+
unsigned Location = SPIRVLastAssignedOutputSemanticLocation;
+ if (auto *L = Decl->getAttr<HLSLVkLocationAttr>())
+ Location = L->getLocation();
// DXC completely ignores the semantic/index pair. Location are assigned from
// the first semantic to the last.
@@ -671,7 +677,7 @@ llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
if (CGM.getTarget().getTriple().isSPIRV())
- return emitSPIRVUserSemanticLoad(B, Type, Semantic, Index);
+ return emitSPIRVUserSemanticLoad(B, Type, Decl, Semantic, Index);
if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticLoad(B, Type, Semantic, Index);
@@ -684,7 +690,7 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index) {
if (CGM.getTarget().getTriple().isSPIRV())
- return emitSPIRVUserSemanticStore(B, Source, Semantic, Index);
+ return emitSPIRVUserSemanticStore(B, Source, Decl, Semantic, Index);
if (CGM.getTarget().getTriple().isDXIL())
return emitDXILUserSemanticStore(B, Source, Semantic, Index);
@@ -783,6 +789,11 @@ void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source,
}
}
+ if (SemanticName == "SV_TARGET") {
+ emitUserSemanticStore(B, Source, Decl, Semantic, Index);
+ return;
+ }
+
llvm_unreachable(
"Store hasn't been implemented yet for this system semantic. FIXME");
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c883282a8d9c8..d057add2db222 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -278,6 +278,7 @@ class CGHLSLRuntime {
HLSLResourceBindingAttr *RBA);
llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+ const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
llvm::Value *emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
@@ -289,6 +290,7 @@ class CGHLSLRuntime {
std::optional<unsigned> Index);
void emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
+ const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
void emitDXILUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source,
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index e3af5023c74d0..c9d1ee76a2e52 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7703,6 +7703,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
case ParsedAttr::AT_HLSLUnparsedSemantic:
S.HLSL().handleSemanticAttr(D, AL);
break;
+ case ParsedAttr::AT_HLSLVkLocation:
+ S.HLSL().handleVkLocationAttr(D, AL);
+ break;
case ParsedAttr::AT_AbiTag:
handleAbiTagAttr(S, D, AL);
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index ecab3946b58c7..2a69703d0d1e9 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -771,6 +771,22 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
}
}
+static bool isPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD,
+ HLSLAppliedSemanticAttr *Semantic) {
+ if (AstContext.getTargetInfo().getTriple().getOS() != llvm::Triple::Vulkan)
+ return false;
+
+ const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
+ assert(ShaderAttr && "Entry point has no shader attribute");
+ llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
+ auto SemanticName = Semantic->getSemanticName().upper();
+
+ if (ST == llvm::Triple::Pixel && SemanticName == "SV_POSITION")
+ return true;
+
+ return false;
+}
+
bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
DeclaratorDecl *OutputDecl,
DeclaratorDecl *D,
@@ -800,6 +816,22 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
unsigned Location = ActiveSemantic.Index.value_or(0);
+ if (!isPipelineBuiltin(getASTContext(), FD, A)) {
+ bool HasVkLocation = false;
+ if (auto *A = D->getAttr<HLSLVkLocationAttr>()) {
+ HasVkLocation = true;
+ Location = A->getLocation();
+ }
+
+ auto &UsesExplicitVkLocations =
+ IsInput ? InputUsesExplicitVkLocations : OutputUsesExplicitVkLocations;
+ if (UsesExplicitVkLocations.value_or(HasVkLocation) != HasVkLocation) {
+ Diag(D->getLocation(), diag::err_hlsl_semantic_partial_explicit_indexing);
+ return false;
+ }
+ UsesExplicitVkLocations = HasVkLocation;
+ }
+
const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
unsigned ElementCount = AT ? AT->getZExtSize() : 1;
ActiveSemantic.Index = Location + ElementCount;
@@ -873,14 +905,14 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
case llvm::Triple::Miss:
case llvm::Triple::Callable:
if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) {
- DiagnoseAttrStageMismatch(NT, ST,
+ diagnoseAttrStageMismatch(NT, ST,
{llvm::Triple::Compute,
llvm::Triple::Amplification,
llvm::Triple::Mesh});
FD->setInvalidDecl();
}
if (const auto *WS = FD->getAttr<HLSLWaveSizeAttr>()) {
- DiagnoseAttrStageMismatch(WS, ST,
+ diagnoseAttrStageMismatch(WS, ST,
{llvm::Triple::Compute,
llvm::Triple::Amplification,
llvm::Triple::Mesh});
@@ -954,7 +986,8 @@ void SemaHLSL::checkSemanticAnnotation(
SemanticName == "SV_GROUPID") {
if (ST != llvm::Triple::Compute)
- DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Compute});
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Compute, IOType::In}});
if (SemanticAttr->getSemanticIndex() != 0) {
std::string PrettyName =
@@ -969,10 +1002,15 @@ void SemaHLSL::checkSemanticAnnotation(
if (SemanticName == "SV_POSITION") {
// SV_Position can be an input or output in vertex shaders,
// but only an input in pixel shaders.
- if (ST == llvm::Triple::Vertex || (ST == llvm::Triple::Pixel && IsInput))
- return;
- DiagnoseAttrStageMismatch(SemanticAttr, ST,
- {llvm::Triple::Pixel, llvm::Triple::Vertex});
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Vertex, IOType::InOut},
+ {llvm::Triple::Pixel, IOType::In}});
+ return;
+ }
+
+ if (SemanticName == "SV_TARGET") {
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ {{llvm::Triple::Pixel, IOType::Out}});
return;
}
@@ -982,7 +1020,7 @@ void SemaHLSL::checkSemanticAnnotation(
llvm_unreachable("Unknown SemanticAttr");
}
-void SemaHLSL::DiagnoseAttrStageMismatch(
+void SemaHLSL::diagnoseAttrStageMismatch(
const Attr *A, llvm::Triple::EnvironmentType Stage,
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages) {
SmallVector<StringRef, 8> StageStrings;
@@ -996,6 +1034,50 @@ void SemaHLSL::DiagnoseAttrStageMismatch(
<< (AllowedStages.size() != 1) << join(StageStrings, ", ");
}
+void SemaHLSL::diagnoseSemanticStageMismatch(
+ const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ std::initializer_list<SemanticStageInfo> Allowed) {
+
+ for (auto &Case : Allowed) {
+ if (Case.Stage != Stage)
+ continue;
+
+ if (IsInput && Case.AllowedIOTypesMask & IOType::In)
+ return;
+ if (!IsInput && Case.AllowedIOTypesMask & IOType::Out)
+ return;
+
+ SmallVector<std::string, 8> ValidCases;
+ llvm::transform(
+ Allowed, std::back_inserter(ValidCases), [](SemanticStageInfo Case) {
+ SmallVector<std::string, 2> ValidType;
+ if (Case.AllowedIOTypesMask & IOType::In)
+ ValidType.push_back("input");
+ if (Case.AllowedIOTypesMask & IOType::Out)
+ ValidType.push_back("output");
+ return std::string(
+ HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage)) +
+ " " + join(ValidType, "/");
+ });
+ Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
+ << A->getAttrName() << (IsInput ? "input" : "output")
+ << llvm::Triple::getEnvironmentTypeName(Case.Stage)
+ << join(ValidCases, ", ");
+ return;
+ }
+
+ SmallVector<StringRef, 8> StageStrings;
+ llvm::transform(
+ Allowed, std::back_inserter(StageStrings), [](SemanticStageInfo Case) {
+ return StringRef(
+ HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage));
+ });
+
+ Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage)
+ << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage)
+ << (Allowed.size() != 1) << join(StageStrings, ", ");
+}
+
template <CastKind Kind>
static void castVector(Sema &S, ExprResult &E, QualType &Ty, unsigned Sz) {
if (const auto *VTy = Ty->getAs<VectorType>())
@@ -1707,6 +1789,15 @@ void SemaHLSL::handleVkBindingAttr(Decl *D, const ParsedAttr &AL) {
HLSLVkBindingAttr(getASTContext(), AL, Binding, Set));
}
+void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) {
+ uint32_t Location;
+ if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Location))
+ return;
+
+ D->addAttr(::new (getASTContext())
+ HLSLVkLocationAttr(getASTContext(), AL, Location));
+}
+
bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) {
const auto *VT = T->getAs<VectorType>();
@@ -1797,6 +1888,16 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
return;
}
+ if (SemanticName == "SV_TARGET") {
+ const auto *VT = ValueType->getAs<VectorType>();
+ if (!ValueType->hasFloatingRepresentation() ||
+ (VT && VT->getNumElements() > 4))
+ Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
+ << AL << "float/float1/float2/float3/float4";
+ D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index));
+ return;
+ }
+
Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
}
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
new file mode 100644
index 0000000000000..4dc622a1eb6bb
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl
@@ -0,0 +1,19 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-DXIL
+
+// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]]
+
+// CHECK: define void @main() {{.*}} {
+float4 main(float4 p : SV_Position) : SV_Target {
+ // CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#]])
+ // CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 16
+
+ // CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0)
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]])
+ return p;
+}
+
+// CHECK-SPIRV-DAG: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK-SPIRV-DAG: ![[#MD_3]] = !{i32 30, i32 0}
+// | `-> Location index
+// `-> SPIR-V decoration 'Location'
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
new file mode 100644
index 0000000000000..c5d86637fb4ea
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL
+
+// CHECK-SPIRV: @SV_Positi...
[truncated]
|
This commit adds the support for vk::location attribute which can be applied to input and output variables.
As in/inout parameters are not supported yet, vk::location on such parameters is not tested.
As implemented in DXC, vk::location has the following rules: