[clang] [HLSL][SPIR-V] Add support for HLSL semantics (PR #149363)

Nathan Gauër via cfe-commits cfe-commits at lists.llvm.org
Thu Jul 17 10:28:09 PDT 2025


https://github.com/Keenuts created https://github.com/llvm/llvm-project/pull/149363

WIP PR for the WG-HLSL semantic proposal

TODO: validate DXIL packing and SPIR-V location assignment.

>From 8eb96a452cacee0e2284b6466351758db2bf3a80 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Mon, 2 Jun 2025 14:33:23 +0200
Subject: [PATCH] [HLSL][SPIR-V] Add support for HLSL semantics

TODO: validate DXIL packing and SPIR-V location assignment.
---
 clang/include/clang/AST/Attr.h                |  35 ++
 clang/include/clang/Basic/Attr.td             |  84 ++--
 clang/include/clang/Basic/AttrDocs.td         |  36 +-
 .../clang/Basic/DiagnosticFrontendKinds.td    |  11 +
 .../clang/Basic/DiagnosticParseKinds.td       |   5 +-
 .../clang/Basic/DiagnosticSemaKinds.td        |   4 +
 clang/include/clang/Parse/Parser.h            |   8 +
 clang/include/clang/Sema/SemaHLSL.h           |  23 +-
 clang/lib/Basic/Attributes.cpp                |   7 +-
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 401 ++++++++++++++++--
 clang/lib/CodeGen/CGHLSLRuntime.h             |  70 ++-
 clang/lib/Parse/ParseHLSL.cpp                 |  79 +++-
 clang/lib/Sema/ParsedAttr.cpp                 |   1 +
 clang/lib/Sema/SemaDeclAttr.cpp               |  34 +-
 clang/lib/Sema/SemaHLSL.cpp                   | 140 ++++--
 .../CodeGenHLSL/semantics/SV_Position.ps.hlsl |   6 +-
 .../semantics/semantic.struct.hlsl            |  72 ++++
 clang/test/CodeGenHLSL/sret_output.hlsl       |  21 +-
 ...a-attribute-supported-attributes-list.test |   1 +
 clang/test/ParserHLSL/semantic_parsing.hlsl   |   2 +-
 clang/test/SemaHLSL/Semantics/groupindex.hlsl |  12 +-
 .../Semantics/invalid_entry_parameter.hlsl    |  32 +-
 .../SemaHLSL/Semantics/position.ps.size.hlsl  |   4 +-
 .../test/SemaHLSL/Semantics/position.vs.hlsl  |   2 +-
 clang/utils/TableGen/ClangAttrEmitter.cpp     |   6 +
 25 files changed, 917 insertions(+), 179 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl

diff --git a/clang/include/clang/AST/Attr.h b/clang/include/clang/AST/Attr.h
index 994f236337b99..ab434a6971558 100644
--- a/clang/include/clang/AST/Attr.h
+++ b/clang/include/clang/AST/Attr.h
@@ -232,6 +232,41 @@ class HLSLAnnotationAttr : public InheritableAttr {
   }
 };
 
+class HLSLSemanticAttr : public HLSLAnnotationAttr {
+  unsigned SemanticIndex : 30;
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned SemanticIndexable : 1;
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned SemanticExplicitIndex : 1;
+
+protected:
+  HLSLSemanticAttr(ASTContext &Context, const AttributeCommonInfo &CommonInfo,
+                   attr::Kind AK, bool IsLateParsed,
+                   bool InheritEvenIfAlreadyPresent, bool SemanticIndexable)
+      : HLSLAnnotationAttr(Context, CommonInfo, AK, IsLateParsed,
+                           InheritEvenIfAlreadyPresent) {
+    this->SemanticIndexable = SemanticIndexable;
+    this->SemanticIndex = 0;
+    this->SemanticExplicitIndex = 0;
+  }
+
+public:
+  bool isSemanticIndexable() const { return SemanticIndexable; }
+
+  void setSemanticIndex(unsigned SemanticIndex) {
+    this->SemanticIndex = SemanticIndex;
+    this->SemanticExplicitIndex = true;
+  }
+
+  unsigned getSemanticIndex() const { return SemanticIndex; }
+
+  // Implement isa/cast/dyncast/etc.
+  static bool classof(const Attr *A) {
+    return A->getKind() >= attr::FirstHLSLAnnotationAttr &&
+           A->getKind() <= attr::LastHLSLAnnotationAttr;
+  }
+};
+
 /// A parameter attribute which changes the argument-passing ABI rule
 /// for the parameter.
 class ParameterABIAttr : public InheritableParamAttr {
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index 27fea7dea0a5e..e52da333c58f6 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -396,7 +396,7 @@ class ClangGCC<string name, bit allowInC = 1, int version = 1>
 }
 
 // HLSL Annotation spellings
-class HLSLAnnotation<string name> : Spelling<name, "HLSLAnnotation">;
+class HLSLAnnotation<string name> : Spelling<name, "HLSLAnnotation"> {}
 
 class Accessor<string name, list<Spelling> spellings> {
   string Name = name;
@@ -766,6 +766,16 @@ class DeclOrStmtAttr : InheritableAttr;
 /// An attribute class for HLSL Annotations.
 class HLSLAnnotationAttr : InheritableAttr;
 
+class HLSLSemanticAttr<bit Indexable> : HLSLAnnotationAttr {
+  bit SemanticIndexable = Indexable;
+  int SemanticIndex = 0;
+  bit SemanticExplicitIndex = 0;
+
+  let Spellings = [];
+  let Subjects = SubjectList<[ParmVar, Field, Function]>;
+  let LangOpts = [HLSL];
+}
+
 /// A target-specific attribute.  This class is meant to be used as a mixin
 /// with InheritableAttr or Attr depending on the attribute's needs.
 class TargetSpecificAttr<TargetSpec target> {
@@ -4832,27 +4842,6 @@ def HLSLNumThreads: InheritableAttr {
   let Documentation = [NumThreadsDocs];
 }
 
-def HLSLSV_GroupThreadID: HLSLAnnotationAttr {
-  let Spellings = [HLSLAnnotation<"sv_groupthreadid">];
-  let Subjects = SubjectList<[ParmVar, Field]>;
-  let LangOpts = [HLSL];
-  let Documentation = [HLSLSV_GroupThreadIDDocs];
-}
-
-def HLSLSV_GroupID: HLSLAnnotationAttr {
-  let Spellings = [HLSLAnnotation<"sv_groupid">];
-  let Subjects = SubjectList<[ParmVar, Field]>;
-  let LangOpts = [HLSL];
-  let Documentation = [HLSLSV_GroupIDDocs];
-}
-
-def HLSLSV_GroupIndex: HLSLAnnotationAttr {
-  let Spellings = [HLSLAnnotation<"sv_groupindex">];
-  let Subjects = SubjectList<[ParmVar, GlobalVar]>;
-  let LangOpts = [HLSL];
-  let Documentation = [HLSLSV_GroupIndexDocs];
-}
-
 def HLSLResourceBinding: InheritableAttr {
   let Spellings = [HLSLAnnotation<"register">];
   let Subjects = SubjectList<[HLSLBufferObj, ExternalGlobalVar], ErrorDiag>;
@@ -4902,13 +4891,43 @@ def HLSLResourceBinding: InheritableAttr {
   }];
 }
 
-def HLSLSV_Position : HLSLAnnotationAttr {
-  let Spellings = [HLSLAnnotation<"sv_position">];
-  let Subjects = SubjectList<[ParmVar, Field]>;
+def HLSLUnparsedSemantic : HLSLAnnotationAttr {
+  let Spellings = [];
+  let Args = [DefaultIntArgument<"Index", 0>,
+              DefaultBoolArgument<"ExplicitIndex", 0>];
+  let Subjects = SubjectList<[ParmVar, Field, Function]>;
   let LangOpts = [HLSL];
+  let Documentation = [InternalOnly];
+}
+
+def HLSLUserSemantic : HLSLSemanticAttr</* Indexable= */ 1> {
+  let Documentation = [InternalOnly];
+}
+
+def HLSLSV_Target : HLSLSemanticAttr</* Indexable= */ 1> {
+  let Documentation = [HLSLSV_TargetDocs];
+}
+
+def HLSLSV_Position : HLSLSemanticAttr</* Indexable= */ 1> {
   let Documentation = [HLSLSV_PositionDocs];
 }
 
+def HLSLSV_GroupThreadID : HLSLSemanticAttr</* Indexable= */ 0> {
+  let Documentation = [HLSLSV_GroupThreadIDDocs];
+}
+
+def HLSLSV_GroupID : HLSLSemanticAttr</* Indexable= */ 0> {
+  let Documentation = [HLSLSV_GroupIDDocs];
+}
+
+def HLSLSV_GroupIndex : HLSLSemanticAttr</* Indexable= */ 0> {
+  let Documentation = [HLSLSV_GroupIndexDocs];
+}
+
+def HLSLSV_DispatchThreadID : HLSLSemanticAttr</* Indexable= */ 0> {
+  let Documentation = [HLSLSV_DispatchThreadIDDocs];
+}
+
 def HLSLPackOffset: HLSLAnnotationAttr {
   let Spellings = [HLSLAnnotation<"packoffset">];
   let LangOpts = [HLSL];
@@ -4921,13 +4940,6 @@ def HLSLPackOffset: HLSLAnnotationAttr {
   }];
 }
 
-def HLSLSV_DispatchThreadID: HLSLAnnotationAttr {
-  let Spellings = [HLSLAnnotation<"sv_dispatchthreadid">];
-  let Subjects = SubjectList<[ParmVar, Field]>;
-  let LangOpts = [HLSL];
-  let Documentation = [HLSLSV_DispatchThreadIDDocs];
-}
-
 def HLSLShader : InheritableAttr {
   let Spellings = [Microsoft<"shader">];
   let Subjects = SubjectList<[HLSLEntry]>;
@@ -5031,6 +5043,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 43442f177ab7b..5db40e361e989 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8462,6 +8462,16 @@ The full documentation is available here: https://docs.microsoft.com/en-us/windo
   }];
 }
 
