[HLSL] Support cbuffer/tbuffer for hlsl.

This is first part for support cbuffer/tbuffer.

The format for cbuffer/tbuffer is
BufferType [Name] [: register(b#)] { VariableDeclaration [: packoffset(c#.xyzw)]; ... };

More details at https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-constants

New keyword 'cbuffer' and 'tbuffer' are added.
New AST node HLSLBufferDecl is added.
Build AST for simple cbuffer/tbuffer without attribute support.

The special thing is variables declared inside cbuffer is exposed into global scope.
So isTransparentContext should return true for HLSLBuffer.

Reviewed By: aaron.ballman

Differential Revision: https://reviews.llvm.org/D129883
This commit is contained in:
Xiang Li 2022-07-15 10:45:57 -07:00
parent 1f4d3c681c
commit 782ac2182c
36 changed files with 566 additions and 14 deletions

View File

@ -4671,6 +4671,51 @@ public:
static bool classofKind(Kind K) { return K == Empty; }
};
/// HLSLBufferDecl - Represent a cbuffer or tbuffer declaration.
class HLSLBufferDecl final : public NamedDecl, public DeclContext {
/// LBraceLoc - The ending location of the source range.
SourceLocation LBraceLoc;
/// RBraceLoc - The ending location of the source range.
SourceLocation RBraceLoc;
/// KwLoc - The location of the cbuffer or tbuffer keyword.
SourceLocation KwLoc;
/// IsCBuffer - Whether the buffer is a cbuffer (and not a tbuffer).
bool IsCBuffer;
HLSLBufferDecl(DeclContext *DC, bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
SourceLocation LBrace);
public:
static HLSLBufferDecl *Create(ASTContext &C, DeclContext *LexicalParent,
bool CBuffer, SourceLocation KwLoc,
IdentifierInfo *ID, SourceLocation IDLoc,
SourceLocation LBrace);
static HLSLBufferDecl *CreateDeserialized(ASTContext &C, unsigned ID);
SourceRange getSourceRange() const LLVM_READONLY {
return SourceRange(getLocStart(), RBraceLoc);
}
SourceLocation getLocStart() const LLVM_READONLY { return KwLoc; }
SourceLocation getLBraceLoc() const { return LBraceLoc; }
SourceLocation getRBraceLoc() const { return RBraceLoc; }
void setRBraceLoc(SourceLocation L) { RBraceLoc = L; }
bool isCBuffer() const { return IsCBuffer; }
// Implement isa/cast/dyncast/etc.
static bool classof(const Decl *D) { return classofKind(D->getKind()); }
static bool classofKind(Kind K) { return K == HLSLBuffer; }
static DeclContext *castToDeclContext(const HLSLBufferDecl *D) {
return static_cast<DeclContext *>(const_cast<HLSLBufferDecl *>(D));
}
static HLSLBufferDecl *castFromDeclContext(const DeclContext *DC) {
return static_cast<HLSLBufferDecl *>(const_cast<DeclContext *>(DC));
}
friend class ASTDeclReader;
friend class ASTDeclWriter;
};
/// Insertion operator for diagnostics. This allows sending NamedDecl's
/// into a diagnostic with <<.
inline const StreamingDiagnostic &operator<<(const StreamingDiagnostic &PD,

View File

@ -246,6 +246,7 @@ public:
void VisitEnumConstantDecl(const EnumConstantDecl *ECD);
void VisitRecordDecl(const RecordDecl *RD);
void VisitCXXRecordDecl(const CXXRecordDecl *RD);
void VisitHLSLBufferDecl(const HLSLBufferDecl *D);
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D);
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D);
void VisitTemplateTemplateParmDecl(const TemplateTemplateParmDecl *D);

View File

