clang 19.0.0git
SemaHLSL.cpp
Go to the documentation of this file.
1//===- SemaHLSL.cpp - Semantic Analysis for HLSL constructs ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8// This implements Semantic Analysis for HLSL constructs.
9//===----------------------------------------------------------------------===//
10
11#include "clang/Sema/SemaHLSL.h"
13#include "clang/Basic/LLVM.h"
15#include "clang/Sema/Sema.h"
16#include "llvm/ADT/STLExtras.h"
17#include "llvm/ADT/StringExtras.h"
18#include "llvm/ADT/StringRef.h"
19#include "llvm/Support/ErrorHandling.h"
20#include "llvm/TargetParser/Triple.h"
21#include <iterator>
22
23using namespace clang;
24
26
27Decl *SemaHLSL::ActOnStartBuffer(Scope *BufferScope, bool CBuffer,
28 SourceLocation KwLoc, IdentifierInfo *Ident,
29 SourceLocation IdentLoc,
30 SourceLocation LBrace) {
31 // For anonymous namespace, take the location of the left brace.
32 DeclContext *LexicalParent = SemaRef.getCurLexicalContext();
34 getASTContext(), LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
35
36 SemaRef.PushOnScopeChains(Result, BufferScope);
37 SemaRef.PushDeclContext(BufferScope, Result);
38
39 return Result;
40}
41
42// Calculate the size of a legacy cbuffer type based on
43// https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-packing-rules
44static unsigned calculateLegacyCbufferSize(const ASTContext &Context,
45 QualType T) {
46 unsigned Size = 0;
47 constexpr unsigned CBufferAlign = 128;
48 if (const RecordType *RT = T->getAs<RecordType>()) {
49 const RecordDecl *RD = RT->getDecl();
50 for (const FieldDecl *Field : RD->fields()) {
51 QualType Ty = Field->getType();
52 unsigned FieldSize = calculateLegacyCbufferSize(Context, Ty);
53 unsigned FieldAlign = 32;
54 if (Ty->isAggregateType())
55 FieldAlign = CBufferAlign;
56 Size = llvm::alignTo(Size, FieldAlign);
57 Size += FieldSize;
58 }
59 } else if (const ConstantArrayType *AT = Context.getAsConstantArrayType(T)) {
60 if (unsigned ElementCount = AT->getSize().getZExtValue()) {
61 unsigned ElementSize =
62 calculateLegacyCbufferSize(Context, AT->getElementType());
63 unsigned AlignedElementSize = llvm::alignTo(ElementSize, CBufferAlign);
64 Size = AlignedElementSize * (ElementCount - 1) + ElementSize;
65 }
66 } else if (const VectorType *VT = T->getAs<VectorType>()) {
67 unsigned ElementCount = VT->getNumElements();
68 unsigned ElementSize =
69 calculateLegacyCbufferSize(Context, VT->getElementType());
70 Size = ElementSize * ElementCount;
71 } else {
72 Size = Context.getTypeSize(T);
73 }
74 return Size;
75}
76
78 auto *BufDecl = cast<HLSLBufferDecl>(Dcl);
79 BufDecl->setRBraceLoc(RBrace);
80
81 // Validate packoffset.
83 bool HasPackOffset = false;
84 bool HasNonPackOffset = false;
85 for (auto *Field : BufDecl->decls()) {
86 VarDecl *Var = dyn_cast<VarDecl>(Field);
87 if (!Var)
88 continue;
89 if (Field->hasAttr<HLSLPackOffsetAttr>()) {
90 PackOffsetVec.emplace_back(Var, Field->getAttr<HLSLPackOffsetAttr>());
91 HasPackOffset = true;
92 } else {
93 HasNonPackOffset = true;
94 }
95 }
96
97 if (HasPackOffset && HasNonPackOffset)
98 Diag(BufDecl->getLocation(), diag::warn_hlsl_packoffset_mix);
99
100 if (HasPackOffset) {
101 ASTContext &Context = getASTContext();
102 // Make sure no overlap in packoffset.
103 // Sort PackOffsetVec by offset.
104 std::sort(PackOffsetVec.begin(), PackOffsetVec.end(),
105 [](const std::pair<VarDecl *, HLSLPackOffsetAttr *> &LHS,
106 const std::pair<VarDecl *, HLSLPackOffsetAttr *> &RHS) {
107 return LHS.second->getOffset() < RHS.second->getOffset();
108 });
109
110 for (unsigned i = 0; i < PackOffsetVec.size() - 1; i++) {
111 VarDecl *Var = PackOffsetVec[i].first;
112 HLSLPackOffsetAttr *Attr = PackOffsetVec[i].second;
113 unsigned Size = calculateLegacyCbufferSize(Context, Var->getType());
114 unsigned Begin = Attr->getOffset() * 32;
115 unsigned End = Begin + Size;
116 unsigned NextBegin = PackOffsetVec[i + 1].second->getOffset() * 32;
117 if (End > NextBegin) {
118 VarDecl *NextVar = PackOffsetVec[i + 1].first;
119 Diag(NextVar->getLocation(), diag::err_hlsl_packoffset_overlap)
120 << NextVar << Var;
121 }
122 }
123 }
124
126}
127
128HLSLNumThreadsAttr *SemaHLSL::mergeNumThreadsAttr(Decl *D,
129 const AttributeCommonInfo &AL,
130 int X, int Y, int Z) {
131 if (HLSLNumThreadsAttr *NT = D->getAttr<HLSLNumThreadsAttr>()) {
132 if (NT->getX() != X || NT->getY() != Y || NT->getZ() != Z) {
133 Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL;
134 Diag(AL.getLoc(), diag::note_conflicting_attribute);
135 }
136 return nullptr;
137 }
138 return ::new (getASTContext())
139 HLSLNumThreadsAttr(getASTContext(), AL, X, Y, Z);
140}
141
142HLSLShaderAttr *
144 HLSLShaderAttr::ShaderType ShaderType) {
145 if (HLSLShaderAttr *NT = D->getAttr<HLSLShaderAttr>()) {
146 if (NT->getType() != ShaderType) {
147 Diag(NT->getLocation(), diag::err_hlsl_attribute_param_mismatch) << AL;
148 Diag(AL.getLoc(), diag::note_conflicting_attribute);
149 }
150 return nullptr;
151 }
152 return HLSLShaderAttr::Create(getASTContext(), ShaderType, AL);
153}
154
155HLSLParamModifierAttr *
157 HLSLParamModifierAttr::Spelling Spelling) {
158 // We can only merge an `in` attribute with an `out` attribute. All other
159 // combinations of duplicated attributes are ill-formed.
160 if (HLSLParamModifierAttr *PA = D->getAttr<HLSLParamModifierAttr>()) {
161 if ((PA->isIn() && Spelling == HLSLParamModifierAttr::Keyword_out) ||
162 (PA->isOut() && Spelling == HLSLParamModifierAttr::Keyword_in)) {
163 D->dropAttr<HLSLParamModifierAttr>();
164 SourceRange AdjustedRange = {PA->getLocation(), AL.getRange().getEnd()};
165 return HLSLParamModifierAttr::Create(
166 getASTContext(), /*MergedSpelling=*/true, AdjustedRange,
167 HLSLParamModifierAttr::Keyword_inout);
168 }
169 Diag(AL.getLoc(), diag::err_hlsl_duplicate_parameter_modifier) << AL;
170 Diag(PA->getLocation(), diag::note_conflicting_attribute);
171 return nullptr;
172 }
173 return HLSLParamModifierAttr::Create(getASTContext(), AL);
174}
175
178
180 return;
181
182 StringRef Env = TargetInfo.getTriple().getEnvironmentName();
183 HLSLShaderAttr::ShaderType ShaderType;
184 if (HLSLShaderAttr::ConvertStrToShaderType(Env, ShaderType)) {
185 if (const auto *Shader = FD->getAttr<HLSLShaderAttr>()) {
186 // The entry point is already annotated - check that it matches the
187 // triple.
188 if (Shader->getType() != ShaderType) {
189 Diag(Shader->getLocation(), diag::err_hlsl_entry_shader_attr_mismatch)
190 << Shader;
191 FD->setInvalidDecl();
192 }
193 } else {
194 // Implicitly add the shader attribute if the entry function isn't
195 // explicitly annotated.
196 FD->addAttr(HLSLShaderAttr::CreateImplicit(getASTContext(), ShaderType,
197 FD->getBeginLoc()));
198 }
199 } else {
200 switch (TargetInfo.getTriple().getEnvironment()) {
201 case llvm::Triple::UnknownEnvironment:
202 case llvm::Triple::Library:
203 break;
204 default:
205 llvm_unreachable("Unhandled environment in triple");
206 }
207 }
208}
209
211 const auto *ShaderAttr = FD->getAttr<HLSLShaderAttr>();
212 assert(ShaderAttr && "Entry point has no shader attribute");
213 HLSLShaderAttr::ShaderType ST = ShaderAttr->getType();
214
215 switch (ST) {
216 case HLSLShaderAttr::Pixel:
217 case HLSLShaderAttr::Vertex:
218 case HLSLShaderAttr::Geometry:
219 case HLSLShaderAttr::Hull:
220 case HLSLShaderAttr::Domain:
221 case HLSLShaderAttr::RayGeneration:
222 case HLSLShaderAttr::Intersection:
223 case HLSLShaderAttr::AnyHit:
224 case HLSLShaderAttr::ClosestHit:
225 case HLSLShaderAttr::Miss:
226 case HLSLShaderAttr::Callable:
227 if (const auto *NT = FD->getAttr<HLSLNumThreadsAttr>()) {
229 {HLSLShaderAttr::Compute,
230 HLSLShaderAttr::Amplification,
231 HLSLShaderAttr::Mesh});
232 FD->setInvalidDecl();
233 }
234 break;
235
236 case HLSLShaderAttr::Compute:
237 case HLSLShaderAttr::Amplification:
238 case HLSLShaderAttr::Mesh:
239 if (!FD->hasAttr<HLSLNumThreadsAttr>()) {
240 Diag(FD->getLocation(), diag::err_hlsl_missing_numthreads)
241 << HLSLShaderAttr::ConvertShaderTypeToStr(ST);
242 FD->setInvalidDecl();
243 }
244 break;
245 }
246
247 for (ParmVarDecl *Param : FD->parameters()) {
248 if (const auto *AnnotationAttr = Param->getAttr<HLSLAnnotationAttr>()) {
249 CheckSemanticAnnotation(FD, Param, AnnotationAttr);
250 } else {
251 // FIXME: Handle struct parameters where annotations are on struct fields.
252 // See: https://github.com/llvm/llvm-project/issues/57875
253 Diag(FD->getLocation(), diag::err_hlsl_missing_semantic_annotation);
254 Diag(Param->getLocation(), diag::note_previous_decl) << Param;
255 FD->setInvalidDecl();
256 }
257 }
258 // FIXME: Verify return type semantic annotation.
259}
260
262 FunctionDecl *EntryPoint, const Decl *Param,
263 const HLSLAnnotationAttr *AnnotationAttr) {
264 auto *ShaderAttr = EntryPoint->getAttr<HLSLShaderAttr>();
265 assert(ShaderAttr && "Entry point has no shader attribute");
266 HLSLShaderAttr::ShaderType ST = ShaderAttr->getType();
267
268 switch (AnnotationAttr->getKind()) {
269 case attr::HLSLSV_DispatchThreadID:
270 case attr::HLSLSV_GroupIndex:
271 if (ST == HLSLShaderAttr::Compute)
272 return;
273 DiagnoseAttrStageMismatch(AnnotationAttr, ST, {HLSLShaderAttr::Compute});
274 break;
275 default:
276 llvm_unreachable("Unknown HLSLAnnotationAttr");
277 }
278}
279
281 const Attr *A, HLSLShaderAttr::ShaderType Stage,
282 std::initializer_list<HLSLShaderAttr::ShaderType> AllowedStages) {
283 SmallVector<StringRef, 8> StageStrings;
284 llvm::transform(AllowedStages, std::back_inserter(StageStrings),
285 [](HLSLShaderAttr::ShaderType ST) {
286 return StringRef(
287 HLSLShaderAttr::ConvertShaderTypeToStr(ST));
288 });
289 Diag(A->getLoc(), diag::err_hlsl_attr_unsupported_in_stage)
290 << A << HLSLShaderAttr::ConvertShaderTypeToStr(Stage)
291 << (AllowedStages.size() != 1) << join(StageStrings, ", ");
292}
const Environment & Env
Definition: HTMLLogger.cpp:148
#define X(type, name)
Definition: Value.h:143
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
static unsigned calculateLegacyCbufferSize(const ASTContext &Context, QualType T)
Definition: SemaHLSL.cpp:44
This file declares semantic analysis for HLSL constructs.
SourceLocation Begin
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
const ConstantArrayType * getAsConstantArrayType(QualType T) const
Definition: ASTContext.h:2768
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
Definition: ASTContext.h:2341
const TargetInfo & getTargetInfo() const
Definition: ASTContext.h:757
Attr - This represents one attribute.
Definition: Attr.h:42
attr::Kind getKind() const
Definition: Attr.h:88
SourceLocation getLoc() const
Represents the canonical version of C arrays with a specified constant size.
Definition: Type.h:3556
DeclContext - This is used only as base class of specific decl types that can act as declaration cont...
Definition: DeclBase.h:1436
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
T * getAttr() const
Definition: DeclBase.h:579
void addAttr(Attr *A)
Definition: DeclBase.cpp:991
void setInvalidDecl(bool Invalid=true)
setInvalidDecl - Indicates the Decl had a semantic error.
Definition: DeclBase.cpp:132
SourceLocation getLocation() const
Definition: DeclBase.h:445
void dropAttr()
Definition: DeclBase.h:562
bool hasAttr() const
Definition: DeclBase.h:583
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Decl.h:822
Represents a member of a struct/union/class.
Definition: Decl.h:3057
Represents a function declaration or definition.
Definition: Decl.h:1971
ArrayRef< ParmVarDecl * > parameters() const
Definition: Decl.h:2683
HLSLBufferDecl - Represent a cbuffer or tbuffer declaration.
Definition: Decl.h:4940
static HLSLBufferDecl * Create(ASTContext &C, DeclContext *LexicalParent, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *ID, SourceLocation IDLoc, SourceLocation LBrace)
Definition: Decl.cpp:5644
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:276
Represents a parameter to a function.
Definition: Decl.h:1761
A (possibly-)qualified type.
Definition: Type.h:940
Represents a struct/union/class.
Definition: Decl.h:4168
field_range fields() const
Definition: Decl.h:4374
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Definition: Type.h:5549
Scope - A scope is a transient data structure that is used while parsing the program.
Definition: Scope.h:41
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:56
ASTContext & getASTContext() const
Definition: SemaBase.cpp:9
Sema & SemaRef
Definition: SemaBase.h:40
void CheckEntryPoint(FunctionDecl *FD)
Definition: SemaHLSL.cpp:210
HLSLNumThreadsAttr * mergeNumThreadsAttr(Decl *D, const AttributeCommonInfo &AL, int X, int Y, int Z)
Definition: SemaHLSL.cpp:128
void CheckSemanticAnnotation(FunctionDecl *EntryPoint, const Decl *Param, const HLSLAnnotationAttr *AnnotationAttr)
Definition: SemaHLSL.cpp:261
HLSLShaderAttr * mergeShaderAttr(Decl *D, const AttributeCommonInfo &AL, HLSLShaderAttr::ShaderType ShaderType)
Definition: SemaHLSL.cpp:143
void ActOnTopLevelFunction(FunctionDecl *FD)
Definition: SemaHLSL.cpp:176
void ActOnFinishBuffer(Decl *Dcl, SourceLocation RBrace)
Definition: SemaHLSL.cpp:77
HLSLParamModifierAttr * mergeParamModifierAttr(Decl *D, const AttributeCommonInfo &AL, HLSLParamModifierAttr::Spelling Spelling)
Definition: SemaHLSL.cpp:156
SemaHLSL(Sema &S)
Definition: SemaHLSL.cpp:25
Decl * ActOnStartBuffer(Scope *BufferScope, bool CBuffer, SourceLocation KwLoc, IdentifierInfo *Ident, SourceLocation IdentLoc, SourceLocation LBrace)
Definition: SemaHLSL.cpp:27
void DiagnoseAttrStageMismatch(const Attr *A, HLSLShaderAttr::ShaderType Stage, std::initializer_list< HLSLShaderAttr::ShaderType > AllowedStages)
Definition: SemaHLSL.cpp:280
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:451
void PushOnScopeChains(NamedDecl *D, Scope *S, bool AddToContext=true)
Add this decl to the scope shadowed decl chains.
Definition: SemaDecl.cpp:1523
DeclContext * getCurLexicalContext() const
Definition: Sema.h:692
void PushDeclContext(Scope *S, DeclContext *DC)
Set the current declaration context until it gets popped.
Definition: SemaDecl.cpp:1330
void PopDeclContext()
Definition: SemaDecl.cpp:1337
Encodes a location in the source.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
Exposes information about the current target.
Definition: TargetInfo.h:218
TargetOptions & getTargetOpts() const
Retrieve the target options.
Definition: TargetInfo.h:312
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
Definition: TargetInfo.h:1256
std::string HLSLEntry
The entry point name for HLSL shader being compiled as specified by -E.
bool isAggregateType() const
Determines whether the type is a C++ aggregate type or C aggregate or union type.
Definition: Type.cpp:2327
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:8126
QualType getType() const
Definition: Decl.h:717
Represents a variable declaration or definition.
Definition: Decl.h:918
Represents a GCC generic vector type.
Definition: Type.h:3969
Defines the clang::TargetInfo interface.
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
const FunctionProtoType * T