+def HLSLSV_PositionDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{TODO}];
+}
+
+def HLSLSV_TargetDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{TODO}];
+}
+
 def HLSLSV_GroupIDDocs : Documentation {
   let Category = DocCatFunction;
   let Content = [{
@@ -8544,20 +8554,6 @@ The full documentation is available here: https://docs.microsoft.com/en-us/windo
   }];
 }
 
-def HLSLSV_PositionDocs : Documentation {
-  let Category = DocCatFunction;
-  let Content = [{
-The ``SV_Position`` semantic, when applied to an input parameter in a pixel
-shader, contains the location of the pixel center (x, y) in screen space.
-This semantic can be applied to the parameter, or a field in a struct used
-as an input parameter.
-This attribute is supported as an input in pixel, hull, domain and mesh shaders.
-This attribute is supported as an output in vertex, geometry and domain shaders.
-
-The full documentation is available here: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-semantics
-  }];
-}
-
 def HLSLGroupSharedAddressSpaceDocs : Documentation {
   let Category = DocCatVariable;
   let Content = [{
@@ -8617,6 +8613,18 @@ https://github.com/microsoft/hlsl-specs/blob/main/proposals/0011-inline-spirv.md
   }];
 }
 
+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 AnnotateTypeDocs : Documentation {
   let Category = DocCatType;
   let Heading = "annotate_type";
diff --git a/clang/include/clang/Basic/DiagnosticFrontendKinds.td b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
index 8a8db27490f06..a170dc43bacd0 100644
--- a/clang/include/clang/Basic/DiagnosticFrontendKinds.td
+++ b/clang/include/clang/Basic/DiagnosticFrontendKinds.td
@@ -393,6 +393,17 @@ def warn_hlsl_langstd_minimal :
           "recommend using %1 instead">,
   InGroup<HLSLDXCCompat>;
 
+def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
+
+def err_hlsl_semantic_indexing_not_supported
+    : Error<"semantic %0 does not allow indexing">;
+
+def err_hlsl_semantic_missing : Error<"semantic annotations must be present "
+                                      "for all input and outputs of an entry "
+                                      "function or patch constant function">;
+
+def note_hlsl_semantic_used_here : Note<"%0 used here">;
+
 // ClangIR frontend errors
 def err_cir_to_cir_transform_failed : Error<
     "CIR-to-CIR transformation failed">, DefaultFatal;
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td
index 6c30da376dafb..2963d317335ac 100644
--- a/clang/include/clang/Basic/DiagnosticParseKinds.td
+++ b/clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1842,9 +1842,8 @@ def note_max_tokens_total_override : Note<"total token limit set here">;
 
 def err_expected_semantic_identifier : Error<
   "expected HLSL Semantic identifier">;
-def err_invalid_declaration_in_hlsl_buffer : Error<
-  "invalid declaration inside %select{tbuffer|cbuffer}0">;
-def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">;
+def err_invalid_declaration_in_hlsl_buffer
+    : Error<"invalid declaration inside %select{tbuffer|cbuffer}0">;
 def err_hlsl_separate_attr_arg_and_number : Error<"wrong argument format for hlsl attribute, use %0 instead">;
 def ext_hlsl_access_specifiers : ExtWarn<
   "access specifiers are a clang HLSL extension">,
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index f2f2152b8bbbe..7d7fcb93d626e 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -12982,6 +12982,9 @@ def err_hlsl_duplicate_parameter_modifier : Error<"duplicate parameter modifier
 def err_hlsl_missing_semantic_annotation : Error<
   "semantic annotations must be present for all parameters of an entry "
   "function or patch constant function">;
+def err_hlsl_unknown_semantic : Error<"unknown HLSL semantic %0">;
+def err_hlsl_semantic_output_not_supported
+    : Error<"semantic %0 does not support output">;
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
 
@@ -13430,4 +13433,5 @@ def err_acc_device_type_multiple_archs
 // AMDGCN builtins diagnostics
 def err_amdgcn_load_lds_size_invalid_value : Error<"invalid size value">;
 def note_amdgcn_load_lds_size_valid_value : Note<"size must be %select{1, 2, or 4|1, 2, 4, 12 or 16}0">;
+
 } // end of sema component.
diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h
index a47e23ffbd357..b9accdc7aa10b 100644
--- a/clang/include/clang/Parse/Parser.h
+++ b/clang/include/clang/Parse/Parser.h
@@ -5164,6 +5164,14 @@ class Parser : public CodeCompletionHandler {
       ParseHLSLAnnotations(Attrs, EndLoc);
   }
 
+  struct ParsedSemantic {
+    StringRef Name;
+    unsigned Index;
+    bool Explicit;
+  };
+
+  ParsedSemantic ParseHLSLSemantic();
+
   void ParseHLSLAnnotations(ParsedAttributes &Attrs,
                             SourceLocation *EndLoc = nullptr,
                             bool CouldBeBitField = false);
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 7d7eae4db532c..c54c96302d908 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -110,6 +110,7 @@ class SemaHLSL : public SemaBase {
   bool ActOnUninitializedVarDecl(VarDecl *D);
   void ActOnEndOfTranslationUnit(TranslationUnitDecl *TU);
   void CheckEntryPoint(FunctionDecl *FD);
+  bool isSemanticValid(FunctionDecl *FD, DeclaratorDecl *D);
   void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param,
                                const HLSLAnnotationAttr *AnnotationAttr);
   void DiagnoseAttrStageMismatch(
@@ -140,17 +141,31 @@ class SemaHLSL : public SemaBase {
   void handleNumThreadsAttr(Decl *D, const ParsedAttr &AL);
   void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL);
   void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL);
-  void handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL);
-  void handleSV_GroupThreadIDAttr(Decl *D, const ParsedAttr &AL);
-  void handleSV_GroupIDAttr(Decl *D, const ParsedAttr &AL);
-  void handleSV_PositionAttr(Decl *D, const ParsedAttr &AL);
+  // void handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL);
+  // void handleSV_GroupThreadIDAttr(Decl *D, const ParsedAttr &AL);
+  // void handleSV_GroupIDAttr(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);
   void handleParamModifierAttr(Decl *D, const ParsedAttr &AL);
   bool handleResourceTypeAttr(QualType T, const ParsedAttr &AL);
 
+  template <typename T>
+  T *createSemanticAttr(const ParsedAttr &AL,
+                        std::optional<unsigned> Location) {
+    T *Attr = ::new (getASTContext()) T(getASTContext(), AL);
+    if (Attr->isSemanticIndexable())
+      Attr->setSemanticIndex(Location ? *Location : 0);
+    return Attr;
+    // FIXME
+  }
+
+  void diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
+                                  std::optional<unsigned> Index);
+  void handleSemanticAttr(Decl *D, const ParsedAttr &AL);
+
   void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL);
+  void handleVkLocationAttr(Decl *D, const ParsedAttr &AL);
 
   bool CheckBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
   QualType ProcessResourceTypeAttributes(QualType Wrapped);
diff --git a/clang/lib/Basic/Attributes.cpp b/clang/lib/Basic/Attributes.cpp
index 004e5209a44a7..9e31097c5f557 100644
--- a/clang/lib/Basic/Attributes.cpp
+++ b/clang/lib/Basic/Attributes.cpp
@@ -189,7 +189,12 @@ AttributeCommonInfo::Kind
 AttributeCommonInfo::getParsedKind(const IdentifierInfo *Name,
                                    const IdentifierInfo *ScopeName,
                                    Syntax SyntaxUsed) {
-  return ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed);
+  AttributeCommonInfo::Kind Kind =
+      ::getAttrKind(normalizeName(Name, ScopeName, SyntaxUsed), SyntaxUsed);
+  if (SyntaxUsed == AS_HLSLAnnotation &&
+      Kind == AttributeCommonInfo::Kind::UnknownAttribute)
+    return AttributeCommonInfo::Kind::AT_HLSLUnparsedSemantic;
+  return Kind;
 }
 
 AttributeCommonInfo::AttrArgsInfo
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 3103f1798e14e..c7cdbe9f93444 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -22,6 +22,7 @@
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/Type.h"
 #include "clang/Basic/TargetOptions.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Frontend/HLSL/HLSLRootSignatureUtils.h"
 #include "llvm/IR/Constants.h"
@@ -337,6 +338,16 @@ void clang::CodeGen::CGHLSLRuntime::setHLSLEntryAttributes(
                 WaveSizeAttr->getPreferred());
     Fn->addFnAttr(WaveSizeKindStr, WaveSizeStr);
   }
