Author: Nathan Gauër
Date: 2025-11-06T15:36:17+01:00
New Revision: b8a814e4fce292a24449702de07d0705f5045c0b

URL: 
https://github.com/llvm/llvm-project/commit/b8a814e4fce292a24449702de07d0705f5045c0b
DIFF: 
https://github.com/llvm/llvm-project/commit/b8a814e4fce292a24449702de07d0705f5045c0b.diff

LOG: [HLSL] Add support for user semantics (#153424)

This commit adds support for HLSL input semantics. User semantics are
all semantics not starting with `SV_`.
Those semantics ends up with a Location assignment in SPIR-V.

Note: user semantics means Location, but the opposite is not true.
Depending on the stage, some system semantics can rely on a Location
index. This is not implemented in this PR.

Added: 
    clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl
    clang/test/CodeGenHLSL/semantics/semantic.array.hlsl
    clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl
    clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl
    clang/test/SemaHLSL/Semantics/semantics-valid.hlsl

Modified: 
    clang/include/clang/Basic/Attr.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/SemaHLSL.cpp
    clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
    clang/test/ParserHLSL/semantic_parsing.hlsl
    llvm/include/llvm/IR/IntrinsicsDirectX.td

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Basic/Attr.td 
b/clang/include/clang/Basic/Attr.td
index 749f531ec9ab1..1013bfc575747 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -5017,6 +5017,10 @@ def HLSLUnparsedSemantic : HLSLAnnotationAttr {
   let Documentation = [InternalOnly];
 }
 
+def HLSLUserSemantic : HLSLSemanticAttr</* Indexable= */ 1> {
+  let Documentation = [InternalOnly];
+}
+
 def HLSLSV_Position : HLSLSemanticAttr</* Indexable= */ 1> {
   let Documentation = [HLSLSV_PositionDocs];
 }

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td 
b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index fa509536bf021..f43707e513351 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13184,6 +13184,7 @@ def err_hlsl_semantic_indexing_not_supported
     : Error<"semantic %0 does not allow indexing">;
 def err_hlsl_init_priority_unsupported : Error<
   "initializer priorities are not supported in HLSL">;
+def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">;
 
 def 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 8c3b6ae176389..28b03ac4c4676 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -20,7 +20,9 @@
 #include "clang/Basic/DiagnosticSema.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Sema/SemaBase.h"
+#include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/TargetParser/Triple.h"
 #include <initializer_list>
 
@@ -259,9 +261,11 @@ class SemaHLSL : public SemaBase {
   HLSLSemanticAttr *createSemantic(const SemanticInfo &Semantic,
                                    DeclaratorDecl *TargetDecl);
   bool determineActiveSemanticOnScalar(FunctionDecl *FD, DeclaratorDecl *D,
-                                       SemanticInfo &ActiveSemantic);
+                                       SemanticInfo &ActiveSemantic,
+                                       llvm::StringSet<> 
&ActiveInputSemantics);
   bool determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *D,
-                               SemanticInfo &ActiveSemantic);
+                               SemanticInfo &ActiveSemantic,
+                               llvm::StringSet<> &ActiveInputSemantics);
 
   void processExplicitBindingsOnDecl(VarDecl *D);
 

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 945f9e2451bc1..e392a12044a39 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -549,6 +549,16 @@ 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);
+}
+
 static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
                                            llvm::Type *Ty, const Twine &Name,
                                            unsigned BuiltInID) {
@@ -562,6 +572,69 @@ static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, 
llvm::Module &M,
   return B.CreateLoad(Ty, GV);
 }
 
