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