+
+  if (HLSLVkLocationAttr *LocationAttr = FD->getAttr<HLSLVkLocationAttr>()) {
+    // const StringRef NumThreadsKindStr = "hlsl.numthreads";
+    // std::string NumThreadsStr =
+    //     formatv("{0},{1},{2}", NumThreadsAttr->getX(),
+    //     NumThreadsAttr->getY(),
+    //             NumThreadsAttr->getZ());
+    // Fn->addFnAttr(NumThreadsKindStr, NumThreadsStr);
+  }
+
   // HLSL entry functions are materialized for module functions with
   // HLSLShaderAttr attribute. SetLLVMFunctionAttributesForDefinition called
   // later in the compiler-flow for such module functions is not aware of and
@@ -371,6 +382,17 @@ static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV,
   GV->addMetadata("spirv.Decorations", *Decoration);
 }
 
+static void addLocationDecoration(llvm::GlobalVariable *GV, unsigned Location) {
+  LLVMContext &Ctx = GV->getContext();
+  IRBuilder<> B(GV->getContext());
+  MDNode *Operands =
+      MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(/* Location */ 30)),
+                        ConstantAsMetadata::get(B.getInt32(Location))});
+  MDNode *Decoration = MDNode::get(Ctx, {Operands});
+  GV->addMetadata("spirv.Decorations", *Decoration);
+}
+
+#if 0
 static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
                                            llvm::Type *Ty, const Twine &Name,
                                            unsigned BuiltInID) {
@@ -383,38 +405,325 @@ static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
   GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
   return B.CreateLoad(Ty, GV);
 }
+#endif
 
-llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B,
-                                              const ParmVarDecl &D,
-                                              llvm::Type *Ty) {
-  assert(D.hasAttrs() && "Entry parameter missing annotation attribute!");
-  if (D.hasAttr<HLSLSV_GroupIndexAttr>()) {
+static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M,
+                                            llvm::Type *Ty, unsigned Location,
+                                            StringRef Name = "") {
+  auto *GV = new llvm::GlobalVariable(
+      M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage,
+      /* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr,
+      llvm::GlobalVariable::GeneralDynamicTLSModel,
+      /* AddressSpace */ 7, /* isExternallyInitialized= */ true);
+  GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
+  addLocationDecoration(GV, Location);
+  return B.CreateLoad(Ty, GV);
+}
+
+llvm::Value *
+CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                         SemanticInfo &ActiveSemantic) {
+  Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
+  Twine VariableName = BaseName.concat(Twine(ActiveSemantic.Index));
+  unsigned Location = SPIRVLastAssignedSemanticLocation;
+
+  // 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;
+  SPIRVLastAssignedSemanticLocation += ElementCount;
+
+  return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location,
+                                 VariableName.str());
+}
+
+llvm::Value *
+CGHLSLRuntime::emitUserSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
+                                    const clang::DeclaratorDecl *Decl,
+                                    SemanticInfo &ActiveSemantic) {
+  assert(nullptr != dyn_cast<HLSLUserSemanticAttr>(ActiveSemantic.Semantic));
+
+  uint32_t Location = ActiveSemantic.Index;
+  if (HLSLVkLocationAttr *LocationAttr = Decl->getAttr<HLSLVkLocationAttr>())
+    Location = LocationAttr->getLocation();
+
+  llvm::Value *SemanticValue =
+      emitSPIRVUserSemanticLoad(B, Type, ActiveSemantic);
+
+  llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Type);
+  unsigned ElementCount = AT ? AT->getNumElements() : 1;
+  ActiveSemantic.Index += ElementCount;
+
+  // Mark the semantic/index pair as active and detect collisions.
+  Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
+  for (unsigned I = 0; I < ElementCount; I++) {
+    Twine VariableName = BaseName.concat(Twine(Location + I));
+    auto [_, Inserted] = ActiveInputSemantics.insert(VariableName.str());
+    if (!Inserted) {
+      CGM.getDiags().Report(Decl->getInnerLocStart(),
+                            diag::err_hlsl_semantic_index_overlap)
+          << VariableName.str();
+      return nullptr;
+    }
+  }
+
+  return SemanticValue;
+}
+
+llvm::Value *
+CGHLSLRuntime::emitSystemSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
+                                      const clang::DeclaratorDecl *Decl,
+                                      SemanticInfo &ActiveSemantic) {
+
+#define CHECK_NO_INDEXING(Info)                                                \
+  if ((Info).Index != 0) {                                                     \
+    CGM.getDiags().Report(Decl->getInnerLocStart(),                            \
+                          diag::err_hlsl_semantic_indexing_not_supported)      \
+        << (Info).Semantic;                                                    \
+    return nullptr;                                                            \
+  }
+
+  if (HLSLSV_GroupIndexAttr *S =
+          dyn_cast<HLSLSV_GroupIndexAttr>(ActiveSemantic.Semantic)) {
+    CHECK_NO_INDEXING(ActiveSemantic)
     llvm::Function *GroupIndex =
         CGM.getIntrinsic(getFlattenedThreadIdInGroupIntrinsic());
     return B.CreateCall(FunctionCallee(GroupIndex));
   }
-  if (D.hasAttr<HLSLSV_DispatchThreadIDAttr>()) {
+
+  if (HLSLSV_DispatchThreadIDAttr *S =
+          dyn_cast<HLSLSV_DispatchThreadIDAttr>(ActiveSemantic.Semantic)) {
+    CHECK_NO_INDEXING(ActiveSemantic)
     llvm::Function *ThreadIDIntrinsic =
         CGM.getIntrinsic(getThreadIdIntrinsic());
-    return buildVectorInput(B, ThreadIDIntrinsic, Ty);
+    return buildVectorInput(B, ThreadIDIntrinsic, Type);
   }
-  if (D.hasAttr<HLSLSV_GroupThreadIDAttr>()) {
+
+  if (HLSLSV_GroupThreadIDAttr *S =
+          dyn_cast<HLSLSV_GroupThreadIDAttr>(ActiveSemantic.Semantic)) {
+    CHECK_NO_INDEXING(ActiveSemantic)
     llvm::Function *GroupThreadIDIntrinsic =
         CGM.getIntrinsic(getGroupThreadIdIntrinsic());
-    return buildVectorInput(B, GroupThreadIDIntrinsic, Ty);
+    return buildVectorInput(B, GroupThreadIDIntrinsic, Type);
   }
-  if (D.hasAttr<HLSLSV_GroupIDAttr>()) {
+
+  if (HLSLSV_GroupIDAttr *S =
+          dyn_cast<HLSLSV_GroupIDAttr>(ActiveSemantic.Semantic)) {
+    CHECK_NO_INDEXING(ActiveSemantic)
     llvm::Function *GroupIDIntrinsic = CGM.getIntrinsic(getGroupIdIntrinsic());
-    return buildVectorInput(B, GroupIDIntrinsic, Ty);
+    return buildVectorInput(B, GroupIDIntrinsic, Type);
+  }
+
+#undef CHECK_NO_INDEXING
+
+  if (HLSLSV_PositionAttr *S =
+          dyn_cast<HLSLSV_PositionAttr>(ActiveSemantic.Semantic))
+    return emitSPIRVUserSemanticLoad(B, Type, ActiveSemantic);
+
+  llvm_unreachable("non-handled system semantic. FIXME.");
+}
+
+llvm::Value *
+CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
+                                        const clang::DeclaratorDecl *Decl,
+                                        SemanticInfo &ActiveSemantic) {
+
+  if (!ActiveSemantic.Semantic) {
+    ActiveSemantic.Semantic = Decl->getAttr<HLSLSemanticAttr>();
+    if (!ActiveSemantic.Semantic) {
+      CGM.getDiags().Report(Decl->getInnerLocStart(),
+                            diag::err_hlsl_semantic_missing);
+      return nullptr;
+    }
+    ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
   }
-  if (D.hasAttr<HLSLSV_PositionAttr>()) {
-    if (getArch() == llvm::Triple::spirv)
-      return createSPIRVBuiltinLoad(B, CGM.getModule(), Ty, "sv_position",
-                                    /* BuiltIn::Position */ 0);
-    llvm_unreachable("SV_Position semantic not implemented for this target.");
+
+  if (auto *UserSemantic =
+          dyn_cast<HLSLUserSemanticAttr>(ActiveSemantic.Semantic))
+    return emitUserSemanticLoad(B, Type, Decl, ActiveSemantic);
+  return emitSystemSemanticLoad(B, Type, Decl, ActiveSemantic);
+}
+
+llvm::Value *
+CGHLSLRuntime::handleStructSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
+                                        const clang::DeclaratorDecl *Decl,
+                                        SemanticInfo &ActiveSemantic) {
+  const llvm::StructType *ST = cast<StructType>(Type);
+  const clang::RecordDecl *RD = Decl->getType()->getAsRecordDecl();
+
+  assert(std::distance(RD->field_begin(), RD->field_end()) ==
+         ST->getNumElements());
+
+  if (!ActiveSemantic.Semantic) {
+    ActiveSemantic.Semantic = Decl->getAttr<HLSLSemanticAttr>();
+    ActiveSemantic.Index = ActiveSemantic.Semantic
+                               ? ActiveSemantic.Semantic->getSemanticIndex()
+                               : 0;
   }
-  assert(false && "Unhandled parameter attribute");
-  return nullptr;
+
+  llvm::Value *Aggregate = llvm::PoisonValue::get(Type);
+  auto FieldDecl = RD->field_begin();
+  for (unsigned I = 0; I < ST->getNumElements(); ++I) {
+    SemanticInfo Info = ActiveSemantic;
+    llvm::Value *ChildValue =
+        handleSemanticLoad(B, ST->getElementType(I), *FieldDecl, Info);
+    if (!ChildValue) {
+      CGM.getDiags().Report(Decl->getInnerLocStart(),
+                            diag::note_hlsl_semantic_used_here)
+          << Decl;
+      return nullptr;
+    }
+
+    Aggregate = B.CreateInsertValue(Aggregate, ChildValue, I);
+    ++FieldDecl;
+  }
+
+  return Aggregate;
+}
+
+llvm::Value *
+CGHLSLRuntime::handleSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
+                                  const clang::DeclaratorDecl *Decl,
+                                  SemanticInfo &ActiveSemantic) {
+
+  if (Type->isStructTy())
+    return handleStructSemanticLoad(B, Type, Decl, ActiveSemantic);
+  return handleScalarSemanticLoad(B, Type, Decl, ActiveSemantic);
+}
+
+static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M,
+                                     llvm::Value *Value, unsigned Location,
+                                     StringRef Name = "") {
+  auto *GV = new llvm::GlobalVariable(
+      M, Value->getType(), /* isConstant= */ false,
+      llvm::GlobalValue::ExternalLinkage,
+      /* Initializer= */ nullptr, /* Name= */ Name, /* insertBefore= */ nullptr,
+      llvm::GlobalVariable::GeneralDynamicTLSModel,
+      /* AddressSpace */ 8, /* isExternallyInitialized= */ false);
+  GV->setVisibility(llvm::GlobalValue::HiddenVisibility);
+  addLocationDecoration(GV, Location);
+  B.CreateStore(Value, GV);
+}
+
+void CGHLSLRuntime::emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B,
+                                               llvm::Value *Source,
+                                               SemanticInfo &ActiveSemantic) {
+  Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
+  Twine VariableName = BaseName.concat(Twine(ActiveSemantic.Index));
+  unsigned Location = SPIRVLastAssignedSemanticLocation;
+
+  // DXC completely ignores the semantic/index pair. Location are assigned from
+  // the first semantic to the last.
+  llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Source->getType());
+  unsigned ElementCount = AT ? AT->getNumElements() : 1;
+  SPIRVLastAssignedSemanticLocation += ElementCount;
+
+  createSPIRVLocationStore(B, CGM.getModule(), Source, Location,
+                           VariableName.str());
+}
+
+void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source,
+                                          const clang::DeclaratorDecl *Decl,
+                                          SemanticInfo &ActiveSemantic) {
+  assert(nullptr != dyn_cast<HLSLUserSemanticAttr>(ActiveSemantic.Semantic));
+
+  uint32_t Location = ActiveSemantic.Index;
+  if (HLSLVkLocationAttr *LocationAttr = Decl->getAttr<HLSLVkLocationAttr>())
+    Location = LocationAttr->getLocation();
+
+  emitSPIRVUserSemanticStore(B, Source, ActiveSemantic);
+
+  llvm::ArrayType *AT = dyn_cast<llvm::ArrayType>(Source->getType());
+  unsigned ElementCount = AT ? AT->getNumElements() : 1;
+  ActiveSemantic.Index += ElementCount;
+
+  // Mark the semantic/index pair as active and detect collisions.
+  Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName());
+  for (unsigned I = 0; I < ElementCount; I++) {
+    Twine VariableName = BaseName.concat(Twine(Location + I));
+    auto [_, Inserted] = ActiveOutputSemantics.insert(VariableName.str());
+    if (!Inserted) {
+      CGM.getDiags().Report(Decl->getInnerLocStart(),
+                            diag::err_hlsl_semantic_index_overlap)
+          << VariableName.str();
+      return;
+    }
+  }
+}
+
+void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source,
+                                            const clang::DeclaratorDecl *Decl,
+                                            SemanticInfo &ActiveSemantic) {
+
+  if (HLSLSV_TargetAttr *S =
+          dyn_cast<HLSLSV_TargetAttr>(ActiveSemantic.Semantic))
+    emitSPIRVUserSemanticStore(B, Source, ActiveSemantic);
+  else
+    llvm_unreachable("non-handled system semantic. FIXME.");
+}
+
+void CGHLSLRuntime::handleScalarSemanticStore(IRBuilder<> &B,
+                                              llvm::Value *Source,
+                                              const clang::DeclaratorDecl *Decl,
+                                              SemanticInfo &ActiveSemantic) {
+
+  if (!ActiveSemantic.Semantic) {
+    ActiveSemantic.Semantic = Decl->getAttr<HLSLSemanticAttr>();
+    if (!ActiveSemantic.Semantic) {
+      CGM.getDiags().Report(Decl->getInnerLocStart(),
+                            diag::err_hlsl_semantic_missing);
+      return;
+    }
+    ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex();
+  }
+
+  if (auto *UserSemantic =
+          dyn_cast<HLSLUserSemanticAttr>(ActiveSemantic.Semantic))
+    emitUserSemanticStore(B, Source, Decl, ActiveSemantic);
+  else
+    emitSystemSemanticStore(B, Source, Decl, ActiveSemantic);
+}
+
+void CGHLSLRuntime::handleStructSemanticStore(IRBuilder<> &B,
+                                              llvm::Value *Source,
+                                              const clang::DeclaratorDecl *Decl,
+                                              SemanticInfo &ActiveSemantic) {
+
+  const llvm::StructType *ST = cast<StructType>(Source->getType());
+
+  const clang::RecordDecl *RD = nullptr;
+  if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(Decl))
+    RD = FD->getDeclaredReturnType()->getAsRecordDecl();
+  else
+    RD = Decl->getType()->getAsRecordDecl();
+  assert(RD);
+
+  assert(std::distance(RD->field_begin(), RD->field_end()) ==
+         ST->getNumElements());
+
+  if (!ActiveSemantic.Semantic) {
+    ActiveSemantic.Semantic = Decl->getAttr<HLSLSemanticAttr>();
+    ActiveSemantic.Index = ActiveSemantic.Semantic
+                               ? ActiveSemantic.Semantic->getSemanticIndex()
+                               : 0;
+  }
+
+  auto FieldDecl = RD->field_begin();
+  for (unsigned I = 0; I < ST->getNumElements(); ++I) {
+    llvm::Value *Extract = B.CreateExtractValue(Source, I);
+    SemanticInfo Info = ActiveSemantic;
+    handleSemanticStore(B, Extract, *FieldDecl, Info);
+    ++FieldDecl;
+  }
+}
+
+void CGHLSLRuntime::handleSemanticStore(IRBuilder<> &B, llvm::Value *Source,
+                                        const clang::DeclaratorDecl *Decl,
+                                        SemanticInfo &ActiveSemantic) {
+  if (Source->getType()->isStructTy())
+    handleStructSemanticStore(B, Source, Decl, ActiveSemantic);
+  else
+    handleScalarSemanticStore(B, Source, Decl, ActiveSemantic);
 }
 
 void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
