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