[clang] [HLSL][SPIR-V] Handle SV_Postion builtin in PS (PR #141759)
Nathan Gauër via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 10 06:13:53 PDT 2025
https://github.com/Keenuts updated https://github.com/llvm/llvm-project/pull/141759
>From 84fd2cd7f42a3087b3807628d020189bf8b3cc17 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Mon, 5 May 2025 18:01:17 +0200
Subject: [PATCH 1/6] [HLSL][SPIR-V] Handle SV_Postion builtin in PS
This commit is using the same mechanism as vk::ext_builtin_input to
implement the SV_Position semantic input.
The HLSL signature is not yet ready for DXIL, hence this commit only
implements the SPIR-V side.
This is incomplete as it doesn't allow the semantic on hull/domain and
other shaders, but it's a first step to validate the overall input/output
semantic logic.
---
clang/include/clang/Basic/Attr.td | 7 ++++
clang/include/clang/Basic/AttrDocs.td | 14 +++++++
clang/include/clang/Sema/SemaHLSL.h | 2 +
clang/lib/CodeGen/CGHLSLRuntime.cpp | 41 ++++++++++++++-----
clang/lib/Parse/ParseHLSL.cpp | 1 +
clang/lib/Sema/SemaDeclAttr.cpp | 3 ++
clang/lib/Sema/SemaHLSL.cpp | 27 ++++++++++++
.../CodeGenHLSL/semantics/SV_Position.ps.hlsl | 10 +++++
.../test/SemaHLSL/Semantics/position.ps.hlsl | 7 ++++
.../test/SemaHLSL/Semantics/position.vs.hlsl | 6 +++
10 files changed, 108 insertions(+), 10 deletions(-)
create mode 100644 clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
create mode 100644 clang/test/SemaHLSL/Semantics/position.ps.hlsl
create mode 100644 clang/test/SemaHLSL/Semantics/position.vs.hlsl
diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td
index f889e41c8699f..a5633f7dd37da 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -4879,6 +4879,13 @@ def HLSLResourceBinding: InheritableAttr {
}];
}
+def HLSLSV_Position : HLSLAnnotationAttr {
+ let Spellings = [HLSLAnnotation<"sv_position">];
+ let Subjects = SubjectList<[ParmVar, Field]>;
+ let LangOpts = [HLSL];
+ let Documentation = [HLSLSV_PositionDocs];
+}
+
def HLSLPackOffset: HLSLAnnotationAttr {
let Spellings = [HLSLAnnotation<"packoffset">];
let LangOpts = [HLSL];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index ea3c43f38d9fe..d9747bd52536c 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8529,6 +8529,20 @@ 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 input parameter.
+This attribute is supported as input in pixel, hull, domain and mesh shaders.
+This attribute is supported as 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 = [{
diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h
index 66d09f49680be..ba5f06f93dc30 100644
--- a/clang/include/clang/Sema/SemaHLSL.h
+++ b/clang/include/clang/Sema/SemaHLSL.h
@@ -125,6 +125,7 @@ class SemaHLSL : public SemaBase {
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 handlePackOffsetAttr(Decl *D, const ParsedAttr &AL);
void handleShaderAttr(Decl *D, const ParsedAttr &AL);
void handleResourceBindingAttr(Decl *D, const ParsedAttr &AL);
@@ -146,6 +147,7 @@ class SemaHLSL : public SemaBase {
// Diagnose whether the input ID is uint/unit2/uint3 type.
bool diagnoseInputIDType(QualType T, const ParsedAttr &AL);
+ bool diagnosePositionType(QualType T, const ParsedAttr &AL);
bool CanPerformScalarCast(QualType SrcTy, QualType DestTy);
bool ContainsBitField(QualType BaseTy);
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 6d267e6164845..9115492d1b1dd 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -384,6 +384,28 @@ static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) {
return B.CreateCall(F, {B.getInt32(0)});
}
+static void addBuiltinDecoration(llvm::GlobalVariable *GV, unsigned BuiltIn) {
+ LLVMContext &Ctx = GV->getContext();
+ IRBuilder<> B(GV->getContext());
+ MDNode *Operands =
+ MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(11)),
+ ConstantAsMetadata::get(B.getInt32(BuiltIn))});
+ 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) {
+ auto *GV = new llvm::GlobalVariable(
+ M, Ty, /* isConstant= */ true, llvm::GlobalValue::ExternalLinkage,
+ /* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr,
+ llvm::GlobalVariable::GeneralDynamicTLSModel,
+ /* AddressSpace */ 7, /* isExternallyInitialized= */ true);
+ addBuiltinDecoration(GV, BuiltInID);
+ return B.CreateLoad(Ty, GV);
+}
+
llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B,
const ParmVarDecl &D,
llvm::Type *Ty) {
@@ -407,6 +429,13 @@ llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B,
llvm::Function *GroupIDIntrinsic = CGM.getIntrinsic(getGroupIdIntrinsic());
return buildVectorInput(B, GroupIDIntrinsic, Ty);
}
+ if (D.hasAttr<HLSLSV_PositionAttr>()) {
+ if (getArch() == llvm::Triple::spirv)
+ return createSPIRVBuiltinLoad(B, CGM.getModule(), Ty, "sv_position",
+ /* BuiltIn::Position */ 0);
+ llvm_unreachable(
+ "Shader signature for semantics not implemented for DXIL.");
+ }
assert(false && "Unhandled parameter attribute");
return nullptr;
}
@@ -626,16 +655,8 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
llvm::GlobalVariable *GV) {
- if (auto Attr = VD->getAttr<HLSLVkExtBuiltinInputAttr>()) {
- LLVMContext &Ctx = GV->getContext();
- IRBuilder<> B(GV->getContext());
- MDNode *Operands = MDNode::get(
- Ctx, {ConstantAsMetadata::get(
- B.getInt32(/* Spirv::Decoration::BuiltIn */ 11)),
- ConstantAsMetadata::get(B.getInt32(Attr->getBuiltIn()))});
- MDNode *Decoration = MDNode::get(Ctx, {Operands});
- GV->addMetadata("spirv.Decorations", *Decoration);
- }
+ if (auto Attr = VD->getAttr<HLSLVkExtBuiltinInputAttr>())
+ addBuiltinDecoration(GV, Attr->getBuiltIn());
}
llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp
index 5569605c287b1..53d46465e3362 100644
--- a/clang/lib/Parse/ParseHLSL.cpp
+++ b/clang/lib/Parse/ParseHLSL.cpp
@@ -289,6 +289,7 @@ void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
case ParsedAttr::AT_HLSLSV_GroupID:
case ParsedAttr::AT_HLSLSV_GroupIndex:
case ParsedAttr::AT_HLSLSV_DispatchThreadID:
+ case ParsedAttr::AT_HLSLSV_Position:
break;
default:
llvm_unreachable("invalid HLSL Annotation");
diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index da0e3265767d8..3cc641d855125 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -7557,6 +7557,9 @@ 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;
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index 9065cc5a1d4a5..bcc7f7b40715e 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -764,6 +764,13 @@ void SemaHLSL::CheckSemanticAnnotation(
return;
DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Compute});
break;
+ case attr::HLSLSV_Position:
+ // TODO: 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;
default:
llvm_unreachable("Unknown HLSLAnnotationAttr");
}
@@ -1147,6 +1154,26 @@ void SemaHLSL::handleSV_DispatchThreadIDAttr(Decl *D, const ParsedAttr &AL) {
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";
+ return false;
+ }
+
+ return true;
+}
+
+void SemaHLSL::handleSV_PositionAttr(Decl *D, const ParsedAttr &AL) {
+ auto *VD = cast<ValueDecl>(D);
+ if (!diagnosePositionType(VD->getType(), AL))
+ return;
+
+ 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))
diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
new file mode 100644
index 0000000000000..58b91fc9264dd
--- /dev/null
+++ b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl
@@ -0,0 +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 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
+ // CHECK: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#P]])
+ return p;
+}
diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
new file mode 100644
index 0000000000000..32bc5f55b2abd
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-pixel -x hlsl -finclude-default-header -o - %s -ast-dump | FileCheck %s
+
+float4 main(float4 a : SV_Position) {
+// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:8 main 'float4 (float4)'
+// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:20 a 'float4':'vector<float, 4>'
+// CHECK-NEXT: HLSLSV_PositionAttr 0x{{[0-9a-fA-F]+}} <{{.*}}>
+}
diff --git a/clang/test/SemaHLSL/Semantics/position.vs.hlsl b/clang/test/SemaHLSL/Semantics/position.vs.hlsl
new file mode 100644
index 0000000000000..19f781fa3757c
--- /dev/null
+++ b/clang/test/SemaHLSL/Semantics/position.vs.hlsl
@@ -0,0 +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}}
+float4 main(float4 a : SV_Position) {
+ return a;
+}
>From cbf88cecc08305738fd362f3c5c3aa88e5100298 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 10 Jun 2025 14:42:18 +0200
Subject: [PATCH 2/6] typo fix
---
clang/include/clang/Basic/AttrDocs.td | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index d9747bd52536c..047f51ffa59ed 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -8535,9 +8535,9 @@ def HLSLSV_PositionDocs : Documentation {
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 input parameter.
-This attribute is supported as input in pixel, hull, domain and mesh shaders.
-This attribute is supported as output in vertex, geometry and domain shaders.
+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
}];
>From ca842d2102a90bb7294afeda6245ab5d4dcb2afd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 10 Jun 2025 14:42:25 +0200
Subject: [PATCH 3/6] add comment
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 9115492d1b1dd..64de9146a444b 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -387,9 +387,10 @@ static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) {
static void addBuiltinDecoration(llvm::GlobalVariable *GV, unsigned BuiltIn) {
LLVMContext &Ctx = GV->getContext();
IRBuilder<> B(GV->getContext());
- MDNode *Operands =
- MDNode::get(Ctx, {ConstantAsMetadata::get(B.getInt32(11)),
- ConstantAsMetadata::get(B.getInt32(BuiltIn))});
+ MDNode *Operands = MDNode::get(
+ Ctx,
+ {ConstantAsMetadata::get(B.getInt32(/* Spirv::Decoration::BuiltIn */ 11)),
+ ConstantAsMetadata::get(B.getInt32(BuiltIn))});
MDNode *Decoration = MDNode::get(Ctx, {Operands});
GV->addMetadata("spirv.Decorations", *Decoration);
}
>From 3598e6793dc2f0f8ebd4383061f6326ce8d9c5ab Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 10 Jun 2025 14:44:00 +0200
Subject: [PATCH 4/6] add SPIRV prefix to function name
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 64de9146a444b..4550cdaff781d 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -384,7 +384,8 @@ static Value *buildVectorInput(IRBuilder<> &B, Function *F, llvm::Type *Ty) {
return B.CreateCall(F, {B.getInt32(0)});
}
-static void addBuiltinDecoration(llvm::GlobalVariable *GV, unsigned BuiltIn) {
+static void addSPIRVBuiltinDecoration(llvm::GlobalVariable *GV,
+ unsigned BuiltIn) {
LLVMContext &Ctx = GV->getContext();
IRBuilder<> B(GV->getContext());
MDNode *Operands = MDNode::get(
@@ -403,7 +404,7 @@ static llvm::Value *createSPIRVBuiltinLoad(IRBuilder<> &B, llvm::Module &M,
/* Initializer= */ nullptr, Name, /* insertBefore= */ nullptr,
llvm::GlobalVariable::GeneralDynamicTLSModel,
/* AddressSpace */ 7, /* isExternallyInitialized= */ true);
- addBuiltinDecoration(GV, BuiltInID);
+ addSPIRVBuiltinDecoration(GV, BuiltInID);
return B.CreateLoad(Ty, GV);
}
@@ -657,7 +658,7 @@ void CGHLSLRuntime::initializeBufferFromBinding(const HLSLBufferDecl *BufDecl,
void CGHLSLRuntime::handleGlobalVarDefinition(const VarDecl *VD,
llvm::GlobalVariable *GV) {
if (auto Attr = VD->getAttr<HLSLVkExtBuiltinInputAttr>())
- addBuiltinDecoration(GV, Attr->getBuiltIn());
+ addSPIRVBuiltinDecoration(GV, Attr->getBuiltIn());
}
llvm::Instruction *CGHLSLRuntime::getConvergenceToken(BasicBlock &BB) {
>From 6aaed035264664af180aad2420a6bf1cc0d198ce Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 10 Jun 2025 14:45:25 +0200
Subject: [PATCH 5/6] change unreachable message
---
clang/lib/CodeGen/CGHLSLRuntime.cpp | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp
index 4550cdaff781d..720dac8383c05 100644
--- a/clang/lib/CodeGen/CGHLSLRuntime.cpp
+++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp
@@ -435,8 +435,7 @@ llvm::Value *CGHLSLRuntime::emitInputSemantic(IRBuilder<> &B,
if (getArch() == llvm::Triple::spirv)
return createSPIRVBuiltinLoad(B, CGM.getModule(), Ty, "sv_position",
/* BuiltIn::Position */ 0);
- llvm_unreachable(
- "Shader signature for semantics not implemented for DXIL.");
+ llvm_unreachable("SV_Position semantic not implemented for this target.");
}
assert(false && "Unhandled parameter attribute");
return nullptr;
>From ce43937d80a6eb4fb863b0ee9c2f9aada73d9812 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= <brioche at google.com>
Date: Tue, 10 Jun 2025 14:50:37 +0200
Subject: [PATCH 6/6] link TODO to issue #
---
clang/lib/Sema/SemaHLSL.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp
index bcc7f7b40715e..aa5c83b0f744f 100644
--- a/clang/lib/Sema/SemaHLSL.cpp
+++ b/clang/lib/Sema/SemaHLSL.cpp
@@ -765,8 +765,8 @@ void SemaHLSL::CheckSemanticAnnotation(
DiagnoseAttrStageMismatch(AnnotationAttr, ST, {llvm::Triple::Compute});
break;
case attr::HLSLSV_Position:
- // TODO: allow use on other shader types & output once the overall semantic
- // logic is implemented.
+ // 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});
More information about the cfe-commits
mailing list