@@ -437,7 +746,6 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
 
   BasicBlock *BB = BasicBlock::Create(Ctx, "entry", EntryFn);
   IRBuilder<> B(BB);
-  llvm::SmallVector<Value *> Args;
 
   SmallVector<OperandBundleDef, 1> OB;
   if (CGM.shouldEmitConvergenceTokens()) {
@@ -448,25 +756,64 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl *FD,
     OB.emplace_back("convergencectrl", bundleArgs);
   }
 
-  // FIXME: support struct parameters where semantics are on members.
-  // See: https://github.com/llvm/llvm-project/issues/57874
+  std::unordered_map<const DeclaratorDecl *, llvm::Value *> OutputSemantic;
+
+  llvm::SmallVector<Value *> Args;
   unsigned SRetOffset = 0;
   for (const auto &Param : Fn->args()) {
+
     if (Param.hasStructRetAttr()) {
-      // FIXME: support output.
-      // See: https://github.com/llvm/llvm-project/issues/57874
       SRetOffset = 1;
-      Args.emplace_back(PoisonValue::get(Param.getType()));
+      llvm::Type *VarType = Param.getParamStructRetType();
+      llvm::Value *Var = B.CreateAlloca(VarType);
+      Args.push_back(Var);
+      OutputSemantic.emplace(FD, Var);
       continue;
     }
+
     const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset);
-    Args.push_back(emitInputSemantic(B, *PD, Param.getType()));
+    llvm::Value *SemanticValue = nullptr;
+    if (HLSLParamModifierAttr *MA = PD->getAttr<HLSLParamModifierAttr>()) {
+      if (MA->isOut()) {
+        llvm::Type *VarType = CGM.getTypes().ConvertType(
+            cast<clang::ReferenceType>(PD->getType())->getPointeeType());
+        llvm::Value *Var = B.CreateAlloca(VarType);
+        SemanticValue = Var;
+        OutputSemantic.emplace(PD, Var);
+      } else
+        llvm_unreachable("Not handled yet");
+    } else {
+      llvm::Type *ParamType =
+          Param.hasByValAttr() ? Param.getParamByValType() : Param.getType();
+      SemanticInfo ActiveSemantic = {nullptr, 0};
+      SemanticValue = handleSemanticLoad(B, ParamType, PD, ActiveSemantic);
+      if (!SemanticValue)
+        return;
+      if (Param.hasByValAttr()) {
+        llvm::Value *Var = B.CreateAlloca(Param.getParamByValType());
+        B.CreateStore(SemanticValue, Var);
+        SemanticValue = Var;
+      }
+    }
+    Args.push_back(SemanticValue);
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
   CI->setCallingConv(Fn->getCallingConv());
