1*12c85518Srobert //===--- ParseHLSL.cpp - HLSL-specific parsing support --------------------===//
2*12c85518Srobert //
3*12c85518Srobert // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*12c85518Srobert // See https://llvm.org/LICENSE.txt for license information.
5*12c85518Srobert // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*12c85518Srobert //
7*12c85518Srobert //===----------------------------------------------------------------------===//
8*12c85518Srobert //
9*12c85518Srobert // This file implements the parsing logic for HLSL language features.
10*12c85518Srobert //
11*12c85518Srobert //===----------------------------------------------------------------------===//
12*12c85518Srobert
13*12c85518Srobert #include "clang/AST/Attr.h"
14*12c85518Srobert #include "clang/Basic/AttributeCommonInfo.h"
15*12c85518Srobert #include "clang/Parse/ParseDiagnostic.h"
16*12c85518Srobert #include "clang/Parse/Parser.h"
17*12c85518Srobert #include "clang/Parse/RAIIObjectsForParser.h"
18*12c85518Srobert
19*12c85518Srobert using namespace clang;
20*12c85518Srobert
validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,SourceLocation BufferLoc,bool IsCBuffer,Parser & P)21*12c85518Srobert static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,
22*12c85518Srobert SourceLocation BufferLoc,
23*12c85518Srobert bool IsCBuffer, Parser &P) {
24*12c85518Srobert // The parse is failed, just return false.
25*12c85518Srobert if (!DG)
26*12c85518Srobert return false;
27*12c85518Srobert DeclGroupRef Decls = DG.get();
28*12c85518Srobert bool IsValid = true;
29*12c85518Srobert // Only allow function, variable, record decls inside HLSLBuffer.
30*12c85518Srobert for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
31*12c85518Srobert Decl *D = *I;
32*12c85518Srobert if (isa<CXXRecordDecl, RecordDecl, FunctionDecl, VarDecl>(D))
33*12c85518Srobert continue;
34*12c85518Srobert
35*12c85518Srobert // FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
36*12c85518Srobert if (isa<HLSLBufferDecl, NamespaceDecl>(D)) {
37*12c85518Srobert P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
38*12c85518Srobert << IsCBuffer;
39*12c85518Srobert IsValid = false;
40*12c85518Srobert continue;
41*12c85518Srobert }
42*12c85518Srobert
43*12c85518Srobert IsValid = false;
44*12c85518Srobert P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
45*12c85518Srobert << IsCBuffer;
46*12c85518Srobert }
47*12c85518Srobert return IsValid;
48*12c85518Srobert }
49*12c85518Srobert
ParseHLSLBuffer(SourceLocation & DeclEnd)50*12c85518Srobert Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
51*12c85518Srobert assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
52*12c85518Srobert "Not a cbuffer or tbuffer!");
53*12c85518Srobert bool IsCBuffer = Tok.is(tok::kw_cbuffer);
54*12c85518Srobert SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
55*12c85518Srobert
56*12c85518Srobert if (!Tok.is(tok::identifier)) {
57*12c85518Srobert Diag(Tok, diag::err_expected) << tok::identifier;
58*12c85518Srobert return nullptr;
59*12c85518Srobert }
60*12c85518Srobert
61*12c85518Srobert IdentifierInfo *Identifier = Tok.getIdentifierInfo();
62*12c85518Srobert SourceLocation IdentifierLoc = ConsumeToken();
63*12c85518Srobert
64*12c85518Srobert ParsedAttributes Attrs(AttrFactory);
65*12c85518Srobert MaybeParseHLSLSemantics(Attrs, nullptr);
66*12c85518Srobert
67*12c85518Srobert ParseScope BufferScope(this, Scope::DeclScope);
68*12c85518Srobert BalancedDelimiterTracker T(*this, tok::l_brace);
69*12c85518Srobert if (T.consumeOpen()) {
70*12c85518Srobert Diag(Tok, diag::err_expected) << tok::l_brace;
71*12c85518Srobert return nullptr;
72*12c85518Srobert }
73*12c85518Srobert
74*12c85518Srobert Decl *D = Actions.ActOnStartHLSLBuffer(getCurScope(), IsCBuffer, BufferLoc,
75*12c85518Srobert Identifier, IdentifierLoc,
76*12c85518Srobert T.getOpenLocation());
77*12c85518Srobert
78*12c85518Srobert while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
79*12c85518Srobert // FIXME: support attribute on constants inside cbuffer/tbuffer.
80*12c85518Srobert ParsedAttributes DeclAttrs(AttrFactory);
81*12c85518Srobert ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
82*12c85518Srobert
83*12c85518Srobert DeclGroupPtrTy Result =
84*12c85518Srobert ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
85*12c85518Srobert if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
86*12c85518Srobert *this)) {
87*12c85518Srobert T.skipToEnd();
88*12c85518Srobert DeclEnd = T.getCloseLocation();
89*12c85518Srobert BufferScope.Exit();
90*12c85518Srobert Actions.ActOnFinishHLSLBuffer(D, DeclEnd);
91*12c85518Srobert return nullptr;
92*12c85518Srobert }
93*12c85518Srobert }
94*12c85518Srobert
95*12c85518Srobert T.consumeClose();
96*12c85518Srobert DeclEnd = T.getCloseLocation();
97*12c85518Srobert BufferScope.Exit();
98*12c85518Srobert Actions.ActOnFinishHLSLBuffer(D, DeclEnd);
99*12c85518Srobert
100*12c85518Srobert Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
101*12c85518Srobert return D;
102*12c85518Srobert }
103*12c85518Srobert
fixSeparateAttrArgAndNumber(StringRef ArgStr,SourceLocation ArgLoc,Token Tok,ArgsVector & ArgExprs,Parser & P,ASTContext & Ctx,Preprocessor & PP)104*12c85518Srobert static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
105*12c85518Srobert Token Tok, ArgsVector &ArgExprs,
106*12c85518Srobert Parser &P, ASTContext &Ctx,
107*12c85518Srobert Preprocessor &PP) {
108*12c85518Srobert StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
109*12c85518Srobert SourceLocation EndNumLoc = Tok.getEndLoc();
110*12c85518Srobert
111*12c85518Srobert P.ConsumeToken(); // consume constant.
112*12c85518Srobert std::string FixedArg = ArgStr.str() + Num.str();
113*12c85518Srobert P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
114*12c85518Srobert << FixedArg
115*12c85518Srobert << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
116*12c85518Srobert ArgsUnion &Slot = ArgExprs.back();
117*12c85518Srobert Slot = IdentifierLoc::create(Ctx, ArgLoc, PP.getIdentifierInfo(FixedArg));
118*12c85518Srobert }
119*12c85518Srobert
ParseHLSLSemantics(ParsedAttributes & Attrs,SourceLocation * EndLoc)120*12c85518Srobert void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs,
121*12c85518Srobert SourceLocation *EndLoc) {
122*12c85518Srobert // FIXME: HLSLSemantic is shared for Semantic and resource binding which is
123*12c85518Srobert // confusing. Need a better name to avoid misunderstanding. Issue
124*12c85518Srobert // https://github.com/llvm/llvm-project/issues/57882
125*12c85518Srobert assert(Tok.is(tok::colon) && "Not a HLSL Semantic");
126*12c85518Srobert ConsumeToken();
127*12c85518Srobert
128*12c85518Srobert IdentifierInfo *II = nullptr;
129*12c85518Srobert if (Tok.is(tok::kw_register))
130*12c85518Srobert II = PP.getIdentifierInfo("register");
131*12c85518Srobert else if (Tok.is(tok::identifier))
132*12c85518Srobert II = Tok.getIdentifierInfo();
133*12c85518Srobert
134*12c85518Srobert if (!II) {
135*12c85518Srobert Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
136*12c85518Srobert return;
137*12c85518Srobert }
138*12c85518Srobert
139*12c85518Srobert SourceLocation Loc = ConsumeToken();
140*12c85518Srobert if (EndLoc)
141*12c85518Srobert *EndLoc = Tok.getLocation();
142*12c85518Srobert ParsedAttr::Kind AttrKind =
143*12c85518Srobert ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLSemantic);
144*12c85518Srobert
145*12c85518Srobert ArgsVector ArgExprs;
146*12c85518Srobert switch (AttrKind) {
147*12c85518Srobert case ParsedAttr::AT_HLSLResourceBinding: {
148*12c85518Srobert if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
149*12c85518Srobert SkipUntil(tok::r_paren, StopAtSemi); // skip through )
150*12c85518Srobert return;
151*12c85518Srobert }
152*12c85518Srobert if (!Tok.is(tok::identifier)) {
153*12c85518Srobert Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
154*12c85518Srobert SkipUntil(tok::r_paren, StopAtSemi); // skip through )
155*12c85518Srobert return;
156*12c85518Srobert }
157*12c85518Srobert StringRef SlotStr = Tok.getIdentifierInfo()->getName();
158*12c85518Srobert SourceLocation SlotLoc = Tok.getLocation();
159*12c85518Srobert ArgExprs.push_back(ParseIdentifierLoc());
160*12c85518Srobert
161*12c85518Srobert // Add numeric_constant for fix-it.
162*12c85518Srobert if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant))
163*12c85518Srobert fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
164*12c85518Srobert Actions.Context, PP);
165*12c85518Srobert
166*12c85518Srobert if (Tok.is(tok::comma)) {
167*12c85518Srobert ConsumeToken(); // consume comma
168*12c85518Srobert if (!Tok.is(tok::identifier)) {
169*12c85518Srobert Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
170*12c85518Srobert SkipUntil(tok::r_paren, StopAtSemi); // skip through )
171*12c85518Srobert return;
172*12c85518Srobert }
173*12c85518Srobert StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
174*12c85518Srobert SourceLocation SpaceLoc = Tok.getLocation();
175*12c85518Srobert ArgExprs.push_back(ParseIdentifierLoc());
176*12c85518Srobert
177*12c85518Srobert // Add numeric_constant for fix-it.
178*12c85518Srobert if (SpaceStr.equals("space") && Tok.is(tok::numeric_constant))
179*12c85518Srobert fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
180*12c85518Srobert Actions.Context, PP);
181*12c85518Srobert }
182*12c85518Srobert if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
183*12c85518Srobert SkipUntil(tok::r_paren, StopAtSemi); // skip through )
184*12c85518Srobert return;
185*12c85518Srobert }
186*12c85518Srobert } break;
187*12c85518Srobert case ParsedAttr::UnknownAttribute:
188*12c85518Srobert Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
189*12c85518Srobert return;
190*12c85518Srobert case ParsedAttr::AT_HLSLSV_GroupIndex:
191*12c85518Srobert case ParsedAttr::AT_HLSLSV_DispatchThreadID:
192*12c85518Srobert break;
193*12c85518Srobert default:
194*12c85518Srobert llvm_unreachable("invalid HLSL Semantic");
195*12c85518Srobert break;
196*12c85518Srobert }
197*12c85518Srobert
198*12c85518Srobert Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(),
199*12c85518Srobert ArgExprs.size(), ParsedAttr::AS_HLSLSemantic);
200*12c85518Srobert }
201