clang 24.0.0git
ParseHLSL.cpp
Go to the documentation of this file.
1//===--- ParseHLSL.cpp - HLSL-specific parsing support --------------------===//
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//
9// This file implements the parsing logic for HLSL language features.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/AST/Attr.h"
16#include "clang/Parse/Parser.h"
18#include "clang/Sema/SemaHLSL.h"
19#include <limits>
20
21using namespace clang;
22
24 SourceLocation BufferLoc,
25 bool IsCBuffer, Parser &P) {
26 // The parse is failed, just return false.
27 if (!DG)
28 return false;
29 DeclGroupRef Decls = DG.get();
30 bool IsValid = true;
31 // Only allow function, variable, record, and empty decls inside HLSLBuffer.
32 for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
33 Decl *D = *I;
35 continue;
36
37 // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
39 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
40 << IsCBuffer;
41 IsValid = false;
42 continue;
43 }
44
45 IsValid = false;
46 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
47 << IsCBuffer;
48 }
49 return IsValid;
50}
51
52Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd,
53 ParsedAttributes &Attrs) {
54 assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
55 "Not a cbuffer or tbuffer!");
56 bool IsCBuffer = Tok.is(tok::kw_cbuffer);
57 SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
58
59 if (!Tok.is(tok::identifier)) {
60 Diag(Tok, diag::err_expected) << tok::identifier;
61 return nullptr;
62 }
63
64 IdentifierInfo *Identifier = Tok.getIdentifierInfo();
65 SourceLocation IdentifierLoc = ConsumeToken();
66
67 MaybeParseHLSLAnnotations(Attrs, nullptr);
68
69 ParseScope BufferScope(this, Scope::DeclScope);
70 BalancedDelimiterTracker T(*this, tok::l_brace);
71 if (T.consumeOpen()) {
72 Diag(Tok, diag::err_expected) << tok::l_brace;
73 return nullptr;
74 }
75
76 Decl *D = Actions.HLSL().ActOnStartBuffer(getCurScope(), IsCBuffer, BufferLoc,
77 Identifier, IdentifierLoc,
78 T.getOpenLocation());
79 Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
80
81 while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
82 // FIXME: support attribute on constants inside cbuffer/tbuffer.
83 ParsedAttributes DeclAttrs(AttrFactory);
84 ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
85
87 ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
88 if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
89 *this)) {
90 T.skipToEnd();
91 DeclEnd = T.getCloseLocation();
92 BufferScope.Exit();
93 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
94 return nullptr;
95 }
96 }
97
98 T.consumeClose();
99 DeclEnd = T.getCloseLocation();
100 BufferScope.Exit();
101 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
102
103 return D;
104}
105
106static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
107 Token Tok, ArgsVector &ArgExprs,
108 Parser &P, ASTContext &Ctx,
109 Preprocessor &PP) {
110 StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
111 SourceLocation EndNumLoc = Tok.getEndLoc();
112
113 P.ConsumeToken(); // consume constant.
114 std::string FixedArg = ArgStr.str() + Num.str();
115 P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
116 << FixedArg
117 << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
118 ArgsUnion &Slot = ArgExprs.back();
119 Slot = new (Ctx) IdentifierLoc(ArgLoc, PP.getIdentifierInfo(FixedArg));
120}
121
122Parser::ParsedSemantic Parser::ParseHLSLSemantic() {
123 assert(Tok.is(tok::identifier) && "Not a HLSL Annotation");
124
125 // Semantic pattern: [A-Za-z_]([A-Za-z_0-9]*[A-Za-z_])?[0-9]*
126 // The first part is the semantic name, the second is the optional
127 // semantic index. The semantic index is the number at the end of
128 // the semantic, including leading zeroes. Digits located before
129 // the last letter are part of the semantic name.
130 SmallString<256> Buffer;
131 Buffer.resize(Tok.getLength() + 1);
132 StringRef Identifier = PP.getSpelling(Tok, Buffer);
133 assert(Identifier.size() > 0);
134 // Determine the start of the semantic index.
135 unsigned IndexIndex = Identifier.find_last_not_of("0123456789") + 1;
136
137 // ParseHLSLSemantic being called on an indentifier, the first
138 // character cannot be a digit. This error should be handled by
139 // the caller. We can assert here.
140 StringRef SemanticName = Identifier.take_front(IndexIndex);
141 assert(SemanticName.size() > 0);
142
143 uint32_t Index = 0;
144 bool Explicit = false;
145 if (IndexIndex != Identifier.size()) {
146 Explicit = true;
147 StringRef IndexStr = Identifier.substr(IndexIndex);
148 if (IndexStr.getAsInteger(10, Index)) {
149 Diag(Tok, diag::err_hlsl_semantic_index_out_of_range)
150 << PP.getIdentifierInfo(SemanticName) << IndexStr
151 << std::numeric_limits<uint32_t>::max();
152 }
153 }
154
155 return {SemanticName, Index, Explicit};
156}
157
158void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
159 SourceLocation *EndLoc,
160 bool CouldBeBitField) {
161
162 assert(Tok.is(tok::colon) && "Not a HLSL Annotation");
163 Token OldToken = Tok;
164 ConsumeToken();
165
166 IdentifierInfo *II = nullptr;
167 if (Tok.is(tok::kw_register))
168 II = PP.getIdentifierInfo("register");
169 else if (Tok.is(tok::identifier))
170 II = Tok.getIdentifierInfo();
171
172 if (!II) {
173 if (CouldBeBitField) {
174 UnconsumeToken(OldToken);
175 return;
176 }
177 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
178 return;
179 }
180
181 ParsedAttr::Kind AttrKind =
183 Parser::ParsedSemantic Semantic;
184 if (AttrKind == ParsedAttr::AT_HLSLUnparsedSemantic)
185 Semantic = ParseHLSLSemantic();
186
187 SourceLocation Loc = ConsumeToken();
188 if (EndLoc)
189 *EndLoc = Tok.getLocation();
190
191 SourceLocation AttrEndLoc = Loc;
192 ArgsVector ArgExprs;
193 switch (AttrKind) {
194 case ParsedAttr::AT_HLSLResourceBinding: {
195 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
196 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
197 return;
198 }
199 if (!Tok.is(tok::identifier)) {
200 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
201 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
202 return;
203 }
204 StringRef SlotStr = Tok.getIdentifierInfo()->getName();
205 SourceLocation SlotLoc = Tok.getLocation();
206 ArgExprs.push_back(ParseIdentifierLoc());
207
208 if (SlotStr.size() == 1) {
209 if (!Tok.is(tok::numeric_constant)) {
210 Diag(Tok.getLocation(), diag::err_expected) << tok::numeric_constant;
211 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
212 return;
213 }
214 // Add numeric_constant for fix-it.
215 fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
216 Actions.Context, PP);
217 }
218 if (Tok.is(tok::comma)) {
219 ConsumeToken(); // consume comma
220 if (!Tok.is(tok::identifier)) {
221 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
222 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
223 return;
224 }
225 StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
226 SourceLocation SpaceLoc = Tok.getLocation();
227 ArgExprs.push_back(ParseIdentifierLoc());
228
229 // Add numeric_constant for fix-it.
230 if (SpaceStr == "space" && Tok.is(tok::numeric_constant))
231 fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
232 Actions.Context, PP);
233 }
234 AttrEndLoc = Tok.getLocation(); // location of the closing ')'
235 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
236 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
237 return;
238 }
239 } break;
240 case ParsedAttr::AT_HLSLPackOffset: {
241 // Parse 'packoffset( c[Subcomponent][.component] )'.
242 // Check '('.
243 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
244 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
245 return;
246 }
247 // Check c[Subcomponent] as an identifier.
248 if (!Tok.is(tok::identifier)) {
249 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
250 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
251 return;
252 }
253 StringRef OffsetStr = Tok.getIdentifierInfo()->getName();
254 SourceLocation SubComponentLoc = Tok.getLocation();
255 if (OffsetStr[0] != 'c') {
256 Diag(Tok.getLocation(), diag::err_hlsl_packoffset_invalid_reg)
257 << OffsetStr;
258 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
259 return;
260 }
261 OffsetStr = OffsetStr.substr(1);
262 unsigned SubComponent = 0;
263 if (!OffsetStr.empty()) {
264 // Make sure SubComponent is a number.
265 if (OffsetStr.getAsInteger(10, SubComponent)) {
266 Diag(SubComponentLoc.getLocWithOffset(1),
267 diag::err_hlsl_unsupported_register_number);
268 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
269 return;
270 }
271 }
272 unsigned Component = 0;
273 ConsumeToken(); // consume identifier.
274 SourceLocation ComponentLoc;
275 if (Tok.is(tok::period)) {
276 ConsumeToken(); // consume period.
277 if (!Tok.is(tok::identifier)) {
278 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
279 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
280 return;
281 }
282 StringRef ComponentStr = Tok.getIdentifierInfo()->getName();
283 ComponentLoc = Tok.getLocation();
284 ConsumeToken(); // consume identifier.
285 // Make sure Component is a single character.
286 if (ComponentStr.size() != 1) {
287 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
288 << ComponentStr;
289 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
290 return;
291 }
292 switch (ComponentStr[0]) {
293 case 'x':
294 case 'r':
295 Component = 0;
296 break;
297 case 'y':
298 case 'g':
299 Component = 1;
300 break;
301 case 'z':
302 case 'b':
303 Component = 2;
304 break;
305 case 'w':
306 case 'a':
307 Component = 3;
308 break;
309 default:
310 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
311 << ComponentStr;
312 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
313 return;
314 }
315 }
316 ASTContext &Ctx = Actions.getASTContext();
317 QualType SizeTy = Ctx.getSizeType();
318 uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
319 ArgExprs.push_back(IntegerLiteral::Create(
320 Ctx, llvm::APInt(SizeTySize, SubComponent), SizeTy, SubComponentLoc));
321 ArgExprs.push_back(IntegerLiteral::Create(
322 Ctx, llvm::APInt(SizeTySize, Component), SizeTy, ComponentLoc));
323 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
324 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
325 return;
326 }
327 } break;
328 case ParsedAttr::AT_HLSLUnparsedSemantic: {
329 ASTContext &Ctx = Actions.getASTContext();
330 ArgExprs.push_back(IntegerLiteral::Create(
331 Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.IntTy), Semantic.Index), Ctx.IntTy,
332 SourceLocation()));
333 ArgExprs.push_back(IntegerLiteral::Create(
334 Ctx, llvm::APInt(1, Semantic.Explicit), Ctx.BoolTy, SourceLocation()));
335 II = PP.getIdentifierInfo(Semantic.Name);
336 break;
337 }
338 case ParsedAttr::UnknownAttribute: // FIXME: maybe this is obsolete?
339 break;
340 default:
341 llvm_unreachable("invalid HLSL Annotation");
342 break;
343 }
344
345 Attrs.addNew(II, SourceRange(Loc, AttrEndLoc), AttributeScopeInfo(),
346 ArgExprs.data(), ArgExprs.size(),
347 ParsedAttr::Form::HLSLAnnotation());
348}
Token Tok
The Token.
static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG, SourceLocation BufferLoc, bool IsCBuffer, Parser &P)
Definition ParseHLSL.cpp:23
static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc, Token Tok, ArgsVector &ArgExprs, Parser &P, ASTContext &Ctx, Preprocessor &PP)
This file declares semantic analysis for HLSL constructs.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:245
CanQualType BoolTy
CanQualType IntTy
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
@ AS_HLSLAnnotation
<vardecl> : <annotation>
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
SourceLocation getLocation() const
Definition DeclBase.h:447
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
Definition Diagnostic.h:140
A simple pair of identifier info and location.
static IntegerLiteral * Create(const ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l)
Returns a new integer literal with value 'V' and type 'type'.
Definition Expr.cpp:985
PtrTy get() const
Definition Ownership.h:81
ParsedAttributes - A collection of parsed attributes.
Definition ParsedAttr.h:950
ParsedAttr * addNew(IdentifierInfo *attrName, SourceRange attrRange, AttributeScopeInfo scope, ArgsUnion *args, unsigned numArgs, ParsedAttr::Form form, SourceLocation ellipsisLoc=SourceLocation())
Add attribute with expression arguments.
Definition ParsedAttr.h:991
ParseScope - Introduces a new scope for parsing.
Definition Parser.h:503
Parser - This implements a parser for the C family of languages.
Definition Parser.h:266
DiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID)
Definition Parser.cpp:100
SourceLocation ConsumeToken()
ConsumeToken - Consume the current 'peek token' and lex the next one.
Definition Parser.h:358
OpaquePtr< DeclGroupRef > DeclGroupPtrTy
Definition Parser.h:315
Scope * getCurScope() const
Definition Parser.h:307
bool SkipUntil(tok::TokenKind T, SkipUntilFlags Flags=static_cast< SkipUntilFlags >(0))
SkipUntil - Read tokens until we get to the specified token, then consume it (unless StopBeforeMatch ...
Definition Parser.h:602
@ StopAtSemi
Stop skipping at semicolon.
Definition Parser.h:581
friend class BalancedDelimiterTracker
Definition Parser.h:295
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
IdentifierInfo * getIdentifierInfo(StringRef Name) const
Return information about the specified preprocessor identifier token.
@ DeclScope
This is a scope that can contain a declaration.
Definition Scope.h:63
Encodes a location in the source.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
A trivial tuple used to represent a source range.
Token - This structure provides full information about a lexed token.
Definition Token.h:36
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
Definition Address.h:330
llvm::PointerUnion< Expr *, IdentifierLoc * > ArgsUnion
A union of the various pointer types that can be passed to an ParsedAttr as an argument.
Definition ParsedAttr.h:103
llvm::SmallVector< ArgsUnion, 12U > ArgsVector
Definition ParsedAttr.h:104
@ Result
The result type of a method or function.
Definition TypeBase.h:906
const FunctionProtoType * T
unsigned long uint64_t
int32_t uint32_t