-  // FIXME: Handle codegen for return type semantics.
-  // See: https://github.com/llvm/llvm-project/issues/57875
+
+  if (Fn->getReturnType() != CGM.VoidTy)
+    OutputSemantic.emplace(FD, CI);
+
+  for (auto &[Decl, Source] : OutputSemantic) {
+    llvm::Value *SourceValue = nullptr;
+    if (AllocaInst *AI = dyn_cast<AllocaInst>(Source))
+      SourceValue = B.CreateLoad(AI->getAllocatedType(), Source);
+    else
+      SourceValue = Source;
+    SemanticInfo ActiveSemantic = {nullptr, 0};
+    handleSemanticStore(B, SourceValue, Decl, ActiveSemantic);
+  }
+
   B.CreateRetVoid();
 
   // Add and identify root signature to function, if applicable
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h
index 89d2aff85d913..484f903bb9fbb 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -21,6 +21,8 @@
 #include "llvm/IR/IntrinsicsDirectX.h"
 #include "llvm/IR/IntrinsicsSPIRV.h"
 
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/HLSLRuntime.h"
 
@@ -29,6 +31,7 @@
 #include "llvm/Frontend/HLSL/HLSLResource.h"
 
 #include <optional>
+#include <unordered_set>
 #include <vector>
 
 // A function generator macro for picking the right intrinsic
@@ -133,8 +136,67 @@ class CGHLSLRuntime {
 protected:
   CodeGenModule &CGM;
 
-  llvm::Value *emitInputSemantic(llvm::IRBuilder<> &B, const ParmVarDecl &D,
-                                 llvm::Type *Ty);
+  void collectInputSemantic(llvm::IRBuilder<> &B, const DeclaratorDecl *D,
+                            llvm::Type *Type,
+                            SmallVectorImpl<llvm::Value *> &Inputs);
+
+  struct SPIRVState {
+    uint32_t NextLocation;
+  };
+
+  struct SemanticInfo {
+    clang::HLSLSemanticAttr *Semantic;
+    uint32_t Index;
+  };
+
+  llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                         SemanticInfo &ActiveSemantic);
+
+  llvm::Value *emitUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                    const clang::DeclaratorDecl *Decl,
+                                    SemanticInfo &ActiveSemantic);
+
+  llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                      const clang::DeclaratorDecl *Decl,
+                                      SemanticInfo &ActiveSemantic);
+
+  llvm::Value *handleScalarSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                        const clang::DeclaratorDecl *Decl,
+                                        SemanticInfo &ActiveSemantic);
+
+  llvm::Value *handleStructSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                        const clang::DeclaratorDecl *Decl,
+                                        SemanticInfo &ActiveSemantic);
+
+  llvm::Value *handleSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                  const clang::DeclaratorDecl *Decl,
+                                  SemanticInfo &ActiveSemantic);
+  ///
+
+  void emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Value,
+                                  SemanticInfo &ActiveSemantic);
+
+  void emitUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Value,
+                             const clang::DeclaratorDecl *Decl,
+                             SemanticInfo &ActiveSemantic);
+
+  void emitSystemSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Value,
+                               const clang::DeclaratorDecl *Decl,
+                               SemanticInfo &ActiveSemantic);
+
+  void handleScalarSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Value,
+                                 const clang::DeclaratorDecl *Decl,
+                                 SemanticInfo &ActiveSemantic);
+
+  void handleStructSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Value,
+                                 const clang::DeclaratorDecl *Decl,
+                                 SemanticInfo &ActiveSemantic);
+
+  void handleSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Value,
+                           const clang::DeclaratorDecl *Decl,
+                           SemanticInfo &ActiveSemantic);
+
+  ///
 
 public:
   CGHLSLRuntime(CodeGenModule &CGM) : CGM(CGM) {}
@@ -172,6 +234,10 @@ class CGHLSLRuntime {
   llvm::Triple::ArchType getArch();
 
   llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes;
+
+  std::unordered_set<std::string> ActiveInputSemantics;
+  std::unordered_set<std::string> ActiveOutputSemantics;
+  unsigned SPIRVLastAssignedSemanticLocation = 0;
 };
 
 } // namespace CodeGen
diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp
index e6caa81b309ca..72e1cc9eff63f 100644
--- a/clang/lib/Parse/ParseHLSL.cpp
+++ b/clang/lib/Parse/ParseHLSL.cpp
@@ -118,6 +118,48 @@ static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
   Slot = new (Ctx) IdentifierLoc(ArgLoc, PP.getIdentifierInfo(FixedArg));
 }
 
+Parser::ParsedSemantic Parser::ParseHLSLSemantic() {
+  assert(Tok.is(tok::identifier) && "Not a HLSL Annotation");
+
+  // Semantic pattern: [A-Za-z_]+[0-9]*
+  // The first part is the semantic name, the second is the optional
+  // semantic index.
+  bool Invalid = false;
+  SmallString<256> Buffer;
+  Buffer.resize(Tok.getLength() + 1);
+  StringRef Identifier = PP.getSpelling(Tok, Buffer);
+  if (Invalid) {
+    // FIXME: fix error message.
+    Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
+    return {/* Name= */ "", /* Location= */ 0, /* Explicit= */ false};
+  }
+
+  unsigned I = 0;
+  for (; I < Identifier.size() && !isDigit(Identifier[I]); ++I)
+    continue;
+  StringRef SemanticName = Identifier.take_front(I);
+
+  if (SemanticName.size() == 0) {
+    // FIXME: fix error message.
+    Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
+    return {/* Name= */ "", /* Location= */ 0, /* Explicit= */ false};
+  }
+
+  unsigned Index = 0;
+  bool Explicit = I < Identifier.size();
+  for (; I < Identifier.size() && isDigit(Identifier[I]); ++I)
+    Index = Index * 10 + Identifier[I] - '0';
+
+  // The attribute has letters after the index.
+  if (I != Identifier.size()) {
+    // FIXME: fix error message.
+    Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
+    return {/* Name= */ "", /* Location= */ 0, /* Explicit= */ false};
+  }
+
+  return {SemanticName, Index, Explicit};
+}
+
 void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
                                   SourceLocation *EndLoc,
                                   bool CouldBeBitField) {
@@ -141,11 +183,15 @@ void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
     return;
   }
 
+  ParsedAttr::Kind AttrKind =
+      ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLAnnotation);
+  Parser::ParsedSemantic Semantic;
+  if (AttrKind == ParsedAttr::AT_HLSLUnparsedSemantic)
+    Semantic = ParseHLSLSemantic();
+
   SourceLocation Loc = ConsumeToken();
   if (EndLoc)
     *EndLoc = Tok.getLocation();
-  ParsedAttr::Kind AttrKind =
-      ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLAnnotation);
 
   ArgsVector ArgExprs;
   switch (AttrKind) {
@@ -282,19 +328,30 @@ void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
       return;
     }
   } break;
-  case ParsedAttr::UnknownAttribute:
-    Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
-    return;
-  case ParsedAttr::AT_HLSLSV_GroupThreadID:
-  case ParsedAttr::AT_HLSLSV_GroupID:
-  case ParsedAttr::AT_HLSLSV_GroupIndex:
-  case ParsedAttr::AT_HLSLSV_DispatchThreadID:
-  case ParsedAttr::AT_HLSLSV_Position:
+  case ParsedAttr::UnknownAttribute: {
+    break;
+  }
+  case ParsedAttr::AT_HLSLUnparsedSemantic: {
+    ASTContext &Ctx = Actions.getASTContext();
+    ArgExprs.push_back(IntegerLiteral::Create(
+        Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.IntTy), Semantic.Index), Ctx.IntTy,
+        SourceLocation()));
+    ArgExprs.push_back(IntegerLiteral::Create(
+        Ctx, llvm::APInt(1, Semantic.Explicit), Ctx.BoolTy, SourceLocation()));
+    II = PP.getIdentifierInfo(Semantic.Name.upper());
     break;
-  default:
+  }
+  // case ParsedAttr::AT_HLSLSV_GroupThreadID:
+  // case ParsedAttr::AT_HLSLSV_GroupID:
+  // case ParsedAttr::AT_HLSLSV_GroupIndex:
+  // case ParsedAttr::AT_HLSLSV_DispatchThreadID:
+  case ParsedAttr::AT_HLSLVkLocation:
+    break;
+  default: {
     llvm_unreachable("invalid HLSL Annotation");
     break;
   }
+  }
 
   Attrs.addNew(II, Loc, AttributeScopeInfo(), ArgExprs.data(), ArgExprs.size(),
                ParsedAttr::Form::HLSLAnnotation());
