https://github.com/inbelic created https://github.com/llvm/llvm-project/pull/224139
This pr adds the following semantic analysis for semantics: - Validate scalar/vector shapes, element types, and supported widths for system semantics - Enforce indexing restrictions and SV_Target bounds - Add diag that shows previous use of overlapping semantics As well as, simplifying some of the stages as well. Resolves #189765 Assisted by: GPT-6 Astra >From 182b95dc7d400d51ad161f466c6b5a5cc32d3446 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Wed, 16 Sep 2026 19:03:13 +0000 Subject: [PATCH 1/5] validate system-semantic types in entry-point context --- .../clang/Basic/DiagnosticSemaKinds.td | 5 + clang/include/clang/Sema/SemaHLSL.h | 10 +- clang/lib/Sema/SemaHLSL.cpp | 154 ++++++++++++------ .../Semantics/invalid_entry_parameter.hlsl | 44 +++-- .../SemaHLSL/Semantics/position.ps.size.hlsl | 19 ++- .../Semantics/semantic-stage.vs-ps.hlsl | 27 +++ .../SemaHLSL/Semantics/semantic-types.hlsl | 44 +++++ .../test/SemaHLSL/Semantics/vertexid.vs.hlsl | 29 ++-- 8 files changed, 248 insertions(+), 84 deletions(-) create mode 100644 clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl create mode 100644 clang/test/SemaHLSL/Semantics/semantic-types.hlsl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index a7b40a7b64c2d..f51e08097b991 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13676,6 +13676,11 @@ def note_hlsl_semantic_used_here : Note<"%0 used here">; def err_hlsl_unknown_semantic : Error<"unknown HLSL semantic %0">; def err_hlsl_semantic_indexing_not_supported : Error<"semantic %0 does not allow indexing">; +def err_hlsl_semantic_invalid_type + : Error<"semantic %0 must be a " + "%select{scalar|scalar or vector of up to %2 components}1 of " + "%select{16 or 32 bit integer|32 bit integer|" + "16 or 32 bit floating-point}3 type (was %4)">; 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">; diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 6c0e5b52f7cb3..09c0d3e7e0891 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -231,11 +231,6 @@ class SemaHLSL : public SemaBase { QualType ActOnTemplateShorthand(TemplateDecl *Template, SourceLocation NameLoc); - // Diagnose whether the index type is uint/unit2/uint3 type. - bool diagnoseIndexType(QualType T, const ParsedAttr &AL); - // Diagnose whether the type is float/float2/float3/float4 type. - bool diagnoseFloatType(QualType T, const ParsedAttr &AL); - bool CanPerformScalarCast(QualType SrcTy, QualType DestTy); bool CanPerformElementwiseCast(Expr *Src, QualType DestType); bool CanPerformAggregateSplatCast(Expr *Src, QualType DestType); @@ -340,6 +335,11 @@ class SemaHLSL : public SemaBase { llvm::hlsl::IOType CurrentIOType, llvm::dxbc::PSV::SemanticKind SemanticKind); + // Called only for system-value interpretations. + void diagnoseSystemSemanticType(const Decl *D, + const HLSLAppliedSemanticAttr *A, + llvm::dxbc::PSV::SemanticKind SemanticKind); + void handleGlobalStructOrArrayOfWithResources(VarDecl *VD); // Infer a common global binding info for an Expr diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index d9978d1d171bb..b08c190c0ab2c 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1090,8 +1090,15 @@ void SemaHLSL::checkSemanticAnnotation( llvm::hlsl::getSemanticKind(SemanticAttr->getSemanticName()); llvm::hlsl::SemanticInterpretation Interpretation = llvm::hlsl::getInterpretationKind(Kind, ST, SC.CurrentIOType); - if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid) + if (Interpretation == llvm::hlsl::SemanticInterpretation::Invalid) { diagnoseSemanticStageMismatch(SemanticAttr, ST, SC.CurrentIOType, Kind); + return; + } + + // SV_-prefixed names can have arbitrary interpretations, e.g. SV_Position + // on a vertex input. Do not apply system-value constraints to them. + if (Interpretation == llvm::hlsl::SemanticInterpretation::Arbitrary) + return; switch (Kind) { case SemanticKind::DispatchThreadID: @@ -1109,6 +1116,99 @@ void SemaHLSL::checkSemanticAnnotation( default: break; } + + diagnoseSystemSemanticType(Param, SemanticAttr, Kind); +} + +static QualType getElementTypeOf(QualType T, bool IncludeMatrix) { + if (const auto *VT = T->getAs<clang::VectorType>()) + return VT->getElementType(); + if (IncludeMatrix) + if (const auto *MT = T->getAs<clang::MatrixType>()) + return MT->getElementType(); + return T; +} + +static unsigned getComponentCountOf(QualType T) { + if (const auto *VT = T->getAs<clang::VectorType>()) + return VT->getNumElements(); + return 1; +} + +static bool isFloatOrHalfElement(QualType Elem) { + return Elem->isHalfType() || Elem->isFloat16Type() || Elem->isFloat32Type(); +} + +// System-value integer types exclude bool. +static bool isIntElementOfWidth(const ASTContext &Ctx, QualType Elem, + uint64_t Width) { + if (!Elem->isIntegerType() || Elem->isBooleanType()) + return false; + return Ctx.getTypeSize(Elem) == Width; +} + +static bool isIntUpTo32Element(const ASTContext &Ctx, QualType Elem) { + return isIntElementOfWidth(Ctx, Elem, 16) || + isIntElementOfWidth(Ctx, Elem, 32); +} + +void SemaHLSL::diagnoseSystemSemanticType(const Decl *D, + const HLSLAppliedSemanticAttr *A, + SemanticKind Kind) { + ASTContext &Ctx = getASTContext(); + + QualType T; + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + T = FD->getReturnType(); + else + T = cast<ValueDecl>(D)->getType(); + + // `out` and `inout` parameters are passed by reference. + if (const auto *RT = T->getAs<ReferenceType>()) + T = RT->getPointeeType(); + + // Array semantics constrain each element's type. + QualType DeclaredTy = T; + while (const ConstantArrayType *AT = Ctx.getAsConstantArrayType(T)) + T = AT->getElementType(); + + QualType ElemTy = getElementTypeOf(T, /*IncludeMatrix=*/false); + unsigned Components = getComponentCountOf(T); + + // Numeric selectors below choose the shape and element-type wording in + // err_hlsl_semantic_invalid_type. + switch (Kind) { + case SemanticKind::DispatchThreadID: + case SemanticKind::GroupID: + case SemanticKind::GroupThreadID: + if (!isIntUpTo32Element(Ctx, ElemTy) || Components > 3) + Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type) + << A->getAttrName() << /* scalar or vector of up to */ 1 << 3 + << /* 16 or 32 bit integer */ 0 << DeclaredTy; + break; + case SemanticKind::GroupIndex: + if (!isIntElementOfWidth(Ctx, ElemTy, 32) || Components != 1) + Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type) + << A->getAttrName() << /* scalar */ 0 << 1 << /* 32 bit integer */ 1 + << DeclaredTy; + break; + case SemanticKind::VertexID: + if (!isIntUpTo32Element(Ctx, ElemTy) || Components != 1) + Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type) + << A->getAttrName() << /* scalar */ 0 << 1 + << /* 16 or 32 bit integer */ 0 << DeclaredTy; + break; + case SemanticKind::Position: + case SemanticKind::Target: + if (!isFloatOrHalfElement(ElemTy) || Components > 4) + Diag(A->getLoc(), diag::err_hlsl_semantic_invalid_type) + << A->getAttrName() << /* scalar or vector of up to */ 1 << 4 + << /* 16 or 32 bit floating-point */ 2 << DeclaredTy; + break; + default: + // Other recognized system semantics do not yet have type checks. + break; + } } void SemaHLSL::diagnoseAttrStageMismatch( @@ -1876,61 +1976,18 @@ void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) { HLSLVkLocationAttr(getASTContext(), AL, Location)); } -bool SemaHLSL::diagnoseIndexType(QualType T, const ParsedAttr &AL) { - const auto *VT = T->getAs<VectorType>(); - - if (!T->hasUnsignedIntegerRepresentation() || - (VT && VT->getNumElements() > 3)) { - Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) - << AL << "uint/uint2/uint3"; - return false; - } - - return true; -} - -bool SemaHLSL::diagnoseFloatType(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/float4"; - return false; - } - - return true; -} - void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL, SemanticKind Kind, std::optional<unsigned> Index) { - auto *VD = cast<ValueDecl>(D); - QualType ValueType = VD->getType(); - if (auto *FD = dyn_cast<FunctionDecl>(D)) - ValueType = FD->getReturnType(); - - // `out` and `inout` parameters are passed by reference. - if (HLSLParamModifierAttr *MA = D->getAttr<HLSLParamModifierAttr>()) - if (MA->isAnyOut()) - ValueType = cast<ReferenceType>(ValueType)->getPointeeType(); - switch (Kind) { case SemanticKind::DispatchThreadID: case SemanticKind::GroupThreadID: case SemanticKind::GroupID: - diagnoseIndexType(ValueType, AL); - break; case SemanticKind::GroupIndex: - break; case SemanticKind::Position: case SemanticKind::Target: - diagnoseFloatType(ValueType, AL); - break; - case SemanticKind::VertexID: { - uint64_t SizeInBits = SemaRef.Context.getTypeSize(ValueType); - if (!ValueType->isUnsignedIntegerType() || SizeInBits != 32) - Diag(AL.getLoc(), diag::err_hlsl_attr_invalid_type) << AL << "uint"; + case SemanticKind::VertexID: break; - } default: Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; return; @@ -3333,11 +3390,8 @@ static bool CheckFloatRepresentation(Sema *S, SourceLocation Loc, static bool CheckFloatOrHalfRepresentation(Sema *S, SourceLocation Loc, int ArgOrdinal, clang::QualType PassedType) { - clang::QualType BaseType = PassedType; - if (const auto *VT = PassedType->getAs<clang::VectorType>()) - BaseType = VT->getElementType(); - else if (const auto *MT = PassedType->getAs<clang::MatrixType>()) - BaseType = MT->getElementType(); + clang::QualType BaseType = + getElementTypeOf(PassedType, /*IncludeMatrix=*/true); if (!BaseType->isHalfType() && !BaseType->isFloat32Type()) return S->Diag(Loc, diag::err_builtin_invalid_arg_type) diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl index cc457362f8793..1701d0bff0a95 100644 --- a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl +++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl @@ -1,7 +1,9 @@ -// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -ast-dump -verify -o - %s +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s -[numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +// Type and index checks require an entry point. + +[shader("compute")][numthreads(8,8,1)] +// expected-error@+1 {{semantic 'SV_DispatchThreadID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'float')}} void CSMain(float ID : SV_DispatchThreadID) { } @@ -10,9 +12,18 @@ struct ST { int a; float b; }; -[numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_DispatchThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} + +// The second field gets index 1, which SV_DispatchThreadID rejects. +[shader("compute")][numthreads(8,8,1)] void CSMain2(ST ID : SV_DispatchThreadID) { +// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}} +// expected-error@-2 {{semantic 'SV_DispatchThreadID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'float')}} + +} + +[shader("compute")][numthreads(8,8,1)] +// expected-error@+1 {{semantic 'SV_DispatchThreadID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'uint4' (aka 'vector<uint, 4>'))}} +void CSMain3(uint4 ID : SV_DispatchThreadID) { } @@ -28,14 +39,15 @@ struct ST2 { uint s : SV_DispatchThreadID; }; -[numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +[shader("compute")][numthreads(8,8,1)] +// expected-error@+1 {{semantic 'SV_GroupID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'float')}} void CSMain_GID(float ID : SV_GroupID) { } -[numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +[shader("compute")][numthreads(8,8,1)] void CSMain2_GID(ST GID : SV_GroupID) { +// expected-error@-1 {{semantic 'SV_GroupID' does not allow indexing}} +// expected-error@-2 {{semantic 'SV_GroupID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'float')}} } @@ -50,15 +62,15 @@ struct ST2_GID { uint s_gid : SV_GroupID; }; -[numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +[shader("compute")][numthreads(8,8,1)] +// expected-error@+1 {{semantic 'SV_GroupThreadID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'float')}} void CSMain_GThreadID(float ID : SV_GroupThreadID) { } -[numthreads(8,8,1)] -// expected-error@+1 {{attribute 'SV_GroupThreadID' only applies to a field or parameter of type 'uint/uint2/uint3'}} +[shader("compute")][numthreads(8,8,1)] void CSMain2_GThreadID(ST GID : SV_GroupThreadID) { - +// expected-error@-1 {{semantic 'SV_GroupThreadID' does not allow indexing}} +// expected-error@-2 {{semantic 'SV_GroupThreadID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'float')}} } void foo_GThreadID() { @@ -72,6 +84,10 @@ struct ST2_GThreadID { uint s_gthreadid : SV_GroupThreadID; }; +[shader("compute")][numthreads(8,8,1)] +// expected-error@+1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit integer type (was 'uint2' (aka 'vector<uint, 2>'))}} +void CSMain_GIndex(uint2 GI : SV_GroupIndex) { +} [shader("vertex")] // expected-error@+4 {{semantic 'SV_GroupIndex' is not supported in vertex shader inputs}} diff --git a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl index 124d401a9990c..5c7fe8b423110 100644 --- a/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl +++ b/clang/test/SemaHLSL/Semantics/position.ps.size.hlsl @@ -1,10 +1,21 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -x hlsl -finclude-default-header -o - %s -verify -verify-ignore-unexpected // RUN: %clang_cc1 -triple spirv-unknown-vulkan1.3-library -x hlsl -finclude-default-header -o - %s -verify -verify-ignore-unexpected -// expected-error@+1 {{attribute 'SV_Position' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}} -void main(vector<float, 5> a : SV_Position) { +[shader("pixel")] +void too_many_components(vector<float, 5> a : SV_Position) { +// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up to 4 components of 16 or 32 bit floating-point type (was 'vector<float, 5>' (vector of 5 'float' values))}} } -// expected-error@+1 {{attribute 'SV_Position' only applies to a field or parameter of type 'float/float1/float2/float3/float4'}} -void main(int2 a : SV_Position) { +[shader("pixel")] +void not_a_float(int2 a : SV_Position) { +// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up to 4 components of 16 or 32 bit floating-point type (was 'int2' (aka 'vector<int, 2>'))}} +} + +[shader("pixel")] +void too_wide(double4 a : SV_Position) { +// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up to 4 components of 16 or 32 bit floating-point type (was 'double4' (aka 'vector<double, 4>'))}} +} + +[shader("pixel")] +void ok(float4 a : SV_Position) { } diff --git a/clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl b/clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl new file mode 100644 index 0000000000000..6a616c5e2dd2c --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantic-stage.vs-ps.hlsl @@ -0,0 +1,27 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s + +[shader("vertex")] +uint vs_vertexid_out(uint ID : SV_VertexID) : SV_VertexID { return ID; } +// expected-error@-1 {{semantic 'SV_VertexID' is not supported in vertex shader outputs}} + +[shader("pixel")] +float4 ps_dispatchthreadid_in(uint3 ID : SV_DispatchThreadID) : SV_Target { return 0; } +// expected-error@-1 {{semantic 'SV_DispatchThreadID' is not supported in pixel shader inputs}} + +[shader("pixel")] +float4 ps_groupindex_in(uint GI : SV_GroupIndex) : SV_Target { return 0; } +// expected-error@-1 {{semantic 'SV_GroupIndex' is not supported in pixel shader inputs}} + +[shader("pixel")] +void ps_vertexid_out(out uint ID : SV_VertexID) { ID = 0; } +// expected-error@-1 {{semantic 'SV_VertexID' is not supported in pixel shader outputs}} + +// SV_Position is arbitrary on vertex inputs, so int2 is accepted. +[shader("vertex")] +float4 vs_position_in(int2 P : SV_Position) : SV_Position { return 0; } + +[shader("vertex")] +float4 vs_user(float4 A : IN) : OUT { return A; } + +[shader("pixel")] +float4 ps_user_in(float4 A : IN) : SV_Target { return A; } diff --git a/clang/test/SemaHLSL/Semantics/semantic-types.hlsl b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl new file mode 100644 index 0000000000000..0078352ae2b92 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -fnative-half-type -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s + +[shader("pixel")] +double4 target_double(float4 P : SV_Position) : SV_Target { return (double4)P; } +// expected-error@-1 {{semantic 'SV_Target' must be a scalar or vector of up to 4 components of 16 or 32 bit floating-point type (was 'double4' (aka 'vector<double, 4>'))}} + +[shader("pixel")] +float4 position_double(double4 P : SV_Position) : SV_Target { return (float4)P; } +// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up to 4 components of 16 or 32 bit floating-point type (was 'double4' (aka 'vector<double, 4>'))}} + +[shader("pixel")] +float4 position_half(half4 P : SV_Position) : SV_Target { return (float4)P; } + +[shader("compute")][numthreads(1,1,1)] +void group_index_vector(uint2 GI : SV_GroupIndex) {} +// expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit integer type (was 'uint2' (aka 'vector<uint, 2>'))}} + +[shader("compute")][numthreads(1,1,1)] +void group_index_16bit(half GI : SV_GroupIndex) {} +// expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit integer type (was 'half')}} + +[shader("compute")][numthreads(1,1,1)] +void group_index_ok(uint GI : SV_GroupIndex) {} + +[shader("compute")][numthreads(1,1,1)] +void thread_id_signed(int3 ID : SV_DispatchThreadID) {} + +[shader("compute")][numthreads(1,1,1)] +void thread_id_scalar(uint ID : SV_GroupThreadID) {} + +[shader("compute")][numthreads(1,1,1)] +void thread_id_too_wide(uint4 ID : SV_GroupID) {} +// expected-error@-1 {{semantic 'SV_GroupID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'uint4' (aka 'vector<uint, 4>'))}} + +[shader("compute")][numthreads(1,1,1)] +void thread_id_float(float3 ID : SV_GroupID) {} +// expected-error@-1 {{semantic 'SV_GroupID' must be a scalar or vector of up to 3 components of 16 or 32 bit integer type (was 'float3' (aka 'vector<float, 3>'))}} + +// SV_Position on a vertex input is arbitrary, so double4 is allowed. +[shader("vertex")] +float4 position_vs_in(double4 P : SV_Position) : SV_Position { return (float4)P; } + +[shader("pixel")] +float4 user_double(double4 P : USER) : SV_Target { return (float4)P; } diff --git a/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl b/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl index c90cc2113eb43..c29bc89ce0b45 100644 --- a/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl +++ b/clang/test/SemaHLSL/Semantics/vertexid.vs.hlsl @@ -1,36 +1,43 @@ // RUN: %clang_cc1 -fnative-half-type -fnative-int16-type -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -verify -o - %s // RUN: %clang_cc1 -fnative-half-type -fnative-int16-type -triple spirv-pc-vulkan1.3-library -finclude-default-header -x hlsl -verify -o - %s +// SV_VertexID accepts 16- and 32-bit signed or unsigned scalars. + +[shader("vertex")] float bad_type_float(float id : SV_VertexID) : A { -// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}} +// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit integer type (was 'float')}} return id; } +[shader("vertex")] uint3 bad_type_vector(uint3 id : SV_VertexID) : A { -// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}} +// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit integer type (was 'uint3' (aka 'vector<uint, 3>'))}} return id; } -int bad_type_signed(int id : SV_VertexID) : A { -// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}} +[shader("vertex")] +uint64_t bad_type_size(uint64_t id : SV_VertexID) : A { +// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit integer type (was 'uint64_t' (aka 'unsigned long'))}} return id; } -uint64_t bad_type_size(uint64_t id : SV_VertexID) : A { -// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}} +[shader("vertex")] +float bad_type_bool(bool id : SV_VertexID) : A { +// expected-error@-1 {{semantic 'SV_VertexID' must be a scalar of 16 or 32 bit integer type (was 'bool')}} return id; } -uint32_t ok(uint32_t id : SV_VertexID) : A { +[shader("vertex")] +uint32_t ok_unsigned(uint32_t id : SV_VertexID) : A { return id; } -uint16_t bad_type_size_2(uint16_t id : SV_VertexID) : A { -// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}} +[shader("vertex")] +int ok_signed(int id : SV_VertexID) : A { return id; } -char bad_type_size_2(char id : SV_VertexID) : A { -// expected-error@-1 {{attribute 'SV_VertexID' only applies to a field or parameter of type 'uint'}} +[shader("vertex")] +uint16_t ok_16bit(uint16_t id : SV_VertexID) : A { return id; } >From 2f1c5298bf29b471949e66b614a45fb0610ca973 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Wed, 16 Sep 2026 19:03:36 +0000 Subject: [PATCH 2/5] validate semantic indices and render-target bounds --- .../clang/Basic/DiagnosticSemaKinds.td | 2 + clang/include/clang/Sema/SemaHLSL.h | 8 ++- clang/lib/Sema/SemaHLSL.cpp | 52 ++++++++++++------- .../Semantics/invalid_entry_parameter.hlsl | 7 +++ .../Semantics/position.ps.struct.hlsl | 7 +-- .../SemaHLSL/Semantics/semantic-indexing.hlsl | 17 ++++++ .../test/SemaHLSL/Semantics/target.index.hlsl | 37 +++++++++++++ 7 files changed, 107 insertions(+), 23 deletions(-) create mode 100644 clang/test/SemaHLSL/Semantics/target.index.hlsl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index f51e08097b991..15b682a2c6b43 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13676,6 +13676,8 @@ def note_hlsl_semantic_used_here : Note<"%0 used here">; def err_hlsl_unknown_semantic : Error<"unknown HLSL semantic %0">; def err_hlsl_semantic_indexing_not_supported : Error<"semantic %0 does not allow indexing">; +def err_hlsl_semantic_index_out_of_range + : Error<"semantic %0 index %1 exceeds the maximum supported index %2">; def err_hlsl_semantic_invalid_type : Error<"semantic %0 must be a " "%select{scalar|scalar or vector of up to %2 components}1 of " diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 09c0d3e7e0891..4812c36ceac82 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -309,7 +309,8 @@ class SemaHLSL : public SemaBase { void checkSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param, const HLSLAppliedSemanticAttr *SemanticAttr, - const SemanticContext &SC); + const SemanticContext &SC, + unsigned ElementCount); bool determineActiveSemanticOnScalar(FunctionDecl *FD, DeclaratorDecl *OutputDecl, @@ -335,6 +336,11 @@ class SemaHLSL : public SemaBase { llvm::hlsl::IOType CurrentIOType, llvm::dxbc::PSV::SemanticKind SemanticKind); + // Check ElementCount consecutive indices for a system-value interpretation. + void diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A, + llvm::dxbc::PSV::SemanticKind SemanticKind, + unsigned ElementCount); + // Called only for system-value interpretations. void diagnoseSystemSemanticType(const Decl *D, const HLSLAppliedSemanticAttr *A, diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index b08c190c0ab2c..30ff68b5849d0 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -913,7 +913,11 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, if (!A) return false; - checkSemanticAnnotation(FD, D, A, SC); + // Each array element occupies a separate semantic index. + const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType()); + unsigned ElementCount = AT ? AT->getZExtSize() : 1; + + checkSemanticAnnotation(FD, D, A, SC, ElementCount); OutputDecl->addAttr(A); unsigned Location = ActiveSemantic.Index.value_or(0); @@ -933,8 +937,6 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, SC.UsesExplicitVkLocations = HasVkLocation; } - const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType()); - unsigned ElementCount = AT ? AT->getZExtSize() : 1; ActiveSemantic.Index = Location + ElementCount; Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName()); @@ -1081,7 +1083,8 @@ void SemaHLSL::CheckEntryPoint(FunctionDecl *FD) { void SemaHLSL::checkSemanticAnnotation( FunctionDecl *EntryPoint, const Decl *Param, - const HLSLAppliedSemanticAttr *SemanticAttr, const SemanticContext &SC) { + const HLSLAppliedSemanticAttr *SemanticAttr, const SemanticContext &SC, + unsigned ElementCount) { auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>(); assert(ShaderAttr && "Entry point has no shader attribute"); llvm::Triple::EnvironmentType ST = ShaderAttr->getType(); @@ -1100,24 +1103,35 @@ void SemaHLSL::checkSemanticAnnotation( if (Interpretation == llvm::hlsl::SemanticInterpretation::Arbitrary) return; + diagnoseSemanticIndex(SemanticAttr, Kind, ElementCount); + diagnoseSystemSemanticType(Param, SemanticAttr, Kind); +} + +void SemaHLSL::diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A, + SemanticKind Kind, unsigned ElementCount) { + assert(ElementCount > 0 && "a semantic covers at least one element"); + uint32_t LastIndex = A->getSemanticIndex() + ElementCount - 1; + if (LastIndex == 0) + return; + switch (Kind) { - case SemanticKind::DispatchThreadID: - case SemanticKind::GroupID: - case SemanticKind::GroupIndex: - case SemanticKind::GroupThreadID: - if (SemanticAttr->getSemanticIndex() != 0) { - std::string PrettyName = - "'" + SemanticAttr->getSemanticName().str() + "'"; - Diag(SemanticAttr->getLoc(), - diag::err_hlsl_semantic_indexing_not_supported) - << PrettyName; - } - break; + // These semantics are limited by signature packing, not semantic indices. + case SemanticKind::Arbitrary: + case SemanticKind::ClipDistance: + case SemanticKind::CullDistance: + return; + case SemanticKind::Target: { + constexpr unsigned MaxTargetIndex = 7; + if (LastIndex > MaxTargetIndex) + Diag(A->getLoc(), diag::err_hlsl_semantic_index_out_of_range) + << A->getAttrName() << LastIndex << MaxTargetIndex; + } + return; default: - break; + Diag(A->getLoc(), diag::err_hlsl_semantic_indexing_not_supported) + << A->getAttrName(); + return; } - - diagnoseSystemSemanticType(Param, SemanticAttr, Kind); } static QualType getElementTypeOf(QualType T, bool IncludeMatrix) { diff --git a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl index 1701d0bff0a95..1e0fc13517ba5 100644 --- a/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl +++ b/clang/test/SemaHLSL/Semantics/invalid_entry_parameter.hlsl @@ -27,6 +27,13 @@ void CSMain3(uint4 ID : SV_DispatchThreadID) { } +// Array elements also require distinct semantic indices. +[shader("compute")][numthreads(8,8,1)] +void CSMain4(uint3 ID[2] : SV_DispatchThreadID) { +// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}} + +} + void foo() { // expected-warning@+1 {{'SV_DispatchThreadID' attribute only applies to parameters, non-static data members, and functions}} uint V : SV_DispatchThreadID; diff --git a/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl b/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl index d8fdd58ba0855..480c9b41a1899 100644 --- a/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl +++ b/clang/test/SemaHLSL/Semantics/position.ps.struct.hlsl @@ -4,16 +4,17 @@ struct S { float4 f0 : SV_Position; // CHECK: FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:10 f0 'float4':'vector<float, 4>' // CHECK-NEXT: HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:15> "SV_Position" 0 - float4 f1 : SV_Position3; +// Use a user semantic to test non-zero index propagation. + float4 f1 : USER3; // CHECK: FieldDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:10 referenced f1 'float4':'vector<float, 4>' -// CHECK-NEXT: HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:15> "SV_Position" 3 +// CHECK-NEXT: HLSLParsedSemanticAttr 0x{{[0-9a-f]+}} <col:15> "USER" 3 }; float4 main(S s) : SV_Target { // CHECK: FunctionDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> line:[[@LINE-1]]:8 main 'float4 (S)' // CHECK-NEXT: ParmVarDecl 0x{{[0-9a-fA-F]+}} <{{.*}}> col:15 used s 'S' // CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:4:15> "SV_Position" 0 -// CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:7:15> "SV_Position" 3 +// CHECK-NEXT: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <line:8:15> "USER" 3 // CHECK: HLSLAppliedSemanticAttr 0x{{[0-9a-f]+}} <col:20> "SV_Target" 0 return s.f1; diff --git a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl index b78d165c164b1..c0a24929dfb23 100644 --- a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl +++ b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl @@ -17,3 +17,20 @@ void derived_index(Pair GI : SV_GroupIndex) {} [shader("compute")][numthreads(1,1,1)] void no_index(uint GI : SV_GroupIndex) {} + +// An array also derives an index per element. +[shader("compute")][numthreads(1,1,1)] +void array_index(uint3 ID[2] : SV_DispatchThreadID) {} +// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}} + +// SV_Position is non-indexable on pixel shader inputs. +[shader("pixel")] +float4 position_ps(float4 P : SV_Position1) : SV_Target { return P; } +// expected-error@-1 {{semantic 'SV_Position' does not allow indexing}} + +// On vertex shader inputs, SV_Position is arbitrary and may be indexed. +[shader("vertex")] +float4 position_vs(float4 P : SV_Position1) : SV_Position { return P; } + +[shader("pixel")] +float4 user_index(float4 P : USER7) : SV_Target { return P; } diff --git a/clang/test/SemaHLSL/Semantics/target.index.hlsl b/clang/test/SemaHLSL/Semantics/target.index.hlsl new file mode 100644 index 0000000000000..b711c94471b5f --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/target.index.hlsl @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s + +// SV_Target indices select one of eight render target slots. +[shader("pixel")] +float4 target_zero(float4 P : SV_Position) : SV_Target { return P; } + +[shader("pixel")] +float4 target_last(float4 P : SV_Position) : SV_Target7 { return P; } + +[shader("pixel")] +float4 target_past_last(float4 P : SV_Position) : SV_Target8 { return P; } +// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum supported index 7}} + +[shader("pixel")] +float4 target_way_past_last(float4 P : SV_Position) : SV_Target100 { return P; } +// expected-error@-1 {{semantic 'SV_Target' index 100 exceeds the maximum supported index 7}} + +// Both array elements must fit in the render-target index range. +[shader("pixel")] +void target_array_fits(out float4 T[2] : SV_Target6) { T[0] = 0; T[1] = 0; } + +[shader("pixel")] +void target_array_overflows(out float4 T[2] : SV_Target7) { T[0] = 0; T[1] = 0; } +// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum supported index 7}} + +// The return semantic assigns consecutive indices to the fields. +struct TwoTargets { + float4 A; + float4 B; +}; + +[shader("pixel")] +TwoTargets target_struct_fits() : SV_Target6 { return (TwoTargets)0; } + +[shader("pixel")] +TwoTargets target_struct_overflows() : SV_Target7 { return (TwoTargets)0; } +// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum supported index 7}} >From 24e0a148da1573b561151684346ea11dbe0c7306 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Wed, 16 Sep 2026 19:03:44 +0000 Subject: [PATCH 3/5] simplify parsedsemanticattr creation --- clang/include/clang/Sema/SemaHLSL.h | 3 -- clang/lib/Sema/SemaHLSL.cpp | 30 ++++--------------- clang/test/ParserHLSL/semantic_parsing.hlsl | 4 +++ .../SemaHLSL/Semantics/semantic-indexing.hlsl | 12 ++++++++ 4 files changed, 22 insertions(+), 27 deletions(-) diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 4812c36ceac82..e94b4f28c67f7 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -207,9 +207,6 @@ class SemaHLSL : public SemaBase { Location.value_or(0)); } - void diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL, - llvm::dxbc::PSV::SemanticKind SemanticKind, - std::optional<unsigned> Index); void handleSemanticAttr(Decl *D, const ParsedAttr &AL); void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 30ff68b5849d0..111764187ee6a 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -1990,26 +1990,6 @@ void SemaHLSL::handleVkLocationAttr(Decl *D, const ParsedAttr &AL) { HLSLVkLocationAttr(getASTContext(), AL, Location)); } -void SemaHLSL::diagnoseSystemSemanticAttr(Decl *D, const ParsedAttr &AL, - SemanticKind Kind, - std::optional<unsigned> Index) { - switch (Kind) { - case SemanticKind::DispatchThreadID: - case SemanticKind::GroupThreadID: - case SemanticKind::GroupID: - case SemanticKind::GroupIndex: - case SemanticKind::Position: - case SemanticKind::Target: - case SemanticKind::VertexID: - break; - default: - Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; - return; - } - - D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index)); -} - void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) { uint32_t IndexValue(0), ExplicitIndex(0); if (!SemaRef.checkUInt32Argument(AL, AL.getArgAsExpr(0), IndexValue) || @@ -2021,10 +2001,12 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) { ExplicitIndex ? std::optional<unsigned>(IndexValue) : std::nullopt; SemanticKind Kind = llvm::hlsl::getSemanticKind(AL.getAttrName()->getName()); - if (Kind == SemanticKind::Arbitrary) - D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index)); - else - diagnoseSystemSemanticAttr(D, AL, Kind, Index); + if (Kind == SemanticKind::Invalid) { + Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; + return; + } + + D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index)); } void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) { diff --git a/clang/test/ParserHLSL/semantic_parsing.hlsl b/clang/test/ParserHLSL/semantic_parsing.hlsl index 232d47a4da7fc..30218d2334e0e 100644 --- a/clang/test/ParserHLSL/semantic_parsing.hlsl +++ b/clang/test/ParserHLSL/semantic_parsing.hlsl @@ -7,6 +7,10 @@ void Entry(int GI : ) { } // expected-error@+1 {{unknown HLSL semantic 'SV_IWantAPony'}} void Pony(int GI : SV_IWantAPony) { } +// A lowercase SV_ prefix must not turn an unknown name into a user semantic. +// expected-error@+1 {{unknown HLSL semantic 'sv_iwantapony'}} +void IndexedPony(int GI : sv_iwantapony1) { } + // expected-error@+3 {{expected HLSL Semantic identifier}} // expected-error@+2 {{expected ')'}} // expected-note@+1 {{to match this '('}} diff --git a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl index c0a24929dfb23..3c0f37900bc35 100644 --- a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl +++ b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl @@ -34,3 +34,15 @@ float4 position_vs(float4 P : SV_Position1) : SV_Position { return P; } [shader("pixel")] float4 user_index(float4 P : USER7) : SV_Target { return P; } + +// Clip/cull indices are allowed even with a system-value interpretation. +[shader("pixel")] +float4 clip_cull_index(float Clip : SV_ClipDistance1, + float Cull : sv_culldistance1) : SV_Target { + return Clip + Cull; +} + +// Recognizing the name does not bypass shader-stage validation. +[shader("compute")][numthreads(1,1,1)] +void clip_compute(float Clip : SV_ClipDistance1) {} +// expected-error@-1 {{semantic 'SV_ClipDistance' is not supported in compute shader inputs}} >From 58dd2ea3f0a5750e51bbc301523582a158cb4df4 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Wed, 16 Sep 2026 19:04:01 +0000 Subject: [PATCH 4/5] diagnose case-insensitive semantic overlaps with prior locations --- .../clang/Basic/DiagnosticSemaKinds.td | 1 + clang/include/clang/Sema/SemaHLSL.h | 8 +-- clang/lib/Sema/SemaHLSL.cpp | 8 ++- .../SemaHLSL/Semantics/output-parameters.hlsl | 1 + .../SemaHLSL/Semantics/semantic-overlap.hlsl | 62 +++++++++++++++++++ 5 files changed, 73 insertions(+), 7 deletions(-) create mode 100644 clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 15b682a2c6b43..377304b405f05 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13686,6 +13686,7 @@ def err_hlsl_semantic_invalid_type 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 note_hlsl_semantic_index_previous_use : Note<"previous use is here">; def err_hlsl_semantic_unsupported_iotype_for_stage : Error<"semantic %0 is not supported in %1 shader %2">; def err_hlsl_semantic_partial_explicit_indexing diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index e94b4f28c67f7..3c54efe00c830 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -23,7 +23,7 @@ #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" -#include "llvm/ADT/StringSet.h" +#include "llvm/ADT/StringMap.h" #include "llvm/Frontend/HLSL/SemanticSignatures.h" #include "llvm/TargetParser/Triple.h" #include <initializer_list> @@ -292,9 +292,9 @@ class SemaHLSL : public SemaBase { // Present if any semantic sharing the same IO type has an explicit or // implicit SPIR-V location index assigned. std::optional<bool> UsesExplicitVkLocations = std::nullopt; - // The set of semantics found to be active during flattening. Used to detect - // index collisions. - llvm::StringSet<> ActiveSemantics = {}; + // Lowercased semantic names with indices, mapped to their first use for + // overlap diagnostics. + llvm::StringMap<SourceLocation> ActiveSemantics = {}; // The IOType of this semantic set. llvm::hlsl::IOType CurrentIOType; }; diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 111764187ee6a..6a2b70955e512 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -941,12 +941,14 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName()); for (unsigned I = 0; I < ElementCount; ++I) { - Twine VariableName = BaseName.concat(Twine(Location + I)); + std::string VariableName = BaseName.concat(Twine(Location + I)).str(); - auto [_, Inserted] = SC.ActiveSemantics.insert(VariableName.str()); + auto [It, Inserted] = SC.ActiveSemantics.try_emplace( + StringRef(VariableName).lower(), D->getLocation()); if (!Inserted) { Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap) - << VariableName.str(); + << VariableName; + Diag(It->second, diag::note_hlsl_semantic_index_previous_use); return false; } } diff --git a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl index 78dfeb23a2570..06a3b17b13f9f 100644 --- a/clang/test/SemaHLSL/Semantics/output-parameters.hlsl +++ b/clang/test/SemaHLSL/Semantics/output-parameters.hlsl @@ -21,6 +21,7 @@ void cs_group_index_out(out uint GI : SV_GroupIndex) { GI = 0; } [shader("pixel")] float4 ps_overlap(out float4 Color : SV_Target) : SV_Target { // expected-error@-1 {{semantic index overlap SV_Target0}} +// expected-note@-2 {{previous use is here}} Color = 0; return 0; } diff --git a/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl new file mode 100644 index 0000000000000..ad5717c72b7a8 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl @@ -0,0 +1,62 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s + +struct Collide { + float4 A : USER0; +// expected-note@-1 {{previous use is here}} + float4 B : USER0; +// expected-error@-1 {{semantic index overlap USER0}} +// expected-note@-2 {{'B' used here}} +}; + +[shader("pixel")] +float4 collide(Collide C) : SV_Target { return C.A; } +// expected-note@-1 {{'C' declared here}} + +// Semantic names are case-insensitive. +struct CollideCase { + float4 A : user0; +// expected-note@-1 {{previous use is here}} + float4 B : USER0; +// expected-error@-1 {{semantic index overlap USER0}} +// expected-note@-2 {{'B' used here}} +}; + +[shader("pixel")] +float4 collide_case(CollideCase C) : SV_Target { return C.A; } +// expected-note@-1 {{'C' declared here}} + +struct CollidePosition { + float4 A : SV_Position; +// expected-note@-1 {{previous use is here}} + float4 B : sv_position; +// expected-error@-1 {{semantic index overlap sv_position0}} +// expected-note@-2 {{'B' used here}} +}; + +[shader("pixel")] +float4 collide_position(CollidePosition C) : SV_Target { return C.A; } +// expected-note@-1 {{'C' declared here}} + +// The array reserves USER0 and USER1. +struct CollideArray { + float4 A[2] : USER0; +// expected-note@-1 {{previous use is here}} + float4 B : USER1; +// expected-error@-1 {{semantic index overlap USER1}} +// expected-note@-2 {{'B' used here}} +}; + +[shader("pixel")] +float4 collide_array(CollideArray C) : SV_Target { return C.B; } +// expected-note@-1 {{'C' declared here}} + +struct NoCollide { + float4 A : USER0; + float4 B : USER1; +}; + +[shader("pixel")] +float4 no_collide(NoCollide C) : SV_Target { return C.A; } + +[shader("pixel")] +float4 separate_signatures(float4 C : USER0) : SV_Target { return C; } >From 1b47917a4fba2a844d5bffde413f1da39caa8b81 Mon Sep 17 00:00:00 2001 From: Finn Plummer <[email protected]> Date: Wed, 16 Sep 2026 20:02:31 +0000 Subject: [PATCH 5/5] review: clean-ups and corrections --- .../clang/Basic/DiagnosticSemaKinds.td | 3 +- clang/include/clang/Sema/SemaHLSL.h | 9 -- clang/lib/Sema/SemaHLSL.cpp | 91 ++++++++++++------- .../SemaHLSL/Semantics/semantic-indexing.hlsl | 16 ++-- .../SemaHLSL/Semantics/semantic-overlap.hlsl | 28 +++++- .../SemaHLSL/Semantics/semantic-types.hlsl | 9 ++ .../Semantics/semantic-zero-sized-array.hlsl | 34 +++++++ .../test/SemaHLSL/Semantics/target.index.hlsl | 27 ++++++ 8 files changed, 162 insertions(+), 55 deletions(-) create mode 100644 clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 377304b405f05..7068a52b46edb 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -13678,6 +13678,8 @@ def err_hlsl_semantic_indexing_not_supported : Error<"semantic %0 does not allow indexing">; def err_hlsl_semantic_index_out_of_range : Error<"semantic %0 index %1 exceeds the maximum supported index %2">; +def err_hlsl_semantic_zero_sized_array + : Error<"semantic %0 cannot be applied to a zero-sized array">; def err_hlsl_semantic_invalid_type : Error<"semantic %0 must be a " "%select{scalar|scalar or vector of up to %2 components}1 of " @@ -13686,7 +13688,6 @@ def err_hlsl_semantic_invalid_type 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 note_hlsl_semantic_index_previous_use : Note<"previous use is here">; def err_hlsl_semantic_unsupported_iotype_for_stage : Error<"semantic %0 is not supported in %1 shader %2">; def err_hlsl_semantic_partial_explicit_indexing diff --git a/clang/include/clang/Sema/SemaHLSL.h b/clang/include/clang/Sema/SemaHLSL.h index 3c54efe00c830..43852a1ec0804 100644 --- a/clang/include/clang/Sema/SemaHLSL.h +++ b/clang/include/clang/Sema/SemaHLSL.h @@ -199,14 +199,6 @@ class SemaHLSL : public SemaBase { void propagateContextualMatrixLayout(Expr *E, QualType DestType); bool handleResourceTypeAttr(QualType T, const ParsedAttr &AL); - template <typename T> - T *createSemanticAttr(const AttributeCommonInfo &ACI, - std::optional<unsigned> Location) { - return ::new (getASTContext()) - T(getASTContext(), ACI, ACI.getAttrName()->getName(), - Location.value_or(0)); - } - void handleSemanticAttr(Decl *D, const ParsedAttr &AL); void handleVkExtBuiltinInputAttr(Decl *D, const ParsedAttr &AL); @@ -338,7 +330,6 @@ class SemaHLSL : public SemaBase { llvm::dxbc::PSV::SemanticKind SemanticKind, unsigned ElementCount); - // Called only for system-value interpretations. void diagnoseSystemSemanticType(const Decl *D, const HLSLAppliedSemanticAttr *A, llvm::dxbc::PSV::SemanticKind SemanticKind); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp index 6a2b70955e512..99557a55647dd 100644 --- a/clang/lib/Sema/SemaHLSL.cpp +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -914,8 +914,15 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, return false; // Each array element occupies a separate semantic index. - const ConstantArrayType *AT = dyn_cast<ConstantArrayType>(D->getType()); - unsigned ElementCount = AT ? AT->getZExtSize() : 1; + QualType T = D == FD ? FD->getReturnType() : D->getType(); + const ConstantArrayType *AT = + getASTContext().getAsConstantArrayType(T.getNonReferenceType()); + if (isZeroSizedArray(AT)) { + Diag(A->getLoc(), diag::err_hlsl_semantic_zero_sized_array) + << A->getAttrName(); + return false; + } + unsigned ElementCount = AT ? ASTContext::getConstantArrayElementCount(AT) : 1; checkSemanticAnnotation(FD, D, A, SC, ElementCount); OutputDecl->addAttr(A); @@ -939,16 +946,15 @@ bool SemaHLSL::determineActiveSemanticOnScalar(FunctionDecl *FD, ActiveSemantic.Index = Location + ElementCount; - Twine BaseName = Twine(ActiveSemantic.Semantic->getAttrName()->getName()); + StringRef BaseName = ActiveSemantic.Semantic->getAttrName()->getName(); + std::string LowerName = BaseName.lower(); for (unsigned I = 0; I < ElementCount; ++I) { - std::string VariableName = BaseName.concat(Twine(Location + I)).str(); - auto [It, Inserted] = SC.ActiveSemantics.try_emplace( - StringRef(VariableName).lower(), D->getLocation()); + (Twine(LowerName) + Twine(Location + I)).str(), D->getLocation()); if (!Inserted) { Diag(D->getLocation(), diag::err_hlsl_semantic_index_overlap) - << VariableName; - Diag(It->second, diag::note_hlsl_semantic_index_previous_use); + << (BaseName + Twine(Location + I)).str(); + Diag(It->second, diag::note_previous_use); return false; } } @@ -1111,6 +1117,8 @@ void SemaHLSL::checkSemanticAnnotation( void SemaHLSL::diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A, SemanticKind Kind, unsigned ElementCount) { + assert(Kind != SemanticKind::Invalid && Kind != SemanticKind::Arbitrary && + "expected a recognized system semantic"); assert(ElementCount > 0 && "a semantic covers at least one element"); uint32_t LastIndex = A->getSemanticIndex() + ElementCount - 1; if (LastIndex == 0) @@ -1118,7 +1126,6 @@ void SemaHLSL::diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A, switch (Kind) { // These semantics are limited by signature packing, not semantic indices. - case SemanticKind::Arbitrary: case SemanticKind::ClipDistance: case SemanticKind::CullDistance: return; @@ -1127,8 +1134,8 @@ void SemaHLSL::diagnoseSemanticIndex(const HLSLAppliedSemanticAttr *A, if (LastIndex > MaxTargetIndex) Diag(A->getLoc(), diag::err_hlsl_semantic_index_out_of_range) << A->getAttrName() << LastIndex << MaxTargetIndex; - } return; + } default: Diag(A->getLoc(), diag::err_hlsl_semantic_indexing_not_supported) << A->getAttrName(); @@ -1171,6 +1178,8 @@ static bool isIntUpTo32Element(const ASTContext &Ctx, QualType Elem) { void SemaHLSL::diagnoseSystemSemanticType(const Decl *D, const HLSLAppliedSemanticAttr *A, SemanticKind Kind) { + assert(Kind != SemanticKind::Invalid && Kind != SemanticKind::Arbitrary && + "expected a recognized system semantic"); ASTContext &Ctx = getASTContext(); QualType T; @@ -1180,8 +1189,7 @@ void SemaHLSL::diagnoseSystemSemanticType(const Decl *D, T = cast<ValueDecl>(D)->getType(); // `out` and `inout` parameters are passed by reference. - if (const auto *RT = T->getAs<ReferenceType>()) - T = RT->getPointeeType(); + T = T.getNonReferenceType(); // Array semantics constrain each element's type. QualType DeclaredTy = T; @@ -1999,8 +2007,6 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) { assert(0 && "HLSLUnparsedSemantic is expected to have 2 int arguments."); } assert(IndexValue > 0 ? ExplicitIndex : true); - std::optional<unsigned> Index = - ExplicitIndex ? std::optional<unsigned>(IndexValue) : std::nullopt; SemanticKind Kind = llvm::hlsl::getSemanticKind(AL.getAttrName()->getName()); if (Kind == SemanticKind::Invalid) { @@ -2008,7 +2014,39 @@ void SemaHLSL::handleSemanticAttr(Decl *D, const ParsedAttr &AL) { return; } - D->addAttr(createSemanticAttr<HLSLParsedSemanticAttr>(AL, Index)); + switch (Kind) { + // FIXME: These semantics do not yet have CodeGen support. + case SemanticKind::InstanceID: + case SemanticKind::RenderTargetArrayIndex: + case SemanticKind::ViewPortArrayIndex: + case SemanticKind::ClipDistance: + case SemanticKind::CullDistance: + case SemanticKind::OutputControlPointID: + case SemanticKind::DomainLocation: + case SemanticKind::PrimitiveID: + case SemanticKind::GSInstanceID: + case SemanticKind::SampleIndex: + case SemanticKind::IsFrontFace: + case SemanticKind::Coverage: + case SemanticKind::InnerCoverage: + case SemanticKind::Depth: + case SemanticKind::DepthLessEqual: + case SemanticKind::DepthGreaterEqual: + case SemanticKind::StencilRef: + case SemanticKind::TessFactor: + case SemanticKind::InsideTessFactor: + case SemanticKind::ViewID: + case SemanticKind::Barycentrics: + case SemanticKind::ShadingRate: + case SemanticKind::CullPrimitive: + Diag(AL.getLoc(), diag::err_hlsl_unknown_semantic) << AL; + return; + default: + break; + } + + D->addAttr(HLSLParsedSemanticAttr::Create( + getASTContext(), AL.getAttrName()->getName(), IndexValue, AL)); } void SemaHLSL::handlePackOffsetAttr(Decl *D, const ParsedAttr &AL) { @@ -3375,9 +3413,7 @@ static bool CheckFloatRepresentation(Sema *S, SourceLocation Loc, int ArgOrdinal, clang::QualType PassedType) { clang::QualType BaseType = - PassedType->isVectorType() - ? PassedType->castAs<clang::VectorType>()->getElementType() - : PassedType; + getElementTypeOf(PassedType, /*IncludeMatrix=*/false); if (!BaseType->isFloat32Type()) return S->Diag(Loc, diag::err_builtin_invalid_arg_type) << ArgOrdinal << /* scalar or vector of */ 5 << /* no int */ 0 @@ -3402,11 +3438,7 @@ static bool CheckAnyDoubleRepresentation(Sema *S, SourceLocation Loc, int ArgOrdinal, clang::QualType PassedType) { clang::QualType BaseType = - PassedType->isVectorType() - ? PassedType->castAs<clang::VectorType>()->getElementType() - : PassedType->isMatrixType() - ? PassedType->castAs<clang::MatrixType>()->getElementType() - : PassedType; + getElementTypeOf(PassedType, /*IncludeMatrix=*/true); if (!BaseType->isDoubleType()) { // FIXME: adopt standard `err_builtin_invalid_arg_type` instead of using // this custom error. @@ -3806,22 +3838,13 @@ static StringRef getCurrentResourceMethodName(Sema &S, StringRef DefaultName) { return MD->getName(); } -// Returns the element type of a typed resource's contained type. Typed resource -// element types are scalars or vectors of scalars, so anything that is not a -// vector is already the element type. -static QualType getTypedResourceElementType(QualType ContainedType) { - if (const auto *VecTy = ContainedType->getAs<VectorType>()) - return VecTy->getElementType(); - return ContainedType; -} - // Sampling from and gathering on resources with a 'double' element type is not // supported. Such resources are still valid declarations whose contents can be // accessed by other means, like Load or the subscript operator. static bool CheckNoDoubleElementType(Sema &S, CallExpr *TheCall, QualType ContainedType, StringRef DefaultName) { - QualType EltTy = getTypedResourceElementType(ContainedType); + QualType EltTy = getElementTypeOf(ContainedType, /*IncludeMatrix=*/false); if (!EltTy->isSpecificBuiltinType(BuiltinType::Double)) return false; @@ -3843,7 +3866,7 @@ static bool CheckIntegerElementTypeShaderModel(Sema &S, CallExpr *TheCall, // 'bool' is an integer type in HLSL, but sampling bool resources is never // allowed, so it must not be reported as requiring shader model 6.7. - QualType EltTy = getTypedResourceElementType(ContainedType); + QualType EltTy = getElementTypeOf(ContainedType, /*IncludeMatrix=*/false); if (!EltTy->isIntegerType() || EltTy->isBooleanType()) return false; diff --git a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl index 3c0f37900bc35..733df5f14b02b 100644 --- a/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl +++ b/clang/test/SemaHLSL/Semantics/semantic-indexing.hlsl @@ -23,6 +23,11 @@ void no_index(uint GI : SV_GroupIndex) {} void array_index(uint3 ID[2] : SV_DispatchThreadID) {} // expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}} +// Inner dimensions also derive semantic indices. +[shader("compute")][numthreads(1,1,1)] +void nested_array_index(uint3 ID[1][2] : SV_DispatchThreadID) {} +// expected-error@-1 {{semantic 'SV_DispatchThreadID' does not allow indexing}} + // SV_Position is non-indexable on pixel shader inputs. [shader("pixel")] float4 position_ps(float4 P : SV_Position1) : SV_Target { return P; } @@ -35,14 +40,7 @@ float4 position_vs(float4 P : SV_Position1) : SV_Position { return P; } [shader("pixel")] float4 user_index(float4 P : USER7) : SV_Target { return P; } -// Clip/cull indices are allowed even with a system-value interpretation. -[shader("pixel")] -float4 clip_cull_index(float Clip : SV_ClipDistance1, - float Cull : sv_culldistance1) : SV_Target { - return Clip + Cull; -} - // Recognizing the name does not bypass shader-stage validation. [shader("compute")][numthreads(1,1,1)] -void clip_compute(float Clip : SV_ClipDistance1) {} -// expected-error@-1 {{semantic 'SV_ClipDistance' is not supported in compute shader inputs}} +void position_compute(float4 P : SV_Position) {} +// expected-error@-1 {{semantic 'SV_Position' is not supported in compute shader inputs}} diff --git a/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl index ad5717c72b7a8..ad52208ea7654 100644 --- a/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl +++ b/clang/test/SemaHLSL/Semantics/semantic-overlap.hlsl @@ -50,6 +50,29 @@ struct CollideArray { float4 collide_array(CollideArray C) : SV_Target { return C.B; } // expected-note@-1 {{'C' declared here}} +// All four elements of the nested array reserve semantic indices. +struct CollideNestedArray { + float4 A[2][2] : USER0; +// expected-note@-1 {{previous use is here}} + float4 B : USER3; +// expected-error@-1 {{semantic index overlap USER3}} +// expected-note@-2 {{'B' used here}} +}; + +[shader("pixel")] +float4 collide_nested_array(CollideNestedArray C) : SV_Target { return C.B; } +// expected-note@-1 {{'C' declared here}} + +struct NoCollideNestedArray { + float4 A[2][2] : USER0; + float4 B : USER4; +}; + +[shader("pixel")] +float4 no_collide_nested_array(NoCollideNestedArray C) : SV_Target { + return C.B; +} + struct NoCollide { float4 A : USER0; float4 B : USER1; @@ -58,5 +81,6 @@ struct NoCollide { [shader("pixel")] float4 no_collide(NoCollide C) : SV_Target { return C.A; } -[shader("pixel")] -float4 separate_signatures(float4 C : USER0) : SV_Target { return C; } +// The same semantic index is allowed in separate input and output signatures. +[shader("vertex")] +float4 separate_signatures(float4 C : USER0) : USER0 { return C; } diff --git a/clang/test/SemaHLSL/Semantics/semantic-types.hlsl b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl index 0078352ae2b92..84c5347f641a1 100644 --- a/clang/test/SemaHLSL/Semantics/semantic-types.hlsl +++ b/clang/test/SemaHLSL/Semantics/semantic-types.hlsl @@ -11,6 +11,15 @@ float4 position_double(double4 P : SV_Position) : SV_Target { return (float4)P; [shader("pixel")] float4 position_half(half4 P : SV_Position) : SV_Target { return (float4)P; } +// A single matrix element does not make a matrix a scalar semantic value. +[shader("pixel")] +float4 position_matrix(float1x1 P : SV_Position) : SV_Target { return 0; } +// expected-error@-1 {{semantic 'SV_Position' must be a scalar or vector of up to 4 components of 16 or 32 bit floating-point type (was 'float1x1' (aka 'matrix<float, 1, 1>'))}} + +[shader("compute")][numthreads(1,1,1)] +void group_index_matrix(uint1x1 GI : SV_GroupIndex) {} +// expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit integer type (was 'uint1x1' (aka 'matrix<uint, 1, 1>'))}} + [shader("compute")][numthreads(1,1,1)] void group_index_vector(uint2 GI : SV_GroupIndex) {} // expected-error@-1 {{semantic 'SV_GroupIndex' must be a scalar of 32 bit integer type (was 'uint2' (aka 'vector<uint, 2>'))}} diff --git a/clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl b/clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl new file mode 100644 index 0000000000000..3805c6c20a837 --- /dev/null +++ b/clang/test/SemaHLSL/Semantics/semantic-zero-sized-array.hlsl @@ -0,0 +1,34 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -finclude-default-header -x hlsl -fsyntax-only -verify %s + +[shader("compute")][numthreads(1,1,1)] +void empty_input(uint V[0] : SV_GroupIndex) {} +// expected-error@-1 {{semantic 'SV_GroupIndex' cannot be applied to a zero-sized array}} +// expected-note@-2 {{'V' declared here}} + +[shader("pixel")] +void empty_output(out float4 V[0] : SV_Target) {} +// expected-error@-1 {{semantic 'SV_Target' cannot be applied to a zero-sized array}} +// expected-note@-2 {{'V' declared here}} + +// A zero in any array dimension makes the semantic range empty. +[shader("compute")][numthreads(1,1,1)] +void empty_inner_dimension(uint V[2][0] : SV_GroupIndex) {} +// expected-error@-1 {{semantic 'SV_GroupIndex' cannot be applied to a zero-sized array}} +// expected-note@-2 {{'V' declared here}} + +// User semantics and typedefs must not bypass the check. +typedef float4 EmptyArray[0]; + +[shader("vertex")] +float4 empty_user_input(EmptyArray V : USER) : SV_Position { return 0; } +// expected-error@-1 {{semantic 'USER' cannot be applied to a zero-sized array}} +// expected-note@-2 {{'V' declared here}} + +struct EmptyOutput { + EmptyArray V : SV_Target; +// expected-error@-1 {{semantic 'SV_Target' cannot be applied to a zero-sized array}} +// expected-note@-2 {{'V' used here}} +}; + +[shader("pixel")] +EmptyOutput empty_return() { return (EmptyOutput)0; } diff --git a/clang/test/SemaHLSL/Semantics/target.index.hlsl b/clang/test/SemaHLSL/Semantics/target.index.hlsl index b711c94471b5f..ee17e0516481c 100644 --- a/clang/test/SemaHLSL/Semantics/target.index.hlsl +++ b/clang/test/SemaHLSL/Semantics/target.index.hlsl @@ -23,6 +23,33 @@ void target_array_fits(out float4 T[2] : SV_Target6) { T[0] = 0; T[1] = 0; } void target_array_overflows(out float4 T[2] : SV_Target7) { T[0] = 0; T[1] = 0; } // expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum supported index 7}} +// Every dimension contributes to the semantic index range. +typedef float4 TargetGrid[2][2]; + +[shader("pixel")] +void target_nested_array_fits(out TargetGrid T : SV_Target4) {} + +[shader("pixel")] +void target_nested_array_overflows(out float4 T[2][2] : SV_Target5) {} +// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum supported index 7}} + +struct NestedTargets { + TargetGrid A; + float4 B; +}; + +// Inherited indices must advance past every element of the nested array. +[shader("pixel")] +NestedTargets target_nested_struct_fits() : SV_Target3 { + return (NestedTargets)0; +} + +[shader("pixel")] +NestedTargets target_nested_struct_overflows() : SV_Target4 { +// expected-error@-1 {{semantic 'SV_Target' index 8 exceeds the maximum supported index 7}} + return (NestedTargets)0; +} + // The return semantic assigns consecutive indices to the fields. struct TwoTargets { float4 A; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
