xref: /freebsd/contrib/llvm-project/clang/lib/Parse/ParseHLSL.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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/Parse/ParseDiagnostic.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 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>(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 
79   while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
80     // FIXME: support attribute on constants inside cbuffer/tbuffer.
81     ParsedAttributes DeclAttrs(AttrFactory);
82     ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
83 
84     DeclGroupPtrTy Result =
85         ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
86     if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
87                                        *this)) {
88       T.skipToEnd();
89       DeclEnd = T.getCloseLocation();
90       BufferScope.Exit();
91       Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
92       return nullptr;
93     }
94   }
95 
96   T.consumeClose();
97   DeclEnd = T.getCloseLocation();
98   BufferScope.Exit();
99   Actions.HLSL().ActOnFinishBuffer(D, DeclEnd);
100 
101   Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
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 = IdentifierLoc::create(Ctx, 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     // Add numeric_constant for fix-it.
167     if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant))
168       fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
169                                   Actions.Context, PP);
170 
171     if (Tok.is(tok::comma)) {
172       ConsumeToken(); // consume comma
173       if (!Tok.is(tok::identifier)) {
174         Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
175         SkipUntil(tok::r_paren, StopAtSemi); // skip through )
176         return;
177       }
178       StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
179       SourceLocation SpaceLoc = Tok.getLocation();
180       ArgExprs.push_back(ParseIdentifierLoc());
181 
182       // Add numeric_constant for fix-it.
183       if (SpaceStr == "space" && Tok.is(tok::numeric_constant))
184         fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
185                                     Actions.Context, PP);
186     }
187     if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
188       SkipUntil(tok::r_paren, StopAtSemi); // skip through )
189       return;
190     }
191   } break;
192   case ParsedAttr::AT_HLSLPackOffset: {
193     // Parse 'packoffset( c[Subcomponent][.component] )'.
194     // Check '('.
195     if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
196       SkipUntil(tok::r_paren, StopAtSemi); // skip through )
197       return;
198     }
199     // Check c[Subcomponent] as an identifier.
200     if (!Tok.is(tok::identifier)) {
201       Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
202       SkipUntil(tok::r_paren, StopAtSemi); // skip through )
203       return;
204     }
205     StringRef OffsetStr = Tok.getIdentifierInfo()->getName();
206     SourceLocation SubComponentLoc = Tok.getLocation();
207     if (OffsetStr[0] != 'c') {
208       Diag(Tok.getLocation(), diag::err_hlsl_packoffset_invalid_reg)
209           << OffsetStr;
210       SkipUntil(tok::r_paren, StopAtSemi); // skip through )
211       return;
212     }
213     OffsetStr = OffsetStr.substr(1);
214     unsigned SubComponent = 0;
215     if (!OffsetStr.empty()) {
216       // Make sure SubComponent is a number.
217       if (OffsetStr.getAsInteger(10, SubComponent)) {
218         Diag(SubComponentLoc.getLocWithOffset(1),
219              diag::err_hlsl_unsupported_register_number);
220         SkipUntil(tok::r_paren, StopAtSemi); // skip through )
221         return;
222       }
223     }
224     unsigned Component = 0;
225     ConsumeToken(); // consume identifier.
226     SourceLocation ComponentLoc;
227     if (Tok.is(tok::period)) {
228       ConsumeToken(); // consume period.
229       if (!Tok.is(tok::identifier)) {
230         Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
231         SkipUntil(tok::r_paren, StopAtSemi); // skip through )
232         return;
233       }
234       StringRef ComponentStr = Tok.getIdentifierInfo()->getName();
235       ComponentLoc = Tok.getLocation();
236       ConsumeToken(); // consume identifier.
237       // Make sure Component is a single character.
238       if (ComponentStr.size() != 1) {
239         Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
240             << ComponentStr;
241         SkipUntil(tok::r_paren, StopAtSemi); // skip through )
242         return;
243       }
244       switch (ComponentStr[0]) {
245       case 'x':
246       case 'r':
247         Component = 0;
248         break;
249       case 'y':
250       case 'g':
251         Component = 1;
252         break;
253       case 'z':
254       case 'b':
255         Component = 2;
256         break;
257       case 'w':
258       case 'a':
259         Component = 3;
260         break;
261       default:
262         Diag(ComponentLoc, diag::err_hlsl_unsupported_component)
263             << ComponentStr;
264         SkipUntil(tok::r_paren, StopAtSemi); // skip through )
265         return;
266       }
267     }
268     ASTContext &Ctx = Actions.getASTContext();
269     QualType SizeTy = Ctx.getSizeType();
270     uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);
271     ArgExprs.push_back(IntegerLiteral::Create(
272         Ctx, llvm::APInt(SizeTySize, SubComponent), SizeTy, SubComponentLoc));
273     ArgExprs.push_back(IntegerLiteral::Create(
274         Ctx, llvm::APInt(SizeTySize, Component), SizeTy, ComponentLoc));
275     if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
276       SkipUntil(tok::r_paren, StopAtSemi); // skip through )
277       return;
278     }
279   } break;
280   case ParsedAttr::UnknownAttribute:
281     Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
282     return;
283   case ParsedAttr::AT_HLSLSV_GroupIndex:
284   case ParsedAttr::AT_HLSLSV_DispatchThreadID:
285     break;
286   default:
287     llvm_unreachable("invalid HLSL Annotation");
288     break;
289   }
290 
291   Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(),
292                ArgExprs.size(), ParsedAttr::Form::HLSLAnnotation());
293 }
294