+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,
+                                         HLSLSemanticAttr *Semantic,
+                                         std::optional<unsigned> Index) {
+  Twine BaseName = Twine(Semantic->getAttrName()->getName());
+  Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
+
+  unsigned Location = SPIRVLastAssignedInputSemanticLocation;
+
+  // 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());
+}
+
+llvm::Value *
+CGHLSLRuntime::emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                        HLSLSemanticAttr *Semantic,
+                                        std::optional<unsigned> Index) {
+  Twine BaseName = Twine(Semantic->getAttrName()->getName());
+  Twine VariableName = BaseName.concat(Twine(Index.value_or(0)));
+
+  // DXIL packing rules etc shall be handled here.
+  // FIXME: generate proper sigpoint, index, col, row values.
+  // FIXME: also DXIL loads vectors element by element.
+  SmallVector<Value *> Args{B.getInt32(4), B.getInt32(0), B.getInt32(0),
+                            B.getInt8(0),
+                            llvm::PoisonValue::get(B.getInt32Ty())};
+
+  llvm::Intrinsic::ID IntrinsicID = llvm::Intrinsic::dx_load_input;
+  llvm::Value *Value = B.CreateIntrinsic(/*ReturnType=*/Type, IntrinsicID, 
Args,
+                                         nullptr, VariableName);
+  return Value;
+}
+
+llvm::Value *CGHLSLRuntime::emitUserSemanticLoad(
+    IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
+    HLSLSemanticAttr *Semantic, std::optional<unsigned> Index) {
+  if (CGM.getTarget().getTriple().isSPIRV())
+    return emitSPIRVUserSemanticLoad(B, Type, Semantic, Index);
+
+  if (CGM.getTarget().getTriple().isDXIL())
+    return emitDXILUserSemanticLoad(B, Type, Semantic, Index);
+
+  llvm_unreachable("Unsupported target for user-semantic load.");
+}
+
 llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad(
     IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl,
     Attr *Semantic, std::optional<unsigned> Index) {
@@ -626,6 +699,9 @@ CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, 
const FunctionDecl *FD,
   std::optional<unsigned> Index = std::nullopt;
   if (Semantic->isSemanticIndexExplicit())
     Index = Semantic->getSemanticIndex();
+
+  if (isa<HLSLUserSemanticAttr>(Semantic))
+    return emitUserSemanticLoad(B, Type, Decl, Semantic, Index);
   return emitSystemSemanticLoad(B, Type, Decl, Semantic, Index);
 }
 

diff  --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index d35df524fdc84..9d31714ab8606 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -200,9 +200,25 @@ class CGHLSLRuntime {
                                     llvm::GlobalVariable *BufGV);
   void initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
                                    llvm::GlobalVariable *GV);
+  void initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
+                                   llvm::GlobalVariable *GV,
+                                   HLSLResourceBindingAttr *RBA);
+
+  llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type 
*Type,
+                                         HLSLSemanticAttr *Semantic,
+                                         std::optional<unsigned> Index);
+  llvm::Value *emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                        HLSLSemanticAttr *Semantic,
+                                        std::optional<unsigned> Index);
+  llvm::Value *emitUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type,
+                                    const clang::DeclaratorDecl *Decl,
+                                    HLSLSemanticAttr *Semantic,
+                                    std::optional<unsigned> Index);
+
   llvm::Triple::ArchType getArch();
 
   llvm::DenseMap<const clang::RecordType *, llvm::TargetExtType *> LayoutTypes;
+  unsigned SPIRVLastAssignedInputSemanticLocation = 0;
 };
 
 } // namespace CodeGen

