This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG782ac2182c2b: [HLSL] Support cbuffer/tbuffer for hlsl. 
(authored by python3kgae).

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D129883/new/

https://reviews.llvm.org/D129883

Files:
  clang/include/clang/AST/Decl.h
  clang/include/clang/AST/JSONNodeDumper.h
  clang/include/clang/AST/RecursiveASTVisitor.h
  clang/include/clang/AST/TextNodeDumper.h
  clang/include/clang/Basic/DeclNodes.td
  clang/include/clang/Basic/DiagnosticParseKinds.td
  clang/include/clang/Basic/TokenKinds.def
  clang/include/clang/Parse/Parser.h
  clang/include/clang/Sema/Sema.h
  clang/include/clang/Serialization/ASTBitCodes.h
  clang/lib/AST/Decl.cpp
  clang/lib/AST/DeclBase.cpp
  clang/lib/AST/DeclPrinter.cpp
  clang/lib/AST/JSONNodeDumper.cpp
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/Basic/IdentifierTable.cpp
  clang/lib/Parse/ParseDecl.cpp
  clang/lib/Parse/ParseHLSL.cpp
  clang/lib/Parse/Parser.cpp
  clang/lib/Sema/CMakeLists.txt
  clang/lib/Sema/IdentifierResolver.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaHLSL.cpp
  clang/lib/Sema/SemaLookup.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/lib/Serialization/ASTCommon.cpp
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/lib/Serialization/ASTWriter.cpp
  clang/lib/Serialization/ASTWriterDecl.cpp
  clang/test/AST/HLSL/Inputs/empty.hlsl
  clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
  clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
  clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
  clang/test/ParserHLSL/cb_error.hlsl
  clang/test/ParserHLSL/invalid_inside_cb.hlsl
  clang/test/SemaHLSL/cb_error.hlsl

Index: clang/test/SemaHLSL/cb_error.hlsl
===================================================================
--- /dev/null
+++ clang/test/SemaHLSL/cb_error.hlsl
@@ -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;
+}
Index: clang/test/ParserHLSL/invalid_inside_cb.hlsl
===================================================================
--- /dev/null
+++ clang/test/ParserHLSL/invalid_inside_cb.hlsl
@@ -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;
+}
Index: clang/test/ParserHLSL/cb_error.hlsl
===================================================================
--- /dev/null
+++ clang/test/ParserHLSL/cb_error.hlsl
@@ -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;}
+}
Index: clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
===================================================================
--- /dev/null
+++ clang/test/AST/HLSL/pch_hlsl_buffer.hlsl
@@ -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'
Index: clang/test/AST/HLSL/cbuffer_tbuffer.hlsl
===================================================================
--- /dev/null
+++ 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]+}} <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;
+}
Index: clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
===================================================================
--- /dev/null
+++ clang/test/AST/HLSL/ast-dump-comment-cbuffe-tbufferr.hlsl
@@ -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'
Index: clang/lib/Serialization/ASTWriterDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterDecl.cpp
+++ clang/lib/Serialization/ASTWriterDecl.cpp
@@ -131,10 +131,9 @@
     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::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);
Index: clang/lib/Serialization/ASTWriter.cpp
===================================================================
--- clang/lib/Serialization/ASTWriter.cpp
+++ clang/lib/Serialization/ASTWriter.cpp
@@ -1017,6 +1017,7 @@
   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);
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -322,6 +322,7 @@
     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::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 @@
   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");
Index: clang/lib/Serialization/ASTCommon.cpp
===================================================================
--- clang/lib/Serialization/ASTCommon.cpp
+++ 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<T> but are not actually
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -876,6 +876,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");
Index: clang/lib/Sema/SemaLookup.cpp
===================================================================
--- clang/lib/Sema/SemaLookup.cpp
+++ clang/lib/Sema/SemaLookup.cpp
@@ -519,7 +519,8 @@
     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;
     }
Index: clang/lib/Sema/SemaHLSL.cpp
===================================================================
--- /dev/null
+++ clang/lib/Sema/SemaHLSL.cpp
@@ -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();
+}
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -7123,6 +7123,9 @@
     return true;
   if (DC->isRecord())
     return false;
+  if (DC->getDeclKind() == Decl::HLSLBuffer)
+    return false;
+
   if (isa<RequiresExprBodyDecl>(DC))
     return false;
   llvm_unreachable("Unexpected context");
Index: clang/lib/Sema/IdentifierResolver.cpp
===================================================================
--- clang/lib/Sema/IdentifierResolver.cpp
+++ clang/lib/Sema/IdentifierResolver.cpp
@@ -99,7 +99,11 @@
 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())
Index: clang/lib/Sema/CMakeLists.txt
===================================================================
--- clang/lib/Sema/CMakeLists.txt
+++ clang/lib/Sema/CMakeLists.txt
@@ -44,6 +44,7 @@
   SemaExprMember.cpp
   SemaExprObjC.cpp
   SemaFixItUtils.cpp
+  SemaHLSL.cpp
   SemaInit.cpp
   SemaLambda.cpp
   SemaLookup.cpp
