https://github.com/Keenuts updated 
https://github.com/llvm/llvm-project/pull/153224

From f3b509a3d1292fedab3b1dd9b937dd67d30df2f3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brio...@google.com>
Date: Tue, 12 Aug 2025 18:06:17 +0200
Subject: [PATCH] [HLSL] Add support for input semantics in structs

This commit adds the support for semantics annotations on structs, but
only for inputs. Due to the current semantics implemented, we cannot
test much more than nesting/shadowing.

Once user semantics are implemented, we'll be able to test arrays in
structs and more complex cases.

As-is, this commit has one weakness vs DXC: semantics type validation is
not looking at the inner-most type, but the outermost type:

```hlsl
struct Inner {
  uint tid;
};

Inner inner : SV_GroupID
```

This sample would fail today because `SV_GroupID` require the type to be
an integer. This works in DXC as the inner type is a integer.
Because GroupIndex is not correctly validated, I uses this semantic to
test the inheritance/shadowing. But this will need to be fixed in a
later commit.

Requires #152537
---
 clang/lib/CodeGen/CGHLSLRuntime.cpp           | 63 ++++++++++++++++++-
 clang/lib/CodeGen/CGHLSLRuntime.h             |  4 ++
 .../semantics/semantic-struct-1.hlsl          | 23 +++++++
 .../semantics/semantic-struct-2.hlsl          | 25 ++++++++
 .../semantic-struct-nested-inherit.hlsl       | 30 +++++++++
 .../semantic-struct-nested-shadow.hlsl        | 30 +++++++++
 .../semantics/semantic-struct-nested.hlsl     | 30 +++++++++
 7 files changed, 202 insertions(+), 3 deletions(-)
 create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl
 create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl
 create mode 100644 
clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl
 create mode 100644 
clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl
 create mode 100644 clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl

diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp 
b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 60ad3eb75afea..f17e9b1d908e9 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -619,11 +619,51 @@ CGHLSLRuntime::handleScalarSemanticLoad(IRBuilder<> &B, 
llvm::Type *Type,
   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;
+  }
+
+  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;
+    }
+    if (ActiveSemantic.Semantic)
+      ActiveSemantic = Info;
+
+    Aggregate = B.CreateInsertValue(Aggregate, ChildValue, I);
+    ++FieldDecl;
+  }
+
+  return Aggregate;
+}
+
 llvm::Value *
 CGHLSLRuntime::handleSemanticLoad(IRBuilder<> &B, llvm::Type *Type,
                                   const clang::DeclaratorDecl *Decl,
                                   SemanticInfo &ActiveSemantic) {
-  assert(!Type->isStructTy());
+  if (Type->isStructTy())
+    return handleStructSemanticLoad(B, Type, Decl, ActiveSemantic);
   return handleScalarSemanticLoad(B, Type, Decl, ActiveSemantic);
 }
 
@@ -671,8 +711,25 @@ void CGHLSLRuntime::emitEntryFunction(const FunctionDecl 
*FD,
     }
 
     const ParmVarDecl *PD = FD->getParamDecl(Param.getArgNo() - SRetOffset);
-    SemanticInfo ActiveSemantic = {nullptr, 0};
-    Args.push_back(handleSemanticLoad(B, Param.getType(), PD, ActiveSemantic));
+    llvm::Value *SemanticValue = nullptr;
+    if (HLSLParamModifierAttr *MA = PD->getAttr<HLSLParamModifierAttr>()) {
+      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;
+      }
+    }
+
+    assert(SemanticValue);
+    Args.push_back(SemanticValue);
   }
 
   CallInst *CI = B.CreateCall(FunctionCallee(Fn), Args, OB);
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h 
b/clang/lib/CodeGen/CGHLSLRuntime.h
index 25d4c65426b0d..5d28994a5277b 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.h
+++ b/clang/lib/CodeGen/CGHLSLRuntime.h
@@ -156,6 +156,10 @@ class CGHLSLRuntime {
                                         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);
diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl
new file mode 100644
index 0000000000000..ddd0baed41f37
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-1.hlsl
@@ -0,0 +1,23 @@
+// 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 Input {
+  uint Idx : SV_DispatchThreadID;
+
+};
+
+// Make sure SV_DispatchThreadID translated into dx.thread.id.
+
+// CHECK:       define void @foo()
+// 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.Input poison, i32 %[[#ID]], 0
+// CHECK:      %[[#VAR:]] = alloca %struct.Input, align 8
+// CHECK:                   store %struct.Input %[[#TMP]], ptr %[[#VAR]], 
align 4
+// CHECK-DXIL:              call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+// CHECK-SPIRV:             call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void foo(Input input) {}
+
diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl
new file mode 100644
index 0000000000000..0d9c91e746454
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-2.hlsl
@@ -0,0 +1,25 @@
+// 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 Input {
+  uint Idx : SV_DispatchThreadID;
+  uint Gid : SV_GroupID;
+};
+
+// Make sure SV_DispatchThreadID translated into dx.thread.id.
+
+// CHECK:       define void @foo()
+// CHECK-DXIL:  %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
+// CHECK:     %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
+// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.[[TARGET]].group.id(i32 0)
+// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.[[TARGET]].group.id.i32(i32 0)
+// CHECK:     %[[#TMP2:]] = insertvalue %struct.Input %[[#TMP1]], i32 
%[[#GID]], 1
+// CHECK:      %[[#VAR:]] = alloca %struct.Input, align 8
+// CHECK:                   store %struct.Input %[[#TMP2]], ptr %[[#VAR]], 
align 4
+// CHECK-DXIL:              call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+// CHECK-SPIRV:             call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void foo(Input input) {}
diff --git 
a/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl
new file mode 100644
index 0000000000000..f4c4d86933ca1
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-inherit.hlsl
@@ -0,0 +1,30 @@
+// 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 Inner {
+  uint Gid;
+};
+
+struct Input {
+  uint Idx : SV_DispatchThreadID;
+  Inner inner : SV_GroupIndex;
+};
+
+// Make sure SV_DispatchThreadID translated into dx.thread.id.
+
+// CHECK:       define void @foo()
+// CHECK-DXIL:  %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
+// CHECK:     %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
+// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.dx.flattened.thread.id.in.group()
+// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.spv.flattened.thread.id.in.group()
+// CHECK:     %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0
+// CHECK:     %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], 
%struct.Inner %[[#TMP2]], 1
+// CHECK:      %[[#VAR:]] = alloca %struct.Input, align 8
+// CHECK:                   store %struct.Input %[[#TMP3]], ptr %[[#VAR]], 
align 4
+// CHECK-DXIL:              call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+// CHECK-SPIRV:             call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void foo(Input input) {}
diff --git 
a/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl
new file mode 100644
index 0000000000000..e1344dd87a6ed
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested-shadow.hlsl
@@ -0,0 +1,30 @@
+// 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 Inner {
+  uint Gid : SV_GroupID;
+};
+
+struct Input {
+  uint Idx : SV_DispatchThreadID;
+  Inner inner : SV_GroupIndex;
+};
+
+// Make sure SV_DispatchThreadID translated into dx.thread.id.
+
+// CHECK:       define void @foo()
+// CHECK-DXIL:  %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
+// CHECK:     %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
+// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.dx.flattened.thread.id.in.group()
+// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.spv.flattened.thread.id.in.group()
+// CHECK:     %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0
+// CHECK:     %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], 
%struct.Inner %[[#TMP2]], 1
+// CHECK:      %[[#VAR:]] = alloca %struct.Input, align 8
+// CHECK:                   store %struct.Input %[[#TMP3]], ptr %[[#VAR]], 
align 4
+// CHECK-DXIL:              call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+// CHECK-SPIRV:             call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void foo(Input input) {}
diff --git a/clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl 
b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl
new file mode 100644
index 0000000000000..cd6f9460bc617
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic-struct-nested.hlsl
@@ -0,0 +1,30 @@
+// 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 Inner {
+  uint Gid : SV_GroupID;
+};
+
+struct Input {
+  uint Idx : SV_DispatchThreadID;
+  Inner inner;
+};
+
+// Make sure SV_DispatchThreadID translated into dx.thread.id.
+
+// CHECK:       define void @foo()
+// CHECK-DXIL:  %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id(i32 0)
+// CHECK-SPIRV: %[[#ID:]] = call i32 @llvm.[[TARGET]].thread.id.i32(i32 0)
+// CHECK:     %[[#TMP1:]] = insertvalue %struct.Input poison, i32 %[[#ID]], 0
+// CHECK-DXIL: %[[#GID:]] = call i32 @llvm.[[TARGET]].group.id(i32 0)
+// CHECK-SPIRV:%[[#GID:]] = call i32 @llvm.[[TARGET]].group.id.i32(i32 0)
+// CHECK:     %[[#TMP2:]] = insertvalue %struct.Inner poison, i32 %[[#GID]], 0
+// CHECK:     %[[#TMP3:]] = insertvalue %struct.Input %[[#TMP1]], 
%struct.Inner %[[#TMP2]], 1
+// CHECK:      %[[#VAR:]] = alloca %struct.Input, align 8
+// CHECK:                   store %struct.Input %[[#TMP3]], ptr %[[#VAR]], 
align 4
+// CHECK-DXIL:              call void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+// CHECK-SPIRV:             call spir_func void @{{.*}}foo{{.*}}(ptr %[[#VAR]])
+[shader("compute")]
+[numthreads(8,8,1)]
+void foo(Input input) {}

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to