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