Index: clang/lib/Parse/Parser.cpp
===================================================================
--- clang/lib/Parse/Parser.cpp
+++ 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.
Index: clang/lib/Parse/ParseHLSL.cpp
===================================================================
--- clang/lib/Parse/ParseHLSL.cpp
+++ clang/lib/Parse/ParseHLSL.cpp
@@ -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");
Index: clang/lib/Parse/ParseDecl.cpp
===================================================================
--- clang/lib/Parse/ParseDecl.cpp
+++ 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);
Index: clang/lib/Basic/IdentifierTable.cpp
===================================================================
--- clang/lib/Basic/IdentifierTable.cpp
+++ clang/lib/Basic/IdentifierTable.cpp
@@ -109,7 +109,8 @@
     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 @@
     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.
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -2388,3 +2388,11 @@
   if (S->hasStoredFPFeatures())
     printFPOptions(S->getStoredFPFeatures());
 }
+
+void TextNodeDumper::VisitHLSLBufferDecl(const HLSLBufferDecl *D) {
+  if (D->isCBuffer())
+    OS << " cbuffer";
+  else
+    OS << " tbuffer";
+  dumpName(D);
+}
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -901,6 +901,11 @@
   }
 }
 
+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");
Index: clang/lib/AST/DeclPrinter.cpp
===================================================================
--- clang/lib/AST/DeclPrinter.cpp
+++ 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);
@@ -462,12 +463,9 @@
         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::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()) {
Index: clang/lib/AST/DeclBase.cpp
===================================================================
--- clang/lib/AST/DeclBase.cpp
+++ clang/lib/AST/DeclBase.cpp
@@ -750,6 +750,7 @@
     case ObjCMethod:
     case ObjCProperty:
     case MSProperty:
+    case HLSLBuffer:
       return IDNS_Ordinary;
     case Label:
       return IDNS_Label;
@@ -1193,7 +1194,7 @@
   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 @@
     // 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:
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -5210,6 +5210,40 @@
   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
 //===----------------------------------------------------------------------===//
Index: clang/include/clang/Serialization/ASTBitCodes.h
===================================================================
--- clang/include/clang/Serialization/ASTBitCodes.h
+++ clang/include/clang/Serialization/ASTBitCodes.h
@@ -1511,7 +1511,10 @@
   /// 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.
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -5962,6 +5962,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
Index: clang/include/clang/Parse/Parser.h
===================================================================
--- clang/include/clang/Parse/Parser.h
+++ clang/include/clang/Parse/Parser.h
@@ -2821,6 +2821,7 @@
 
   void ParseHLSLSemantics(ParsedAttributes &Attrs,
                           SourceLocation *EndLoc = nullptr);
+  Decl *ParseHLSLBuffer(SourceLocation &DeclEnd);
 
   void MaybeParseMicrosoftAttributes(ParsedAttributes &Attrs) {
     if ((getLangOpts().MicrosoftExt || getLangOpts().HLSL) &&
Index: clang/include/clang/Basic/TokenKinds.def
===================================================================
--- clang/include/clang/Basic/TokenKinds.def
+++ clang/include/clang/Basic/TokenKinds.def
@@ -604,6 +604,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)
 
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -1618,6 +1618,8 @@
 
 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">,
Index: clang/include/clang/Basic/DeclNodes.td
===================================================================
--- clang/include/clang/Basic/DeclNodes.td
+++ clang/include/clang/Basic/DeclNodes.td
@@ -108,4 +108,4 @@
 def Empty : DeclNode<Decl>;
 def RequiresExprBody : DeclNode<Decl>, DeclContext;
 def LifetimeExtendedTemporary : DeclNode<Decl>;
-
+def HLSLBuffer : DeclNode<Named, "HLSLBuffer">, DeclContext;
Index: clang/include/clang/AST/TextNodeDumper.h
===================================================================
--- clang/include/clang/AST/TextNodeDumper.h
+++ clang/include/clang/AST/TextNodeDumper.h
@@ -381,6 +381,7 @@
   void VisitConceptDecl(const ConceptDecl *D);
   void
   VisitLifetimeExtendedTemporaryDecl(const LifetimeExtendedTemporaryDecl *D);
+  void VisitHLSLBufferDecl(const HLSLBufferDecl *D);
 };
 
 } // namespace clang
Index: clang/include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- clang/include/clang/AST/RecursiveASTVisitor.h
+++ clang/include/clang/AST/RecursiveASTVisitor.h
@@ -1538,6 +1538,8 @@
 
 DEF_TRAVERSE_DECL(EmptyDecl, {})
 
+DEF_TRAVERSE_DECL(HLSLBufferDecl, {})
+
 DEF_TRAVERSE_DECL(LifetimeExtendedTemporaryDecl, {
   TRY_TO(TraverseStmt(D->getTemporaryExpr()));
 })
Index: clang/include/clang/AST/JSONNodeDumper.h
===================================================================
--- clang/include/clang/AST/JSONNodeDumper.h
+++ clang/include/clang/AST/JSONNodeDumper.h
@@ -246,6 +246,7 @@
   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);
Index: clang/include/clang/AST/Decl.h
===================================================================
--- clang/include/clang/AST/Decl.h
+++ clang/include/clang/AST/Decl.h
@@ -4671,6 +4671,51 @@
   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,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to