@ -1538,6 +1538,8 @@ DEF_TRAVERSE_DECL(CapturedDecl, {
DEF_TRAVERSE_DECL(EmptyDecl, {})
DEF_TRAVERSE_DECL(HLSLBufferDecl, {})
DEF_TRAVERSE_DECL(LifetimeExtendedTemporaryDecl, {
TRY_TO(TraverseStmt(D->getTemporaryExpr()));
})

View File

@ -381,6 +381,7 @@ public:
void VisitConceptDecl(const ConceptDecl *D);
void
VisitLifetimeExtendedTemporaryDecl(const LifetimeExtendedTemporaryDecl *D);
void VisitHLSLBufferDecl(const HLSLBufferDecl *D);
};
} // namespace clang

View File

@ -108,4 +108,4 @@ def OMPRequires : DeclNode<Decl>;
def Empty : DeclNode<Decl>;
def RequiresExprBody : DeclNode<Decl>, DeclContext;
def LifetimeExtendedTemporary : DeclNode<Decl>;
def HLSLBuffer : DeclNode<Named, "HLSLBuffer">, DeclContext;

View File

@ -1618,6 +1618,8 @@ def note_max_tokens_total_override : Note<"total token limit set here">;
def err_expected_semantic_identifier : Error<
"expected HLSL Semantic identifier">;
def err_invalid_declaration_in_hlsl_buffer : Error<
"invalid declaration inside %select{tbuffer|cbuffer}0">;
def err_unknown_hlsl_semantic : Error<"unknown HLSL semantic %0">;
def ext_hlsl_access_specifiers : ExtWarn<
"access specifiers are a clang HLSL extension">,

View File

@ -604,6 +604,10 @@ KEYWORD(addrspace_cast , KEYOPENCLCXX)
// CUDA/HIP function attributes
KEYWORD(__noinline__ , KEYCUDA)
// HLSL keywords.
KEYWORD(cbuffer , KEYHLSL)
KEYWORD(tbuffer , KEYHLSL)
// OpenMP Type Traits
UNARY_EXPR_OR_TYPE_TRAIT(__builtin_omp_required_simd_align, OpenMPRequiredSimdAlign, KEYALL)

View File

@ -2821,6 +2821,7 @@ private:
void ParseHLSLSemantics(ParsedAttributes &Attrs,
SourceLocation *EndLoc = nullptr);
Decl *ParseHLSLBuffer(SourceLocation &DeclEnd);
void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) &&

View File

@ -5962,6 +5962,12 @@ public:
SourceLocation BuiltinLoc,
SourceLocation RParenLoc);
//===---------------------------- HLSL Features -------------------------===//
Decl *ActOnStartHLSLBuffer(Scope *BufferScope, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *Ident,
SourceLocation IdentLoc, SourceLocation LBrace);
void ActOnFinishHLSLBuffer(Decl *Dcl, SourceLocation RBrace);
//===---------------------------- C++ Features --------------------------===//
// Act on C++ namespaces

View File

@ -1511,7 +1511,10 @@ enum DeclCode {
/// A UnnamedGlobalConstantDecl record.
DECL_UNNAMED_GLOBAL_CONSTANT,
DECL_LAST = DECL_UNNAMED_GLOBAL_CONSTANT
/// A HLSLBufferDecl record.
DECL_HLSL_BUFFER,
DECL_LAST = DECL_HLSL_BUFFER
};
/// Record codes for each kind of statement or expression.

View File

@ -5210,6 +5210,40 @@ EmptyDecl *EmptyDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
return new (C, ID) EmptyDecl(nullptr, SourceLocation());
}
HLSLBufferDecl::HLSLBufferDecl(DeclContext *DC, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *ID,
SourceLocation IDLoc, SourceLocation LBrace)
: NamedDecl(Decl::Kind::HLSLBuffer, DC, IDLoc, DeclarationName(ID)),
DeclContext(Decl::Kind::HLSLBuffer), LBraceLoc(LBrace), KwLoc(KwLoc),
IsCBuffer(CBuffer) {}
HLSLBufferDecl *HLSLBufferDecl::Create(ASTContext &C,
DeclContext *LexicalParent, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *ID,
SourceLocation IDLoc,
SourceLocation LBrace) {
// For hlsl like this
// cbuffer A {
// cbuffer B {
// }
// }
// compiler should treat it as
// cbuffer A {
// }
// cbuffer B {
// }
// FIXME: support nested buffers if required for back-compat.
DeclContext *DC = LexicalParent;
HLSLBufferDecl *Result =
new (C, DC) HLSLBufferDecl(DC, CBuffer, KwLoc, ID, IDLoc, LBrace);
return Result;
}
HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, unsigned ID) {
return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr,
SourceLocation(), SourceLocation());
}
//===----------------------------------------------------------------------===//
// ImportDecl Implementation
//===----------------------------------------------------------------------===//

