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 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 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 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 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