From ee2d404e49084a0db2600d0d76123a681f0d6940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 19 Nov 2025 16:50:50 +0100 Subject: [PATCH 1/2] [HLSL][SPIR-V] Implements SV_Position for VS/PS I/O Current implementation for SV_Position was very basic to allow implementing/testing some semantics. Now that semantic support is more robust, I can move forward and implement the whole semantic logic. DX part is still a bit placeholder. --- clang/include/clang/Sema/SemaHLSL.h | 11 ++++-- clang/lib/CodeGen/CGHLSLRuntime.cpp | 29 ++++++++++----- clang/lib/Sema/SemaHLSL.cpp | 37 ++++++++++++------- .../CodeGenHLSL/semantics/SV_Position.ps.hlsl | 20 +++++++--- .../CodeGenHLSL/semantics/SV_Position.vs.hlsl | 26 +++++++++++++ .../test/SemaHLSL/Semantics/position.ps.hlsl | 14 ++----- .../test/SemaHLSL/Semantics/position.vs.hlsl | 6 --- .../CodeGen/SPIRV/semantics/position.ps.ll | 32 ++++++++++++++++ .../CodeGen/SPIRV/semantics/position.vs.ll | 31 ++++++++++++++++ 9 files changed, 159 insertions(+), 47 deletions(-) create mode 100644 clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl delete mode 100644 clang/test/SemaHLSL/Semantics/position.vs.hlsl create mode 100644 llvm/test/CodeGen/SPIRV/semantics/position.ps.ll create mode 100644 llvm/test/CodeGen/SPIRV/semantics/position.vs.ll diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 86da323892f98..15edb7e77a22b 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -250,15 +250,20 @@ class SemaHLSL : public SemaBase { const RecordType *RT); void checkSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param, - const HLSLAppliedSemanticAttr *SemanticAttr); + const HLSLAppliedSemanticAttr *SemanticAttr, + bool IsInput); + bool determineActiveSemanticOnScalar(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, SemanticInfo &ActiveSemantic, - llvm::StringSet<> &ActiveInputSemantics); + llvm::StringSet<> &ActiveSemantics, + bool IsInput); + bool determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, SemanticInfo &ActiveSemantic, - llvm::StringSet<> &ActiveInputSemantics); + llvm::StringSet<> &ActiveSemantics, + bool IsInput); void processExplicitBindingsOnDecl(VarDecl *D); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 0371af417bdab..b9bdce521c989 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -730,10 +730,18 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad( } if (SemanticName == "SV_POSITION") { - if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Pixel) - return createSPIRVBuiltinLoad(B, CGM.getModule(), Type, - Semantic->getAttrName()->getName(), - /* BuiltIn::FragCoord */ 15); + if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Pixel) { + if (CGM.getTarget().getTriple().isSPIRV()) + return createSPIRVBuiltinLoad(B, CGM.getModule(), Type, + Semantic->getAttrName()->getName(), + /* BuiltIn::FragCoord */ 15); + if (CGM.getTarget().getTriple().isDXIL()) + return emitDXILUserSemanticLoad(B, Type, Semantic, Index); + } + + if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Vertex) { + return emitUserSemanticLoad(B, Type, Decl, Semantic, Index); + } } llvm_unreachable("non-handled system semantic. FIXME."); @@ -759,11 +767,14 @@ void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source, std::optional Index) { std::string SemanticName = Semantic->getAttrName()->getName().upper(); - if (SemanticName == "SV_POSITION") - createSPIRVBuiltinStore(B, CGM.getModule(), Source, - Semantic->getAttrName()->getName(), - /* BuiltIn::Position */ 0); - else + if (SemanticName == "SV_POSITION") { + if (CGM.getTarget().getTriple().isDXIL()) + emitDXILUserSemanticStore(B, Source, Semantic, Index); + else if (CGM.getTarget().getTriple().isSPIRV()) + createSPIRVBuiltinStore(B, CGM.getModule(), Source, + Semantic->getAttrName()->getName(), + /* BuiltIn::Position */ 0); + } else llvm_unreachable("non-handled system semantic. FIXME."); } diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index e7ee3b1adf941..5beb4e7fe8546 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -771,9 +771,12 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) { } } -bool SemaHLSL::determineActiveSemanticOnScalar( - FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, - SemanticInfo &ActiveSemantic, llvm::StringSet<> &UsedSemantics) { +bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, + DeclaratorDecl *OutputDecl, + DeclaratorDecl *D, + SemanticInfo &ActiveSemantic, + llvm::StringSet<> &UsedSemantics, + bool IsInput) { if (ActiveSemantic.Semantic == nullptr) { ActiveSemantic.Semantic = D->getAttr(); if (ActiveSemantic.Semantic) @@ -792,7 +795,7 @@ bool SemaHLSL::determineActiveSemanticOnScalar( if (!A) return false; - checkSemanticAnnotation(FD, D, A); + checkSemanticAnnotation(FD, D, A, IsInput); OutputDecl->addAttr(A); unsigned Location = ActiveSemantic.Index.value_or(0); @@ -820,7 +823,8 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, SemanticInfo &ActiveSemantic, - llvm::StringSet<> &UsedSemantics) { + llvm::StringSet<> &UsedSemantics, + bool IsInput) { if (ActiveSemantic.Semantic == nullptr) { ActiveSemantic.Semantic = D->getAttr(); if (ActiveSemantic.Semantic) @@ -833,12 +837,13 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, const RecordType *RT = dyn_cast(T); if (!RT) return determineActiveSemanticOnScalar(FD, OutputDecl, D, ActiveSemantic, - UsedSemantics); + UsedSemantics, IsInput); const RecordDecl *RD = RT->getDecl(); for (FieldDecl *Field : RD->fields()) { SemanticInfo Info = ActiveSemantic; - if (!determineActiveSemantic(FD, OutputDecl, Field, Info, UsedSemantics)) { + if (!determineActiveSemantic(FD, OutputDecl, Field, Info, UsedSemantics, + IsInput)) { Diag(Field->getLocation(), diag::note_hlsl_semantic_used_here) << Field; return false; } @@ -920,7 +925,7 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { // FIXME: Verify output semantics in parameters. if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic, - ActiveInputSemantics)) { + ActiveInputSemantics, /* IsInput= */ true)) { Diag(Param->getLocation(), diag::note_previous_decl) << Param; FD->setInvalidDecl(); } @@ -932,12 +937,13 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { if (ActiveSemantic.Semantic) ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex(); if (!FD->getReturnType()->isVoidType()) - determineActiveSemantic(FD, FD, FD, ActiveSemantic, ActiveOutputSemantics); + determineActiveSemantic(FD, FD, FD, ActiveSemantic, ActiveOutputSemantics, + /* IsInput= */ false); } void SemaHLSL::checkSemanticAnnotation( FunctionDecl *EntryPoint, const Decl *Param, - const HLSLAppliedSemanticAttr *SemanticAttr) { + const HLSLAppliedSemanticAttr *SemanticAttr, bool IsInput) { auto *ShaderAttr = EntryPoint->getAttr(); assert(ShaderAttr && "Entry point has no shader attribute"); llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); @@ -961,11 +967,14 @@ void SemaHLSL::checkSemanticAnnotation( } if (SemanticName == "SV_POSITION") { - // TODO(#143523): allow use on other shader types & output once the overall - // semantic logic is implemented. - if (ST == llvm::Triple::Pixel) + // SV_Position can is I/O for vertex shaders. + // For pixel shaders, only valid as input. + // Note: for SPIR-V, not backed by a builtin when used as input in a vertex + // shaders. + if (ST == llvm::Triple::Vertex || (ST == llvm::Triple::Pixel && IsInput)) return; - DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Pixel}); + DiagnoseAttrStageMismatch(SemanticAttr, ST, + {llvm::Triple::Pixel, llvm::Triple::Vertex}); return; } diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl index be30e79438831..b7d2283ea7766 100644 --- a/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl +++ b/clang/test/CodeGenHLSL/semantics/SV_Position.ps.hlsl @@ -1,11 +1,21 @@ -// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-DXIL -// CHECK: @SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0 +// CHECK-SPIRV: @SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]] // CHECK: define void @main() {{.*}} { float4 main(float4 p : SV_Position) : A { - // 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]]) - // CHECK: store <4 x float> %[[#R]], ptr addrspace(8) @A0, align 16 + // CHECK-SPIRV: %[[#P:]] = load <4 x float>, ptr addrspace(7) @SV_Position, align 16 + // CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#P]]) + // CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @A0, align 16 + + // CHECK-DXIL: %SV_Position0 = call <4 x float> @llvm.dx.load.input.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison) + // CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0) + // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]]) return p; } + +// CHECK-SPIRV-DAG: ![[#MD_0]] = !{![[#MD_1:]]} +// CHECK-SPIRV-DAG: ![[#MD_1]] = !{i32 11, i32 15} +// | `-> BuiltIn Position +// `-> SPIR-V decoration 'FragCoord' diff --git a/clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl new file mode 100644 index 0000000000000..0156c0bb816c1 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/SV_Position.vs.hlsl @@ -0,0 +1,26 @@ +// RUN: %clang_cc1 -triple dxil-unknown-shadermodel6.8-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-DXIL %s +// RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck --check-prefix=CHECK-SPIRV %s + +// CHECK-SPIRV: @SV_Position0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]] +// CHECK-SPIRV: @SV_Position = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]] + +// CHECK: define void @main() {{.*}} { +float4 main(float4 p : SV_Position) : SV_Position { + // CHECK-SPIRV: %[[#P:]] = load <4 x float>, ptr addrspace(7) @SV_Position0, align 16 + // CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#P]]) + // CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Position, align 16 + + // CHECK-DXIL: %SV_Position0 = call <4 x float> @llvm.dx.load.input.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison) + // CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0) + // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]]) + return p; +} + +// CHECK-SPIRV-DAG: ![[#MD_0]] = !{![[#MD_1:]]} +// CHECK-SPIRV-DAG: ![[#MD_2]] = !{![[#MD_3:]]} +// CHECK-SPIRV-DAG: ![[#MD_1]] = !{i32 30, i32 0} +// | `-> Location 0 +// `-> SPIR-V decoration 'Location' +// CHECK-SPIRV-DAG: ![[#MD_3]] = !{i32 11, i32 0} +// | `-> BuiltIn Position +// `-> SPIR-V decoration 'BuiltIn' diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl index 2d02384821d90..47d07887911d6 100644 --- a/clang/test/SemaHLSL/Semantics/position.ps.hlsl +++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl @@ -1,13 +1,7 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-pixel -x hlsl -finclude-default-header -o - %s -ast-dump | FileCheck %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -finclude-default-header -x hlsl -verify -o - %s +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s -// FIXME(Keenuts): change output semantic to something valid for pixels shaders -float4 main(float4 a : SV_Position2) : A { -// CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:8 main 'float4 (float4)' -// CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:20 used a 'float4':'vector' -// CHECK-NEXT: HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} "SV_Position" 2 -// CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} "SV_Position" 2 - -// CHECK: HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} "A" 0 -// CHECK: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} "A" 0 +float4 main(float4 a : A) : SV_Position { +// expected-error@-1 {{attribute 'SV_Position' is unsupported in 'pixel' shaders, requires one of the following: pixel, vertex}} return a; } diff --git a/clang/test/SemaHLSL/Semantics/position.vs.hlsl b/clang/test/SemaHLSL/Semantics/position.vs.hlsl deleted file mode 100644 index 9d0ff285ce055..0000000000000 --- a/clang/test/SemaHLSL/Semantics/position.vs.hlsl +++ /dev/null @@ -1,6 +0,0 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-vertex -x hlsl -finclude-default-header -o - %s -verify - -// expected-error@+1 {{attribute 'SV_Position' is unsupported in 'vertex' shaders, requires pixel}} -float4 main(float4 a : SV_Position) : A { - return a; -} diff --git a/llvm/test/CodeGen/SPIRV/semantics/position.ps.ll b/llvm/test/CodeGen/SPIRV/semantics/position.ps.ll new file mode 100644 index 0000000000000..2c02987f73928 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/semantics/position.ps.ll @@ -0,0 +1,32 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-vulkan-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK-DAG: OpDecorate %[[#INPUT:]] BuiltIn FragCoord +; CHECK-DAG: OpDecorate %[[#OUTPUT:]] Location 0 + +; CHECK-DAG: %[[#float:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#v4:]] = OpTypeVector %[[#float]] 4 +; CHECK-DAG: %[[#ptr_i:]] = OpTypePointer Input %[[#v4]] +; CHECK-DAG: %[[#ptr_o:]] = OpTypePointer Output %[[#v4]] + +; CHECK-DAG: %[[#INPUT]] = OpVariable %[[#ptr_i]] Input +; CHECK-DAG: %[[#OUTPUT]] = OpVariable %[[#ptr_o]] Output + +@SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0 +@A0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations !2 + +define void @main() #1 { +entry: + %0 = load <4 x float>, ptr addrspace(7) @SV_Position, align 16 + store <4 x float> %0, ptr addrspace(8) @A0, align 16 + ret void + +; CHECK: %[[#TMP:]] = OpLoad %[[#v4]] %[[#INPUT]] Aligned 16 +; CHECK: OpStore %[[#OUTPUT]] %[[#TMP]] Aligned 16 +} + +!0 = !{!1} +!1 = !{i32 11, i32 15} +!2 = !{!3} +!3 = !{i32 30, i32 0} + diff --git a/llvm/test/CodeGen/SPIRV/semantics/position.vs.ll b/llvm/test/CodeGen/SPIRV/semantics/position.vs.ll new file mode 100644 index 0000000000000..73165f3719a97 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/semantics/position.vs.ll @@ -0,0 +1,31 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-vulkan-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK-DAG: OpDecorate %[[#INPUT:]] Location 0 +; CHECK-DAG: OpDecorate %[[#OUTPUT:]] BuiltIn Position + +; CHECK-DAG: %[[#float:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#v4:]] = OpTypeVector %[[#float]] 4 +; CHECK-DAG: %[[#ptr_i:]] = OpTypePointer Input %[[#v4]] +; CHECK-DAG: %[[#ptr_o:]] = OpTypePointer Output %[[#v4]] + +; CHECK-DAG: %[[#INPUT]] = OpVariable %[[#ptr_i]] Input +; CHECK-DAG: %[[#OUTPUT]] = OpVariable %[[#ptr_o]] Output + +@SV_Position0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0 +@SV_Position = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations !2 + +define void @main() #1 { +entry: + %0 = load <4 x float>, ptr addrspace(7) @SV_Position0, align 16 + store <4 x float> %0, ptr addrspace(8) @SV_Position, align 16 + ret void + +; CHECK: %[[#TMP:]] = OpLoad %[[#v4]] %[[#INPUT]] Aligned 16 +; CHECK: OpStore %[[#OUTPUT]] %[[#TMP]] Aligned 16 +} + +!0 = !{!1} +!1 = !{i32 30, i32 0} +!2 = !{!3} +!3 = !{i32 11, i32 0} From 028817092af5b3485a7d9aabb4bd17f4442d2eeb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 19 Nov 2025 16:51:00 +0100 Subject: [PATCH 2/2] [HLSL][SPIR-V] Add support for SV_Target semantic This PR adds the support for the SV_Target semantic and improved the diagnostics when the stage is correct, but the direction is disallowed. --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/SemaHLSL.h | 22 +++++- clang/lib/CodeGen/CGHLSLRuntime.cpp | 2 + clang/lib/Sema/SemaHLSL.cpp | 67 +++++++++++++++---- .../CodeGenHLSL/semantics/SV_Target.ps.hlsl | 19 ++++++ .../test/SemaHLSL/Semantics/position.ps.hlsl | 2 +- .../SemaHLSL/Semantics/target.ps.input.hlsl | 7 ++ .../SemaHLSL/Semantics/target.vs.input.hlsl | 8 +++ .../SemaHLSL/Semantics/target.vs.output.hlsl | 7 ++ .../test/CodeGen/SPIRV/semantics/target.ps.ll | 33 +++++++++ 10 files changed, 153 insertions(+), 16 deletions(-) create mode 100644 clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/target.ps.input.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/target.vs.input.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/target.vs.output.hlsl create mode 100644 llvm/test/CodeGen/SPIRV/semantics/target.ps.ll diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c535fe2b9f241..28ebd74547f5d 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13213,6 +13213,8 @@ def err_hlsl_semantic_indexing_not_supported 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 err_hlsl_semantic_unsupported_direction_for_stage + : Error<"semantic %0 is unsupported as %1 for stage %2">; 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; 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 15edb7e77a22b..2ac88f7e82700 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -134,9 +134,6 @@ class SemaHLSL : public SemaBase { void CheckEntryPoint(FunctionDecl *FD); bool CheckResourceBinOp(BinaryOperatorKind Opc, Expr *LHSExpr, Expr *RHSExpr, SourceLocation Loc); - void DiagnoseAttrStageMismatch( - const Attr *A, llvm::Triple::EnvironmentType Stage, - std::initializer_list AllowedStages); QualType handleVectorBinOpConversion(ExprResult &LHS, ExprResult &RHS, QualType LHSType, QualType RHSType, @@ -244,6 +241,17 @@ class SemaHLSL : public SemaBase { std::optional Index; }; + enum IOType { + In = 0b01, + Out = 0b10, + InOut = 0b11, + }; + + struct SemanticStageInfo { + llvm::Triple::EnvironmentType Stage; + IOType Direction; + }; + private: void collectResourceBindingsOnVarDecl(VarDecl *D); void collectResourceBindingsOnUserRecordDecl(const VarDecl *VD, @@ -269,6 +277,14 @@ class SemaHLSL : public SemaBase { void diagnoseAvailabilityViolations(TranslationUnitDecl *TU); + void diagnoseAttrStageMismatch( + const Attr *A, llvm::Triple::EnvironmentType Stage, + std::initializer_list AllowedStages); + + void diagnoseSemanticStageMismatch( + const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput, + std::initializer_list AllowedStages); + uint32_t getNextImplicitBindingOrderID() { return ImplicitBindingNextOrderID++; } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index b9bdce521c989..0a3b37052b803 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -774,6 +774,8 @@ void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source, createSPIRVBuiltinStore(B, CGM.getModule(), Source, Semantic->getAttrName()->getName(), /* BuiltIn::Position */ 0); + } else if (SemanticName == "SV_TARGET") { + emitUserSemanticStore(B, Source, Decl, Semantic, Index); } else llvm_unreachable("non-handled system semantic. FIXME."); } diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 5beb4e7fe8546..f7b5de114d2c6 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -873,14 +873,14 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { case llvm::Triple::Miss: case llvm::Triple::Callable: if (const auto *NT = FD->getAttr()) { - DiagnoseAttrStageMismatch(NT, ST, + diagnoseAttrStageMismatch(NT, ST, {llvm::Triple::Compute, llvm::Triple::Amplification, llvm::Triple::Mesh}); FD->setInvalidDecl(); } if (const auto *WS = FD->getAttr()) { - DiagnoseAttrStageMismatch(WS, ST, + diagnoseAttrStageMismatch(WS, ST, {llvm::Triple::Compute, llvm::Triple::Amplification, llvm::Triple::Mesh}); @@ -954,7 +954,8 @@ void SemaHLSL::checkSemanticAnnotation( SemanticName == "SV_GROUPID") { if (ST != llvm::Triple::Compute) - DiagnoseAttrStageMismatch(SemanticAttr, ST, {llvm::Triple::Compute}); + diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput, + {{llvm::Triple::Compute, IOType::In}}); if (SemanticAttr->getSemanticIndex() != 0) { std::string PrettyName = @@ -967,14 +968,15 @@ void SemaHLSL::checkSemanticAnnotation( } if (SemanticName == "SV_POSITION") { - // SV_Position can is I/O for vertex shaders. - // For pixel shaders, only valid as input. - // Note: for SPIR-V, not backed by a builtin when used as input in a vertex - // shaders. - if (ST == llvm::Triple::Vertex || (ST == llvm::Triple::Pixel && IsInput)) - return; - DiagnoseAttrStageMismatch(SemanticAttr, ST, - {llvm::Triple::Pixel, llvm::Triple::Vertex}); + diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput, + {{llvm::Triple::Vertex, IOType::InOut}, + {llvm::Triple::Pixel, IOType::In}}); + return; + } + + if (SemanticName == "SV_TARGET") { + diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput, + {{llvm::Triple::Pixel, IOType::Out}}); return; } @@ -984,7 +986,7 @@ void SemaHLSL::checkSemanticAnnotation( llvm_unreachable("Unknown SemanticAttr"); } -void SemaHLSL::DiagnoseAttrStageMismatch( +void SemaHLSL::diagnoseAttrStageMismatch( const Attr *A, llvm::Triple::EnvironmentType Stage, std::initializer_list AllowedStages) { SmallVector StageStrings; @@ -998,6 +1000,37 @@ void SemaHLSL::DiagnoseAttrStageMismatch( << (AllowedStages.size() != 1) << join(StageStrings, ", "); } +void SemaHLSL::diagnoseSemanticStageMismatch( + const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput, + std::initializer_list Allowed) { + + for (auto &Case : Allowed) { + if (Case.Stage != Stage) + continue; + + if (IsInput && Case.Direction & IOType::In) + return; + if (!IsInput && Case.Direction & IOType::Out) + return; + + Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_direction_for_stage) + << A->getAttrName() << (IsInput ? "input" : "output") + << llvm::Triple::getEnvironmentTypeName(Case.Stage); + return; + } + + SmallVector StageStrings; + llvm::transform( + Allowed, std::back_inserter(StageStrings), [](SemanticStageInfo Case) { + return StringRef( + HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage)); + }); + + Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage) + << A->getAttrName() << llvm::Triple::getEnvironmentTypeName(Stage) + << (Allowed.size() != 1) << join(StageStrings, ", "); +} + template static void castVector(Sema &S, ExprResult &E, QualType &Ty, unsigned Sz) { if (const auto *VTy = Ty->getAs()) @@ -1799,6 +1832,16 @@ void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL, return; } + if (SemanticName == "SV_TARGET") { + const auto *VT = ValueType->getAs(); + if (!ValueType->hasFloatingRepresentation() || + (VT && VT->getNumElements() > 4)) + Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) + << AL << "float/float1/float2/float3/float4"; + D->addAttr(createSemanticAttr(AL, Index)); + return; + } + Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; } diff --git a/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl new file mode 100644 index 0000000000000..4dc622a1eb6bb --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/SV_Target.ps.hlsl @@ -0,0 +1,19 @@ +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-SPIRV +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefix=CHECK-DXIL + +// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]] + +// CHECK: define void @main() {{.*}} { +float4 main(float4 p : SV_Position) : SV_Target { + // CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4mainDv4_f(<4 x float> %[[#]]) + // CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 16 + + // CHECK-DXIL: %[[#TMP:]] = call <4 x float> @_Z4mainDv4_f(<4 x float> %SV_Position0) + // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#TMP]]) + return p; +} + +// CHECK-SPIRV-DAG: ![[#MD_2]] = !{![[#MD_3:]]} +// CHECK-SPIRV-DAG: ![[#MD_3]] = !{i32 30, i32 0} +// | `-> Location index +// `-> SPIR-V decoration 'Location' diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl index 47d07887911d6..b96aa121d6aef 100644 --- a/clang/test/SemaHLSL/Semantics/position.ps.hlsl +++ b/clang/test/SemaHLSL/Semantics/position.ps.hlsl @@ -2,6 +2,6 @@ // RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s float4 main(float4 a : A) : SV_Position { -// expected-error@-1 {{attribute 'SV_Position' is unsupported in 'pixel' shaders, requires one of the following: pixel, vertex}} +// expected-error@-1 {{semantic 'SV_Position' is unsupported as output for stage pixel}} return a; } diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl new file mode 100644 index 0000000000000..6aabff60fac39 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-pixel -finclude-default-header -x hlsl -verify -o - %s +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -finclude-default-header -x hlsl -verify -o - %s + +float4 main(float4 a : SV_Target) : A { +// expected-error@-1 {{semantic 'SV_Target' is unsupported as input for stage pixel}} + return a; +} diff --git a/clang/test/SemaHLSL/Semantics/target.vs.input.hlsl b/clang/test/SemaHLSL/Semantics/target.vs.input.hlsl new file mode 100644 index 0000000000000..add24732fc05a --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/target.vs.input.hlsl @@ -0,0 +1,8 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-vertex -finclude-default-header -x hlsl -verify -o - %s +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-vertex -finclude-default-header -x hlsl -verify -o - %s + +float4 main(float4 a : SV_Target) : A { +// expected-error@-1 {{attribute 'SV_Target' is unsupported in 'vertex' shaders, requires pixel}} + return a; +} + diff --git a/clang/test/SemaHLSL/Semantics/target.vs.output.hlsl b/clang/test/SemaHLSL/Semantics/target.vs.output.hlsl new file mode 100644 index 0000000000000..0481bcdad0177 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/target.vs.output.hlsl @@ -0,0 +1,7 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-vertex -finclude-default-header -x hlsl -verify -o - %s +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-vertex -finclude-default-header -x hlsl -verify -o - %s + +float4 main(float4 a : SV_Position) : SV_Target { +// expected-error@-1 {{attribute 'SV_Target' is unsupported in 'vertex' shaders, requires pixel}} + return a; +} diff --git a/llvm/test/CodeGen/SPIRV/semantics/target.ps.ll b/llvm/test/CodeGen/SPIRV/semantics/target.ps.ll new file mode 100644 index 0000000000000..249ffc078f158 --- /dev/null +++ b/llvm/test/CodeGen/SPIRV/semantics/target.ps.ll @@ -0,0 +1,33 @@ +; RUN: llc -O0 -verify-machineinstrs -mtriple=spirv-vulkan-unknown %s -o - | FileCheck %s +; RUN: %if spirv-tools %{ llc -O0 -mtriple=spirv-vulkan-unknown %s -o - -filetype=obj | spirv-val %} + +; CHECK-DAG: OpDecorate %[[#INPUT:]] BuiltIn FragCoord +; CHECK-DAG: OpDecorate %[[#OUTPUT:]] Location 0 + +; CHECK-DAG: %[[#float:]] = OpTypeFloat 32 +; CHECK-DAG: %[[#v4:]] = OpTypeVector %[[#float]] 4 +; CHECK-DAG: %[[#ptr_i:]] = OpTypePointer Input %[[#v4]] +; CHECK-DAG: %[[#ptr_o:]] = OpTypePointer Output %[[#v4]] + +; CHECK-DAG: %[[#INPUT]] = OpVariable %[[#ptr_i]] Input +; CHECK-DAG: %[[#OUTPUT]] = OpVariable %[[#ptr_o]] Output + +@SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations !0 +@SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations !2 + +define void @main() #1 { +entry: + %0 = load <4 x float>, ptr addrspace(7) @SV_Position, align 16 + store <4 x float> %0, ptr addrspace(8) @SV_Target0, align 16 + ret void + +; CHECK: %[[#TMP:]] = OpLoad %[[#v4]] %[[#INPUT]] Aligned 16 +; CHECK: OpStore %[[#OUTPUT]] %[[#TMP]] Aligned 16 +} + +!0 = !{!1} +!1 = !{i32 11, i32 15} +!2 = !{!3} +!3 = !{i32 30, i32 0} + +