343 lines · cpp
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-exception6//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 20using namespace clang;21 22static 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 51Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd,52 ParsedAttributes &Attrs) {53 assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&54 "Not a cbuffer or tbuffer!");55 bool IsCBuffer = Tok.is(tok::kw_cbuffer);56 SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.57 58 if (!Tok.is(tok::identifier)) {59 Diag(Tok, diag::err_expected) << tok::identifier;60 return nullptr;61 }62 63 IdentifierInfo *Identifier = Tok.getIdentifierInfo();64 SourceLocation IdentifierLoc = ConsumeToken();65 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 105static 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 << FixedArg116 << FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);117 ArgsUnion &Slot = ArgExprs.back();118 Slot = new (Ctx) IdentifierLoc(ArgLoc, PP.getIdentifierInfo(FixedArg));119}120 121Parser::ParsedSemantic Parser::ParseHLSLSemantic() {122 assert(Tok.is(tok::identifier) && "Not a HLSL Annotation");123 124 // Semantic pattern: [A-Za-z_]([A-Za-z_0-9]*[A-Za-z_])?[0-9]*125 // The first part is the semantic name, the second is the optional126 // semantic index. The semantic index is the number at the end of127 // the semantic, including leading zeroes. Digits located before128 // the last letter are part of the semantic name.129 SmallString<256> Buffer;130 Buffer.resize(Tok.getLength() + 1);131 StringRef Identifier = PP.getSpelling(Tok, Buffer);132 assert(Identifier.size() > 0);133 // Determine the start of the semantic index.134 unsigned IndexIndex = Identifier.find_last_not_of("0123456789") + 1;135 136 // ParseHLSLSemantic being called on an indentifier, the first137 // character cannot be a digit. This error should be handled by138 // the caller. We can assert here.139 StringRef SemanticName = Identifier.take_front(IndexIndex);140 assert(SemanticName.size() > 0);141 142 unsigned Index = 0;143 bool Explicit = false;144 if (IndexIndex != Identifier.size()) {145 Explicit = true;146 [[maybe_unused]] bool Failure =147 Identifier.substr(IndexIndex).getAsInteger(10, Index);148 // Given the logic above, this should never fail.149 assert(!Failure);150 }151 152 return {SemanticName, Index, Explicit};153}154 155void Parser::ParseHLSLAnnotations(ParsedAttributes &Attrs,156 SourceLocation *EndLoc,157 bool CouldBeBitField) {158 159 assert(Tok.is(tok::colon) && "Not a HLSL Annotation");160 Token OldToken = Tok;161 ConsumeToken();162 163 IdentifierInfo *II = nullptr;164 if (Tok.is(tok::kw_register))165 II = PP.getIdentifierInfo("register");166 else if (Tok.is(tok::identifier))167 II = Tok.getIdentifierInfo();168 169 if (!II) {170 if (CouldBeBitField) {171 UnconsumeToken(OldToken);172 return;173 }174 Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);175 return;176 }177 178 ParsedAttr::Kind AttrKind =179 ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLAnnotation);180 Parser::ParsedSemantic Semantic;181 if (AttrKind == ParsedAttr::AT_HLSLUnparsedSemantic)182 Semantic = ParseHLSLSemantic();183 184 SourceLocation Loc = ConsumeToken();185 if (EndLoc)186 *EndLoc = Tok.getLocation();187 188 ArgsVector ArgExprs;189 switch (AttrKind) {190 case ParsedAttr::AT_HLSLResourceBinding: {191 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {192 SkipUntil(tok::r_paren, StopAtSemi); // skip through )193 return;194 }195 if (!Tok.is(tok::identifier)) {196 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;197 SkipUntil(tok::r_paren, StopAtSemi); // skip through )198 return;199 }200 StringRef SlotStr = Tok.getIdentifierInfo()->getName();201 SourceLocation SlotLoc = Tok.getLocation();202 ArgExprs.push_back(ParseIdentifierLoc());203 204 if (SlotStr.size() == 1) {205 if (!Tok.is(tok::numeric_constant)) {206 Diag(Tok.getLocation(), diag::err_expected) << tok::numeric_constant;207 SkipUntil(tok::r_paren, StopAtSemi); // skip through )208 return;209 }210 // Add numeric_constant for fix-it.211 fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,212 Actions.Context, PP);213 }214 if (Tok.is(tok::comma)) {215 ConsumeToken(); // consume comma216 if (!Tok.is(tok::identifier)) {217 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;218 SkipUntil(tok::r_paren, StopAtSemi); // skip through )219 return;220 }221 StringRef SpaceStr = Tok.getIdentifierInfo()->getName();222 SourceLocation SpaceLoc = Tok.getLocation();223 ArgExprs.push_back(ParseIdentifierLoc());224 225 // Add numeric_constant for fix-it.226 if (SpaceStr == "space" && Tok.is(tok::numeric_constant))227 fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,228 Actions.Context, PP);229 }230 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {231 SkipUntil(tok::r_paren, StopAtSemi); // skip through )232 return;233 }234 } break;235 case ParsedAttr::AT_HLSLPackOffset: {236 // Parse 'packoffset( c[Subcomponent][.component] )'.237 // Check '('.238 if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {239 SkipUntil(tok::r_paren, StopAtSemi); // skip through )240 return;241 }242 // Check c[Subcomponent] as an identifier.243 if (!Tok.is(tok::identifier)) {244 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;245 SkipUntil(tok::r_paren, StopAtSemi); // skip through )246 return;247 }248 StringRef OffsetStr = Tok.getIdentifierInfo()->getName();249 SourceLocation SubComponentLoc = Tok.getLocation();250 if (OffsetStr[0] != 'c') {251 Diag(Tok.getLocation(), diag::err_hlsl_packoffset_invalid_reg)252 << OffsetStr;253 SkipUntil(tok::r_paren, StopAtSemi); // skip through )254 return;255 }256 OffsetStr = OffsetStr.substr(1);257 unsigned SubComponent = 0;258 if (!OffsetStr.empty()) {259 // Make sure SubComponent is a number.260 if (OffsetStr.getAsInteger(10, SubComponent)) {261 Diag(SubComponentLoc.getLocWithOffset(1),262 diag::err_hlsl_unsupported_register_number);263 SkipUntil(tok::r_paren, StopAtSemi); // skip through )264 return;265 }266 }267 unsigned Component = 0;268 ConsumeToken(); // consume identifier.269 SourceLocation ComponentLoc;270 if (Tok.is(tok::period)) {271 ConsumeToken(); // consume period.272 if (!Tok.is(tok::identifier)) {273 Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;274 SkipUntil(tok::r_paren, StopAtSemi); // skip through )275 return;276 }277 StringRef ComponentStr = Tok.getIdentifierInfo()->getName();278 ComponentLoc = Tok.getLocation();279 ConsumeToken(); // consume identifier.280 // Make sure Component is a single character.281 if (ComponentStr.size() != 1) {282 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)283 << ComponentStr;284 SkipUntil(tok::r_paren, StopAtSemi); // skip through )285 return;286 }287 switch (ComponentStr[0]) {288 case 'x':289 case 'r':290 Component = 0;291 break;292 case 'y':293 case 'g':294 Component = 1;295 break;296 case 'z':297 case 'b':298 Component = 2;299 break;300 case 'w':301 case 'a':302 Component = 3;303 break;304 default:305 Diag(ComponentLoc, diag::err_hlsl_unsupported_component)306 << ComponentStr;307 SkipUntil(tok::r_paren, StopAtSemi); // skip through )308 return;309 }310 }311 ASTContext &Ctx = Actions.getASTContext();312 QualType SizeTy = Ctx.getSizeType();313 uint64_t SizeTySize = Ctx.getTypeSize(SizeTy);314 ArgExprs.push_back(IntegerLiteral::Create(315 Ctx, llvm::APInt(SizeTySize, SubComponent), SizeTy, SubComponentLoc));316 ArgExprs.push_back(IntegerLiteral::Create(317 Ctx, llvm::APInt(SizeTySize, Component), SizeTy, ComponentLoc));318 if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {319 SkipUntil(tok::r_paren, StopAtSemi); // skip through )320 return;321 }322 } break;323 case ParsedAttr::AT_HLSLUnparsedSemantic: {324 ASTContext &Ctx = Actions.getASTContext();325 ArgExprs.push_back(IntegerLiteral::Create(326 Ctx, llvm::APInt(Ctx.getTypeSize(Ctx.IntTy), Semantic.Index), Ctx.IntTy,327 SourceLocation()));328 ArgExprs.push_back(IntegerLiteral::Create(329 Ctx, llvm::APInt(1, Semantic.Explicit), Ctx.BoolTy, SourceLocation()));330 II = PP.getIdentifierInfo(Semantic.Name);331 break;332 }333 case ParsedAttr::UnknownAttribute: // FIXME: maybe this is obsolete?334 break;335 default:336 llvm_unreachable("invalid HLSL Annotation");337 break;338 }339 340 Attrs.addNew(II, Loc, AttributeScopeInfo(), ArgExprs.data(), ArgExprs.size(),341 ParsedAttr::Form::HLSLAnnotation());342}343