diff --git a/clang/lib/Sema/ParsedAttr.cpp b/clang/lib/Sema/ParsedAttr.cpp
index 294f88eae931c..e7c10d198f144 100644
--- a/clang/lib/Sema/ParsedAttr.cpp
+++ b/clang/lib/Sema/ParsedAttr.cpp
@@ -103,6 +103,7 @@ namespace {
 } // namespace
 
 const ParsedAttrInfo &ParsedAttrInfo::get(const AttributeCommonInfo &A) {
+
   // If we have a ParsedAttrInfo for this ParsedAttr then return that.
   if ((size_t)A.getParsedKind() < std::size(AttrInfoMap))
     return *AttrInfoMap[A.getParsedKind()];
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index eba29e609cb05..5597a3f7510bb 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -6938,6 +6938,7 @@ static bool MustDelayAttributeArguments(const ParsedAttr &AL) {
 static void
 ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
                      const Sema::ProcessDeclAttributeOptions &Options) {
+
   if (AL.isInvalid() || AL.getKind() == ParsedAttr::IgnoredAttribute)
     return;
 
@@ -7584,30 +7585,27 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
   case ParsedAttr::AT_HLSLWaveSize:
     S.HLSL().handleWaveSizeAttr(D, AL);
     break;
-  case ParsedAttr::AT_HLSLSV_Position:
-    S.HLSL().handleSV_PositionAttr(D, AL);
-    break;
   case ParsedAttr::AT_HLSLVkExtBuiltinInput:
     S.HLSL().handleVkExtBuiltinInputAttr(D, AL);
     break;
   case ParsedAttr::AT_HLSLVkConstantId:
     S.HLSL().handleVkConstantIdAttr(D, AL);
     break;
-  case ParsedAttr::AT_HLSLSV_GroupThreadID:
-    S.HLSL().handleSV_GroupThreadIDAttr(D, AL);
-    break;
-  case ParsedAttr::AT_HLSLSV_GroupID:
-    S.HLSL().handleSV_GroupIDAttr(D, AL);
-    break;
-  case ParsedAttr::AT_HLSLSV_GroupIndex:
-    handleSimpleAttribute<HLSLSV_GroupIndexAttr>(S, D, AL);
-    break;
+  // case ParsedAttr::AT_HLSLSV_GroupThreadID:
+  //   S.HLSL().handleSV_GroupThreadIDAttr(D, AL);
+  //   break;
+  // case ParsedAttr::AT_HLSLSV_GroupID:
+  //   S.HLSL().handleSV_GroupIDAttr(D, AL);
+  //   break;
+  // case ParsedAttr::AT_HLSLSV_GroupIndex:
+  //   handleSimpleAttribute<HLSLSV_GroupIndexAttr>(S, D, AL);
+  //   break;
+  // case ParsedAttr::AT_HLSLSV_DispatchThreadID:
+  //   S.HLSL().handleSV_DispatchThreadIDAttr(D, AL);
+  //   break;
   case ParsedAttr::AT_HLSLGroupSharedAddressSpace:
     handleSimpleAttribute<HLSLGroupSharedAddressSpaceAttr>(S, D, AL);
     break;
-  case ParsedAttr::AT_HLSLSV_DispatchThreadID:
-    S.HLSL().handleSV_DispatchThreadIDAttr(D, AL);
-    break;
   case ParsedAttr::AT_HLSLPackOffset:
     S.HLSL().handlePackOffsetAttr(D, AL);
     break;
@@ -7620,6 +7618,12 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
   case ParsedAttr::AT_HLSLParamModifier:
     S.HLSL().handleParamModifierAttr(D, AL);
     break;
+  case ParsedAttr::AT_HLSLVkLocation:
+    S.HLSL().handleVkLocationAttr(D, AL);
+    break;
+  case ParsedAttr::AT_HLSLUnparsedSemantic:
+    S.HLSL().handleSemanticAttr(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 b5975c2e5782e..49e99b861c117 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -744,6 +744,28 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) {
   }
 }
 
+bool SemaHLSL::isSemanticValid(FunctionDecl *FD, DeclaratorDecl *D) {
+  const auto *AnnotationAttr = D->getAttr<HLSLAnnotationAttr>();
+  if (AnnotationAttr) {
+    CheckSemanticAnnotation(FD, D, AnnotationAttr);
+    return true;
+  }
+
+  const Type *T =
+      D->getType()
+          ->getUnqualifiedDesugaredType(); //.getDesugaredType(getASTContext());
+  const RecordType *RT = dyn_cast<RecordType>(T);
+  if (!RT)
+    return false;
+
+  const RecordDecl *RD = RT->getDecl();
+  for (FieldDecl *Field : RD->fields()) {
+    if (!isSemanticValid(FD, Field))
+      return false;
+  }
+  return true;
+}
+
 void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
   const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
   assert(ShaderAttr && "Entry point has no shader attribute");
@@ -805,15 +827,17 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
   }
 
   for (ParmVarDecl *Param : FD->parameters()) {
-    if (const auto *AnnotationAttr = Param->getAttr<HLSLAnnotationAttr>()) {
-      CheckSemanticAnnotation(FD, Param, AnnotationAttr);
-    } else {
+    if (!isSemanticValid(FD, Param)) {
       // FIXME: Handle struct parameters where annotations are on struct fields.
       // See: https://github.com/llvm/llvm-project/issues/57875
       Diag(FD->getLocation(), diag::err_hlsl_missing_semantic_annotation);
       Diag(Param->getLocation(), diag::note_previous_decl) << Param;
       FD->setInvalidDecl();
     }
+    // if (const auto *AnnotationAttr = Param->getAttr<HLSLAnnotationAttr>()) {
+    //   CheckSemanticAnnotation(FD, Param, AnnotationAttr);
+    // } else {
+    // }
   }
   // FIXME: Verify return type semantic annotation.
 }
@@ -834,13 +858,23 @@ void SemaHLSL::CheckSemanticAnnotation(
       return;
     DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Compute});
     break;
+  case attr::HLSLVkLocation:
+    if (ST == llvm::Triple::Pixel || ST == llvm::Triple::Vertex)
+      return;
+    DiagnoseAttrStageMismatch(AnnotationAttr, ST,
+                              {llvm::Triple::Pixel, llvm::Triple::Vertex});
+    break;
   case attr::HLSLSV_Position:
-    // TODO(#143523): allow use on other shader types & output once the overall
-    // semantic logic is implemented.
     if (ST == llvm::Triple::Pixel)
       return;
     DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Pixel});
     break;
+  case attr::HLSLSV_Target:
+  case attr::HLSLUserSemantic:
+    // FIXME: handle system & stage mismatch.
+    // DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Pixel,
+    // llvm::Triple::Vertex});
+    return;
   default:
     llvm_unreachable("Unknown HLSLAnnotationAttr");
   }
@@ -1379,18 +1413,8 @@ bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) {
   return true;
 }
 
-void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) {
-  auto *VD = cast<ValueDecl>(D);
-  if (!diagnoseInputIDType(VD->getType(), AL))
-    return;
-
-  D->addAttr(::new (getASTContext())
-                 HLSLSV_DispatchThreadIDAttr(getASTContext(), AL));
-}
-
 bool SemaHLSL::diagnosePositionType(QualType T, const ParsedAttr &AL) {
   const auto *VT = T->getAs<VectorType>();
-
   if (!T->hasFloatingRepresentation() || (VT && VT->getNumElements() > 4)) {
     Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type)
         << AL << "float/float1/float2/float3/float4";
@@ -1400,29 +1424,70 @@ bool SemaHLSL::diagnosePositionType(QualType T, const ParsedAttr &AL) {
   return true;
 }
 
-void SemaHLSL::handleSV_PositionAttr(Decl *D, const ParsedAttr &AL) {
-  auto *VD = cast<ValueDecl>(D);
-  if (!diagnosePositionType(VD->getType(), AL))
-    return;
+void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL,
+                                          std::optional<unsigned> Index) {
+  StringRef SemanticName = AL.getAttrName()->getName();
 
-  D->addAttr(::new (getASTContext()) HLSLSV_PositionAttr(getASTContext(), AL));
-}
-
-void SemaHLSL::handleSV_GroupThreadIDAttr(Decl *D, const ParsedAttr &AL) {
   auto *VD = cast<ValueDecl>(D);
-  if (!diagnoseInputIDType(VD->getType(), AL))
-    return;
-
-  D->addAttr(::new (getASTContext())
-                 HLSLSV_GroupThreadIDAttr(getASTContext(), AL));
+  QualType ValueType = VD->getType();
+  if (auto *FD = dyn_cast<FunctionDecl>(D))
+    ValueType = FD->getReturnType();
+
+  bool IsOutput = false;
+  if (HLSLParamModifierAttr *MA = D->getAttr<HLSLParamModifierAttr>()) {
+    if (MA->isOut()) {
+      IsOutput = true;
+      ValueType = cast<ReferenceType>(ValueType)->getPointeeType();
+    }
+  }
+
+#define CHECK_OUTPUT_FORBIDDEN(AL)                                             \
+  if (IsOutput) {                                                              \
+    Diag(AL.getLoc(), diag::err_hlsl_semantic_output_not_supported) << AL;     \
+  }
+
+  if (SemanticName == "SV_DISPATCHTHREADID") {
+    diagnoseInputIDType(ValueType, AL);
+    CHECK_OUTPUT_FORBIDDEN(AL);
+    D->addAttr(createSemanticAttr<HLSLSV_DispatchThreadIDAttr>(AL, Index));
+  } else if (SemanticName == "SV_GROUPINDEX") {
+    // diagnoseInputIDType(ValueType, AL);
+    CHECK_OUTPUT_FORBIDDEN(AL);
+    D->addAttr(createSemanticAttr<HLSLSV_GroupIndexAttr>(AL, Index));
+  } else if (SemanticName == "SV_GROUPTHREADID") {
+    diagnoseInputIDType(ValueType, AL);
+    CHECK_OUTPUT_FORBIDDEN(AL);
+    D->addAttr(createSemanticAttr<HLSLSV_GroupThreadIDAttr>(AL, Index));
+  } else if (SemanticName == "SV_GROUPID") {
+    diagnoseInputIDType(ValueType, AL);
+    CHECK_OUTPUT_FORBIDDEN(AL);
+    D->addAttr(createSemanticAttr<HLSLSV_GroupIDAttr>(AL, Index));
+  } else if (SemanticName == "SV_TARGET" || SemanticName == "SV_POSITION") {
+    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";
+    if (SemanticName == "SV_POSITION")
+      D->addAttr(createSemanticAttr<HLSLSV_PositionAttr>(AL, Index));
+    else
+      D->addAttr(createSemanticAttr<HLSLSV_TargetAttr>(AL, Index));
+  } else
+    Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
 }
 
-void SemaHLSL::handleSV_GroupIDAttr(Decl *D, const ParsedAttr &AL) {
-  auto *VD = cast<ValueDecl>(D);
-  if (!diagnoseInputIDType(VD->getType(), AL))
-    return;
+void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) {
+  uint32_t IndexValue, ExplicitIndex;
+  SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), IndexValue);
+  SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(1), ExplicitIndex);
+  assert(IndexValue > 0 ? ExplicitIndex : true);
+  std::optional<unsigned> Index =
+      ExplicitIndex ? std::optional<unsigned>(IndexValue) : std::nullopt;
 
