diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -4650,6 +4650,51 @@ static bool classofKind(Kind K) { return K == Empty; } }; +/// HLSLBufferDecl - Represent a cbuffer or tbuffer declaration. +class HLSLBufferDecl : 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); + + virtual SourceRange getSourceRange() const LLVM_READONLY { + return SourceRange(getLocStart(), RBraceLoc); + } + const char *getDeclKindName() const; + SourceLocation getLocStart() const LLVM_READONLY { return KwLoc; } + 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(const_cast(D)); + } + static HLSLBufferDecl *castFromDeclContext(const DeclContext *DC) { + return static_cast(const_cast(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, diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1551,6 +1551,8 @@ DEF_TRAVERSE_DECL(EmptyDecl, {}) +DEF_TRAVERSE_DECL(HLSLBufferDecl, {}) + DEF_TRAVERSE_DECL(LifetimeExtendedTemporaryDecl, { TRY_TO(TraverseStmt(D->getTemporaryExpr())); }) diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -380,6 +380,7 @@ void VisitConceptDecl(const ConceptDecl *D); void VisitLifetimeExtendedTemporaryDecl(const LifetimeExtendedTemporaryDecl *D); + void VisitHLSLBufferDecl(const HLSLBufferDecl *D); }; } // namespace clang diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -108,4 +108,4 @@ def Empty : DeclNode; def RequiresExprBody : DeclNode, DeclContext; def LifetimeExtendedTemporary : DeclNode; - +def HLSLBuffer : DeclNode, DeclContext; diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -602,6 +602,10 @@ // 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) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2817,6 +2817,8 @@ void ParseHLSLSemantics(ParsedAttributes &Attrs, SourceLocation *EndLoc = nullptr); + Decl *ParseHLSLBuffer(SourceLocation &DeclEnd, + SourceLocation InlineLoc = SourceLocation()); void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) { if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) && diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5913,6 +5913,12 @@ 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 diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -5169,6 +5169,33 @@ 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) { + DeclContext *DC = C.getTranslationUnitDecl(); + HLSLBufferDecl *Result = + new (C, DC) HLSLBufferDecl(DC, CBuffer, KwLoc, ID, IDLoc, LBrace); + if (DC != LexicalParent) { + Result->setLexicalDeclContext(LexicalParent); + } + + return Result; +} + +HLSLBufferDecl *HLSLBufferDecl::CreateDeserialized(ASTContext &C, unsigned ID) { + return new (C, ID) HLSLBufferDecl(nullptr, false, SourceLocation(), nullptr, + SourceLocation(), SourceLocation()); +} + //===----------------------------------------------------------------------===// // ImportDecl Implementation //===----------------------------------------------------------------------===// diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -749,6 +749,7 @@ case ObjCMethod: case ObjCProperty: case MSProperty: + case HLSLBuffer: return IDNS_Ordinary; case Label: return IDNS_Label; @@ -1187,6 +1188,9 @@ if (getDeclKind() == Decl::Enum) return !cast(this)->isScoped(); + if (getDeclKind() == Decl::HLSLBuffer) + return true; + return getDeclKind() == Decl::LinkageSpec || getDeclKind() == Decl::Export; } @@ -1252,6 +1256,10 @@ // There is only one DeclContext for these entities. return this; + case Decl::HLSLBuffer: + // Each buffer, even with the same name, is a distinct construct. + return this; + case Decl::TranslationUnit: return static_cast(this)->getFirstDecl(); case Decl::Namespace: diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -108,6 +108,7 @@ 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); @@ -467,7 +468,8 @@ isa(*D) || isa(*D) || isa(*D) || - isa(*D)) + isa(*D) || + isa(*D)) Terminator = nullptr; else if (isa(*D)) { DeclContext::decl_iterator Next = D; @@ -1658,6 +1660,22 @@ } } +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()) { diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2377,3 +2377,11 @@ if (S->hasStoredFPFeatures()) printFPOptions(S->getStoredFPFeatures()); } + +void TextNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) { + if (D->isCBuffer()) + OS << " cbuffer"; + else + OS << " tbuffer"; + dumpName(D); +} diff --git a/clang/lib/Basic/IdentifierTable.cpp b/clang/lib/Basic/IdentifierTable.cpp --- a/clang/lib/Basic/IdentifierTable.cpp +++ b/clang/lib/Basic/IdentifierTable.cpp @@ -109,7 +109,8 @@ KEYMSCOMPAT = 0x800000, KEYSYCL = 0x1000000, KEYCUDA = 0x2000000, - KEYMAX = KEYCUDA, // The maximum key + KEYHLSL = 0x4000000, + KEYMAX = KEYHLSL, // The maximum key KEYALLCXX = KEYCXX | KEYCXX11 | KEYCXX20, KEYALL = (KEYMAX | (KEYMAX-1)) & ~KEYNOMS18 & ~KEYNOOPENCL // KEYNOMS18 and KEYNOOPENCL are used to exclude. @@ -162,6 +163,8 @@ return KS_Enabled; if (LangOpts.CUDA && (Flags & KEYCUDA)) return KS_Enabled; + if (LangOpts.HLSL && (Flags & KEYHLSL)) + return KS_Enabled; return KS_Disabled; } diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -1787,6 +1787,11 @@ } 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); diff --git a/clang/lib/Parse/ParseHLSL.cpp b/clang/lib/Parse/ParseHLSL.cpp --- a/clang/lib/Parse/ParseHLSL.cpp +++ b/clang/lib/Parse/ParseHLSL.cpp @@ -13,9 +13,52 @@ #include "clang/Basic/AttributeCommonInfo.h" #include "clang/Parse/ParseDiagnostic.h" #include "clang/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" using namespace clang; +Decl *Parser::ParseHLSLBuffer(SourceLocation &DeclEnd, + SourceLocation InlineLoc) { + 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(); // consume identifier + + 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)) { + ParsedAttributes Attrs(AttrFactory); + // FIXME: support attribute on constants inside cbuffer/tbuffer. + ParseExternalDeclaration(Attrs); + } + + 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"); diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -947,6 +947,16 @@ 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. diff --git a/clang/lib/Sema/CMakeLists.txt b/clang/lib/Sema/CMakeLists.txt --- a/clang/lib/Sema/CMakeLists.txt +++ b/clang/lib/Sema/CMakeLists.txt @@ -44,6 +44,7 @@ SemaExprMember.cpp SemaExprObjC.cpp SemaFixItUtils.cpp + SemaHLSL.cpp SemaInit.cpp SemaLambda.cpp SemaLookup.cpp diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7039,6 +7039,9 @@ return true; if (DC->isRecord()) return false; + if (DC->getDeclKind() == Decl::HLSLBuffer) + return false; + if (isa(DC)) return false; llvm_unreachable("Unexpected context"); diff --git a/clang/lib/Sema/SemaHLSL.cpp b/clang/lib/Sema/SemaHLSL.cpp new file mode 100644 --- /dev/null +++ b/clang/lib/Sema/SemaHLSL.cpp @@ -0,0 +1,37 @@ +//===- 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); + + ActOnDocumentableDecl(Result); + + return Result; +} + +void Sema::ActOnFinishHLSLBuffer(Decl *Dcl, SourceLocation RBrace) { + HLSLBufferDecl *BufDecl = dyn_cast_or_null(Dcl); + assert(BufDecl && "Invalid parameter, expected HLSLBufferDecl"); + BufDecl->setRBraceLoc(RBrace); + PopDeclContext(); +} diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -873,6 +873,10 @@ 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"); diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -433,6 +433,7 @@ case Decl::LifetimeExtendedTemporary: case Decl::RequiresExprBody: case Decl::UnresolvedUsingIfExists: + case Decl::HLSLBuffer: return false; // These indirectly derive from Redeclarable but are not actually diff --git a/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/AST/HLSL/cbuffer_tbuffer.hlsl @@ -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]+}} 'float' '+' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[A]] 'a' 'float' +// CHECK-NEXT: ImplicitCastExpr 0x{{[0-9a-f]+}} 'float' +// CHECK-NEXT: DeclRefExpr 0x{{[0-9a-f]+}} 'float' lvalue Var 0x[[B]] 'b' 'float' + return a + b; +} diff --git a/clang/test/SemaHLSL/cb_error.hlsl b/clang/test/SemaHLSL/cb_error.hlsl new file mode 100644 --- /dev/null +++ b/clang/test/SemaHLSL/cb_error.hlsl @@ -0,0 +1,12 @@ +// 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; +