From 8305fe297e15890e661efe87576de797e1e603fb 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 1/7] [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 | 3 + clang/lib/Sema/SemaHLSL.cpp | 63 ++++++++++++++++--- .../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, 154 insertions(+), 12 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 53aa86a7dabde..1e024258b7c06 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13231,6 +13231,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 f5c07fe2e33ff..e40a2296cbe5c 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -783,6 +783,9 @@ void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source, } } + if (SemanticName == "SV_TARGET") + emitUserSemanticStore(B, Source, Decl, Semantic, Index); + llvm_unreachable( "Store hasn't been implemented yet for this system semantic. FIXME"); } diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index ecab3946b58c7..40ba26ab54a7d 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 = @@ -969,10 +970,15 @@ void SemaHLSL::checkSemanticAnnotation( if (SemanticName == "SV_POSITION") { // SV_Position can be an input or output in vertex shaders, // but only an input in pixel 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; } @@ -982,7 +988,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; @@ -996,6 +1002,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()) @@ -1797,6 +1834,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} + + From 2ebbc2773e585708db5ebb062081e308ffba92e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Mon, 24 Nov 2025 13:20:36 +0100 Subject: [PATCH 2/7] pr-feedback --- .../clang/Basic/DiagnosticSemaKinds.td | 5 +++-- clang/include/clang/Sema/SemaHLSL.h | 2 +- clang/lib/CodeGen/CGHLSLRuntime.cpp | 4 +++- clang/lib/Sema/SemaHLSL.cpp | 20 +++++++++++++++---- .../test/SemaHLSL/Semantics/position.ps.hlsl | 2 +- .../SemaHLSL/Semantics/target.ps.input.hlsl | 2 +- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 1e024258b7c06..48a17df95c1e6 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13231,8 +13231,9 @@ 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 err_hlsl_semantic_unsupported_iotype_for_stage + : Error<"semantic %0 is unsupported in %2 shaders as %1, requires one of " + "the following: %3">; 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 2ac88f7e82700..fb18817f2177b 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -249,7 +249,7 @@ class SemaHLSL : public SemaBase { struct SemanticStageInfo { llvm::Triple::EnvironmentType Stage; - IOType Direction; + IOType AllowedIOTypesMask; }; private: diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index e40a2296cbe5c..58a523b853647 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -783,8 +783,10 @@ void CGHLSLRuntime::emitSystemSemanticStore(IRBuilder<> &B, llvm::Value *Source, } } - if (SemanticName == "SV_TARGET") + if (SemanticName == "SV_TARGET") { emitUserSemanticStore(B, Source, Decl, Semantic, Index); + return; + } llvm_unreachable( "Store hasn't been implemented yet for this system semantic. FIXME"); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 40ba26ab54a7d..62153b8e8794a 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1010,14 +1010,26 @@ void SemaHLSL::diagnoseSemanticStageMismatch( if (Case.Stage != Stage) continue; - if (IsInput && Case.Direction & IOType::In) + if (IsInput && Case.AllowedIOTypesMask & IOType::In) return; - if (!IsInput && Case.Direction & IOType::Out) + if (!IsInput && Case.AllowedIOTypesMask & IOType::Out) return; - Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_direction_for_stage) + SmallVector ValidCases; + llvm::transform( + Allowed, std::back_inserter(ValidCases), [](SemanticStageInfo Case) { + std::string Type = + Case.AllowedIOTypesMask == IOType::InOut + ? " inout" + : (Case.AllowedIOTypesMask & IOType::In ? " in" : " out"); + return std::string( + HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage)) + + Type; + }); + Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage) << A->getAttrName() << (IsInput ? "input" : "output") - << llvm::Triple::getEnvironmentTypeName(Case.Stage); + << llvm::Triple::getEnvironmentTypeName(Case.Stage) + << join(ValidCases, ", "); return; } diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl index b96aa121d6aef..af237d8d69ef0 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 {{semantic 'SV_Position' is unsupported as output for stage pixel}} +// expected-error@-1 {{semantic 'SV_Position' is unsupported in pixel shaders as output, requires one of the following: vertex inout, pixel in}} return a; } diff --git a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl b/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl index 6aabff60fac39..a77b46c0e9f1a 100644 --- a/clang/test/SemaHLSL/Semantics/target.ps.input.hlsl +++ b/clang/test/SemaHLSL/Semantics/target.ps.input.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 : SV_Target) : A { -// expected-error@-1 {{semantic 'SV_Target' is unsupported as input for stage pixel}} +// expected-error@-1 {{semantic 'SV_Target' is unsupported in pixel shaders as input, requires one of the following: pixel out}} return a; } From 9c4c26fc1ab1fb1a1a8767a25a03aea21a4ebc82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Mon, 24 Nov 2025 15:27:22 +0100 Subject: [PATCH 3/7] change language --- clang/lib/Sema/SemaHLSL.cpp | 11 ++++++----- clang/test/SemaHLSL/Semantics/position.ps.hlsl | 2 +- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 62153b8e8794a..0cea7af6024d4 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1018,13 +1018,14 @@ void SemaHLSL::diagnoseSemanticStageMismatch( SmallVector ValidCases; llvm::transform( Allowed, std::back_inserter(ValidCases), [](SemanticStageInfo Case) { - std::string Type = - Case.AllowedIOTypesMask == IOType::InOut - ? " inout" - : (Case.AllowedIOTypesMask & IOType::In ? " in" : " out"); + SmallVector ValidType; + if (Case.AllowedIOTypesMask & IOType::In) + ValidType.push_back("input"); + if (Case.AllowedIOTypesMask & IOType::Out) + ValidType.push_back("output"); return std::string( HLSLShaderAttr::ConvertEnvironmentTypeToStr(Case.Stage)) + - Type; + " " + join(ValidType, "/"); }); Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage) << A->getAttrName() << (IsInput ? "input" : "output") diff --git a/clang/test/SemaHLSL/Semantics/position.ps.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.hlsl index af237d8d69ef0..d0fe19d1a5407 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 {{semantic 'SV_Position' is unsupported in pixel shaders as output, requires one of the following: vertex inout, pixel in}} +// expected-error@-1 {{semantic 'SV_Position' is unsupported in pixel shaders as output, requires one of the following: vertex input/output, pixel input}} return a; } From 24b9a4ebaa14dc6a4d06f4cda8810f2223651fda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Wed, 12 Nov 2025 16:58:45 +0100 Subject: [PATCH 4/7] [HLSL][SPIR-V] Implement vk::location for inputs This commit adds the support for vk::location attribute, focusing on input semantics. --- clang/include/clang/Basic/Attr.td | 8 ++++ clang/include/clang/Basic/AttrDocs.td | 12 ++++++ .../clang/Basic/DiagnosticSemaKinds.td | 3 ++ clang/include/clang/Sema/SemaHLSL.h | 3 ++ clang/lib/CodeGen/CGHLSLRuntime.cpp | 20 +++++---- clang/lib/CodeGen/CGHLSLRuntime.h | 2 + clang/lib/Sema/SemaDeclAttr.cpp | 3 ++ clang/lib/Sema/SemaHLSL.cpp | 41 +++++++++++++++++++ ...antic.explicit-location-output-struct.hlsl | 37 +++++++++++++++++ .../semantics/semantic.explicit-location.hlsl | 19 +++++++++ .../semantic.explicit-mix-builtin.hlsl | 39 ++++++++++++++++++ ...a-attribute-supported-attributes-list.test | 1 + .../semantic.explicit-mix-builtin-vs.hlsl | 16 ++++++++ .../semantic.explicit-mix-location-2.hlsl | 15 +++++++ .../semantic.explicit-mix-location.hlsl | 15 +++++++ 15 files changed, 227 insertions(+), 7 deletions(-) create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 8e5f7ef0bb82d..9e7dc200bc6cf 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -5172,6 +5172,14 @@ def HLSLVkConstantId : InheritableAttr { let Documentation = [VkConstantIdDocs]; } +def HLSLVkLocation : HLSLAnnotationAttr { + let Spellings = [CXX11<"vk", "location">]; + let Args = [IntArgument<"Location">]; + let Subjects = SubjectList<[ParmVar, Field, Function], ErrorDiag>; + let LangOpts = [HLSL]; + let Documentation = [HLSLVkLocationDocs]; +} + def RandomizeLayout : InheritableAttr { let Spellings = [GCC<"randomize_layout">]; let Subjects = SubjectList<[Record]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index c1b1510f363d4..fa365da3ed9aa 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -8981,6 +8981,18 @@ The descriptor set is optional and defaults to 0 if not provided. }]; } +def HLSLVkLocationDocs : Documentation { + let Category = DocCatVariable; + let Content = [{ +Attribute used for specifying the location number for the stage input/output +variables. Allowed on function parameters, function returns, and struct +fields. This parameter has no effect when used outside of an entrypoint +parameter/parameter field/return value. + +This attribute maps to the 'Location' SPIR-V decoration. + }]; +} + def WebAssemblyFuncrefDocs : Documentation { let Category = DocCatType; let Content = [{ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 48a17df95c1e6..04812bd78d9b4 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13234,6 +13234,9 @@ def err_hlsl_semantic_index_overlap : Error<"semantic index overlap %0">; def err_hlsl_semantic_unsupported_iotype_for_stage : Error<"semantic %0 is unsupported in %2 shaders as %1, requires one of " "the following: %3">; +def err_hlsl_semantic_partial_explicit_indexing + : Error<"partial explicit stage input location assignment via " + "vk::location(X) unsupported">; 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 fb18817f2177b..cac89f6e51cae 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -168,6 +168,7 @@ class SemaHLSL : public SemaBase { void handleWaveSizeAttr(Decl *D, const ParsedAttr &AL); void handleVkConstantIdAttr(Decl *D, const ParsedAttr &AL); void handleVkBindingAttr(Decl *D, const ParsedAttr &AL); + void handleVkLocationAttr(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); @@ -240,6 +241,8 @@ class SemaHLSL : public SemaBase { HLSLParsedSemanticAttr *Semantic; std::optional Index; }; + std::optional InputUsesExplicitVkLocations = std::nullopt; + std::optional OutputUsesExplicitVkLocations = std::nullopt; enum IOType { In = 0b01, diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 58a523b853647..913d80b7e5c25 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -582,20 +582,22 @@ static llvm::Value *createSPIRVLocationLoad(IRBuilder<> &B, llvm::Module &M, return B.CreateLoad(Ty, GV); } -llvm::Value * -CGHLSLRuntime::emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, - HLSLAppliedSemanticAttr *Semantic, - std::optional Index) { +llvm::Value *CGHLSLRuntime::emitSPIRVUserSemanticLoad( + llvm::IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, + HLSLAppliedSemanticAttr *Semantic, std::optional Index) { Twine BaseName = Twine(Semantic->getAttrName()->getName()); Twine VariableName = BaseName.concat(Twine(Index.value_or(0))); unsigned Location = SPIRVLastAssignedInputSemanticLocation; + if (auto *L = Decl->getAttr()) + Location = L->getLocation(); // DXC completely ignores the semantic/index pair. Location are assigned from // the first semantic to the last. llvm::ArrayType *AT = dyn_cast(Type); unsigned ElementCount = AT ? AT->getNumElements() : 1; SPIRVLastAssignedInputSemanticLocation += ElementCount; + return createSPIRVLocationLoad(B, CGM.getModule(), Type, Location, VariableName.str()); } @@ -616,10 +618,14 @@ static void createSPIRVLocationStore(IRBuilder<> &B, llvm::Module &M, void CGHLSLRuntime::emitSPIRVUserSemanticStore( llvm::IRBuilder<> &B, llvm::Value *Source, - HLSLAppliedSemanticAttr *Semantic, std::optional Index) { + const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, + std::optional Index) { Twine BaseName = Twine(Semantic->getAttrName()->getName()); Twine VariableName = BaseName.concat(Twine(Index.value_or(0))); + unsigned Location = SPIRVLastAssignedOutputSemanticLocation; + if (auto *L = Decl->getAttr()) + Location = L->getLocation(); // DXC completely ignores the semantic/index pair. Location are assigned from // the first semantic to the last. @@ -671,7 +677,7 @@ llvm::Value *CGHLSLRuntime::emitUserSemanticLoad( IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { if (CGM.getTarget().getTriple().isSPIRV()) - return emitSPIRVUserSemanticLoad(B, Type, Semantic, Index); + return emitSPIRVUserSemanticLoad(B, Type, Decl, Semantic, Index); if (CGM.getTarget().getTriple().isDXIL()) return emitDXILUserSemanticLoad(B, Type, Semantic, Index); @@ -684,7 +690,7 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source, HLSLAppliedSemanticAttr *Semantic, std::optional Index) { if (CGM.getTarget().getTriple().isSPIRV()) - return emitSPIRVUserSemanticStore(B, Source, Semantic, Index); + return emitSPIRVUserSemanticStore(B, Source, Decl, Semantic, Index); if (CGM.getTarget().getTriple().isDXIL()) return emitDXILUserSemanticStore(B, Source, Semantic, Index); diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index c883282a8d9c8..d057add2db222 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -278,6 +278,7 @@ class CGHLSLRuntime { HLSLResourceBindingAttr *RBA); llvm::Value *emitSPIRVUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index); llvm::Value *emitDXILUserSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, @@ -289,6 +290,7 @@ class CGHLSLRuntime { std::optional Index); void emitSPIRVUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source, + const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index); void emitDXILUserSemanticStore(llvm::IRBuilder<> &B, llvm::Value *Source, diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index e3af5023c74d0..c9d1ee76a2e52 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -7703,6 +7703,9 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL, case ParsedAttr::AT_HLSLUnparsedSemantic: S.HLSL().handleSemanticAttr(D, AL); break; + case ParsedAttr::AT_HLSLVkLocation: + S.HLSL().handleVkLocationAttr(D, AL); + break; case ParsedAttr::AT_AbiTag: handleAbiTagAttr(S, D, AL); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 0cea7af6024d4..2a69703d0d1e9 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -771,6 +771,22 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) { } } +static bool isPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD, + HLSLAppliedSemanticAttr *Semantic) { + if (AstContext.getTargetInfo().getTriple().getOS() != llvm::Triple::Vulkan) + return false; + + const auto *ShaderAttr = FD->getAttr(); + assert(ShaderAttr && "Entry point has no shader attribute"); + llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); + auto SemanticName = Semantic->getSemanticName().upper(); + + if (ST == llvm::Triple::Pixel && SemanticName == "SV_POSITION") + return true; + + return false; +} + bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, @@ -800,6 +816,22 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, unsigned Location = ActiveSemantic.Index.value_or(0); + if (!isPipelineBuiltin(getASTContext(), FD, A)) { + bool HasVkLocation = false; + if (auto *A = D->getAttr()) { + HasVkLocation = true; + Location = A->getLocation(); + } + + auto &UsesExplicitVkLocations = + IsInput ? InputUsesExplicitVkLocations : OutputUsesExplicitVkLocations; + if (UsesExplicitVkLocations.value_or(HasVkLocation) != HasVkLocation) { + Diag(D->getLocation(), diag::err_hlsl_semantic_partial_explicit_indexing); + return false; + } + UsesExplicitVkLocations = HasVkLocation; + } + const ConstantArrayType *AT = dyn_cast(D->getType()); unsigned ElementCount = AT ? AT->getZExtSize() : 1; ActiveSemantic.Index = Location + ElementCount; @@ -1757,6 +1789,15 @@ void SemaHLSL::handleVkBindingAttr(Decl *D, const ParsedAttr &AL) { HLSLVkBindingAttr(getASTContext(), AL, Binding, Set)); } +void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) { + uint32_t Location; + if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), Location)) + return; + + D->addAttr(::new (getASTContext()) + HLSLVkLocationAttr(getASTContext(), AL, Location)); +} + bool SemaHLSL::diagnoseInputIDType(QualType T, const ParsedAttr &AL) { const auto *VT = T->getAs(); diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl new file mode 100644 index 0000000000000..c5d86637fb4ea --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location-output-struct.hlsl @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple spirv-pc-vulkan1.3-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,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-prefixes=CHECK,CHECK-DXIL + +// CHECK-SPIRV: @SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]] +// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]] + +struct Output { + [[vk::location(2)]] float4 field : SV_Target; +}; + +// CHECK: define void @main() {{.*}} { +Output main(float4 p : SV_Position) { + // CHECK: %[[#OUT:]] = alloca %struct.Output, align 16 + + // CHECK-SPIRV: %[[#IN:]] = load <4 x float>, ptr addrspace(7) @SV_Position, align 16 + // CHECK-SPIRV: call spir_func void @_Z4mainDv4_f(ptr %[[#OUT]], <4 x float> %[[#IN]]) + + // CHECK-DXIL: call void @_Z4mainDv4_f(ptr %[[#OUT]], <4 x float> %SV_Position0) + + // CHECK: %[[#TMP:]] = load %struct.Output, ptr %[[#OUT]], align 16 + // CHECK: %[[#FIELD:]] = extractvalue %struct.Output %[[#TMP]], 0 + + // CHECK-SPIRV: store <4 x float> %[[#FIELD]], ptr addrspace(8) @SV_Target0, align 16 + // CHECK-DXIL: call void @llvm.dx.store.output.v4f32(i32 4, i32 0, i32 0, i8 0, i32 poison, <4 x float> %[[#FIELD]]) + Output o; + o.field = p; + return o; +} + +// CHECK-SPIRV-DAG: ![[#MD_0]] = !{![[#MD_1:]]} +// CHECK-SPIRV-DAG: ![[#MD_1]] = !{i32 11, i32 15} +// | `-> BuiltIn 'FragCoord' +// `-> SPIR-V decoration 'BuiltIn' +// CHECK-SPIRV-DAG: ![[#MD_2]] = !{![[#MD_3:]]} +// CHECK-SPIRV-DAG: ![[#MD_3]] = !{i32 30, i32 2} +// | `-> Location index +// `-> SPIR-V decoration 'Location' diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.hlsl new file mode 100644 index 0000000000000..41e28bf1259d6 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-location.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() {{.*}} { +[[vk::location(2)]] 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 2} +// | `-> Location index +// `-> SPIR-V decoration 'Location' diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl new file mode 100644 index 0000000000000..bc2ecd926dd51 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.hlsl @@ -0,0 +1,39 @@ +// RUN: %clang_cc1 -triple spirv-linux-vulkan-pixel -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s --check-prefixes=CHECK,CHECK-SPIRV + +// The following code is allowed because the `SV_Position` semantic is here +// translated into a SPIR-V builtin. Meaning there is no implicit `Location` +// assignment. + +struct S2 { + float4 a; + float4 b; +}; + +struct S1 { + float4 position : SV_Position; + [[vk::location(3)]] float4 color0 : COLOR0; +}; + +// CHECK-SPIRV: @SV_Position = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]] +// CHECK-SPIRV: @COLOR0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_2:]] +// CHECK-SPIRV: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_4:]] + +[shader("pixel")] +float4 main(S1 p) : SV_Target { + return p.position + p.color0; +} +// CHECK-SPIRV: %[[#SV_POS:]] = load <4 x float>, ptr addrspace(7) @SV_Position, align 16 +// CHECK: %[[#TMP1:]] = insertvalue %struct.S1 poison, <4 x float> %[[#SV_POS]], 0 +// CHECK-SPIRV: %[[#A0:]] = load <4 x float>, ptr addrspace(7) @COLOR0, align 16 +// CHECK: %[[#TMP2:]] = insertvalue %struct.S1 %[[#TMP1]], <4 x float> %[[#A0]], 1 +// CHECK: %[[#P:]] = alloca %struct.S1, align 16 +// CHECK: store %struct.S1 %[[#TMP2]], ptr %[[#P]], align 16 +// CHECK-SPIRV: %[[#R:]] = call spir_func <4 x float> @_Z4main2S1(ptr %[[#P]]) +// CHECK-SPIRV: store <4 x float> %[[#R]], ptr addrspace(8) @SV_Target0, align 16 + +// CHECK-SPIRV: ![[#MD_0]] = !{![[#MD_1:]]} +// CHECK-SPIRV: ![[#MD_1]] = !{i32 11, i32 15} +// CHECK-SPIRV: ![[#MD_2]] = !{![[#MD_3:]]} +// CHECK-SPIRV: ![[#MD_3]] = !{i32 30, i32 3} +// CHECK-SPIRV: ![[#MD_4]] = !{![[#MD_5:]]} +// CHECK-SPIRV: ![[#MD_5]] = !{i32 30, i32 0} diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test index 747eb17446c87..1e1d4a356f515 100644 --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -89,6 +89,7 @@ // CHECK-NEXT: FunctionReturnThunks (SubjectMatchRule_function) // CHECK-NEXT: GNUInline (SubjectMatchRule_function) // CHECK-NEXT: HIPManaged (SubjectMatchRule_variable) +// CHECK-NEXT: HLSLVkLocation (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_field, SubjectMatchRule_function) // CHECK-NEXT: Hot (SubjectMatchRule_function) // CHECK-NEXT: HybridPatchable (SubjectMatchRule_function) // CHECK-NEXT: IBAction (SubjectMatchRule_objc_method_is_instance) diff --git a/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl new file mode 100644 index 0000000000000..3abd1cb65ffc4 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-builtin-vs.hlsl @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple spirv-linux-vulkan-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s -verify -verify-ignore-unexpected=note + +// This is almost the same as semantic.explicit-mix-builtin.hlsl, except this +// time we build a vertex shader. This means the SV_Position semantic is not +// a BuiltIn anymore, but a Location decorated variable. This means we mix +// implicit and explicit location assignment. +struct S1 { + float4 position : SV_Position; + [[vk::location(3)]] float4 color : A; + // expected-error@-1 {{partial explicit stage input location assignment via vk::location(X) unsupported}} +}; + +[shader("vertex")] +float4 main1(S1 p) : A { + return p.position + p.color; +} diff --git a/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl new file mode 100644 index 0000000000000..3de85863883ad --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location-2.hlsl @@ -0,0 +1,15 @@ +// RUN: %clang --driver-mode=dxc %s -T ps_6_8 -E main1 -O3 -spirv -Xclang -verify -Xclang -verify-ignore-unexpected=note + +// The following code is not legal: both semantics A and B will be lowered +// into a Location decoration. And mixing implicit and explicit Location +// assignment is not supported. +struct S1 { + [[vk::location(3)]] float4 color : B; + float4 position : A; + // expected-error@-1 {{partial explicit stage input location assignment via vk::location(X) unsupported}} +}; + +[shader("pixel")] +float4 main1(S1 p) : SV_Target { + return p.position + p.color; +} diff --git a/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl new file mode 100644 index 0000000000000..8f5b6e48b48e9 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantic.explicit-mix-location.hlsl @@ -0,0 +1,15 @@ +// RUN: %clang --driver-mode=dxc %s -T ps_6_8 -E main1 -O3 -spirv -Xclang -verify -Xclang -verify-ignore-unexpected=note + +// The following code is not legal: both semantics A and B will be lowered +// into a Location decoration. And mixing implicit and explicit Location +// assignment is not supported. +struct S1 { + float4 position : A; + [[vk::location(3)]] float4 color : B; + // expected-error@-1 {{partial explicit stage input location assignment via vk::location(X) unsupported}} +}; + +[shader("pixel")] +float4 main1(S1 p) : SV_Target { + return p.position + p.color; +} From d0cfafea13b3d6968ad7b2e98477b89481fe0e09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Tue, 25 Nov 2025 17:47:56 +0100 Subject: [PATCH 5/7] pr-feedback --- clang/lib/Sema/SemaHLSL.cpp | 16 +++++++--- .../semantic.explicit-mix-builtin.vs.hlsl | 31 +++++++++++++++++++ 2 files changed, 42 insertions(+), 5 deletions(-) create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 2a69703d0d1e9..1b55d6c1f2e55 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -771,8 +771,9 @@ void SemaHLSL::ActOnTopLevelFunction(FunctionDecl *FD) { } } -static bool isPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD, - HLSLAppliedSemanticAttr *Semantic) { +static bool isVkPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD, + HLSLAppliedSemanticAttr *Semantic, + bool IsInput) { if (AstContext.getTargetInfo().getTriple().getOS() != llvm::Triple::Vulkan) return false; @@ -781,8 +782,13 @@ static bool isPipelineBuiltin(const ASTContext &AstContext, FunctionDecl *FD, llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); auto SemanticName = Semantic->getSemanticName().upper(); - if (ST == llvm::Triple::Pixel && SemanticName == "SV_POSITION") - return true; + // The SV_Position semantic is lowered to: + // - Position built-in for vertex output. + // - FragCoord built-in for fragment input. + if (SemanticName == "SV_POSITION") { + return (ST == llvm::Triple::Vertex && !IsInput) || + (ST == llvm::Triple::Pixel && IsInput); + } return false; } @@ -816,7 +822,7 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, unsigned Location = ActiveSemantic.Index.value_or(0); - if (!isPipelineBuiltin(getASTContext(), FD, A)) { + if (!isVkPipelineBuiltin(getASTContext(), FD, A, IsInput)) { bool HasVkLocation = false; if (auto *A = D->getAttr()) { HasVkLocation = true; diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl new file mode 100644 index 0000000000000..43dc30f089d9e --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix-builtin.vs.hlsl @@ -0,0 +1,31 @@ +// RUN: %clang_cc1 -triple spirv-linux-vulkan-vertex -x hlsl -emit-llvm -finclude-default-header -disable-llvm-passes -o - %s | FileCheck %s + +// This is almost the same as semantic.explicit-mix-builtin.hlsl, except this +// time we build a vertex shader. This means the SV_Position semantic output +// is also a BuiltIn, This means we can mix implicit and explicit location +// assignment. +struct S1 { + float4 position : SV_Position; + [[vk::location(3)]] float4 color : A; +}; + +// CHECK: @SV_Position0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]] +// CHECK: @SV_Position = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]] +// CHECK: @A0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_0]] + +[shader("vertex")] +S1 main1(float4 position : SV_Position) { + S1 output; + output.position = position; + output.color = position; + return output; +} + +// CHECK: ![[#MD_0]] = !{![[#MD_1:]]} +// CHECK: ![[#MD_1]] = !{i32 30, i32 0} +// | `-> Location index +// `-> SPIR-V decoration 'Location' +// CHECK: ![[#MD_2]] = !{![[#MD_3:]]} +// CHECK: ![[#MD_3]] = !{i32 11, i32 0} +// | `-> BuiltIn 'Position' +// `-> SPIR-V decoration 'BuiltIn' From 97d25e5608df4c0706b4702d5b17ef54849282df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Thu, 27 Nov 2025 10:34:21 +0100 Subject: [PATCH 6/7] move state to struct --- clang/include/clang/Sema/SemaHLSL.h | 30 +++++++++++----- clang/lib/Sema/SemaHLSL.cpp | 53 +++++++++++++---------------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index cac89f6e51cae..a2faa91d1e54d 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -237,19 +237,33 @@ class SemaHLSL : public SemaBase { IdentifierInfo *RootSigOverrideIdent = nullptr; + // Information about the current subtree being flattened. struct SemanticInfo { HLSLParsedSemanticAttr *Semantic; - std::optional Index; + std::optional Index = std::nullopt; }; - std::optional InputUsesExplicitVkLocations = std::nullopt; - std::optional OutputUsesExplicitVkLocations = std::nullopt; + // Bitmask used to recall if the current semantic subtree is + // input, output or inout. enum IOType { In = 0b01, Out = 0b10, InOut = 0b11, }; + // The context shared by all semantics with the same IOType during + // flattening. + struct SemanticContext { + // Present if any semantic sharing the same IO type has an explicit or + // implicit SPIR-V location index assigned. + std::optional UsesExplicitVkLocations = std::nullopt; + // The set of semantics found to be active during flattening. Used to detect + // index collisions. + llvm::StringSet<> ActiveSemantics = {}; + // The IOType of this semantic set. + IOType CurrentIOType; + }; + struct SemanticStageInfo { llvm::Triple::EnvironmentType Stage; IOType AllowedIOTypesMask; @@ -262,19 +276,17 @@ class SemaHLSL : public SemaBase { void checkSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param, const HLSLAppliedSemanticAttr *SemanticAttr, - bool IsInput); + const SemanticContext &SC); bool determineActiveSemanticOnScalar(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, SemanticInfo &ActiveSemantic, - llvm::StringSet<> &ActiveSemantics, - bool IsInput); + SemanticContext &SC); bool determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, SemanticInfo &ActiveSemantic, - llvm::StringSet<> &ActiveSemantics, - bool IsInput); + SemanticContext &SC); void processExplicitBindingsOnDecl(VarDecl *D); @@ -285,7 +297,7 @@ class SemaHLSL : public SemaBase { std::initializer_list AllowedStages); void diagnoseSemanticStageMismatch( - const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput, + const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType, std::initializer_list AllowedStages); uint32_t getNextImplicitBindingOrderID() { diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 1b55d6c1f2e55..89645e3b67db3 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -797,8 +797,7 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, SemanticInfo &ActiveSemantic, - llvm::StringSet<> &UsedSemantics, - bool IsInput) { + SemaHLSL::SemanticContext &SC) { if (ActiveSemantic.Semantic == nullptr) { ActiveSemantic.Semantic = D->getAttr(); if (ActiveSemantic.Semantic) @@ -817,25 +816,24 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, if (!A) return false; - checkSemanticAnnotation(FD, D, A, IsInput); + checkSemanticAnnotation(FD, D, A, SC); OutputDecl->addAttr(A); unsigned Location = ActiveSemantic.Index.value_or(0); - if (!isVkPipelineBuiltin(getASTContext(), FD, A, IsInput)) { + if (!isVkPipelineBuiltin(getASTContext(), FD, A, + SC.CurrentIOType & IOType::In)) { bool HasVkLocation = false; if (auto *A = D->getAttr()) { HasVkLocation = true; Location = A->getLocation(); } - auto &UsesExplicitVkLocations = - IsInput ? InputUsesExplicitVkLocations : OutputUsesExplicitVkLocations; - if (UsesExplicitVkLocations.value_or(HasVkLocation) != HasVkLocation) { + if (SC.UsesExplicitVkLocations.value_or(HasVkLocation) != HasVkLocation) { Diag(D->getLocation(), diag::err_hlsl_semantic_partial_explicit_indexing); return false; } - UsesExplicitVkLocations = HasVkLocation; + SC.UsesExplicitVkLocations = HasVkLocation; } const ConstantArrayType *AT = dyn_cast(D->getType()); @@ -846,7 +844,7 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, for (unsigned I = 0; I < ElementCount; ++I) { Twine VariableName = BaseName.concat(Twine(Location + I)); - auto [_, Inserted] = UsedSemantics.insert(VariableName.str()); + auto [_, Inserted] = SC.ActiveSemantics.insert(VariableName.str()); if (!Inserted) { Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap) << VariableName.str(); @@ -861,8 +859,7 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, DeclaratorDecl *OutputDecl, DeclaratorDecl *D, SemanticInfo &ActiveSemantic, - llvm::StringSet<> &UsedSemantics, - bool IsInput) { + SemaHLSL::SemanticContext &SC) { if (ActiveSemantic.Semantic == nullptr) { ActiveSemantic.Semantic = D->getAttr(); if (ActiveSemantic.Semantic) @@ -875,13 +872,12 @@ bool SemaHLSL::determineActiveSemantic(FunctionDecl *FD, const RecordType *RT = dyn_cast(T); if (!RT) return determineActiveSemanticOnScalar(FD, OutputDecl, D, ActiveSemantic, - UsedSemantics, IsInput); + SC); const RecordDecl *RD = RT->getDecl(); for (FieldDecl *Field : RD->fields()) { SemanticInfo Info = ActiveSemantic; - if (!determineActiveSemantic(FD, OutputDecl, Field, Info, UsedSemantics, - IsInput)) { + if (!determineActiveSemantic(FD, OutputDecl, Field, Info, SC)) { Diag(Field->getLocation(), diag::note_hlsl_semantic_used_here) << Field; return false; } @@ -954,7 +950,9 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { llvm_unreachable("Unhandled environment in triple"); } - llvm::StringSet<> ActiveInputSemantics; + SemaHLSL::SemanticContext InputSC = {}; + InputSC.CurrentIOType = IOType::In; + for (ParmVarDecl *Param : FD->parameters()) { SemanticInfo ActiveSemantic; ActiveSemantic.Semantic = Param->getAttr(); @@ -962,26 +960,25 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex(); // FIXME: Verify output semantics in parameters. - if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic, - ActiveInputSemantics, /* IsInput= */ true)) { + if (!determineActiveSemantic(FD, Param, Param, ActiveSemantic, InputSC)) { Diag(Param->getLocation(), diag::note_previous_decl) << Param; FD->setInvalidDecl(); } } SemanticInfo ActiveSemantic; - llvm::StringSet<> ActiveOutputSemantics; + SemaHLSL::SemanticContext OutputSC = {}; + OutputSC.CurrentIOType = IOType::Out; ActiveSemantic.Semantic = FD->getAttr(); if (ActiveSemantic.Semantic) ActiveSemantic.Index = ActiveSemantic.Semantic->getSemanticIndex(); if (!FD->getReturnType()->isVoidType()) - determineActiveSemantic(FD, FD, FD, ActiveSemantic, ActiveOutputSemantics, - /* IsInput= */ false); + determineActiveSemantic(FD, FD, FD, ActiveSemantic, OutputSC); } void SemaHLSL::checkSemanticAnnotation( FunctionDecl *EntryPoint, const Decl *Param, - const HLSLAppliedSemanticAttr *SemanticAttr, bool IsInput) { + const HLSLAppliedSemanticAttr *SemanticAttr, const SemanticContext &SC) { auto *ShaderAttr = EntryPoint->getAttr(); assert(ShaderAttr && "Entry point has no shader attribute"); llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); @@ -992,7 +989,7 @@ void SemaHLSL::checkSemanticAnnotation( SemanticName == "SV_GROUPID") { if (ST != llvm::Triple::Compute) - diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput, + diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType, {{llvm::Triple::Compute, IOType::In}}); if (SemanticAttr->getSemanticIndex() != 0) { @@ -1008,14 +1005,14 @@ void SemaHLSL::checkSemanticAnnotation( if (SemanticName == "SV_POSITION") { // SV_Position can be an input or output in vertex shaders, // but only an input in pixel shaders. - diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput, + diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType, {{llvm::Triple::Vertex, IOType::InOut}, {llvm::Triple::Pixel, IOType::In}}); return; } if (SemanticName == "SV_TARGET") { - diagnoseSemanticStageMismatch(SemanticAttr, ST, IsInput, + diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType, {{llvm::Triple::Pixel, IOType::Out}}); return; } @@ -1041,16 +1038,14 @@ void SemaHLSL::diagnoseAttrStageMismatch( } void SemaHLSL::diagnoseSemanticStageMismatch( - const Attr *A, llvm::Triple::EnvironmentType Stage, bool IsInput, + const Attr *A, llvm::Triple::EnvironmentType Stage, IOType CurrentIOType, std::initializer_list Allowed) { for (auto &Case : Allowed) { if (Case.Stage != Stage) continue; - if (IsInput && Case.AllowedIOTypesMask & IOType::In) - return; - if (!IsInput && Case.AllowedIOTypesMask & IOType::Out) + if (CurrentIOType & Case.AllowedIOTypesMask) return; SmallVector ValidCases; @@ -1066,7 +1061,7 @@ void SemaHLSL::diagnoseSemanticStageMismatch( " " + join(ValidType, "/"); }); Diag(A->getLoc(), diag::err_hlsl_semantic_unsupported_iotype_for_stage) - << A->getAttrName() << (IsInput ? "input" : "output") + << A->getAttrName() << (CurrentIOType & IOType::In ? "input" : "output") << llvm::Triple::getEnvironmentTypeName(Case.Stage) << join(ValidCases, ", "); return; From ddf2c2134bff846d2733fe03fe0698e51c8ea77e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nathan=20Gau=C3=ABr?= Date: Thu, 27 Nov 2025 11:01:48 +0100 Subject: [PATCH 7/7] fix shader kind in CG --- clang/lib/CodeGen/CGHLSLRuntime.cpp | 15 ++++--- clang/lib/CodeGen/CGHLSLRuntime.h | 3 +- .../semantics/semantic.explicit-mix.lib.hlsl | 40 +++++++++++++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl diff --git a/clang/lib/CodeGen/CGHLSLRuntime.cpp b/clang/lib/CodeGen/CGHLSLRuntime.cpp index 913d80b7e5c25..30a74a2571247 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.cpp +++ b/clang/lib/CodeGen/CGHLSLRuntime.cpp @@ -699,8 +699,9 @@ void CGHLSLRuntime::emitUserSemanticStore(IRBuilder<> &B, llvm::Value *Source, } llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad( - IRBuilder<> &B, llvm::Type *Type, const clang::DeclaratorDecl *Decl, - HLSLAppliedSemanticAttr *Semantic, std::optional Index) { + IRBuilder<> &B, const FunctionDecl *FD, llvm::Type *Type, + const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, + std::optional Index) { std::string SemanticName = Semantic->getAttrName()->getName().upper(); if (SemanticName == "SV_GROUPINDEX") { @@ -736,8 +737,12 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad( return buildVectorInput(B, GroupIDIntrinsic, Type); } + const auto *ShaderAttr = FD->getAttr(); + assert(ShaderAttr && "Entry point has no shader attribute"); + llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); + if (SemanticName == "SV_POSITION") { - if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Pixel) { + if (ST == Triple::EnvironmentType::Pixel) { if (CGM.getTarget().getTriple().isSPIRV()) return createSPIRVBuiltinLoad(B, CGM.getModule(), Type, Semantic->getAttrName()->getName(), @@ -746,7 +751,7 @@ llvm::Value *CGHLSLRuntime::emitSystemSemanticLoad( return emitDXILUserSemanticLoad(B, Type, Semantic, Index); } - if (CGM.getTriple().getEnvironment() == Triple::EnvironmentType::Vertex) { + if (ST == Triple::EnvironmentType::Vertex) { return emitUserSemanticLoad(B, Type, Decl, Semantic, Index); } } @@ -804,7 +809,7 @@ llvm::Value *CGHLSLRuntime::handleScalarSemanticLoad( std::optional Index = Semantic->getSemanticIndex(); if (Semantic->getAttrName()->getName().starts_with_insensitive("SV_")) - return emitSystemSemanticLoad(B, Type, Decl, Semantic, Index); + return emitSystemSemanticLoad(B, FD, Type, Decl, Semantic, Index); return emitUserSemanticLoad(B, Type, Decl, Semantic, Index); } diff --git a/clang/lib/CodeGen/CGHLSLRuntime.h b/clang/lib/CodeGen/CGHLSLRuntime.h index d057add2db222..77f43e8766745 100644 --- a/clang/lib/CodeGen/CGHLSLRuntime.h +++ b/clang/lib/CodeGen/CGHLSLRuntime.h @@ -179,7 +179,8 @@ class CGHLSLRuntime { protected: CodeGenModule &CGM; - llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B, llvm::Type *Type, + llvm::Value *emitSystemSemanticLoad(llvm::IRBuilder<> &B, + const FunctionDecl *FD, llvm::Type *Type, const clang::DeclaratorDecl *Decl, HLSLAppliedSemanticAttr *Semantic, std::optional Index); diff --git a/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl new file mode 100644 index 0000000000000..456c9bf9aee05 --- /dev/null +++ b/clang/test/CodeGenHLSL/semantics/semantic.explicit-mix.lib.hlsl @@ -0,0 +1,40 @@ +// 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 + +// The followiong file contains both implicit and explicit vk::location, but +// because each entrypoint has only one kind, this is allowed. + +[shader("vertex")] +float4 vs_main(float4 p : SV_Position) : A { + return p; +} + +[shader("pixel")] +float4 ps_main([[vk::location(0)]] float4 p : A) : SV_Target { + return p; +} + +// The following function is not marked as being a shader entrypoint, this +// means the semantics and [[vk::location]] attributes are ignored. +// Otherwise, the partial explicit location assignment would be illegal. +float4 not_an_entry([[vk::location(0)]] float4 a : A, float4 b : B) : C { + return a + b; +} + +// CHECK: @SV_Position0 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]] +// CHECK: @A0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_0:]] +// CHECK: @A0.1 = external hidden thread_local addrspace(7) externally_initialized constant <4 x float>, !spirv.Decorations ![[#MD_0:]] +// CHECK: @SV_Target0 = external hidden thread_local addrspace(8) global <4 x float>, !spirv.Decorations ![[#MD_2:]] + + +// CHECK: define void @vs_main() +// CHECK: %[[#]] = load <4 x float>, ptr addrspace(7) @SV_Position0, align 16 +// CHECK: store <4 x float> %[[#]], ptr addrspace(8) @A0, align 16 + +// CHECK: define void @ps_main() +// CHECK: %[[#]] = load <4 x float>, ptr addrspace(7) @A0.1, align 16 +// CHECK: store <4 x float> %[[#]], ptr addrspace(8) @SV_Target0, align 16 + +// CHECK: ![[#MD_0]] = !{![[#MD_1:]]} +// CHECK: ![[#MD_1]] = !{i32 30, i32 0} +// CHECK: ![[#MD_2]] = !{![[#MD_3:]]} +// CHECK: ![[#MD_3]] = !{i32 30, i32 1}