xref: /freebsd/contrib/llvm-project/clang/lib/Parse/ParseHLSL.cpp (revision 700637cbb5e582861067a11aaca4d053546871d2)
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