View File

@ -750,6 +750,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) {
case ObjCMethod:
case ObjCProperty:
case MSProperty:
case HLSLBuffer:
return IDNS_Ordinary;
case Label:
return IDNS_Label;
@ -1193,7 +1194,7 @@ bool DeclContext::isTransparentContext() const {
if (getDeclKind() == Decl::Enum)
return !cast<EnumDecl>(this)->isScoped();
return getDeclKind() == Decl::LinkageSpec || getDeclKind() == Decl::Export;
return isa<LinkageSpecDecl, ExportDecl, HLSLBufferDecl>(this);
}
static bool isLinkageSpecContext(const DeclContext *DC,
@ -1258,6 +1259,15 @@ DeclContext *DeclContext::getPrimaryContext() {
// There is only one DeclContext for these entities.
return this;
case Decl::HLSLBuffer:
// Each buffer, even with the same name, is a distinct construct.
// Multiple buffers with the same name are allowed for backward
// compatibility.
// As long as buffers have unique resource bindings the names don't matter.
// The names get exposed via the CPU-side reflection API which
// supports querying bindings, so we cannot remove them.
return this;
case Decl::TranslationUnit:
return static_cast<TranslationUnitDecl *>(this)->getFirstDecl();
case Decl::Namespace:

View File

@ -108,6 +108,7 @@ namespace {
void VisitOMPCapturedExprDecl(OMPCapturedExprDecl *D);
void VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *TTP);
void VisitNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *NTTP);
void VisitHLSLBufferDecl(HLSLBufferDecl *D);
void printTemplateParameters(const TemplateParameterList *Params,
bool OmitTemplateKW = false);
@ -462,12 +463,9 @@ void DeclPrinter::VisitDeclContext(DeclContext *DC, bool Indent) {
Terminator = nullptr;
else
Terminator = ";";
} else if (isa<NamespaceDecl>(*D) || isa<LinkageSpecDecl>(*D) ||
isa<ObjCImplementationDecl>(*D) ||
isa<ObjCInterfaceDecl>(*D) ||
isa<ObjCProtocolDecl>(*D) ||
isa<ObjCCategoryImplDecl>(*D) ||
isa<ObjCCategoryDecl>(*D))
} else if (isa<NamespaceDecl, LinkageSpecDecl, ObjCImplementationDecl,
ObjCInterfaceDecl, ObjCProtocolDecl, ObjCCategoryImplDecl,
ObjCCategoryDecl, HLSLBufferDecl>(*D))
Terminator = nullptr;
else if (isa<EnumConstantDecl>(*D)) {
DeclContext::decl_iterator Next = D;
@ -1658,6 +1656,21 @@ void DeclPrinter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) {
}
}
void DeclPrinter::VisitHLSLBufferDecl(HLSLBufferDecl *D) {
if (D->isCBuffer())
Out << "cbuffer ";
else
Out << "tbuffer ";
Out << *D;
prettyPrintAttributes(D);
Out << " {\n";
VisitDeclContext(D);
Indent() << "}";
}
void DeclPrinter::VisitOMPAllocateDecl(OMPAllocateDecl *D) {
Out << "#pragma omp allocate";
if (!D->varlist_empty()) {

View File

@ -901,6 +901,11 @@ void JSONNodeDumper::VisitCXXRecordDecl(const CXXRecordDecl *RD) {
}
}
void JSONNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) {
VisitNamedDecl(D);
JOS.attribute("bufferKind", D->isCBuffer() ? "cbuffer" : "tbuffer");
}
void JSONNodeDumper::VisitTemplateTypeParmDecl(const TemplateTypeParmDecl *D) {
VisitNamedDecl(D);
JOS.attribute("tagUsed", D->wasDeclaredWithTypename() ? "typename" : "class");

View File

@ -2388,3 +2388,11 @@ void TextNodeDumper::VisitCompoundStmt(const CompoundStmt *S) {
if (S->hasStoredFPFeatures())
printFPOptions(S->getStoredFPFeatures());
}
void TextNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) {
if (D->isCBuffer())
OS << " cbuffer";
else
OS << " tbuffer";
dumpName(D);
}

