-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[HLSL] Add support for user semantics #153424
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
dfe8cc3 to
9dabc27
Compare
c94788e to
6cae241
Compare
|
@llvm/pr-subscribers-clang @llvm/pr-subscribers-clang-codegen Author: Nathan Gauër (Keenuts) ChangesThis commit depends on #152537 and #153224 This commit adds support for HLSL input semantics. User semantics are 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. Patch is 26.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/153424.diff 14 Files Affected:
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 13f0d59de93fb..1e30fd5002452 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13181,6 +13181,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..1c268dcd29768 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>
@@ -244,6 +246,8 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ llvm::DenseMap<FunctionDecl *, llvm::StringSet<>> ActiveInputSemantics;
+
struct SemanticInfo {
HLSLSemanticAttr *Semantic;
std::optional<uint32_t> Index;
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 945f9e2451bc1..895c6f13d475f 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 2a485da06908d..d056a514ad4ac 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -774,6 +774,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);
@@ -817,6 +821,33 @@ 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 It = ActiveInputSemantics.find(FD);
+ if (It == ActiveInputSemantics.end()) {
+ llvm::StringSet<> Set({VariableName.str()});
+ auto Item = std::make_pair(FD, std::move(Set));
+ ActiveInputSemantics.insert(std::move(Item));
+ continue;
+ }
+
+ auto [_, Inserted] = ActiveInputSemantics[FD].insert(VariableName.str());
+ if (!Inserted) {
+ Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
+ << VariableName.str();
+ return false;
+ }
+ }
+
return true;
}
@@ -946,6 +977,8 @@ void SemaHLSL::checkSemanticAnnotation(FunctionDecl *EntryPoint,
return;
DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Pixel});
break;
+ case attr::HLSLUserSemantic:
+ return;
default:
llvm_unreachable("Unknown SemanticAttr");
}
@@ -1765,7 +1798,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..220e56b69febc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl
@@ -0,0 +1,32 @@
+// 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_1]] = !{i32 30, i32 0}
+// CHECK-SPIRV-DAG: ![[#METADATA_2]] = !{![[#METADATA_3:]]}
+// CHECK-SPIRV-DAG: ![[#METADATA_3]] = !{i32 30, i32 1}
+// CHECK-SPIRV-DAG: ![[#METADATA_4]] = !{![[#METADATA_5:]]}
+// CHECK-SPIRV-DAG: ![[#METADATA_5]] = !{i32 30, i32 2}
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 ...
[truncated]
|
|
@llvm/pr-subscribers-backend-directx Author: Nathan Gauër (Keenuts) ChangesThis commit depends on #152537 and #153224 This commit adds support for HLSL input semantics. User semantics are 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. Patch is 26.33 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/153424.diff 14 Files Affected:
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 13f0d59de93fb..1e30fd5002452 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -13181,6 +13181,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..1c268dcd29768 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>
@@ -244,6 +246,8 @@ class SemaHLSL : public SemaBase {
IdentifierInfo *RootSigOverrideIdent = nullptr;
+ llvm::DenseMap<FunctionDecl *, llvm::StringSet<>> ActiveInputSemantics;
+
struct SemanticInfo {
HLSLSemanticAttr *Semantic;
std::optional<uint32_t> Index;
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 945f9e2451bc1..895c6f13d475f 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 2a485da06908d..d056a514ad4ac 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -774,6 +774,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);
@@ -817,6 +821,33 @@ 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 It = ActiveInputSemantics.find(FD);
+ if (It == ActiveInputSemantics.end()) {
+ llvm::StringSet<> Set({VariableName.str()});
+ auto Item = std::make_pair(FD, std::move(Set));
+ ActiveInputSemantics.insert(std::move(Item));
+ continue;
+ }
+
+ auto [_, Inserted] = ActiveInputSemantics[FD].insert(VariableName.str());
+ if (!Inserted) {
+ Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap)
+ << VariableName.str();
+ return false;
+ }
+ }
+
return true;
}
@@ -946,6 +977,8 @@ void SemaHLSL::checkSemanticAnnotation(FunctionDecl *EntryPoint,
return;
DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Pixel});
break;
+ case attr::HLSLUserSemantic:
+ return;
default:
llvm_unreachable("Unknown SemanticAttr");
}
@@ -1765,7 +1798,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..220e56b69febc
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/semantic.arbitrary.hlsl
@@ -0,0 +1,32 @@
+// 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_1]] = !{i32 30, i32 0}
+// CHECK-SPIRV-DAG: ![[#METADATA_2]] = !{![[#METADATA_3:]]}
+// CHECK-SPIRV-DAG: ![[#METADATA_3]] = !{i32 30, i32 1}
+// CHECK-SPIRV-DAG: ![[#METADATA_4]] = !{![[#METADATA_5:]]}
+// CHECK-SPIRV-DAG: ![[#METADATA_5]] = !{i32 30, i32 2}
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 ...
[truncated]
|
s-perron
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You say this depends on #153224, but that PR was closed without merging. You might need to update.
| 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be trying to find an existing global variable? This assumes that can be just one load, which may be true for now, but I'm not sure it will always be true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a good point, the thing is I don't think it is well defined yet what happens if:
- input semantics are duplicated
- semantic is duplicated using HLSL semantics and
vk::builtin
Ex: DXC fails to build SPIR-V if I have the following shader:
RWStructuredBuffer<uint> output;
void main(uint a : SV_GroupThreadID, uint b : SV_GroupThreadID) {
output[0] = a + b;
}I don't think we should allow having an I/O semantic also be reachable with a vk::builtin global, nor we should have duplicated I/O semantics.
s-perron
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
This commit adds initial support for user semantics. Those are generic cpp idenfier which translate differently than system semantics. Supporting user semantics will allows testing another side of semantics: shadowing and implicit indices. When a semantic is applied on a parent element (parameter or type definition), the semantics on its children declarations are ignored. Also, the semantic index is used to determine the index of the first element, but increases with each array element or struct field. See https://github.com/llvm/wg-hlsl/blob/main/proposals/0031-semantics.md
Each function is independent, no need to keep this across calls.
6c39d06 to
b95c89a
Compare
|
Moving forward with this PR as I got an approval and no clear objection. |
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.