-  D->addAttr(::new (getASTContext()) HLSLSV_GroupIDAttr(getASTContext(), AL));
+  if (AL.getAttrName()->getName().starts_with("SV_"))
+    diagnoseSystemSemanticAttr(D, AL, Index);
+  else
+    D->addAttr(createSemanticAttr<HLSLUserSemanticAttr>(AL, Index));
 }
 
 void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) {
@@ -1646,6 +1711,15 @@ bool SemaHLSL::handleResourceTypeAttr(QualType T, const ParsedAttr &AL) {
   return true;
 }
 
+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));
+}
+
 // Combines all resource type attributes and creates HLSLAttributedResourceType.
 QualType SemaHLSL::ProcessResourceTypeAttributes(QualType CurrentType) {
   if (!HLSLResourcesTypeAttrs.size())
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
index bdba38e028edd..3834b77b67964 100644
--- a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s
 
-// CHECK: @sv_position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0
+// CHECK: @SV_POSITION0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0
 
 // CHECK: define void @main() {{.*}} {
-float4 main(float4 p : SV_Position) {
-  // CHECK: %[[#P:]] = load <4 x float>, ptr addrspace(7) @sv_position, align 16
+float4 main(float4 p : SV_Position) : SV_TARGET {
+  // CHECK: %[[#P:]] = load <4 x float>, ptr addrspace(7) @SV_POSITION0, align 16
   // CHECK: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#P]])
   return p;
 }
diff --git a/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl
new file mode 100644
index 0000000000000..56e65d8067b1f
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
+// 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,CHECK-SPIRV -DTARGET=spv
+
+// Make sure SV_DispatchThreadID translated into dx.thread.id.
+
+struct S0 {
+  uint Idx : SV_DispatchThreadID;
+};
+
+// CHECK:       define void @main0()
+// CHECK:        %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK:        %[[#TMP:]] = insertvalue %struct.S0 poison, i32 %[[#ID:]], 0
+// CHECK:        %[[#ARG:]] = alloca %struct.S0, align 8
+// CHECK:                     store %struct.S0 %[[#TMP]], ptr %[[#ARG]], align 4
+// CHECK-DXIL:                call void @{{.*}}main0{{.*}}(ptr %[[#ARG]])
+// CHECK-SPIRV:               call spir_func void @{{.*}}main0{{.*}}(ptr %[[#ARG]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void main0(S0 p) {}
+
+struct S1 {
+  uint  a : SV_DispatchThreadID;
+  uint3 b : SV_GroupThreadID;
+};
+
+// CHECK:                     define void @main1()
+// CHECK:         %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK:       %[[#S1A_:]] = insertvalue %struct.S1 poison, i32 %[[#ID:]], 0
+// CHECK:       %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 0)
+// CHECK:      %[[#ID_X_:]] = insertelement <3 x i32> poison, i32 %[[#ID_X]], i64 0
+// CHECK:       %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 1)
+// CHECK:      %[[#ID_XY:]] = insertelement <3 x i32> %[[#ID_X_]], i32 %[[#ID_Y]], i64 1
+// CHECK:       %[[#ID_Z:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 2)
+// CHECK:     %[[#ID_XYZ:]] = insertelement <3 x i32> %[[#ID_XY]], i32 %[[#ID_Z]], i64 2
+// CHECK:       %[[#S1AB:]] = insertvalue %struct.S1 %[[#S1A_]], <3 x i32> %[[#ID_XYZ:]], 1
+// CHECK:        %[[#ARG:]] = alloca %struct.S1, align 8
+// CHECK:                     store %struct.S1 %[[#S1AB]], ptr %[[#ARG]], align 1
+// CHECK-DXIL:                call void @{{.*}}main1{{.*}}(ptr %[[#ARG]])
+// CHECK-SPIRV:               call spir_func void @{{.*}}main1{{.*}}(ptr %[[#ARG]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void main1(S1 p) {}
+
+struct S2C {
+  uint3 b : SV_GroupThreadID;
+};
+
+struct S2 {
+  uint  a : SV_DispatchThreadID;
+  S2C child;
+};
+
+// CHECK:                     define void @main2()
+// CHECK:         %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK:       %[[#S2A_:]] = insertvalue %struct.S2 poison, i32 %[[#ID:]], 0
+
+// CHECK:       %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 0)
+// CHECK:      %[[#ID_X_:]] = insertelement <3 x i32> poison, i32 %[[#ID_X]], i64 0
+// CHECK:       %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 1)
+// CHECK:      %[[#ID_XY:]] = insertelement <3 x i32> %[[#ID_X_]], i32 %[[#ID_Y]], i64 1
+// CHECK:       %[[#ID_Z:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 2)
+// CHECK:     %[[#ID_XYZ:]] = insertelement <3 x i32> %[[#ID_XY]], i32 %[[#ID_Z]], i64 2
+// CHECK:        %[[#S2C:]] = insertvalue %struct.S2C poison, <3 x i32> %[[#ID_XYZ:]], 0
+
+// CHECK:       %[[#S2AB:]] = insertvalue %struct.S2 %[[#S2A_]], %struct.S2C %[[#S2V:]], 1
+// CHECK:        %[[#ARG:]] = alloca %struct.S2, align 8
+// CHECK:                     store %struct.S2 %[[#S2AB]], ptr %[[#ARG]], align 1
+// CHECK-DXIL:                call void @{{.*}}main2{{.*}}(ptr %[[#ARG]])
+// CHECK-SPIRV:               call spir_func void @{{.*}}main2{{.*}}(ptr %[[#ARG]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void main2(S2 p) {}
diff --git a/clang/test/CodeGenHLSL/sret_output.hlsl b/clang/test/CodeGenHLSL/sret_output.hlsl
index eefc9dabab517..05cebe16cb77f 100644
--- a/clang/test/CodeGenHLSL/sret_output.hlsl
+++ b/clang/test/CodeGenHLSL/sret_output.hlsl
@@ -1,21 +1,26 @@
 // RUN: %clang_cc1 -std=hlsl2021 -finclude-default-header -triple dxil-pc-shadermodel6.3-library %s  \
 // RUN:   -emit-llvm -disable-llvm-passes -o - | FileCheck %s
 
-// FIXME: add semantic to a.
-// See https://github.com/llvm/llvm-project/issues/57874
+// CHECK: @SV_TARGET0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations !0
+
 struct S {
-  float a;
+  float4 a : SV_Target;
 };
 
 
-// Make sure sret parameter is generated.
 // CHECK:define internal void @_Z7ps_mainv(ptr dead_on_unwind noalias writable sret(%struct.S) align 1 %agg.result)
-// FIXME: change it to real value instead of poison value once semantic is add to a.
-// Make sure the function with sret is called.
-// CHECK:call void @_Z7ps_mainv(ptr poison)
+// CHECK: %a = getelementptr inbounds nuw %struct.S, ptr %agg.result, i32 0, i32 0
+// CHECK: store <4 x float> zeroinitializer, ptr %a, align 1
+
+// CHECK: %[[#VAR:]] = alloca %struct.S, align 16
+// CHECK:              call void @_Z7ps_mainv(ptr %[[#VAR]])
+// CHECK: %[[#L1:]] = load %struct.S, ptr %[[#VAR]], align 16
+// CHECK: %[[#L2:]] = extractvalue %struct.S %[[#L1]], 0
+// CHECK: store <4 x float> %[[#L2]], ptr addrspace(8) @SV_TARGET0, align 16
+
 [shader("pixel")]
 S ps_main() {
   S s;
-  s.a = 0;
+  s.a = float4(0.f);
   return s;
 };
diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 41d00dae3f69a..9aae4d0a85925 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -86,6 +86,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/ParserHLSL/semantic_parsing.hlsl b/clang/test/ParserHLSL/semantic_parsing.hlsl
index 34df1805c5a95..1301c84e0211b 100644
--- a/clang/test/ParserHLSL/semantic_parsing.hlsl
+++ b/clang/test/ParserHLSL/semantic_parsing.hlsl
@@ -3,5 +3,5 @@
 // expected-error at +1 {{expected HLSL Semantic identifier}}
 void Entry(int GI : ) { }
 
-// expected-error at +1 {{unknown HLSL semantic 'SV_IWantAPony'}}
+// expected-error at +1 {{unknown HLSL semantic 'SV_IWANTAPONY'}}
 void Pony(int GI : SV_IWantAPony) { }
diff --git a/clang/test/SemaHLSL/Semantics/groupindex.hlsl b/clang/test/SemaHLSL/Semantics/groupindex.hlsl
index a33e060c82906..8acdee8e85989 100644
--- a/clang/test/SemaHLSL/Semantics/groupindex.hlsl
+++ b/clang/test/SemaHLSL/Semantics/groupindex.hlsl
@@ -4,26 +4,26 @@
 [shader("compute")][numthreads(32,1,1)]
 void compute(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'pixel' shaders}}
+// expected-error at +2 {{attribute 'SV_GROUPINDEX' is unsupported in 'pixel' shaders}}
 [shader("pixel")]
 void pixel(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'vertex' shaders}}