View File

@ -109,7 +109,8 @@ namespace {
KEYMSCOMPAT = 0x400000,
KEYSYCL = 0x800000,
KEYCUDA = 0x1000000,
KEYMAX = KEYCUDA, // The maximum key
KEYHLSL = 0x2000000,
KEYMAX = KEYHLSL, // The maximum key
KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20,
KEYALL = (KEYMAX | (KEYMAX-1)) & ~KEYNOMS18 &
~KEYNOOPENCL // KEYNOMS18 and KEYNOOPENCL are used to exclude.
@ -199,6 +200,8 @@ static KeywordStatus getKeywordStatusHelper(const LangOptions &LangOpts,
return LangOpts.isSYCL() ? KS_Enabled : KS_Unknown;
case KEYCUDA:
return LangOpts.CUDA ? KS_Enabled : KS_Unknown;
case KEYHLSL:
return LangOpts.HLSL ? KS_Enabled : KS_Unknown;
case KEYNOCXX:
// This is enabled in all non-C++ modes, but might be enabled for other
// reasons as well.

View File

@ -1787,6 +1787,11 @@ Parser::DeclGroupPtrTy Parser::ParseDeclaration(DeclaratorContext Context,
}
return ParseSimpleDeclaration(Context, DeclEnd, DeclAttrs, DeclSpecAttrs,
true, nullptr, DeclSpecStart);
case tok::kw_cbuffer:
case tok::kw_tbuffer:
SingleDecl = ParseHLSLBuffer(DeclEnd);
break;
case tok::kw_namespace:
ProhibitAttributes(DeclAttrs);
ProhibitAttributes(DeclSpecAttrs);

View File

@ -13,9 +13,89 @@
#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();
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());
// FIXME: support attribute on cbuffer/tbuffer.
while (Tok.isNot(tok::r_brace) && Tok.isNot(tok::eof)) {
SourceLocation Loc = Tok.getLocation();
// FIXME: support attribute on constants inside cbuffer/tbuffer.
ParsedAttributes Attrs(AttrFactory);
DeclGroupPtrTy Result = ParseExternalDeclaration(Attrs);
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);
return D;
}
void Parser::ParseHLSLSemantics(ParsedAttributes &Attrs,
SourceLocation *EndLoc) {
assert(Tok.is(tok::colon) && "Not a HLSL Semantic");

View File

@ -947,6 +947,16 @@ Parser::DeclGroupPtrTy Parser::ParseExternalDeclaration(ParsedAttributes &Attrs,
EmptyDeclSpecAttrs);
}
case tok::kw_cbuffer:
case tok::kw_tbuffer:
if (getLangOpts().HLSL) {
SourceLocation DeclEnd;
ParsedAttributes EmptyDeclSpecAttrs(AttrFactory);
return ParseDeclaration(DeclaratorContext::File, DeclEnd, Attrs,
EmptyDeclSpecAttrs);
}
goto dont_know;
case tok::kw_static:
// Parse (then ignore) 'static' prior to a template instantiation. This is
// a GCC extension that we intentionally do not support.

View File

