mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-05-15 10:26:23 +00:00

We would previously reject valid input where GNU attributes preceded the standard attributes on top-level declarations. A previous attribute handling change had begun rejecting this whilst GCC does honour this layout. In practice, this breaks use of `extern "C"` attributed functions which use both standard and GNU attributes as experienced by the Swift runtime. Objective-C deserves an honourable mention for requiring some additional special casing. Because attributes on declarations and definitions differ in semantics, we need to replicate some of the logic for detecting attributes to declarations to which they appertain cannot be attributed. This should match the existing case for the application of GNU attributes to interfaces, protocols, and implementations. Take the opportunity to split out the tooling tests into two cases: ones which process macros and ones which do not. Special thanks to Aaron Ballman for the many hints and extensive rubber ducking that was involved in identifying the various places where we accidentally dropped attributes. Differential Revision: https://reviews.llvm.org/D137979 Fixes: #58229 Reviewed By: aaron.ballman, arphaman
201 lines
7.1 KiB
C++
201 lines
7.1 KiB
C++
//===--- ParseHLSL.cpp - HLSL-specific parsing support --------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file implements the parsing logic for HLSL language features.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/AST/Attr.h"
|
|
#include "clang/Basic/AttributeCommonInfo.h"
|
|
#include "clang/Parse/ParseDiagnostic.h"
|
|
#include "clang/Parse/Parser.h"
|
|
#include "clang/Parse/RAIIObjectsForParser.h"
|
|
|
|
using namespace clang;
|
|
|
|
static bool validateDeclsInsideHLSLBuffer(Parser::DeclGroupPtrTy DG,
|
|
SourceLocation BufferLoc,
|
|
bool IsCBuffer, Parser &P) {
|
|
// The parse is failed, just return false.
|
|
if (!DG)
|
|
return false;
|
|
DeclGroupRef Decls = DG.get();
|
|
bool IsValid = true;
|
|
// Only allow function, variable, record decls inside HLSLBuffer.
|
|
for (DeclGroupRef::iterator I = Decls.begin(), E = Decls.end(); I != E; ++I) {
|
|
Decl *D = *I;
|
|
if (isa<CXXRecordDecl, RecordDecl, FunctionDecl, VarDecl>(D))
|
|
continue;
|
|
|
|
// FIXME: support nested HLSLBuffer and namespace inside HLSLBuffer.
|
|
if (isa<HLSLBufferDecl, NamespaceDecl>(D)) {
|
|
P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
|
|
<< IsCBuffer;
|
|
IsValid = false;
|
|
continue;
|
|
}
|
|
|
|
IsValid = false;
|
|
P.Diag(D->getLocation(), diag::err_invalid_declaration_in_hlsl_buffer)
|
|
<< IsCBuffer;
|
|
}
|
|
return IsValid;
|
|
}
|
|
|
|
Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd) {
|
|
assert((Tok.is(tok::kw_cbuffer) || Tok.is(tok::kw_tbuffer)) &&
|
|
"Not a cbuffer or tbuffer!");
|
|
bool IsCBuffer = Tok.is(tok::kw_cbuffer);
|
|
SourceLocation BufferLoc = ConsumeToken(); // Eat the 'cbuffer' or 'tbuffer'.
|
|
|
|
if (!Tok.is(tok::identifier)) {
|
|
Diag(Tok, diag::err_expected) << tok::identifier;
|
|
return nullptr;
|
|
}
|
|
|
|
IdentifierInfo *Identifier = Tok.getIdentifierInfo();
|
|
SourceLocation IdentifierLoc = ConsumeToken();
|
|
|
|
ParsedAttributes Attrs(AttrFactory);
|
|
MaybeParseHLSLSemantics(Attrs, nullptr);
|
|
|
|
ParseScope BufferScope(this, Scope::DeclScope);
|
|
BalancedDelimiterTracker T(*this, tok::l_brace);
|
|
if (T.consumeOpen()) {
|
|
Diag(Tok, diag::err_expected) << tok::l_brace;
|
|
return nullptr;
|
|
}
|
|
|
|
Decl *D = Actions.ActOnStartHLSLBuffer(getCurScope(), IsCBuffer, BufferLoc,
|
|
Identifier, IdentifierLoc,
|
|
T.getOpenLocation());
|
|
|
|
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
|
|
// FIXME: support attribute on constants inside cbuffer/tbuffer.
|
|
ParsedAttributes DeclAttrs(AttrFactory);
|
|
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
|
|
|
|
DeclGroupPtrTy Result =
|
|
ParseExternalDeclaration(DeclAttrs, EmptyDeclSpecAttrs);
|
|
if (!validateDeclsInsideHLSLBuffer(Result, IdentifierLoc, IsCBuffer,
|
|
*this)) {
|
|
T.skipToEnd();
|
|
DeclEnd = T.getCloseLocation();
|
|
BufferScope.Exit();
|
|
Actions.ActOnFinishHLSLBuffer(D, DeclEnd);
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
T.consumeClose();
|
|
DeclEnd = T.getCloseLocation();
|
|
BufferScope.Exit();
|
|
Actions.ActOnFinishHLSLBuffer(D, DeclEnd);
|
|
|
|
Actions.ProcessDeclAttributeList(Actions.CurScope, D, Attrs);
|
|
return D;
|
|
}
|
|
|
|
static void fixSeparateAttrArgAndNumber(StringRef ArgStr, SourceLocation ArgLoc,
|
|
Token Tok, ArgsVector &ArgExprs,
|
|
Parser &P, ASTContext &Ctx,
|
|
Preprocessor &PP) {
|
|
StringRef Num = StringRef(Tok.getLiteralData(), Tok.getLength());
|
|
SourceLocation EndNumLoc = Tok.getEndLoc();
|
|
|
|
P.ConsumeToken(); // consume constant.
|
|
std::string FixedArg = ArgStr.str() + Num.str();
|
|
P.Diag(ArgLoc, diag::err_hlsl_separate_attr_arg_and_number)
|
|
<< FixedArg
|
|
<< FixItHint::CreateReplacement(SourceRange(ArgLoc, EndNumLoc), FixedArg);
|
|
ArgsUnion &Slot = ArgExprs.back();
|
|
Slot = IdentifierLoc::create(Ctx, ArgLoc, PP.getIdentifierInfo(FixedArg));
|
|
}
|
|
|
|
void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs,
|
|
SourceLocation *EndLoc) {
|
|
// FIXME: HLSLSemantic is shared for Semantic and resource binding which is
|
|
// confusing. Need a better name to avoid misunderstanding. Issue
|
|
// https://github.com/llvm/llvm-project/issues/57882
|
|
assert(Tok.is(tok::colon) && "Not a HLSL Semantic");
|
|
ConsumeToken();
|
|
|
|
IdentifierInfo *II = nullptr;
|
|
if (Tok.is(tok::kw_register))
|
|
II = PP.getIdentifierInfo("register");
|
|
else if (Tok.is(tok::identifier))
|
|
II = Tok.getIdentifierInfo();
|
|
|
|
if (!II) {
|
|
Diag(Tok.getLocation(), diag::err_expected_semantic_identifier);
|
|
return;
|
|
}
|
|
|
|
SourceLocation Loc = ConsumeToken();
|
|
if (EndLoc)
|
|
*EndLoc = Tok.getLocation();
|
|
ParsedAttr::Kind AttrKind =
|
|
ParsedAttr::getParsedKind(II, nullptr, ParsedAttr::AS_HLSLSemantic);
|
|
|
|
ArgsVector ArgExprs;
|
|
switch (AttrKind) {
|
|
case ParsedAttr::AT_HLSLResourceBinding: {
|
|
if (ExpectAndConsume(tok::l_paren, diag::err_expected_lparen_after)) {
|
|
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
|
|
return;
|
|
}
|
|
if (!Tok.is(tok::identifier)) {
|
|
Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
|
|
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
|
|
return;
|
|
}
|
|
StringRef SlotStr = Tok.getIdentifierInfo()->getName();
|
|
SourceLocation SlotLoc = Tok.getLocation();
|
|
ArgExprs.push_back(ParseIdentifierLoc());
|
|
|
|
// Add numeric_constant for fix-it.
|
|
if (SlotStr.size() == 1 && Tok.is(tok::numeric_constant))
|
|
fixSeparateAttrArgAndNumber(SlotStr, SlotLoc, Tok, ArgExprs, *this,
|
|
Actions.Context, PP);
|
|
|
|
if (Tok.is(tok::comma)) {
|
|
ConsumeToken(); // consume comma
|
|
if (!Tok.is(tok::identifier)) {
|
|
Diag(Tok.getLocation(), diag::err_expected) << tok::identifier;
|
|
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
|
|
return;
|
|
}
|
|
StringRef SpaceStr = Tok.getIdentifierInfo()->getName();
|
|
SourceLocation SpaceLoc = Tok.getLocation();
|
|
ArgExprs.push_back(ParseIdentifierLoc());
|
|
|
|
// Add numeric_constant for fix-it.
|
|
if (SpaceStr.equals("space") && Tok.is(tok::numeric_constant))
|
|
fixSeparateAttrArgAndNumber(SpaceStr, SpaceLoc, Tok, ArgExprs, *this,
|
|
Actions.Context, PP);
|
|
}
|
|
if (ExpectAndConsume(tok::r_paren, diag::err_expected)) {
|
|
SkipUntil(tok::r_paren, StopAtSemi); // skip through )
|
|
return;
|
|
}
|
|
} break;
|
|
case ParsedAttr::UnknownAttribute:
|
|
Diag(Loc, diag::err_unknown_hlsl_semantic) << II;
|
|
return;
|
|
case ParsedAttr::AT_HLSLSV_GroupIndex:
|
|
case ParsedAttr::AT_HLSLSV_DispatchThreadID:
|
|
break;
|
|
default:
|
|
llvm_unreachable("invalid HLSL Semantic");
|
|
break;
|
|
}
|
|
|
|
Attrs.addNew(II, Loc, nullptr, SourceLocation(), ArgExprs.data(),
|
|
ArgExprs.size(), ParsedAttr::AS_HLSLSemantic);
|
|
}
|