[clang] 8a40d08 - [HLSL][SPIR-V] Implement vk::location for inputs (#169479)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Dec 2 06:08:36 PST 2025
Author: Nathan Gauër
Date: 2025-12-02T14:08:31Z
New Revision: 8a40d08b836ff5f363783f99efd901a44d8575de
URL: https://github.com/llvm/llvm-project/commit/8a40d08b836ff5f363783f99efd901a44d8575de
DIFF: https://github.com/llvm/llvm-project/commit/8a40d08b836ff5f363783f99efd901a44d8575de.diff
LOG: [HLSL][SPIR-V] Implement vk::location for inputs (#169479)
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:
- input and outputs are handled independently.
- input/output lowered to a SPIR-V builtins are not using the assigned
vk::location and thus ignored.
- input/output lowered to a Location decoration must either all have
explicit locations, or none. Mixing is not allowed (except with
builtins).
Added:
clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl
clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl
clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl
clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl
clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl
clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl
clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl
clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl
Modified:
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/SemaHLSL.h
clang/lib/CodeGen/CGHLSLRuntime.cpp
clang/lib/CodeGen/CGHLSLRuntime.h
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaHLSL.cpp
clang/test/Misc/pragma-attribute-supported-attributes-list.test
Removed:
################################################################################
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 25bdcb750d006..c929da7d538bd 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 ec04db4df324b..69ed958a2a2aa 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13238,6 +13238,9 @@ 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 fb18817f2177b..a2faa91d1e54d 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -168,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);
@@ -236,17 +237,33 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ // Information about the current subtree being flattened.
struct SemanticInfo {
HLSLParsedSemanticAttr *Semantic;
- std::optional<uint32_t> Index;
+ std::optional<uint32_t> Index = std::nullopt;
};
+ // Bitmask used to recall if the current semantic subtree is
+ // input, output or inout.
enum IOType {
In = 0b01,
Out = 0b10,
InOut = 0b11,
};
+ // The context shared by all semantics with the same IOType during
+ // flattening.
+ struct SemanticContext {
+ // Present if any semantic sharing the same IO type has an explicit or
+ // implicit SPIR-V location index assigned.
+ std::optional<bool> UsesExplicitVkLocations = std::nullopt;
+ // The set of semantics found to be active during flattening. Used to detect
+ // index collisions.
+ llvm::StringSet<> ActiveSemantics = {};
+ // The IOType of this semantic set.
+ IOType CurrentIOType;
+ };
+
struct SemanticStageInfo {
llvm::Triple::EnvironmentType Stage;
IOType AllowedIOTypesMask;
@@ -259,19 +276,17 @@ class SemaHLSL : public SemaBase {
void checkSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
const HLSLAppliedSemanticAttr *SemanticAttr,
- bool IsInput);
+ const SemanticContext &SC);
bool determineActiveSemanticOnScalar(FunctionDecl *FD,
DeclaratorDecl *OutputDecl,
DeclaratorDecl *D,
SemanticInfo &ActiveSemantic,
- llvm::StringSet<> &ActiveSemantics,
- bool IsInput);
+ SemanticContext &SC);
bool determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *OutputDecl,
DeclaratorDecl *D, SemanticInfo &ActiveSemantic,
- llvm::StringSet<> &ActiveSemantics,
- bool IsInput);
+ SemanticContext &SC);
void processExplicitBindingsOnDecl(VarDecl *D);
@@ -282,7 +297,7 @@ class SemaHLSL : public SemaBase {
std::initializer_list<llvm::Triple::EnvironmentType> AllowedStages);
void diagnoseSemanticStageMismatch(
- const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType,
std::initializer_list<SemanticStageInfo> AllowedStages);
uint32_t getNextImplicitBindingOrderID() {
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index b63346cc381c6..0c3701eb01679 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);
@@ -693,8 +699,9 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
}
llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
- IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
- HLSLAppliedSemanticAttr *Semantic, std::optional<unsigned> Index) {
+ IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type,
+ const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic,
+ std::optional<unsigned> Index) {
std::string SemanticName = Semantic->getAttrName()->getName().upper();
if (SemanticName == "SV_GROUPINDEX") {
@@ -730,8 +737,12 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
return buildVectorInput(B, GroupIDIntrinsic, Type);
}
+ const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
+ assert(ShaderAttr && "Entry point has no shader attribute");
+ llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
+
if (SemanticName == "SV_POSITION") {
- if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Pixel) {
+ if (ST == Triple::EnvironmentType::Pixel) {
if (CGM.getTarget().getTriple().isSPIRV())
return createSPIRVBuiltinLoad(B, CGM.getModule(), Type,
Semantic->getAttrName()->getName(),
@@ -740,7 +751,7 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
return emitDXILUserSemanticLoad(B, Type, Semantic, Index);
}
- if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Vertex) {
+ if (ST == Triple::EnvironmentType::Vertex) {
return emitUserSemanticLoad(B, Type, Decl, Semantic, Index);
}
}
@@ -798,7 +809,7 @@ llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad(
std::optional<unsigned> Index = Semantic->getSemanticIndex();
if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_"))
- return emitSystemSemanticLoad(B, Type, Decl, Semantic, Index);
+ return emitSystemSemanticLoad(B, FD, Type, Decl, Semantic, Index);
return emitUserSemanticLoad(B, Type, Decl, Semantic, Index);
}
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index c883282a8d9c8..77f43e8766745 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -179,7 +179,8 @@ class CGHLSLRuntime {
protected:
CodeGenModule &CGM;
- llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+ llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B,
+ const FunctionDecl *FD, llvm::Type *Type,
const clang::DeclaratorDecl *Decl,
HLSLAppliedSemanticAttr *Semantic,
std::optional<unsigned> Index);
@@ -278,6 +279,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 +291,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 0cea7af6024d4..89645e3b67db3 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -771,12 +771,33 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
}
}
+static bool isVkPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD,
+ HLSLAppliedSemanticAttr *Semantic,
+ bool IsInput) {
+ 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();
+
+ // The SV_Position semantic is lowered to:
+ // - Position built-in for vertex output.
+ // - FragCoord built-in for fragment input.
+ if (SemanticName == "SV_POSITION") {
+ return (ST == llvm::Triple::Vertex && !IsInput) ||
+ (ST == llvm::Triple::Pixel && IsInput);
+ }
+
+ return false;
+}
+
bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
DeclaratorDecl *OutputDecl,
DeclaratorDecl *D,
SemanticInfo &ActiveSemantic,
- llvm::StringSet<> &UsedSemantics,
- bool IsInput) {
+ SemaHLSL::SemanticContext &SC) {
if (ActiveSemantic.Semantic == nullptr) {
ActiveSemantic.Semantic = D->getAttr<HLSLParsedSemanticAttr>();
if (ActiveSemantic.Semantic)
@@ -795,11 +816,26 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
if (!A)
return false;
- checkSemanticAnnotation(FD, D, A, IsInput);
+ checkSemanticAnnotation(FD, D, A, SC);
OutputDecl->addAttr(A);
unsigned Location = ActiveSemantic.Index.value_or(0);
+ if (!isVkPipelineBuiltin(getASTContext(), FD, A,
+ SC.CurrentIOType & IOType::In)) {
+ bool HasVkLocation = false;
+ if (auto *A = D->getAttr<HLSLVkLocationAttr>()) {
+ HasVkLocation = true;
+ Location = A->getLocation();
+ }
+
+ if (SC.UsesExplicitVkLocations.value_or(HasVkLocation) != HasVkLocation) {
+ Diag(D->getLocation(), diag::err_hlsl_semantic_partial_explicit_indexing);
+ return false;
+ }
+ SC.UsesExplicitVkLocations = HasVkLocation;
+ }
+
const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
unsigned ElementCount = AT ? AT->getZExtSize() : 1;
ActiveSemantic.Index = Location + ElementCount;
@@ -808,7 +844,7 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
for (unsigned I = 0; I < ElementCount; ++I) {
Twine VariableName = BaseName.concat(Twine(Location + I));
- auto [_, Inserted] = UsedSemantics.insert(VariableName.str());
+ auto [_, Inserted] = SC.ActiveSemantics.insert(VariableName.str());
if (!Inserted) {
Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
<< VariableName.str();
@@ -823,8 +859,7 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD,
DeclaratorDecl *OutputDecl,
DeclaratorDecl *D,
SemanticInfo &ActiveSemantic,
- llvm::StringSet<> &UsedSemantics,
- bool IsInput) {
+ SemaHLSL::SemanticContext &SC) {
if (ActiveSemantic.Semantic == nullptr) {
ActiveSemantic.Semantic = D->getAttr<HLSLParsedSemanticAttr>();
if (ActiveSemantic.Semantic)
@@ -837,13 +872,12 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD,
const RecordType *RT = dyn_cast<RecordType>(T);
if (!RT)
return determineActiveSemanticOnScalar(FD, OutputDecl, D, ActiveSemantic,
- UsedSemantics, IsInput);
+ SC);
const RecordDecl *RD = RT->getDecl();
for (FieldDecl *Field : RD->fields()) {
SemanticInfo Info = ActiveSemantic;
- if (!determineActiveSemantic(FD, OutputDecl, Field, Info, UsedSemantics,
- IsInput)) {
+ if (!determineActiveSemantic(FD, OutputDecl, Field, Info, SC)) {
Diag(Field->getLocation(), diag::note_hlsl_semantic_used_here) << Field;
return false;
}
@@ -916,7 +950,9 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
llvm_unreachable("Unhandled environment in triple");
}
- llvm::StringSet<> ActiveInputSemantics;
+ SemaHLSL::SemanticContext InputSC = {};
+ InputSC.CurrentIOType = IOType::In;
+
for (ParmVarDecl *Param : FD->parameters()) {
SemanticInfo ActiveSemantic;
ActiveSemantic.Semantic = Param->getAttr<HLSLParsedSemanticAttr>();
@@ -924,26 +960,25 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
// FIXME: Verify output semantics in parameters.
- if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic,
- ActiveInputSemantics, /* IsInput= */ true)) {
+ if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic, InputSC)) {
Diag(Param->getLocation(), diag::note_previous_decl) << Param;
FD->setInvalidDecl();
}
}
SemanticInfo ActiveSemantic;
- llvm::StringSet<> ActiveOutputSemantics;
+ SemaHLSL::SemanticContext OutputSC = {};
+ OutputSC.CurrentIOType = IOType::Out;
ActiveSemantic.Semantic = FD->getAttr<HLSLParsedSemanticAttr>();
if (ActiveSemantic.Semantic)
ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
if (!FD->getReturnType()->isVoidType())
- determineActiveSemantic(FD, FD, FD, ActiveSemantic, ActiveOutputSemantics,
- /* IsInput= */ false);
+ determineActiveSemantic(FD, FD, FD, ActiveSemantic, OutputSC);
}
void SemaHLSL::checkSemanticAnnotation(
FunctionDecl *EntryPoint, const Decl *Param,
- const HLSLAppliedSemanticAttr *SemanticAttr, bool IsInput) {
+ const HLSLAppliedSemanticAttr *SemanticAttr, const SemanticContext &SC) {
auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>();
assert(ShaderAttr && "Entry point has no shader attribute");
llvm::Triple::EnvironmentType ST = ShaderAttr->getType();
@@ -954,7 +989,7 @@ void SemaHLSL::checkSemanticAnnotation(
SemanticName == "SV_GROUPID") {
if (ST != llvm::Triple::Compute)
- diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
{{llvm::Triple::Compute, IOType::In}});
if (SemanticAttr->getSemanticIndex() != 0) {
@@ -970,14 +1005,14 @@ 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.
- diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
{{llvm::Triple::Vertex, IOType::InOut},
{llvm::Triple::Pixel, IOType::In}});
return;
}
if (SemanticName == "SV_TARGET") {
- diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput,
+ diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType,
{{llvm::Triple::Pixel, IOType::Out}});
return;
}
@@ -1003,16 +1038,14 @@ void SemaHLSL::diagnoseAttrStageMismatch(
}
void SemaHLSL::diagnoseSemanticStageMismatch(
- const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput,
+ const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType,
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)
+ if (CurrentIOType & Case.AllowedIOTypesMask)
return;
SmallVector<std::string, 8> ValidCases;
@@ -1028,7 +1061,7 @@ void SemaHLSL::diagnoseSemanticStageMismatch(
" " + join(ValidType, "/");
});
Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage)
- << A->getAttrName() << (IsInput ? "input" : "output")
+ << A->getAttrName() << (CurrentIOType & IOType::In ? "input" : "output")
<< llvm::Triple::getEnvironmentTypeName(Case.Stage)
<< join(ValidCases, ", ");
return;
@@ -1757,6 +1790,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>();
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_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]]
+// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]]
+
+struct Output {
+ [[vk::location(2)]] float4 field : SV_Target;
+};
+
+// CHECK: define void @main() {{.*}} {
+Output main(float4 p : SV_Position) {
+ // CHECK: %[[#OUT:]] = alloca %struct.Output, align 16
+
+ // CHECK-SPIRV: %[[#IN:]] = load <4 x float>, ptr addrspace(7) @SV_Position, align 16
+ // CHECK-SPIRV: call spir_func void @_Z4mainDv4_f(ptr %[[#OUT]], <4 x float> %[[#IN]])
+
+ // CHECK-DXIL: call void @_Z4mainDv4_f(ptr %[[#OUT]], <4 x float> %SV_Position0)
+
+ // CHECK: %[[#TMP:]] = load %struct.Output, ptr %[[#OUT]], align 16
+ // CHECK: %[[#FIELD:]] = extractvalue %struct.Output %[[#TMP]], 0
+
+ // CHECK-SPIRV: store <4 x float> %[[#FIELD]], ptr addrspace(8) @SV_Target0, align 16
+ // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#FIELD]])
+ Output o;
+ o.field = p;
+ return o;
+}
+
+// CHECK-SPIRV-DAG: ![[#MD_0]] = !{![[#MD_1:]]}
+// CHECK-SPIRV-DAG: ![[#MD_1]] = !{i32 11, i32 15}
+// | `-> BuiltIn 'FragCoord'
+// `-> SPIR-V decoration 'BuiltIn'
+// CHECK-SPIRV-DAG: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK-SPIRV-DAG: ![[#MD_3]] = !{i32 30, i32 2}
+// | `-> Location index
+// `-> SPIR-V decoration 'Location'
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl
new file mode 100644
index 0000000000000..41e28bf1259d6
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.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() {{.*}} {
+[[vk::location(2)]] 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 2}
+// | `-> Location index
+// `-> SPIR-V decoration 'Location'
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl
new file mode 100644
index 0000000000000..bc2ecd926dd51
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl
@@ -0,0 +1,39 @@
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV
+
+// The following code is allowed because the `SV_Position` semantic is here
+// translated into a SPIR-V builtin. Meaning there is no implicit `Location`
+// assignment.
+
+struct S2 {
+ float4 a;
+ float4 b;
+};
+
+struct S1 {
+ float4 position : SV_Position;
+ [[vk::location(3)]] float4 color0 : COLOR0;
+};
+
+// CHECK-SPIRV: @SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]]
+// CHECK-SPIRV: @COLOR0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_2:]]
+// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_4:]]
+
+[shader("pixel")]
+float4 main(S1 p) : SV_Target {
+ return p.position + p.color0;
+}
+// CHECK-SPIRV: %[[#SV_POS:]] = load <4 x float>, ptr addrspace(7) @SV_Position, align 16
+// CHECK: %[[#TMP1:]] = insertvalue %struct.S1 poison, <4 x float> %[[#SV_POS]], 0
+// CHECK-SPIRV: %[[#A0:]] = load <4 x float>, ptr addrspace(7) @COLOR0, align 16
+// CHECK: %[[#TMP2:]] = insertvalue %struct.S1 %[[#TMP1]], <4 x float> %[[#A0]], 1
+// CHECK: %[[#P:]] = alloca %struct.S1, align 16
+// CHECK: store %struct.S1 %[[#TMP2]], ptr %[[#P]], align 16
+// CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4main2S1(ptr %[[#P]])
+// CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 16
+
+// CHECK-SPIRV: ![[#MD_0]] = !{![[#MD_1:]]}
+// CHECK-SPIRV: ![[#MD_1]] = !{i32 11, i32 15}
+// CHECK-SPIRV: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK-SPIRV: ![[#MD_3]] = !{i32 30, i32 3}
+// CHECK-SPIRV: ![[#MD_4]] = !{![[#MD_5:]]}
+// CHECK-SPIRV: ![[#MD_5]] = !{i32 30, i32 0}
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl
new file mode 100644
index 0000000000000..43dc30f089d9e
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl
@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
+
+// This is almost the same as semantic.explicit-mix-builtin.hlsl, except this
+// time we build a vertex shader. This means the SV_Position semantic output
+// is also a BuiltIn, This means we can mix implicit and explicit location
+// assignment.
+struct S1 {
+ float4 position : SV_Position;
+ [[vk::location(3)]] float4 color : A;
+};
+
+// CHECK: @SV_Position0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]]
+// CHECK: @SV_Position = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]]
+// CHECK: @A0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_0]]
+
+[shader("vertex")]
+S1 main1(float4 position : SV_Position) {
+ S1 output;
+ output.position = position;
+ output.color = position;
+ return output;
+}
+
+// CHECK: ![[#MD_0]] = !{![[#MD_1:]]}
+// CHECK: ![[#MD_1]] = !{i32 30, i32 0}
+// | `-> Location index
+// `-> SPIR-V decoration 'Location'
+// CHECK: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK: ![[#MD_3]] = !{i32 11, i32 0}
+// | `-> BuiltIn 'Position'
+// `-> SPIR-V decoration 'BuiltIn'
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl
new file mode 100644
index 0000000000000..456c9bf9aee05
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl
@@ -0,0 +1,40 @@
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK
+
+// The followiong file contains both implicit and explicit vk::location, but
+// because each entrypoint has only one kind, this is allowed.
+
+[shader("vertex")]
+float4 vs_main(float4 p : SV_Position) : A {
+ return p;
+}
+
+[shader("pixel")]
+float4 ps_main([[vk::location(0)]] float4 p : A) : SV_Target {
+ return p;
+}
+
+// The following function is not marked as being a shader entrypoint, this
+// means the semantics and [[vk::location]] attributes are ignored.
+// Otherwise, the partial explicit location assignment would be illegal.
+float4 not_an_entry([[vk::location(0)]] float4 a : A, float4 b : B) : C {
+ return a + b;
+}
+
+// CHECK: @SV_Position0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]]
+// CHECK: @A0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_0:]]
+// CHECK: @A0.1 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]]
+// CHECK: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]]
+
+
+// CHECK: define void @vs_main()
+// CHECK: %[[#]] = load <4 x float>, ptr addrspace(7) @SV_Position0, align 16
+// CHECK: store <4 x float> %[[#]], ptr addrspace(8) @A0, align 16
+
+// CHECK: define void @ps_main()
+// CHECK: %[[#]] = load <4 x float>, ptr addrspace(7) @A0.1, align 16
+// CHECK: store <4 x float> %[[#]], ptr addrspace(8) @SV_Target0, align 16
+
+// CHECK: ![[#MD_0]] = !{![[#MD_1:]]}
+// CHECK: ![[#MD_1]] = !{i32 30, i32 0}
+// CHECK: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK: ![[#MD_3]] = !{i32 30, i32 1}
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 747eb17446c87..1e1d4a356f515 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -89,6 +89,7 @@
// CHECK-NEXT: FunctionReturnThunks (SubjectMatchRule_function)
// CHECK-NEXT: GNUInline (SubjectMatchRule_function)
// CHECK-NEXT: HIPManaged (SubjectMatchRule_variable)
+// CHECK-NEXT: HLSLVkLocation (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_field, SubjectMatchRule_function)
// CHECK-NEXT: Hot (SubjectMatchRule_function)
// CHECK-NEXT: HybridPatchable (SubjectMatchRule_function)
// CHECK-NEXT: IBAction (SubjectMatchRule_objc_method_is_instance)
diff --git a/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl
new file mode 100644
index 0000000000000..3abd1cb65ffc4
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -triple spirv-linux-vulkan-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note
+
+// This is almost the same as semantic.explicit-mix-builtin.hlsl, except this
+// time we build a vertex shader. This means the SV_Position semantic is not
+// a BuiltIn anymore, but a Location decorated variable. This means we mix
+// implicit and explicit location assignment.
+struct S1 {
+ float4 position : SV_Position;
+ [[vk::location(3)]] float4 color : A;
+ // expected-error at -1 {{partial explicit stage input location assignment via vk::location(X) unsupported}}
+};
+
+[shader("vertex")]
+float4 main1(S1 p) : A {
+ return p.position + p.color;
+}
diff --git a/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl
new file mode 100644
index 0000000000000..3de85863883ad
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl
@@ -0,0 +1,15 @@
+// RUN: %clang --driver-mode=dxc %s -T ps_6_8 -E main1 -O3 -spirv -Xclang -verify -Xclang -verify-ignore-unexpected=note
+
+// The following code is not legal: both semantics A and B will be lowered
+// into a Location decoration. And mixing implicit and explicit Location
+// assignment is not supported.
+struct S1 {
+ [[vk::location(3)]] float4 color : B;
+ float4 position : A;
+ // expected-error at -1 {{partial explicit stage input location assignment via vk::location(X) unsupported}}
+};
+
+[shader("pixel")]
+float4 main1(S1 p) : SV_Target {
+ return p.position + p.color;
+}
diff --git a/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl
new file mode 100644
index 0000000000000..8f5b6e48b48e9
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl
@@ -0,0 +1,15 @@
+// RUN: %clang --driver-mode=dxc %s -T ps_6_8 -E main1 -O3 -spirv -Xclang -verify -Xclang -verify-ignore-unexpected=note
+
+// The following code is not legal: both semantics A and B will be lowered
+// into a Location decoration. And mixing implicit and explicit Location
+// assignment is not supported.
+struct S1 {
+ float4 position : A;
+ [[vk::location(3)]] float4 color : B;
+ // expected-error at -1 {{partial explicit stage input location assignment via vk::location(X) unsupported}}
+};
+
+[shader("pixel")]
+float4 main1(S1 p) : SV_Target {
+ return p.position + p.color;
+}
More information about the cfe-commits
mailing list