diff  --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index b9707f0036765..a06c57b15c585 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -775,6 +775,10 @@ HLSLSemanticAttr *SemaHLSL::createSemantic(const 
SemanticInfo &Info,
                                            DeclaratorDecl *TargetDecl) {
   std::string SemanticName = Info.Semantic->getAttrName()->getName().upper();
 
+  if (dyn_cast<HLSLUserSemanticAttr>(Info.Semantic))
+    return createSemanticAttr<HLSLUserSemanticAttr>(*Info.Semantic, TargetDecl,
+                                                    Info.Index);
+
   if (SemanticName == "SV_DISPATCHTHREADID") {
     return createSemanticAttr<HLSLSV_DispatchThreadIDAttr>(
         *Info.Semantic, TargetDecl, Info.Index);
@@ -797,9 +801,10 @@ HLSLSemanticAttr *SemaHLSL::createSemantic(const 
SemanticInfo &Info,
   return nullptr;
 }
 
-bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
-                                               DeclaratorDecl *D,
-                                               SemanticInfo &ActiveSemantic) {
+bool SemaHLSL::determineActiveSemanticOnScalar(
+    FunctionDecl *FD, DeclaratorDecl *D, SemanticInfo &ActiveSemantic,
+    llvm::StringSet<> &ActiveInputSemantics) {
+
   if (ActiveSemantic.Semantic == nullptr) {
     ActiveSemantic.Semantic = D->getAttr<HLSLSemanticAttr>();
     if (ActiveSemantic.Semantic &&
@@ -818,11 +823,31 @@ bool 
SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD,
 
   checkSemanticAnnotation(FD, D, A);
   FD->addAttr(A);
+
+  unsigned Location = ActiveSemantic.Index.value_or(0);
+
+  const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType());
+  unsigned ElementCount = AT ? AT->getZExtSize() : 1;
+  ActiveSemantic.Index = Location + ElementCount;
+
+  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) {
+      Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
+          << VariableName.str();
+      return false;
+    }
+  }
+
   return true;
 }
 
-bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *D,
-                                       SemanticInfo &ActiveSemantic) {
+bool SemaHLSL::determineActiveSemantic(
+    FunctionDecl *FD, DeclaratorDecl *D, SemanticInfo &ActiveSemantic,
+    llvm::StringSet<> &ActiveInputSemantics) {
   if (ActiveSemantic.Semantic == nullptr) {
     ActiveSemantic.Semantic = D->getAttr<HLSLSemanticAttr>();
     if (ActiveSemantic.Semantic &&
@@ -833,12 +858,13 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, 
DeclaratorDecl *D,
   const Type *T = D->getType()->getUnqualifiedDesugaredType();
   const RecordType *RT = dyn_cast<RecordType>(T);
   if (!RT)
-    return determineActiveSemanticOnScalar(FD, D, ActiveSemantic);
+    return determineActiveSemanticOnScalar(FD, D, ActiveSemantic,
+                                           ActiveInputSemantics);
 
   const RecordDecl *RD = RT->getDecl();
   for (FieldDecl *Field : RD->fields()) {
     SemanticInfo Info = ActiveSemantic;
-    if (!determineActiveSemantic(FD, Field, Info)) {
+    if (!determineActiveSemantic(FD, Field, Info, ActiveInputSemantics)) {
       Diag(Field->getLocation(), diag::note_hlsl_semantic_used_here) << Field;
       return false;
     }
@@ -911,12 +937,14 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) {
     llvm_unreachable("Unhandled environment in triple");
   }
 
+  llvm::StringSet<> ActiveInputSemantics;
   for (ParmVarDecl *Param : FD->parameters()) {
     SemanticInfo ActiveSemantic;
     ActiveSemantic.Semantic = nullptr;
     ActiveSemantic.Index = std::nullopt;
 
-    if (!determineActiveSemantic(FD, Param, ActiveSemantic)) {
+    if (!determineActiveSemantic(FD, Param, ActiveSemantic,
+                                 ActiveInputSemantics)) {
       Diag(Param->getLocation(), diag::note_previous_decl) << Param;
       FD->setInvalidDecl();
     }
@@ -947,6 +975,8 @@ void SemaHLSL::checkSemanticAnnotation(FunctionDecl 
*EntryPoint,
       return;
     DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Pixel});
     break;
+  case attr::HLSLUserSemantic:
+    return;
   default:
     llvm_unreachable("Unknown SemanticAttr");
   }
@@ -1766,7 +1796,7 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const 
ParsedAttr &AL) {
   if (AL.getAttrName()->getName().starts_with_insensitive("SV_"))
     diagnoseSystemSemanticAttr(D, AL, Index);
   else
-    Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL;
+    D->addAttr(createSemanticAttr<HLSLUserSemanticAttr>(AL, nullptr, Index));
 }
 
 void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) {

diff  --git a/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl 
b/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
index 7aeb877072d87..b0abaeddff422 100644
--- a/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
+++ b/clang/test/CodeGenHLSL/semantics/DispatchThreadID.hlsl
@@ -24,4 +24,3 @@ void foo(uint Idx : SV_DispatchThreadID) {}
 [shader("compute")]
 [numthreads(8,8,1)]
 void bar(uint2 Idx : SV_DispatchThreadID) {}
-

diff  --git a/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl
new file mode 100644
index 0000000000000..96d5b995fa74a
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl
@@ -0,0 +1,36 @@
+// RUN: %clang_cc1 -triple spirv-unknown-vulkan-vertex -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECK-SPIRV -DTARGET=spv
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-vertex -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
+
+// CHECK-SPIRV-DAG:  @AAA0 = external hidden thread_local addrspace(7) 
externally_initialized constant float, !spirv.Decorations ![[#METADATA_0:]]
+// CHECK-SPIRV-DAG:    @B0 = external hidden thread_local addrspace(7) 
externally_initialized constant i32, !spirv.Decorations ![[#METADATA_2:]]
+// CHECK-SPIRV-DAG:   @CC0 = external hidden thread_local addrspace(7) 
externally_initialized constant <2 x float>, !spirv.Decorations 
![[#METADATA_4:]]
+
+
+// FIXME: replace `float2 c` with a  matrix when available.
+void main(float a : AAA, int b : B, float2 c : CC) {
+  float tmp = a + b + c.x + c.y;
+}
+// CHECK-SPIRV: define internal spir_func void @_Z4mainfiDv2_f(float noundef 
nofpclass(nan inf) %a, i32 noundef %b, <2 x float> noundef nofpclass(nan inf) 
%c) #0 {
+
+// CHECK: define void @main()
+
+// CHECK-DXIL: %AAA0 = call float @llvm.dx.load.input.f32(i32 4, i32 0, i32 0, 
i8 0, i32 poison)
+// CHECK-DXIL:   %B0 = call i32 @llvm.dx.load.input.i32(i32 4, i32 0, i32 0, 
i8 0, i32 poison)
+// CHECK-DXIL   %CC0 = call <2 x float> @llvm.dx.load.input.v2f32(i32 4, i32 
0, i32 0, i8 0, i32 poison)
+// CHECK-DXIL:         call void @_Z4mainfiDv2_f(float %AAA0, i32 %B0, <2 x 
float> %CC0)
+
+// CHECK-SPIRV: %[[#AAA0:]] = load float, ptr addrspace(7) @AAA0, align 4
+// CHECK-SPIRV:   %[[#B0:]] = load i32, ptr addrspace(7) @B0, align 4
+// CHECK-SPIRV:  %[[#CC0:]] = load <2 x float>, ptr addrspace(7) @CC0, align 8
+// CHECK-SPIRV:               call spir_func void @_Z4mainfiDv2_f(float 
%[[#AAA0]], i32 %[[#B0]], <2 x float> %[[#CC0]]) [ "convergencectrl"(token %0) ]
+
+
+// CHECK-SPIRV-DAG: ![[#METADATA_0]] = !{![[#METADATA_1:]]}
+// CHECK-SPIRV-DAG: ![[#METADATA_2]] = !{![[#METADATA_3:]]}
+// CHECK-SPIRV-DAG: ![[#METADATA_4]] = !{![[#METADATA_5:]]}
+
+// CHECK-SPIRV-DAG: ![[#METADATA_1]] = !{i32 30, i32 0}
+// CHECK-SPIRV-DAG: ![[#METADATA_3]] = !{i32 30, i32 1}
+// CHECK-SPIRV-DAG: ![[#METADATA_5]] = !{i32 30, i32 2}
+//                                            |      `- Location index
+//                                            `-> Decoration "Location"

diff  --git a/clang/test/CodeGenHLSL/semantics/semantic.array.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic.array.hlsl
new file mode 100644
index 0000000000000..b2cb3dad9f0ce
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.array.hlsl
@@ -0,0 +1,37 @@
+// 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
+// RUN: %clang_cc1 -triple dxil-px-shadermodel6.3-library -x hlsl -emit-llvm 
-finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s 
--check-prefixes=CHECK,CHECK-DXIL -DTARGET=dx
+
+struct S0 {
+  float4 position[2];
+  float4 color;
+};
+
+// CHECK: %struct.S0 = type { [2 x <4 x float>], <4 x float> }
+
+// CHECK-SPIRV: @A0 = external hidden thread_local addrspace(7) 
externally_initialized constant [2 x <4 x float>], !spirv.Decorations 
![[#MD_0:]]
+// CHECK-SPIRV: @A2 = external hidden thread_local addrspace(7) 
externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_2:]]
+
+// CHECK:       define void @main0()
+// CHECK-DXIL:          %A0 = call [2 x <4 x float>] 
@llvm.dx.load.input.a2v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison)
+// CHECK-DXIL: %[[#TMP0:]] = insertvalue %struct.S0 poison, [2 x <4 x float>] 
%A0, 0
+// CHECK-DXIL:         %A2 = call <4 x float> @llvm.dx.load.input.v4f32(i32 4, 
i32 0, i32 0, i8 0, i32 poison)
+// CHECK-DXIL: %[[#TMP1:]] = insertvalue %struct.S0 %[[#TMP0]], <4 x float> 
%A2, 1
+
+// CHECK-SPIRV:   %[[#A0:]] = load [2 x <4 x float>], ptr addrspace(7) @A0, 
align 16
+// CHECK-SPIRV: %[[#TMP0:]] = insertvalue %struct.S0 poison, [2 x <4 x float>] 
%[[#A0]], 0
+// CHECK-SPIRV:   %[[#A2:]] = load <4 x float>, ptr addrspace(7) @A2, align 16
+// CHECK-SPIRV: %[[#TMP1:]] = insertvalue %struct.S0 %[[#TMP0]], <4 x float> 
%[[#A2]], 1
+
+// CHECK:        %[[#ARG:]] = alloca %struct.S0, align 16
+// CHECK:                     store %struct.S0 %[[#TMP1]], ptr %[[#ARG]], 
align 16
+// CHECK-DXIL:                call void @{{.*}}main0{{.*}}(ptr %[[#ARG]])
+// CHECK-SPIRV:               call spir_func void @{{.*}}main0{{.*}}(ptr 
%[[#ARG]])
+[shader("pixel")]
+void main0(S0 p : A) {
+  float tmp = p.position[0] + p.position[1] + p.color;
+}
+
+// CHECK-SPIRV: ![[#MD_0]] = !{![[#MD_1:]]}
+// CHECK-SPIRV: ![[#MD_1]] = !{i32 30, i32 0}
+// CHECK-SPIRV: ![[#MD_2]] = !{![[#MD_3:]]}
+// CHECK-SPIRV: ![[#MD_3]] = !{i32 30, i32 2}

diff  --git a/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl
new file mode 100644
index 0000000000000..733cf3a1a7b9d
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.struct.hlsl
@@ -0,0 +1,77 @@
+// 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
+
+struct S0 {
+  uint Idx : SV_DispatchThreadID;
+};
+
+// CHECK:       define void @main0()
+// CHECK-DXIL:    %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK-SPIRV:   %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(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 {
+  uint2  a : SV_DispatchThreadID;
+  uint2  b : SV_GroupThreadID;
+};
+
+// CHECK:                     define void @main1()
+// CHECK-DXIL:    %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK-SPIRV:   %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
+// CHECK:        %[[#AX_:]] = insertelement <2 x i32> poison, i32 %[[#ID]], 
i64 0
+// CHECK-DXIL:    %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 1)
+// CHECK-SPIRV:   %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 1)
+// CHECK:        %[[#AXY:]] = insertelement <2 x i32> %[[#AX_]], i32 %[[#ID]], 
i64 1
+// CHECK:       %[[#S1A_:]] = insertvalue %struct.S1 poison, <2 x i32> 
%[[#AXY]], 0
+// CHECK-DXIL:  %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 
0)
+// CHECK-SPIRV: %[[#ID_X:]] = call i32 
@llvm.[[TARGET]].thread.id.in.group.i32(i32 0)
+// CHECK:      %[[#ID_X_:]] = insertelement <2 x i32> poison, i32 %[[#ID_X]], 
i64 0
+// CHECK-DXIL:  %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 
1)
+// CHECK-SPIRV: %[[#ID_Y:]] = call i32 
@llvm.[[TARGET]].thread.id.in.group.i32(i32 1)
+// CHECK:      %[[#ID_XY:]] = insertelement <2 x i32> %[[#ID_X_]], i32 
%[[#ID_Y]], i64 1
+// CHECK:       %[[#S1AB:]] = insertvalue %struct.S1 %[[#S1A_]], <2 x i32> 
%[[#ID_XYZ:]], 1
+// CHECK:        %[[#ARG:]] = alloca %struct.S1, align 8
+// CHECK:                     store %struct.S1 %[[#S1AB]], ptr %[[#ARG]], 
align 8
+// 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 {
+  uint2 b : SV_GroupThreadID;
+};
+
+struct S2 {
+  uint  a : SV_DispatchThreadID;
+  S2C child;
+};
+
+// CHECK:                     define void @main2()
+// CHECK-DXIL:    %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK-SPIRV:   %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
+// CHECK:       %[[#S2A_:]] = insertvalue %struct.S2 poison, i32 %[[#ID:]], 0
+
+// CHECK-DXIL:  %[[#ID_X:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 
0)
+// CHECK-SPIRV: %[[#ID_X:]] = call i32 
@llvm.[[TARGET]].thread.id.in.group.i32(i32 0)
+// CHECK:      %[[#ID_X_:]] = insertelement <2 x i32> poison, i32 %[[#ID_X]], 
i64 0
+// CHECK-DXIL:  %[[#ID_Y:]] = call i32 @llvm.[[TARGET]].thread.id.in.group(i32 
1)
+// CHECK-SPIRV: %[[#ID_Y:]] = call i32 
@llvm.[[TARGET]].thread.id.in.group.i32(i32 1)
+// CHECK:      %[[#ID_XY:]] = insertelement <2 x i32> %[[#ID_X_]], i32 
%[[#ID_Y]], i64 1
+// CHECK:        %[[#S2C:]] = insertvalue %struct.S2C poison, <2 x i32> 
%[[#ID_XY:]], 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/ParserHLSL/semantic_parsing.hlsl 
b/clang/test/ParserHLSL/semantic_parsing.hlsl
index 726deadb7c44c..bff7bd03189e7 100644
--- a/clang/test/ParserHLSL/semantic_parsing.hlsl
+++ b/clang/test/ParserHLSL/semantic_parsing.hlsl
@@ -12,30 +12,33 @@ void Pony(int GI : SV_IWantAPony) { }
 // expected-note@+1 {{to match this '('}}
 void SuperPony(int GI : 0) { }
 
-// expected-error@+1 {{unknown HLSL semantic '_'}}
+// '_' is a valid CPP identifier.
 void MegaPony(int GI : _) { }
 
-// expected-error@+1 {{unknown HLSL semantic 'A0A'}}
+void GarguantuanPony(int GI : _1) { }
+
 void CoolPony(int GI : A0A0) { }
 
-// expected-error@+1 {{unknown HLSL semantic 'A_'}}
 void NicePony(int GI : A_0) { }
 
-// expected-error@+1 {{unknown HLSL semantic 'A'}}
 void CutePony(int GI : A00) { }
 
-// expected-error@+3 {{unknown HLSL semantic 'A'}}
 // expected-error@+2 {{expected ')'}}
 // expected-note@+1 {{to match this '('}}
 void DoublePony(int GI : A00 B) { }
 
-// expected-error@+1 {{unknown HLSL semantic 'é'}}
-void BigPony(int GI : é) { }
+// Unicode can be used:
+// https://timsong-cpp.github.io/cppwp/n3337/charname.allowed
+void FrenchPony(int GI : garçon_de_café) { }
+void UnicodePony(int GI : ℮) { }
+
+// Since P1949 seems Emojis are not allowed, even if in the range
+// mentioned in N3337.
+// https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1949r7.html
 
 // expected-error@+2 {{unexpected character <U+1F60A>}}
 // expected-error@+1 {{expected HLSL Semantic identifier}}
 void UTFPony(int GI : 😊) { }
 
-// expected-error@+2 {{character <U+1F60A> not allowed in an identifier}}
-// expected-error@+1 {{unknown HLSL semantic 'PonyWithA😊'}}
+// expected-error@+1 {{character <U+1F60A> not allowed in an identifier}}
 void SmilingPony(int GI : PonyWithA😊) { }

diff  --git a/clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl 
b/clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl
new file mode 100644
index 0000000000000..fdba6f624d289
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantics-invalid.hlsl
@@ -0,0 +1,17 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -x hlsl 
-fsyntax-only -hlsl-entry main -verify %s
+
+typedef float t_f : SEMANTIC; // expected-warning{{'SEMANTIC' attribute only 
applies to parameters, non-static data members, and functions}}
+
+struct semantic_on_struct : SEMANTIC { // expected-error{{expected class name}}
+  float a;
+};
+
+struct s_fields_multiple_semantics {
+  float a : semantic_a : semantic_c; // expected-error{{use of undeclared 
identifier 'semantic_c'}}
+  float b : semantic_b;
+};
+
+[numthreads(1, 1, 1)]
+void main() {
+  float a : SEM_A; // expected-warning{{'SEM_A' attribute only applies to 
parameters, non-static data members, and functions}}
+}

diff  --git a/clang/test/SemaHLSL/Semantics/semantics-valid.hlsl 
b/clang/test/SemaHLSL/Semantics/semantics-valid.hlsl
new file mode 100644
index 0000000000000..1e6bae4fcbca5
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/semantics-valid.hlsl
@@ -0,0 +1,33 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-compute -hlsl-entry CSMain 
-x hlsl  -finclude-default-header  -ast-dump -o - %s | FileCheck %s
+
+struct s_fields {
+  float a : semantic_a;
+  float b : semantic_b;
+// CHECK: |-CXXRecordDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-3]]:8 
struct s_fields definition
+// CHECK: | |-FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:9 a 'float'
+// CHECK: | | `-HLSLUserSemanticAttr 0x{{[0-9a-fA-F]+}} <col:13>
+// CHECK: | `-FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:9 b 'float'
+// CHECK: |   `-HLSLUserSemanticAttr 0x{{[0-9a-fA-F]+}} <col:13>
+};
+
+float fn_foo1(float a : a, float b : b) : sem_ret { return 1.0f; }
+// CHECK:      |-FunctionDecl {{.*}} <{{.*}}> col:7 fn_foo1 'float (float, 
float)'
+// CHECK-NEXT: | |-ParmVarDecl {{.*}} <{{.*}}> col:21 a 'float'
+// CHECK-NEXT: | | `-HLSLUserSemanticAttr {{.*}} <{{.*}}>
+// CHECK-NEXT: | |-ParmVarDecl {{.*}} <{{.*}}> col:34 b 'float'
+// CHECK-NEXT: | | `-HLSLUserSemanticAttr {{.*}} <{{.*}}>
+// CHECK-NEXT: | |-CompoundStmt {{.*}} <{{.*}}>
+// CHECK-NEXT: | | `-ReturnStmt {{.*}} <{{.*}}>
+// CHECK-NEXT: | |   `-FloatingLiteral {{.*}} <{{.*}}> 'float' 1.000000e+00
+// CHECK-NEXT: | `-HLSLUserSemanticAttr {{.*}} <{{.*}}>
+float fn_foo2(float a : a, float b : b) : sem_ret : also_ret { return 1.0f; }
+// CHECK:      `-FunctionDecl {{.*}} <{{.*}}> col:7 fn_foo2 'float (float, 
float)'
+// CHECK-NEXT:   |-ParmVarDecl {{.*}} <{{.*}}> col:21 a 'float'
+// CHECK-NEXT:   | `-HLSLUserSemanticAttr {{.*}} <{{.*}}>
+// CHECK-NEXT:   |-ParmVarDecl {{.*}} <{{.*}}> col:34 b 'float'
+// CHECK-NEXT:   | `-HLSLUserSemanticAttr {{.*}} <{{.*}}>
+// CHECK-NEXT:   |-CompoundStmt {{.*}} <{{.*}}>
+// CHECK-NEXT:   | `-ReturnStmt {{.*}} <{{.*}}>
+// CHECK-NEXT:   |   `-FloatingLiteral {{.*}} <{{.*}}> 'float' 1.000000e+00
+// CHECK-NEXT:   |-HLSLUserSemanticAttr {{.*}} <{{.*}}>
+// CHECK-NEXT:   `-HLSLUserSemanticAttr {{.*}} <{{.*}}>

diff  --git a/llvm/include/llvm/IR/IntrinsicsDirectX.td 
b/llvm/include/llvm/IR/IntrinsicsDirectX.td
index 9924b905aee63..d7db935ee07f1 100644
--- a/llvm/include/llvm/IR/IntrinsicsDirectX.td
+++ b/llvm/include/llvm/IR/IntrinsicsDirectX.td
@@ -176,4 +176,10 @@ def int_dx_firstbitlow : 
DefaultAttrsIntrinsic<[LLVMScalarOrSameVectorWidth<0, l
 
 def int_dx_group_memory_barrier_with_group_sync
     : DefaultAttrsIntrinsic<[], [], [IntrConvergent]>;
+
+def int_dx_load_input
+    : DefaultAttrsIntrinsic<[llvm_any_ty],
+                            [llvm_i32_ty, llvm_i32_ty, llvm_i32_ty, llvm_i8_ty,
+                             llvm_i32_ty],
+                            [IntrConvergent]>;
 }


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to