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"
14 #include "clang/Basic/AttributeCommonInfo.h"
15 #include "clang/Basic/DiagnosticParse.h"
16 #include "clang/Parse/Parser.h"
17 #include "clang/Parse/RAIIObjectsForParser.h"
18 #include "clang/Sema/SemaHLSL.h"
19
20 using namespace clang;
21
validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,SourceLocation BufferLoc,bool IsCBuffer,Parser & P)22 static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,
23 SourceLocation BufferLoc,
24 bool IsCBuffer, Parser &P) {
25 // The parse is failed, just return false.
26 if (!DG)
27 return false;
28 DeclGroupRef Decls = DG.get();
29 bool IsValid = true;
30 // Only allow function, variable, record, and empty decls inside HLSLBuffer.
31 for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
32 Decl *D = *I;
33 if (isa<CXXRecordDecl, RecordDecl, FunctionDecl, VarDecl, EmptyDecl>(D))
34 continue;
35
36 // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
37 if (isa<HLSLBufferDecl, NamespaceDecl>(D)) {
38 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
39 << IsCBuffer;
40 IsValid = false;
41 continue;
42 }
43
44 IsValid = false;
45 P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
46 << IsCBuffer;
47 }
48 return IsValid;
49 }
50
ParseHLSLBuffer(SourceLocation & DeclEnd)51 Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
52 assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
53 "Not a cbuffer or tbuffer!");
54 bool IsCBuffer = Tok.is(tok::kw_cbuffer);
55 SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
56
57 if (!Tok.is(tok::identifier)) {
58 Diag(Tok, diag::err_expected) << tok::identifier;
59 return nullptr;
60 }
61
62 IdentifierInfo *Identifier = Tok.getIdentifierInfo();
63 SourceLocation IdentifierLoc = ConsumeToken();
64
65 ParsedAttributes Attrs(AttrFactory);
66 MaybeParseHLSLAnnotations(Attrs, nullptr);
67
68 ParseScope BufferScope(this, Scope::DeclScope);
69 BalancedDelimiterTracker T(*this, tok::l_brace);
70 if (T.consumeOpen()) {
71 Diag(Tok, diag::err_expected) << tok::l_brace;
72 return nullptr;
73 }
74
75 Decl *D = Actions.HLSL().ActOnStartBuffer(getCurScope(), IsCBuffer, BufferLoc,
76 Identifier, IdentifierLoc,
77 T.getOpenLocation());
78 Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
79
80 while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
81 // FIXME: support attribute on constants inside cbuffer/tbuffer.
82 ParsedAttributes DeclAttrs(AttrFactory);
83 ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
84
85 DeclGroupPtrTy Result =
86 ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
87 if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
88 *this)) {
89 T.skipToEnd();
90 DeclEnd = T.getCloseLocation();
91 BufferScope.Exit();
92 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
93 return nullptr;
94 }
95 }
96
97 T.consumeClose();
98 DeclEnd = T.getCloseLocation();
99 BufferScope.Exit();
100 Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
101
102 return D;
103 }
104
fixSeparateAttrArgAndNumber(StringRef ArgStr,SourceLocation ArgLoc,Token Tok,ArgsVector & ArgExprs,Parser & P,ASTContext & Ctx,Preprocessor & PP)105 static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
106 Token Tok, ArgsVector &ArgExprs,
107 Parser &P, ASTContext &Ctx,
108 Preprocessor &PP) {
109 StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
110 SourceLocation EndNumLoc = Tok.getEndLoc();
111
112 P.ConsumeToken(); // consume constant.
113 std::string FixedArg = ArgStr.str() + Num.str();
114 P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
115 << FixedArg
116 << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
117 ArgsUnion &Slot = ArgExprs.back();
118 Slot = new (Ctx) IdentifierLoc(ArgLoc, PP.getIdentifierInfo(FixedArg));
119 }
120
ParseHLSLAnnotations(ParsedAttributes & Attrs,SourceLocation * EndLoc,bool CouldBeBitField)121 void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,
122 SourceLocation *EndLoc,
123 bool CouldBeBitField) {
124
125 assert(Tok.is(tok::colon) && "Not a HLSL Annotation");
126 Token OldToken = Tok;
127 ConsumeToken();
128
129 IdentifierInfo *II = nullptr;
130 if (Tok.is(tok::kw_register))
131 II = PP.getIdentifierInfo("register");
132 else if (Tok.is(tok::identifier))
133 II = Tok.getIdentifierInfo();
134
135 if (!II) {
136 if (CouldBeBitField) {
137 UnconsumeToken(OldToken);
138 return;
139 }
140 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
141 return;
142 }
143
144 SourceLocation Loc = ConsumeToken();
145 if (EndLoc)
146 *EndLoc = Tok.getLocation();
147 ParsedAttr::Kind AttrKind =
148 ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLAnnotation);
149
150 ArgsVector ArgExprs;
151 switch (AttrKind) {
152 case ParsedAttr::AT_HLSLResourceBinding: {
153 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
154 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
155 return;
156 }
157 if (!Tok.is(tok::identifier)) {
158 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
159 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
160 return;
161 }
162 StringRef SlotStr = Tok.getIdentifierInfo()->getName();
163 SourceLocation SlotLoc = Tok.getLocation();
164 ArgExprs.push_back(ParseIdentifierLoc());
165
166 if (SlotStr.size() == 1) {
167 if (!Tok.is(tok::numeric_constant)) {
168 Diag(Tok.getLocation(), diag::err_expected) << tok::numeric_constant;
169 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
170 return;
171 }
172 // Add numeric_constant for fix-it.
173 fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
174 Actions.Context, PP);
175 }
176 if (Tok.is(tok::comma)) {
177 ConsumeToken(); // consume comma
178 if (!Tok.is(tok::identifier)) {
179 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
180 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
181 return;
182 }
183 StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
184 SourceLocation SpaceLoc = Tok.getLocation();
185 ArgExprs.push_back(ParseIdentifierLoc());
186
187 // Add numeric_constant for fix-it.
188 if (SpaceStr == "space" && Tok.is(tok::numeric_constant))
189 fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
190 Actions.Context, PP);
191 }
192 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
193 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
194 return;
195 }
196 } break;
197 case ParsedAttr::AT_HLSLPackOffset: {
198 // Parse 'packoffset( c[Subcomponent][.component] )'.
199 // Check '('.
200 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
201 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
202 return;
203 }
204 // Check c[Subcomponent] as an identifier.
205 if (!Tok.is(tok::identifier)) {
206 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
207 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
208 return;
209 }
210 StringRef OffsetStr = Tok.getIdentifierInfo()->getName();
211 SourceLocation SubComponentLoc = Tok.getLocation();
212 if (OffsetStr[0] != 'c') {
213 Diag(Tok.getLocation(), diag::err_hlsl_packoffset_invalid_reg)
214 << OffsetStr;
215 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
216 return;
217 }
218 OffsetStr = OffsetStr.substr(1);
219 unsigned SubComponent = 0;
220 if (!OffsetStr.empty()) {
221 // Make sure SubComponent is a number.
222 if (OffsetStr.getAsInteger(10, SubComponent)) {
223 Diag(SubComponentLoc.getLocWithOffset(1),
224 diag::err_hlsl_unsupported_register_number);
225 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
226 return;
227 }
228 }
229 unsigned Component = 0;
230 ConsumeToken(); // consume identifier.
231 SourceLocation ComponentLoc;
232 if (Tok.is(tok::period)) {
233 ConsumeToken(); // consume period.
234 if (!Tok.is(tok::identifier)) {
235 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
236 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
237 return;
238 }
239 StringRef ComponentStr = Tok.getIdentifierInfo()->getName();
240 ComponentLoc = Tok.getLocation();
241 ConsumeToken(); // consume identifier.
242 // Make sure Component is a single character.
243 if (ComponentStr.size() != 1) {
244 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
245 << ComponentStr;
246 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
247 return;
248 }
249 switch (ComponentStr[0]) {
250 case 'x':
251 case 'r':
252 Component = 0;
253 break;
254 case 'y':
255 case 'g':
256 Component = 1;
257 break;
258 case 'z':
259 case 'b':
260 Component = 2;
261 break;
262 case 'w':
263 case 'a':
264 Component = 3;
265 break;
266 default:
267 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
268 << ComponentStr;
269 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
270 return;
271 }
272 }
273 ASTContext &Ctx = Actions.getASTContext();
274 QualType SizeTy = Ctx.getSizeType();
275 uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
276 ArgExprs.push_back(IntegerLiteral::Create(
277 Ctx, llvm::APInt(SizeTySize, SubComponent), SizeTy, SubComponentLoc));
278 ArgExprs.push_back(IntegerLiteral::Create(
279 Ctx, llvm::APInt(SizeTySize, Component), SizeTy, ComponentLoc));
280 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
281 SkipUntil(tok::r_paren, StopAtSemi); // skip through )
282 return;
283 }
284 } break;
285 case ParsedAttr::UnknownAttribute:
286 Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
287 return;
288 case ParsedAttr::AT_HLSLSV_GroupThreadID:
289 case ParsedAttr::AT_HLSLSV_GroupID:
290 case ParsedAttr::AT_HLSLSV_GroupIndex:
291 case ParsedAttr::AT_HLSLSV_DispatchThreadID:
292 case ParsedAttr::AT_HLSLSV_Position:
293 break;
294 default:
295 llvm_unreachable("invalid HLSL Annotation");
296 break;
297 }
298
299 Attrs.addNew(II, Loc, AttributeScopeInfo(), ArgExprs.data(), ArgExprs.size(),
300 ParsedAttr::Form::HLSLAnnotation());
301 }
302