+// expected-error at +2 {{attribute 'SV_GROUPINDEX' is unsupported in 'vertex' shaders}}
 [shader("vertex")]
 void vertex(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'geometry' shaders}}
+// expected-error at +2 {{attribute 'SV_GROUPINDEX' is unsupported in 'geometry' shaders}}
 [shader("geometry")]
 void geometry(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'domain' shaders}}
+// expected-error at +2 {{attribute 'SV_GROUPINDEX' is unsupported in 'domain' shaders}}
 [shader("domain")]
 void domain(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'amplification' shaders}}
+// expected-error at +2 {{attribute 'SV_GROUPINDEX' is unsupported in 'amplification' shaders}}
 [shader("amplification")][numthreads(32,1,1)]
 void amplification(int GI : SV_GroupIndex) {}
 
-// expected-error at +2 {{attribute 'SV_GroupIndex' is unsupported in 'mesh' shaders}}
+// expected-error at +2 {{attribute 'SV_GROUPINDEX' is unsupported in 'mesh' shaders}}
 [shader("mesh")][numthreads(32,1,1)]
 void mesh(int GI : SV_GroupIndex) {}
diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
index 1bb4ee5182d62..18bae5ee322df 100644
--- a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
+++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl
@@ -1,7 +1,7 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -ast-dump -verify -o - %s
 
 [numthreads(8,8,1)]
-// expected-error at +1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+// expected-error at +1 {{attribute 'SV_DISPATCHTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
 void CSMain(float ID : SV_DispatchThreadID) {
 
 }
@@ -11,71 +11,71 @@ struct ST {
   float b;
 };
 [numthreads(8,8,1)]
-// expected-error at +1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+// expected-error at +1 {{attribute 'SV_DISPATCHTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
 void CSMain2(ST ID : SV_DispatchThreadID) {
 
 }
 
 void foo() {
-// expected-warning at +1 {{'SV_DispatchThreadID' attribute only applies to parameters and non-static data members}}
+// expected-warning at +1 {{'SV_DISPATCHTHREADID' attribute only applies to parameters, non-static data members, and functions}}
   uint V : SV_DispatchThreadID;
 
 }
 
 struct ST2 {
-// expected-warning at +1 {{'SV_DispatchThreadID' attribute only applies to parameters and non-static data members}}
+// expected-warning at +1 {{'SV_DISPATCHTHREADID' attribute only applies to parameters, non-static data members, and functions}}
     static uint X : SV_DispatchThreadID;
     uint s : SV_DispatchThreadID;
 };
 
 [numthreads(8,8,1)]
-// expected-error at +1 {{attribute 'SV_GroupID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+// expected-error at +1 {{attribute 'SV_GROUPID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
 void CSMain_GID(float ID : SV_GroupID) {
 }
 
 [numthreads(8,8,1)]
-// expected-error at +1 {{attribute 'SV_GroupID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+// expected-error at +1 {{attribute 'SV_GROUPID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
 void CSMain2_GID(ST GID : SV_GroupID) {
 
 }
 
 void foo_GID() {
-// expected-warning at +1 {{'SV_GroupID' attribute only applies to parameters and non-static data members}}
+// expected-warning at +1 {{'SV_GROUPID' attribute only applies to parameters, non-static data members, and functions}}
   uint GIS : SV_GroupID;
 }
 
 struct ST2_GID {
-// expected-warning at +1 {{'SV_GroupID' attribute only applies to parameters and non-static data members}}
+// expected-warning at +1 {{'SV_GROUPID' attribute only applies to parameters, non-static data members, and functions}}
     static uint GID : SV_GroupID;
     uint s_gid : SV_GroupID;
 };
 
 [numthreads(8,8,1)]
-// expected-error at +1 {{attribute 'SV_GroupThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+// expected-error at +1 {{attribute 'SV_GROUPTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
 void CSMain_GThreadID(float ID : SV_GroupThreadID) {
 }
 
 [numthreads(8,8,1)]
-// expected-error at +1 {{attribute 'SV_GroupThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
+// expected-error at +1 {{attribute 'SV_GROUPTHREADID' only applies to a field or parameter of type 'uint/uint2/uint3'}}
 void CSMain2_GThreadID(ST GID : SV_GroupThreadID) {
 
 }
 
 void foo_GThreadID() {
-// expected-warning at +1 {{'SV_GroupThreadID' attribute only applies to parameters and non-static data members}}
+// expected-warning at +1 {{'SV_GROUPTHREADID' attribute only applies to parameters, non-static data members, and functions}}
   uint GThreadIS : SV_GroupThreadID;
 }
 
 struct ST2_GThreadID {
-// expected-warning at +1 {{'SV_GroupThreadID' attribute only applies to parameters and non-static data members}}
+// expected-warning at +1 {{'SV_GROUPTHREADID' attribute only applies to parameters, non-static data members, and functions}}
     static uint GThreadID : SV_GroupThreadID;
     uint s_gthreadid : SV_GroupThreadID;
 };
 
 
 [shader("vertex")]
-// expected-error at +4 {{attribute 'SV_GroupIndex' is unsupported in 'vertex' shaders, requires compute}}
-// expected-error at +3 {{attribute 'SV_DispatchThreadID' is unsupported in 'vertex' shaders, requires compute}}
-// expected-error at +2 {{attribute 'SV_GroupID' is unsupported in 'vertex' shaders, requires compute}}
-// expected-error at +1 {{attribute 'SV_GroupThreadID' is unsupported in 'vertex' shaders, requires compute}}
+// expected-error at +4 {{attribute 'SV_GROUPINDEX' is unsupported in 'vertex' shaders, requires compute}}
+// expected-error at +3 {{attribute 'SV_DISPATCHTHREADID' is unsupported in 'vertex' shaders, requires compute}}
+// expected-error at +2 {{attribute 'SV_GROUPID' is unsupported in 'vertex' shaders, requires compute}}
+// expected-error at +1 {{attribute 'SV_GROUPTHREADID' is unsupported in 'vertex' shaders, requires compute}}
 void vs_main(int GI : SV_GroupIndex, uint ID : SV_DispatchThreadID, uint GID : SV_GroupID, uint GThreadID : SV_GroupThreadID) {}
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl
index 124d401a9990c..b57ce4e325ca2 100644
--- a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -o - %s -verify -verify-ignore-unexpected
 // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library  -x hlsl -finclude-default-header -o - %s -verify -verify-ignore-unexpected
 
-// expected-error at +1 {{attribute 'SV_Position' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}}
+// expected-error at +1 {{attribute 'SV_POSITION' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}}
 void main(vector<float, 5> a : SV_Position) {
 }
 
-// expected-error at +1 {{attribute 'SV_Position' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}}
+// expected-error at +1 {{attribute 'SV_POSITION' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}}
 void main(int2 a : SV_Position) {
 }
diff --git a/clang/test/SemaHLSL/Semantics/position.vs.hlsl b/clang/test/SemaHLSL/Semantics/position.vs.hlsl
index 19f781fa3757c..e5e8da2484d93 100644
--- a/clang/test/SemaHLSL/Semantics/position.vs.hlsl
+++ b/clang/test/SemaHLSL/Semantics/position.vs.hlsl
@@ -1,6 +1,6 @@
 // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-vertex -x hlsl -finclude-default-header -o - %s -verify
 
-// expected-error at +1 {{attribute 'SV_Position' is unsupported in 'vertex' shaders, requires pixel}}
+// expected-error at +1 {{attribute 'SV_POSITION' is unsupported in 'vertex' shaders, requires pixel}}
 float4 main(float4 a : SV_Position) {
   return a;
 }
diff --git a/clang/utils/TableGen/ClangAttrEmitter.cpp b/clang/utils/TableGen/ClangAttrEmitter.cpp
index dfeb6b1b1ec19..81984effaf84f 100644
--- a/clang/utils/TableGen/ClangAttrEmitter.cpp
+++ b/clang/utils/TableGen/ClangAttrEmitter.cpp
@@ -2725,12 +2725,15 @@ static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS,
     assert(!Supers.empty() && "Forgot to specify a superclass for the attr");
     std::string SuperName;
     bool Inheritable = false;
+    bool HLSLSemantic = false;
     for (const Record *R : reverse(Supers)) {
       if (R->getName() != "TargetSpecificAttr" &&
           R->getName() != "DeclOrTypeAttr" && SuperName.empty())
         SuperName = R->getName().str();
       if (R->getName() == "InheritableAttr")
         Inheritable = true;
+      if (R->getName() == "HLSLSemanticAttr")
+        HLSLSemantic = true;
     }
 
     if (Header)
@@ -3054,6 +3057,9 @@ static void emitAttributes(const RecordKeeper &Records, raw_ostream &OS,
            << (R.getValueAsBit("InheritEvenIfAlreadyPresent") ? "true"
                                                               : "false");
       }
+      if (HLSLSemantic) {
+        OS << ", " << (R.getValueAsBit("SemanticIndexable") ? "true" : "false");
+      }
       OS << ")\n";
 
       for (auto const &ai : Args) {



More information about the cfe-commits mailing list