@ -44,6 +44,7 @@ add_clang_library(clangSema
SemaExprMember.cpp
SemaExprObjC.cpp
SemaFixItUtils.cpp
SemaHLSL.cpp
SemaInit.cpp
SemaLambda.cpp
SemaLookup.cpp

View File

@ -99,7 +99,11 @@ IdentifierResolver::~IdentifierResolver() {
bool IdentifierResolver::isDeclInScope(Decl *D, DeclContext *Ctx, Scope *S,
bool AllowInlineNamespace) const {
Ctx = Ctx->getRedeclContext();
// The names for HLSL cbuffer/tbuffers only used by the CPU-side
// reflection API which supports querying bindings. It will not have name
// conflict with other Decls.
if (LangOpt.HLSL && isa<HLSLBufferDecl>(D))
return false;
if (Ctx->isFunctionOrMethod() || (S && S->isFunctionPrototypeScope())) {
// Ignore the scopes associated within transparent declaration contexts.
while (S->getEntity() && S->getEntity()->isTransparentContext())

View File

@ -7123,6 +7123,9 @@ static bool shouldConsiderLinkage(const VarDecl *VD) {
return true;
if (DC->isRecord())
return false;
if (DC->getDeclKind() == Decl::HLSLBuffer)
return false;
if (isa<RequiresExprBodyDecl>(DC))
return false;
llvm_unreachable("Unexpected context");

View File

@ -0,0 +1,34 @@
//===- SemaHLSL.cpp - Semantic Analysis for HLSL constructs ---------------===//
//
// 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 implements Semantic Analysis for HLSL constructs.
//===----------------------------------------------------------------------===//
#include "clang/Sema/Sema.h"
using namespace clang;
Decl *Sema::ActOnStartHLSLBuffer(Scope *BufferScope, bool CBuffer,
SourceLocation KwLoc, IdentifierInfo *Ident,
SourceLocation IdentLoc,
SourceLocation LBrace) {
// For anonymous namespace, take the location of the left brace.
DeclContext *LexicalParent = getCurLexicalContext();
HLSLBufferDecl *Result = HLSLBufferDecl::Create(
Context, LexicalParent, CBuffer, KwLoc, Ident, IdentLoc, LBrace);
PushOnScopeChains(Result, BufferScope);
PushDeclContext(BufferScope, Result);
return Result;
}
void Sema::ActOnFinishHLSLBuffer(Decl *Dcl, SourceLocation RBrace) {
auto *BufDecl = cast<HLSLBufferDecl>(Dcl);
BufDecl->setRBraceLoc(RBrace);
PopDeclContext();
}

View File

@ -519,7 +519,8 @@ void LookupResult::resolveKind() {
D = cast<NamedDecl>(D->getCanonicalDecl());
// Ignore an invalid declaration unless it's the only one left.
if (D->isInvalidDecl() && !(I == 0 && N == 1)) {
// Also ignore HLSLBufferDecl which not have name conflict with other Decls.
if ((D->isInvalidDecl() || isa<HLSLBufferDecl>(D)) && !(I == 0 && N == 1)) {
Decls[I] = Decls[--N];
continue;
}

View File

@ -876,6 +876,10 @@ TemplateDeclInstantiator::VisitTranslationUnitDecl(TranslationUnitDecl *D) {
llvm_unreachable("Translation units cannot be instantiated");
}
Decl *TemplateDeclInstantiator::VisitHLSLBufferDecl(HLSLBufferDecl *Decl) {
llvm_unreachable("HLSL buffer declarations cannot be instantiated");
}
Decl *
TemplateDeclInstantiator::VisitPragmaCommentDecl(PragmaCommentDecl *D) {
llvm_unreachable("pragma comment cannot be instantiated");

View File

@ -433,6 +433,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) {
case Decl::LifetimeExtendedTemporary:
case Decl::RequiresExprBody:
case Decl::UnresolvedUsingIfExists:
case Decl::HLSLBuffer:
return false;
// These indirectly derive from Redeclarable<T> but are not actually

View File

@ -322,6 +322,7 @@ namespace clang {
void VisitNamedDecl(NamedDecl *ND);
void VisitLabelDecl(LabelDecl *LD);
void VisitNamespaceDecl(NamespaceDecl *D);
void VisitHLSLBufferDecl(HLSLBufferDecl *D);
void VisitUsingDirectiveDecl(UsingDirectiveDecl *D);
void VisitNamespaceAliasDecl(NamespaceAliasDecl *D);
void VisitTypeDecl(TypeDecl *TD);
@ -1735,6 +1736,15 @@ void ASTDeclReader::VisitNamespaceDecl(NamespaceDecl *D) {
}
}
void ASTDeclReader::VisitHLSLBufferDecl(HLSLBufferDecl *D) {
VisitNamedDecl(D);
VisitDeclContext(D);
D->IsCBuffer = Record.readBool();
D->KwLoc = readSourceLocation();
D->LBraceLoc = readSourceLocation();
D->RBraceLoc = readSourceLocation();
}
void ASTDeclReader::VisitNamespaceAliasDecl(NamespaceAliasDecl *D) {
RedeclarableResult Redecl = VisitRedeclarable(D);
VisitNamedDecl(D);
@ -3855,6 +3865,9 @@ Decl *ASTReader::ReadDeclRecord(DeclID ID) {
case DECL_OBJC_TYPE_PARAM:
D = ObjCTypeParamDecl::CreateDeserialized(Context, ID);
break;
case DECL_HLSL_BUFFER:
D = HLSLBufferDecl::CreateDeserialized(Context, ID);
break;
}
assert(D && "Unknown declaration reading AST file");

View File

@ -1017,6 +1017,7 @@ void ASTWriter::WriteBlockInfoBlock() {
RECORD(DECL_PRAGMA_DETECT_MISMATCH);
RECORD(DECL_OMP_DECLARE_REDUCTION);
RECORD(DECL_OMP_ALLOCATE);
RECORD(DECL_HLSL_BUFFER);
// Statements and Exprs can occur in the Decls and Types block.
AddStmtsExprs(Stream, Record);

View File

@ -131,10 +131,9 @@ namespace clang {
void VisitCapturedDecl(CapturedDecl *D);
void VisitEmptyDecl(EmptyDecl *D);
void VisitLifetimeExtendedTemporaryDecl(LifetimeExtendedTemporaryDecl *D);
void VisitDeclContext(DeclContext *DC);
template <typename T> void VisitRedeclarable(Redeclarable<T> *D);
void VisitHLSLBufferDecl(HLSLBufferDecl *D);
// FIXME: Put in the same order is DeclNodes.td?
void VisitObjCMethodDecl(ObjCMethodDecl *D);
@ -1864,6 +1863,17 @@ void ASTDeclWriter::VisitRedeclarable(Redeclarable<T> *D) {
}
}
void ASTDeclWriter::VisitHLSLBufferDecl(HLSLBufferDecl *D) {
VisitNamedDecl(D);
VisitDeclContext(D);
Record.push_back(D->isCBuffer());
Record.AddSourceLocation(D->getLocStart());
Record.AddSourceLocation(D->getLBraceLoc());
Record.AddSourceLocation(D->getRBraceLoc());
Code = serialization::DECL_HLSL_BUFFER;
}
void ASTDeclWriter::VisitOMPThreadPrivateDecl(OMPThreadPrivateDecl *D) {
Record.writeOMPChildren(D->Data);
VisitDecl(D);

View File

View File

@ -0,0 +1,51 @@
// RUN: %clang_cc1 -Wdocumentation -ast-dump=json -x hlsl -triple dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefix=JSON
// RUN: %clang_cc1 -Wdocumentation -ast-dump -x hlsl -triple dxil-pc-shadermodel6.3-library %s | FileCheck %s --check-prefix=AST
// JSON:"kind": "HLSLBufferDecl",
// JSON:"name": "A",
// JSON-NEXT:"bufferKind": "cbuffer",
// JSON:"kind": "TextComment",
// JSON:"text": " CBuffer decl."
/// CBuffer decl.
cbuffer A {
// JSON: "kind": "VarDecl",
// JSON: "name": "a",
// JSON: "qualType": "float"
float a;
// JSON: "kind": "VarDecl",
// JSON: "name": "b",
// JSON: "qualType": "int"
int b;
}
// JSON:"kind": "HLSLBufferDecl",
// JSON:"name": "B",
// JSON-NEXT:"bufferKind": "tbuffer",
// JSON:"kind": "TextComment",
// JSON:"text": " TBuffer decl."
/// TBuffer decl.
tbuffer B {
// JSON: "kind": "VarDecl",
// JSON: "name": "c",
// JSON: "qualType": "float"
float c;
// JSON: "kind": "VarDecl",
// JSON: "name": "d",
// JSON: "qualType": "int"
int d;
}
// AST:HLSLBufferDecl {{.*}}:11:1, line:20:1> line:11:9 cbuffer A
// AST-NEXT:FullComment {{.*}}<line:10:4, col:17>
// AST-NEXT:`-ParagraphComment {{.*}}<col:4, col:17>
// AST-NEXT:`-TextComment {{.*}}<col:4, col:17> Text=" CBuffer decl."
// AST-NEXT:-VarDecl {{.*}}<line:15:5, col:11> col:11 a 'float'
// AST-NEXT:`-VarDecl {{.*}}<line:19:5, col:9> col:9 b 'int'
// AST-NEXT:HLSLBufferDecl {{.*}}<line:29:1, line:38:1> line:29:9 tbuffer B
// AST-NEXT:-FullComment {{.*}}<line:28:4, col:17>
// AST-NEXT: `-ParagraphComment {{.*}}<col:4, col:17>
// AST-NEXT: `-TextComment {{.*}}<col:4, col:17> Text=" TBuffer decl."
// AST-NEXT:-VarDecl {{.*}}<line:33:5, col:11> col:11 c 'float'
// AST-NEXT:`-VarDecl {{.*}} <line:37:5, col:9> col:9 d 'int'

View File

@ -0,0 +1,22 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -ast-dump -o - %s | FileCheck %s
// CHECK:HLSLBufferDecl 0x[[CB:[0-9a-f]+]] {{.*}} line:5:9 cbuffer CB
// CHECK-NEXT:VarDecl 0x[[A:[0-9a-f]+]] {{.*}} col:9 used a 'float'
cbuffer CB {
float a;
}
// CHECK:HLSLBufferDecl 0x[[TB:[0-9a-f]+]] {{.*}} line:11:9 tbuffer TB
// CHECK-NEXT:VarDecl 0x[[B:[0-9a-f]+]] {{.*}} col:9 used b 'float'
tbuffer TB {
float b;
}
float foo() {
// CHECK: BinaryOperator 0x{{[0-9a-f]+}} <col:10, col:14> 'float' '+'
// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:10> 'float' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:10> 'float' lvalue Var 0x[[A]] 'a' 'float'
// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} <col:14> 'float' <LValueToRValue>
// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} <col:14> 'float' lvalue Var 0x[[B]] 'b' 'float'
return a + b;
}

View File

@ -0,0 +1,30 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl \
// RUN: -emit-pch -o %t %s
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl \
// RUN: -include-pch %t -fsyntax-only -ast-dump-all %S/Inputs/empty.hlsl \
// RUN: | FileCheck %s
cbuffer A {
float a;
}
tbuffer B {
float b;
}
float foo() {
return a + b;
}
// Make sure cbuffer/tbuffer works for PCH.
// CHECK:HLSLBufferDecl 0x{{[0-9a-f]+}} <{{.*}}:7:1, line:9:1> line:7:9 imported <undeserialized declarations> cbuffer A
// CHECK-NEXT:`-VarDecl 0x[[A:[0-9a-f]+]] <line:8:3, col:9> col:9 imported used a 'float'
// CHECK-NEXT:HLSLBufferDecl 0x{{[0-9a-f]+}} <line:11:1, line:13:1> line:11:9 imported <undeserialized declarations> tbuffer B
// CHECK-NEXT:`-VarDecl 0x[[B:[0-9a-f]+]] <line:12:3, col:9> col:9 imported used b 'float'
// CHECK-NEXT:FunctionDecl 0x{{[0-9a-f]+}} <line:15:1, line:17:1> line:15:7 imported foo 'float ()'
// CHECK-NEXT:CompoundStmt 0x{{[0-9a-f]+}} <col:13, line:17:1>
// CHECK-NEXT:ReturnStmt 0x{{[0-9a-f]+}} <line:16:3, col:14>
// CHECK-NEXT:BinaryOperator 0x{{[0-9a-f]+}} <col:10, col:14> 'float' '+'
// CHECK-NEXT:ImplicitCastExpr 0x{{[0-9a-f]+}} <col:10> 'float' <LValueToRValue>
// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} <col:10> 'float' lvalue Var 0x[[A]] 'a' 'float'
// CHECK-NEXT:`-ImplicitCastExpr 0x{{[0-9a-f]+}} <col:14> 'float' <LValueToRValue>
// CHECK-NEXT:`-DeclRefExpr 0x{{[0-9a-f]+}} <col:14> 'float' lvalue Var 0x[[B]] 'b' 'float'

View File

@ -0,0 +1,74 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
// expected-error@+2 {{expected identifier}}
// expected-error@+1 {{expected unqualified-id}}
cbuffer { ... };
// expected-error@+1 {{expected '{'}}
cbuffer missing_definition;
// expected-error@+1 {{expected unqualified-id}}
int cbuffer;
// expected-error@+1 {{expected identifier}}
cbuffer;
// expected-error@+2 {{expected identifier}}
// expected-error@+1 {{expected unqualified-id}}
tbuffer { ... };
// expected-error@+1 {{expected '{'}}
tbuffer missing_definition;
// expected-error@+1 {{expected unqualified-id}}
int tbuffer;
// expected-error@+1 {{expected identifier}}
tbuffer;
// expected-error@+1 {{expected unqualified-id}}
cbuffer A {}, B{}
// cbuffer inside namespace is supported.
namespace N {
cbuffer A {
float g;
}
}
cbuffer A {
// expected-error@+1 {{invalid declaration inside cbuffer}}
namespace N {
}
}
cbuffer A {
// expected-error@+1 {{invalid declaration inside cbuffer}}
cbuffer Nested {
}
}
struct S {
// expected-error@+1 {{expected member name or ';' after declaration specifiers}}
cbuffer what {
int y;
}
};
void func() {
// expected-error@+1 {{expected expression}}
tbuffer derp {
int z;
}
decltype(derp) another {
int a;
}
}
// struct decl inside cb is supported.
cbuffer A {
struct S2 {
float s;
};
S2 s;
}
// function decl inside cb is supported.
cbuffer A {
float foo_inside_cb() { return 1.2;}
}

View File

@ -0,0 +1,21 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
// template not allowed inside cbuffer.
cbuffer A {
// expected-error@+2 {{invalid declaration inside cbuffer}}
template<typename T>
T foo(T t) { return t;}
}
cbuffer A {
// expected-error@+2 {{invalid declaration inside cbuffer}}
template<typename T>
struct S { float s;};
}
// typealias not allowed inside cbuffer.
cbuffer A {
// expected-error@+2 {{invalid declaration inside cbuffer}}
// expected-warning@+1 {{alias declarations are a C++11 extension}}
using F32 = float;
}

View File

@ -0,0 +1,49 @@
// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.3-library -x hlsl -o - -fsyntax-only %s -verify
// expected-note@+1 {{declared here}}
cbuffer a {
int x;
};
int foo() {
// expected-error@+1 {{'a' does not refer to a value}}
return sizeof(a);
}
// expected-error@+1 {{expected unqualified-id}}
template <typename Ty> cbuffer a { Ty f; };
// For back-compat reason, it is OK for multiple cbuffer/tbuffer use same name in hlsl.
// And these cbuffer name only used for reflection, cannot be removed.
cbuffer A {
float A;
}
cbuffer A {
float b;
}
tbuffer A {
float a;
}
float bar() {
// cbuffer/tbuffer name will not conflict with other variables.
return A;
}
cbuffer a {
// expected-error@+2 {{unknown type name 'oh'}}
// expected-error@+1 {{expected ';' after top level declarator}}
oh no!
// expected-warning@+1 {{missing terminating ' character}}
this isn't even valid HLSL code
despite seeming totally reasonable
once you understand that HLSL
is so flaming weird.
}
tbuffer B {
// expected-error@+1 {{unknown type name 'flaot'}}
flaot f;
}