[clang] [llvm] [HLSL][RootSignature] Implement Parsing of Descriptor Tables (PR #122982)

Finn Plummer via llvm-commits llvm-commits at lists.llvm.org
Thu Jan 16 09:33:56 PST 2025


https://github.com/inbelic updated https://github.com/llvm/llvm-project/pull/122982

>From 6a11fba7ecdb78b2ba1e63773a30b32109f6d78e Mon Sep 17 00:00:00 2001
From: Finn Plummer <canadienfinn at gmail.com>
Date: Tue, 14 Jan 2025 22:22:45 +0000
Subject: [PATCH 1/3] [HLSL][RootSignature] Implement Lexing of
 DescriptorTables

- Define required tokens to parse a Descriptor Table in TokenKinds.def
- Implements a Lexer to handle all of the defined tokens in
ParseHLSLRootSignature
---
 .../Parse/HLSLRootSignatureTokenKinds.def     | 121 ++++++++++++++
 .../clang/Parse/ParseHLSLRootSignature.h      |  96 +++++++++++
 clang/lib/Parse/CMakeLists.txt                |   1 +
 clang/lib/Parse/ParseHLSLRootSignature.cpp    | 151 ++++++++++++++++++
 clang/unittests/CMakeLists.txt                |   1 +
 clang/unittests/Parse/CMakeLists.txt          |  26 +++
 .../Parse/ParseHLSLRootSignatureTest.cpp      | 130 +++++++++++++++
 7 files changed, 526 insertions(+)
 create mode 100644 clang/include/clang/Parse/HLSLRootSignatureTokenKinds.def
 create mode 100644 clang/include/clang/Parse/ParseHLSLRootSignature.h
 create mode 100644 clang/lib/Parse/ParseHLSLRootSignature.cpp
 create mode 100644 clang/unittests/Parse/CMakeLists.txt
 create mode 100644 clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp

diff --git a/clang/include/clang/Parse/HLSLRootSignatureTokenKinds.def b/clang/include/clang/Parse/HLSLRootSignatureTokenKinds.def
new file mode 100644
index 00000000000000..aaa2dabcb43f4e
--- /dev/null
+++ b/clang/include/clang/Parse/HLSLRootSignatureTokenKinds.def
@@ -0,0 +1,121 @@
+//===--- HLSLRootSignature.def - Tokens and Enum Database -------*- C++ -*-===//
+
+//
+// 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 defines the TokenKinds used in the Root Signature DSL. This
+// includes keywords, enums and a small subset of punctuators. Users of this
+// file must optionally #define the TOK, KEYWORD, ENUM or specific ENUM macros
+// to make use of this file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TOK
+#define TOK(X)
+#endif
+#ifndef PUNCTUATOR
+#define PUNCTUATOR(X,Y) TOK(pu_ ## X)
+#endif
+#ifndef KEYWORD
+#define KEYWORD(X) TOK(kw_ ## X)
+#endif
+#ifndef ENUM
+#define ENUM(NAME, LIT) TOK(en_ ## NAME)
+#endif
+
+// Defines the various types of enum
+#ifndef DESCRIPTOR_RANGE_OFFSET_ENUM
+#define DESCRIPTOR_RANGE_OFFSET_ENUM(NAME, LIT) ENUM(NAME, LIT)
+#endif
+#ifndef ROOT_DESCRIPTOR_FLAG_ENUM
+#define ROOT_DESCRIPTOR_FLAG_ENUM(NAME, LIT) ENUM(NAME, LIT)
+#endif
+// Note: ON denotes that the flag unique from the above Root Descriptor Flags.
+//  This is required to avoid token kind enum conflicts.
+#ifndef DESCRIPTOR_RANGE_FLAG_ENUM_OFF
+#define DESCRIPTOR_RANGE_FLAG_ENUM_OFF(NAME, LIT)
+#endif
+#ifndef DESCRIPTOR_RANGE_FLAG_ENUM_ON
+#define DESCRIPTOR_RANGE_FLAG_ENUM_ON(NAME, LIT) ENUM(NAME, LIT)
+#endif
+#ifndef DESCRIPTOR_RANGE_FLAG_ENUM
+#define DESCRIPTOR_RANGE_FLAG_ENUM(NAME, LIT, ON) DESCRIPTOR_RANGE_FLAG_ENUM_##ON(NAME, LIT)
+#endif
+#ifndef SHADER_VISIBILITY_ENUM
+#define SHADER_VISIBILITY_ENUM(NAME, LIT) ENUM(NAME, LIT)
+#endif
+
+// General Tokens:
+TOK(invalid)
+TOK(int_literal)
+
+// Register Tokens:
+TOK(bReg)
+TOK(tReg)
+TOK(uReg)
+TOK(sReg)
+
+// Punctuators:
+PUNCTUATOR(l_paren, '(')
+PUNCTUATOR(r_paren, ')')
+PUNCTUATOR(comma,   ',')
+PUNCTUATOR(or,      '|')
+PUNCTUATOR(equal,   '=')
+
+// RootElement Keywords:
+KEYWORD(DescriptorTable)
+
+// DescriptorTable Keywords:
+KEYWORD(CBV)
+KEYWORD(SRV)
+KEYWORD(UAV)
+KEYWORD(Sampler)
+
+// General Parameter Keywords:
+KEYWORD(space)
+KEYWORD(visibility)
+KEYWORD(flags)
+
+// View Parameter Keywords:
+KEYWORD(numDescriptors)
+KEYWORD(offset)
+
+// Descriptor Range Offset Enum:
+DESCRIPTOR_RANGE_OFFSET_ENUM(DescriptorRangeOffsetAppend, "DESCRIPTOR_RANGE_OFFSET_APPEND")
+
+// Root Descriptor Flag Enums:
+ROOT_DESCRIPTOR_FLAG_ENUM(DataVolatile, "DATA_VOLATILE")
+ROOT_DESCRIPTOR_FLAG_ENUM(DataStaticWhileSetAtExecute, "DATA_STATIC_WHILE_SET_AT_EXECUTE")
+ROOT_DESCRIPTOR_FLAG_ENUM(DataStatic, "DATA_STATIC")
+
+// Descriptor Range Flag Enums:
+DESCRIPTOR_RANGE_FLAG_ENUM(DescriptorsVolatile, "DESCRIPTORS_VOLATILE", ON)
+DESCRIPTOR_RANGE_FLAG_ENUM(DataVolatile, "DATA_VOLATILE", OFF)
+DESCRIPTOR_RANGE_FLAG_ENUM(DataStaticWhileSetAtExecute, "DATA_STATIC_WHILE_SET_AT_EXECUTE", OFF)
+DESCRIPTOR_RANGE_FLAG_ENUM(DataStatic, "DATA_STATIC", OFF)
+DESCRIPTOR_RANGE_FLAG_ENUM(DescriptorsStaticKeepingBufferBoundsChecks, "DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS", ON)
+
+// Shader Visibiliy Enums:
+SHADER_VISIBILITY_ENUM(All, "SHADER_VISIBILITY_ALL")
+SHADER_VISIBILITY_ENUM(Vertex, "SHADER_VISIBILITY_VERTEX")
+SHADER_VISIBILITY_ENUM(Hull, "SHADER_VISIBILITY_HULL")
+SHADER_VISIBILITY_ENUM(Domain, "SHADER_VISIBILITY_DOMAIN")
+SHADER_VISIBILITY_ENUM(Geometry, "SHADER_VISIBILITY_GEOMETRY")
+SHADER_VISIBILITY_ENUM(Pixel, "SHADER_VISIBILITY_PIXEL")
+SHADER_VISIBILITY_ENUM(Amplification, "SHADER_VISIBILITY_AMPLIFICATION")
+SHADER_VISIBILITY_ENUM(Mesh, "SHADER_VISIBILITY_MESH")
+
+#undef SHADER_VISIBILITY_ENUM
+#undef DESCRIPTOR_RANGE_FLAG_ENUM
+#undef DESCRIPTOR_RANGE_FLAG_ENUM_OFF
+#undef DESCRIPTOR_RANGE_FLAG_ENUM_ON
+#undef ROOT_DESCRIPTOR_FLAG_ENUM
+#undef DESCRIPTOR_RANGE_OFFSET_ENUM
+#undef ENUM
+#undef KEYWORD
+#undef PUNCTUATOR
+#undef TOK
diff --git a/clang/include/clang/Parse/ParseHLSLRootSignature.h b/clang/include/clang/Parse/ParseHLSLRootSignature.h
new file mode 100644
index 00000000000000..6c534411e754a0
--- /dev/null
+++ b/clang/include/clang/Parse/ParseHLSLRootSignature.h
@@ -0,0 +1,96 @@
+//===--- ParseHLSLRootSignature.h -------------------------------*- C++ -*-===//
+//
+// 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 defines the ParseHLSLRootSignature interface.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H
+#define LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H
+
+#include "clang/Lex/LiteralSupport.h"
+#include "clang/Lex/Preprocessor.h"
+
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSwitch.h"
+
+namespace llvm {
+namespace hlsl {
+namespace root_signature {
+
+struct RootSignatureToken {
+  enum Kind {
+#define TOK(X) X,
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+  };
+
+  Kind Kind = Kind::invalid;
+
+  // Retain the SouceLocation of the token for diagnostics
+  clang::SourceLocation TokLoc;
+
+  // Retain if the uint32_t bits represent a signed integer
+  bool Signed = false;
+  union {
+    uint32_t IntLiteral = 0;
+    float FloatLiteral;
+  };
+
+  // Constructors
+  RootSignatureToken() {}
+  RootSignatureToken(clang::SourceLocation TokLoc) : TokLoc(TokLoc) {}
+};
+using TokenKind = enum RootSignatureToken::Kind;
+
+class RootSignatureLexer {
+public:
+  RootSignatureLexer(StringRef Signature, clang::SourceLocation SourceLoc,
+                     clang::Preprocessor &PP)
+      : Buffer(Signature), SourceLoc(SourceLoc), PP(PP) {}
+
+  // Consumes the internal buffer as a list of tokens and will emplace them
+  // onto the given tokens.
+  //
+  // It will consume until it successfully reaches the end of the buffer,
+  // or, until the first error is encountered. The return value denotes if
+  // there was a failure.
+  bool Lex(SmallVector<RootSignatureToken> &Tokens);
+
+  // Get the current source location of the lexer
+  clang::SourceLocation GetLocation() { return SourceLoc; };
+
+private:
+  // Internal buffer to iterate over
+  StringRef Buffer;
+
+  // Passed down parameters from Sema
+  clang::SourceLocation SourceLoc;
+  clang::Preprocessor &PP;
+
+  bool LexNumber(RootSignatureToken &Result);
+
+  // Consumes the internal buffer for a single token.
+  //
+  // The return value denotes if there was a failure.
+  bool LexToken(RootSignatureToken &Token);
+
+  // Advance the buffer by the specified number of characters. Updates the
+  // SourceLocation appropriately.
+  void AdvanceBuffer(unsigned NumCharacters = 1) {
+    Buffer = Buffer.drop_front(NumCharacters);
+    SourceLoc = SourceLoc.getLocWithOffset(NumCharacters);
+  }
+};
+
+} // namespace root_signature
+} // namespace hlsl
+} // namespace llvm
+
+#endif // LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H
diff --git a/clang/lib/Parse/CMakeLists.txt b/clang/lib/Parse/CMakeLists.txt
index 22e902f7e1bc50..00fde537bb9c60 100644
--- a/clang/lib/Parse/CMakeLists.txt
+++ b/clang/lib/Parse/CMakeLists.txt
@@ -14,6 +14,7 @@ add_clang_library(clangParse
   ParseExpr.cpp
   ParseExprCXX.cpp
   ParseHLSL.cpp
+  ParseHLSLRootSignature.cpp
   ParseInit.cpp
   ParseObjc.cpp
   ParseOpenMP.cpp
diff --git a/clang/lib/Parse/ParseHLSLRootSignature.cpp b/clang/lib/Parse/ParseHLSLRootSignature.cpp
new file mode 100644
index 00000000000000..428dd85ea98eea
--- /dev/null
+++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp
@@ -0,0 +1,151 @@
+#include "clang/Parse/ParseHLSLRootSignature.h"
+
+namespace llvm {
+namespace hlsl {
+namespace root_signature {
+
+// Lexer Definitions
+
+static bool IsPreprocessorNumberChar(char C) {
+  // TODO: extend for float support with or without hexadecimal/exponent
+  return isdigit(C); // integer support
+}
+
+bool RootSignatureLexer::LexNumber(RootSignatureToken &Result) {
+  // NumericLiteralParser does not handle the sign so we will manually apply it
+  Result.Signed = Buffer.front() == '-';
+  if (Result.Signed)
+    AdvanceBuffer();
+
+  // Retrieve the possible number
+  StringRef NumSpelling = Buffer.take_while(IsPreprocessorNumberChar);
+
+  // Parse the numeric value and so semantic checks on its specification
+  clang::NumericLiteralParser Literal(NumSpelling, SourceLoc,
+                                      PP.getSourceManager(), PP.getLangOpts(),
+                                      PP.getTargetInfo(), PP.getDiagnostics());
+  if (Literal.hadError)
+    return true; // Error has already been reported so just return
+
+  // Retrieve the number value to store into the token
+  if (Literal.isIntegerLiteral()) {
+    Result.Kind = TokenKind::int_literal;
+
+    APSInt X = APSInt(32, Result.Signed);
+    if (Literal.GetIntegerValue(X))
+      return true; // TODO: Report overflow error
+
+    X = Result.Signed ? -X : X;
+    Result.IntLiteral = (uint32_t)X.getZExtValue();
+  } else {
+    return true; // TODO: report unsupported number literal specification
+  }
+
+  AdvanceBuffer(NumSpelling.size());
+  return false;
+}
+
+bool RootSignatureLexer::Lex(SmallVector<RootSignatureToken> &Tokens) {
+  // Discard any leading whitespace
+  AdvanceBuffer(Buffer.take_while(isspace).size());
+
+  while (!Buffer.empty()) {
+    RootSignatureToken Result;
+    if (LexToken(Result))
+      return true;
+
+    // Successfully Lexed the token so we can store it
+    Tokens.push_back(Result);
+
+    // Discard any trailing whitespace
+    AdvanceBuffer(Buffer.take_while(isspace).size());
+  }
+
+  return false;
+}
+
+bool RootSignatureLexer::LexToken(RootSignatureToken &Result) {
+  // Record where this token is in the text of diagnostics
+  Result.TokLoc = SourceLoc;
+
+  char C = Buffer.front();
+
+  // Punctuators
+  switch (C) {
+#define PUNCTUATOR(X, Y)                                                       \
+  case Y: {                                                                    \
+    Result.Kind = TokenKind::pu_##X;                                           \
+    AdvanceBuffer();                                                           \
+    return false;                                                              \
+  }
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+  default:
+    break;
+  }
+
+  // Numeric constant
+  if (isdigit(C) || C == '-')
+    return LexNumber(Result);
+
+  // All following tokens require at least one additional character
+  if (Buffer.size() <= 1)
+    return true; // TODO: Report invalid token error
+
+  // Peek at the next character to deteremine token type
+  char NextC = Buffer[1];
+
+  // Registers: [tsub][0-9+]
+  if ((C == 't' || C == 's' || C == 'u' || C == 'b') && isdigit(NextC)) {
+    AdvanceBuffer();
+
+    if (LexNumber(Result))
+      return true;
+
+    // Lex number could also parse a float so ensure it was an unsigned int
+    if (Result.Kind != TokenKind::int_literal || Result.Signed)
+      return true; // Return invalid number literal for register error
+
+    // Convert character to the register type.
+    // This is done after LexNumber to override the TokenKind
+    switch (C) {
+    case 'b':
+      Result.Kind = TokenKind::bReg;
+      break;
+    case 't':
+      Result.Kind = TokenKind::tReg;
+      break;
+    case 'u':
+      Result.Kind = TokenKind::uReg;
+      break;
+    case 's':
+      Result.Kind = TokenKind::sReg;
+      break;
+    default:
+      llvm_unreachable("Switch for an expected token was not provided");
+      return true;
+    }
+    return false;
+  }
+
+  // Keywords and Enums:
+  StringRef TokSpelling =
+      Buffer.take_while([](char C) { return isalnum(C) || C == '_'; });
+
+  // Define a large string switch statement for all the keywords and enums
+  auto Switch = llvm::StringSwitch<TokenKind>(TokSpelling);
+#define KEYWORD(NAME) Switch.Case(#NAME, TokenKind::kw_##NAME);
+#define ENUM(NAME, LIT) Switch.CaseLower(LIT, TokenKind::en_##NAME);
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+
+  // Then attempt to retreive a string from it
+  auto Kind = Switch.Default(TokenKind::invalid);
+  if (Kind == TokenKind::invalid)
+    return true; // TODO: Report invalid identifier
+
+  Result.Kind = Kind;
+  AdvanceBuffer(TokSpelling.size());
+  return false;
+}
+
+} // namespace root_signature
+} // namespace hlsl
diff --git a/clang/unittests/CMakeLists.txt b/clang/unittests/CMakeLists.txt
index 85d265426ec80b..9b3ce8aa7de739 100644
--- a/clang/unittests/CMakeLists.txt
+++ b/clang/unittests/CMakeLists.txt
@@ -25,6 +25,7 @@ endfunction()
 
 add_subdirectory(Basic)
 add_subdirectory(Lex)
+add_subdirectory(Parse)
 add_subdirectory(Driver)
 if(CLANG_ENABLE_STATIC_ANALYZER)
   add_subdirectory(Analysis)
diff --git a/clang/unittests/Parse/CMakeLists.txt b/clang/unittests/Parse/CMakeLists.txt
new file mode 100644
index 00000000000000..1b7eb4934a46c8
--- /dev/null
+++ b/clang/unittests/Parse/CMakeLists.txt
@@ -0,0 +1,26 @@
+set(LLVM_LINK_COMPONENTS
+  Support
+  )
+
+add_clang_unittest(ParseTests
+  ParseHLSLRootSignatureTest.cpp
+  )
+
+clang_target_link_libraries(ParseTests
+  PRIVATE
+  clangAST
+  clangASTMatchers
+  clangBasic
+  clangFrontend
+  clangParse
+  clangSema
+  clangSerialization
+  clangTooling
+  )
+
+target_link_libraries(ParseTests
+  PRIVATE
+  LLVMTestingAnnotations
+  LLVMTestingSupport
+  clangTesting
+  )
diff --git a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
new file mode 100644
index 00000000000000..c430b0657dacf5
--- /dev/null
+++ b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
@@ -0,0 +1,130 @@
+//=== ParseHLSLRootSignatureTest.cpp - Parse Root Signature tests ---------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/DiagnosticOptions.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/HeaderSearchOptions.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/ModuleLoader.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/PreprocessorOptions.h"
+
+#include "clang/Parse/ParseHLSLRootSignature.h"
+#include "gtest/gtest.h"
+
+using namespace llvm::hlsl::root_signature;
+using namespace clang;
+
+namespace {
+
+// The test fixture.
+class ParseHLSLRootSignatureTest : public ::testing::Test {
+protected:
+  ParseHLSLRootSignatureTest()
+      : FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
+        Diags(DiagID, new DiagnosticOptions, new IgnoringDiagConsumer()),
+        SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) {
+    TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
+    Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
+  }
+
+  Preprocessor *CreatePP(StringRef Source, TrivialModuleLoader &ModLoader) {
+    std::unique_ptr<llvm::MemoryBuffer> Buf =
+        llvm::MemoryBuffer::getMemBuffer(Source);
+    SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
+
+    HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
+                            Diags, LangOpts, Target.get());
+    Preprocessor *PP =
+        new Preprocessor(std::make_shared<PreprocessorOptions>(), Diags,
+                         LangOpts, SourceMgr, HeaderInfo, ModLoader,
+                         /*IILookup =*/nullptr,
+                         /*OwnsHeaderSearch =*/false);
+    PP->Initialize(*Target);
+    PP->EnterMainSourceFile();
+    return PP;
+  }
+
+  void CheckTokens(SmallVector<RootSignatureToken> &Computed,
+                   SmallVector<TokenKind> &Expected) {
+    ASSERT_EQ(Computed.size(), Expected.size());
+    for (unsigned I = 0, E = Expected.size(); I != E; ++I) {
+      ASSERT_EQ(Computed[I].Kind, Expected[I]);
+    }
+  }
+
+  FileSystemOptions FileMgrOpts;
+  FileManager FileMgr;
+  IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
+  DiagnosticsEngine Diags;
+  SourceManager SourceMgr;
+  LangOptions LangOpts;
+  std::shared_ptr<TargetOptions> TargetOpts;
+  IntrusiveRefCntPtr<TargetInfo> Target;
+};
+
+TEST_F(ParseHLSLRootSignatureTest, LexValidTokensTest) {
+  const llvm::StringLiteral Source = R"cc(
+    -42
+
+    b0 t43 u987 s234
+
+    (),|=
+
+    DescriptorTable
+
+    CBV SRV UAV Sampler
+    space visibility flags
+    numDescriptors offset
+
+    DESCRIPTOR_RANGE_OFFSET_APPEND
+
+    DATA_VOLATILE
+    DATA_STATIC_WHILE_SET_AT_EXECUTE
+    DATA_STATIC
+    DESCRIPTORS_VOLATILE
+    DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS
+
+    shader_visibility_all
+    shader_visibility_vertex
+    shader_visibility_hull
+    shader_visibility_domain
+    shader_visibility_geometry
+    shader_visibility_pixel
+    shader_visibility_amplification
+    shader_visibility_mesh
+  )cc";
+
+  TrivialModuleLoader ModLoader;
+  Preprocessor *PP = CreatePP(Source, ModLoader);
+  auto TokLoc = SourceLocation();
+
+  RootSignatureLexer Lexer(Source, TokLoc, *PP);
+
+  SmallVector<RootSignatureToken> Tokens = {
+      RootSignatureToken() // invalid token for completeness
+  };
+  ASSERT_FALSE(Lexer.Lex(Tokens));
+
+  SmallVector<TokenKind> Expected = {
+#define TOK(NAME) TokenKind::NAME,
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+  };
+
+  CheckTokens(Tokens, Expected);
+
+  delete PP;
+}
+
+} // anonymous namespace

>From d5b19cbe5c9fa31c37dc9ccb3c40ca75c9e68e06 Mon Sep 17 00:00:00 2001
From: Finn Plummer <canadienfinn at gmail.com>
Date: Tue, 14 Jan 2025 22:23:22 +0000
Subject: [PATCH 2/3] [HLSL][RootSignature] Implement Parsing of Descriptor
 Tables

- Defines the in-memory data layout for the Descriptor Table Clauses,
its dependent flags/enums and parent RootElement in HLSLRootSignature.h
- Implements a Parser and its required Parsing methods in
ParseHLSLRootSignature
---
 .../clang/Parse/ParseHLSLRootSignature.h      |  68 ++++
 clang/lib/Parse/ParseHLSLRootSignature.cpp    | 328 ++++++++++++++++++
 .../Parse/ParseHLSLRootSignatureTest.cpp      |  85 +++++
 .../llvm/Frontend/HLSL/HLSLRootSignature.h    | 140 ++++++++
 4 files changed, 621 insertions(+)
 create mode 100644 llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h

diff --git a/clang/include/clang/Parse/ParseHLSLRootSignature.h b/clang/include/clang/Parse/ParseHLSLRootSignature.h
index 6c534411e754a0..9464bd8f2f9e0f 100644
--- a/clang/include/clang/Parse/ParseHLSLRootSignature.h
+++ b/clang/include/clang/Parse/ParseHLSLRootSignature.h
@@ -21,6 +21,8 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/ADT/StringSwitch.h"
 
+#include "llvm/Frontend/HLSL/HLSLRootSignature.h"
+
 namespace llvm {
 namespace hlsl {
 namespace root_signature {
@@ -89,6 +91,72 @@ class RootSignatureLexer {
   }
 };
 
+class RootSignatureParser {
+public:
+  RootSignatureParser(SmallVector<RootElement> &Elements,
+                      const SmallVector<RootSignatureToken> &Tokens);
+
+  // Iterates over the provided tokens and constructs the in-memory
+  // representations of the RootElements.
+  //
+  // The return value denotes if there was a failure and the method will
+  // return on the first encountered failure, or, return false if it
+  // can sucessfully reach the end of the tokens.
+  bool Parse();
+
+private:
+  bool ReportError(); // TODO: Implement this to report error through Diags
+
+  // Root Element helpers
+  bool ParseRootElement();
+  bool ParseDescriptorTable();
+  bool ParseDescriptorTableClause();
+
+  // Common parsing helpers
+  bool ParseRegister(Register &Register);
+
+  // Various flags/enum parsing helpers
+  bool ParseDescriptorRangeFlags(DescriptorRangeFlags &Flags);
+  bool ParseShaderVisibility(ShaderVisibility &Flag);
+
+  // Increment the token iterator if we have not reached the end.
+  // Return value denotes if we were already at the last token.
+  bool ConsumeNextToken();
+
+  // Attempt to retrieve the next token, if TokenKind is invalid then there was
+  // no next token.
+  RootSignatureToken PeekNextToken();
+
+  // Peek if the next token is of the expected kind.
+  //
+  // Return value denotes if it failed to match the expected kind, either it is
+  // the end of the stream or it didn't match any of the expected kinds.
+  bool PeekExpectedToken(TokenKind Expected);
+  bool PeekExpectedToken(ArrayRef<TokenKind> AnyExpected);
+
+  // Consume the next token and report an error if it is not of the expected
+  // kind.
+  //
+  // Return value denotes if it failed to match the expected kind, either it is
+  // the end of the stream or it didn't match any of the expected kinds.
+  bool ConsumeExpectedToken(TokenKind Expected);
+  bool ConsumeExpectedToken(ArrayRef<TokenKind> AnyExpected);
+
+  // Peek if the next token is of the expected kind and if it is then consume
+  // it.
+  //
+  // Return value denotes if it failed to match the expected kind, either it is
+  // the end of the stream or it didn't match any of the expected kinds. It will
+  // not report an error if there isn't a match.
+  bool TryConsumeExpectedToken(TokenKind Expected);
+  bool TryConsumeExpectedToken(ArrayRef<TokenKind> Expected);
+
+private:
+  SmallVector<RootElement> &Elements;
+  SmallVector<RootSignatureToken>::const_iterator CurTok;
+  SmallVector<RootSignatureToken>::const_iterator LastTok;
+};
+
 } // namespace root_signature
 } // namespace hlsl
 } // namespace llvm
diff --git a/clang/lib/Parse/ParseHLSLRootSignature.cpp b/clang/lib/Parse/ParseHLSLRootSignature.cpp
index 428dd85ea98eea..9f12ff12866dcd 100644
--- a/clang/lib/Parse/ParseHLSLRootSignature.cpp
+++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp
@@ -147,5 +147,333 @@ bool RootSignatureLexer::LexToken(RootSignatureToken &Result) {
   return false;
 }
 
+// Parser Definitions
+
+RootSignatureParser::RootSignatureParser(
+    SmallVector<RootElement> &Elements,
+    const SmallVector<RootSignatureToken> &Tokens)
+    : Elements(Elements) {
+  CurTok = Tokens.begin();
+  LastTok = Tokens.end();
+}
+
+bool RootSignatureParser::ReportError() { return true; }
+
+bool RootSignatureParser::Parse() {
+  CurTok--; // Decrement once here so we can use the ...ExpectedToken api
+
+  // Iterate as many RootElements as possible
+  bool HasComma = true;
+  while (HasComma &&
+         !TryConsumeExpectedToken(ArrayRef{TokenKind::kw_DescriptorTable})) {
+    if (ParseRootElement())
+      return true;
+    HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+  }
+  if (HasComma)
+    return ReportError(); // report 'comma' denotes a required extra item
+
+  // Ensure that we are at the end of the tokens
+  CurTok++;
+  if (CurTok != LastTok)
+    return ReportError(); // report expected end of input but got more
+  return false;
+}
+
+bool RootSignatureParser::ParseRootElement() {
+  // Dispatch onto the correct parse method
+  switch (CurTok->Kind) {
+  case TokenKind::kw_DescriptorTable:
+    return ParseDescriptorTable();
+  default:
+    llvm_unreachable("Switch for an expected token was not provided");
+    return true;
+  }
+}
+
+bool RootSignatureParser::ParseDescriptorTable() {
+  DescriptorTable Table;
+
+  if (ConsumeExpectedToken(TokenKind::pu_l_paren))
+    return true;
+
+  // Iterate as many DescriptorTableClaues as possible
+  bool HasComma = true;
+  while (!TryConsumeExpectedToken({TokenKind::kw_CBV, TokenKind::kw_SRV,
+                                   TokenKind::kw_UAV, TokenKind::kw_Sampler})) {
+    if (ParseDescriptorTableClause())
+      return true;
+    Table.NumClauses++;
+    HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+  }
+
+  // Consume optional 'visibility' paramater
+  if (HasComma && !TryConsumeExpectedToken(TokenKind::kw_visibility)) {
+    if (ConsumeExpectedToken(TokenKind::pu_equal))
+      return true;
+
+    if (ParseShaderVisibility(Table.Visibility))
+      return true;
+
+    HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+  }
+
+  if (HasComma)
+    return ReportError(); // report 'comma' denotes a required extra item
+
+  if (ConsumeExpectedToken(TokenKind::pu_r_paren))
+    return true;
+
+  Elements.push_back(RootElement(Table));
+  return false;
+}
+
+bool RootSignatureParser::ParseDescriptorTableClause() {
+  // Determine the type of Clause first so we can initialize the struct with
+  // the correct default flags
+  ClauseType CT;
+  switch (CurTok->Kind) {
+  case TokenKind::kw_CBV:
+    CT = ClauseType::CBV;
+    break;
+  case TokenKind::kw_SRV:
+    CT = ClauseType::SRV;
+    break;
+  case TokenKind::kw_UAV:
+    CT = ClauseType::UAV;
+    break;
+  case TokenKind::kw_Sampler:
+    CT = ClauseType::Sampler;
+    break;
+  default:
+    llvm_unreachable("Switch for an expected token was not provided");
+    return true;
+  }
+  DescriptorTableClause Clause(CT);
+
+  if (ConsumeExpectedToken(TokenKind::pu_l_paren))
+    return true;
+
+  // Consume mandatory Register paramater
+  if (ConsumeExpectedToken(
+          {TokenKind::bReg, TokenKind::tReg, TokenKind::uReg, TokenKind::sReg}))
+    return true;
+  if (ParseRegister(Clause.Register))
+    return true;
+
+  // Start parsing the optional parameters
+  bool HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+
+  // Consume optional 'numDescriptors' paramater
+  if (HasComma && !TryConsumeExpectedToken(TokenKind::kw_numDescriptors)) {
+    if (ConsumeExpectedToken(TokenKind::pu_equal))
+      return true;
+    if (ConsumeExpectedToken(TokenKind::int_literal))
+      return true;
+
+    Clause.NumDescriptors = CurTok->IntLiteral;
+
+    HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+  }
+
+  // Consume optional 'space' paramater
+  if (HasComma && !TryConsumeExpectedToken(TokenKind::kw_space)) {
+    if (ConsumeExpectedToken(TokenKind::pu_equal))
+      return true;
+    if (ConsumeExpectedToken(TokenKind::int_literal))
+      return true;
+
+    Clause.Space = CurTok->IntLiteral;
+
+    HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+  }
+
+  // Consume optional 'offset' paramater
+  if (HasComma && !TryConsumeExpectedToken(TokenKind::kw_offset)) {
+    if (ConsumeExpectedToken(TokenKind::pu_equal))
+      return true;
+    if (ConsumeExpectedToken(ArrayRef{
+            TokenKind::int_literal, TokenKind::en_DescriptorRangeOffsetAppend}))
+      return true;
+
+    // Offset defaults to DescriptorTableOffsetAppend so only change if we have
+    // an int arg
+    if (CurTok->Kind == TokenKind::int_literal)
+      Clause.Offset = CurTok->IntLiteral;
+
+    HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+  }
+
+  // Consume optional 'flags' paramater
+  if (HasComma && !TryConsumeExpectedToken(TokenKind::kw_flags)) {
+    if (ConsumeExpectedToken(TokenKind::pu_equal))
+      return true;
+    if (ParseDescriptorRangeFlags(Clause.Flags))
+      return true;
+
+    HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
+  }
+
+  if (HasComma)
+    return ReportError(); // report 'comma' denotes a required extra item
+  if (ConsumeExpectedToken(TokenKind::pu_r_paren))
+    return true;
+
+  Elements.push_back(Clause);
+  return false;
+}
+
+bool RootSignatureParser::ParseRegister(Register &Register) {
+  switch (CurTok->Kind) {
+  case TokenKind::bReg:
+    Register.ViewType = RegisterType::BReg;
+    break;
+  case TokenKind::tReg:
+    Register.ViewType = RegisterType::TReg;
+    break;
+  case TokenKind::uReg:
+    Register.ViewType = RegisterType::UReg;
+    break;
+  case TokenKind::sReg:
+    Register.ViewType = RegisterType::SReg;
+    break;
+  default:
+    llvm_unreachable("Switch for an expected token was not provided");
+    return true;
+  }
+
+  Register.Number = CurTok->IntLiteral;
+
+  return false;
+}
+
+bool RootSignatureParser::ParseDescriptorRangeFlags(
+    DescriptorRangeFlags &Flags) {
+
+  // Define the possible flag kinds
+  SmallVector<TokenKind> FlagToks = {
+      TokenKind::int_literal, // This is used to capture the possible '0'
+#define DESCRIPTOR_RANGE_FLAG_ENUM(NAME, LIT, ON) TokenKind::en_##NAME,
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+  };
+
+  if (PeekExpectedToken(FlagToks))
+    return ReportError(); // report there must be at least one flag specified
+
+  // Since there is at least one flag specified then reset the default flag
+  Flags = DescriptorRangeFlags::None;
+
+  // Iterate over the given list of flags
+  bool HasOr = true;
+  while (HasOr && !TryConsumeExpectedToken(FlagToks)) {
+    switch (CurTok->Kind) {
+    case TokenKind::int_literal: {
+      if (CurTok->IntLiteral != 0)
+        return ReportError(); // report invalid flag value error
+      // No need to 'or' with 0 so just break
+      break;
+    }
+    // Set each specified flag set in the flags
+#define DESCRIPTOR_RANGE_FLAG_ENUM(NAME, LIT, ON)                              \
+  case TokenKind::en_##NAME: {                                                 \
+    Flags |= DescriptorRangeFlags::NAME;                                       \
+    break;                                                                     \
+  }
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+    default:
+      llvm_unreachable("Switch for an expected token was not provided");
+      return true;
+    }
+    HasOr = !TryConsumeExpectedToken(TokenKind::pu_or);
+  }
+  if (HasOr)
+    return ReportError(); // report 'or' denotes a required extra item
+
+  return false;
+}
+
+bool RootSignatureParser::ParseShaderVisibility(ShaderVisibility &Flag) {
+
+  // Define the possible flag kinds
+  SmallVector<TokenKind> FlagToks = {
+#define SHADER_VISIBILITY_ENUM(NAME, LIT) TokenKind::en_##NAME,
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+  };
+
+  // Required mandatory flag argument
+  if (ConsumeExpectedToken(FlagToks))
+    return true;
+
+  switch (CurTok->Kind) {
+#define SHADER_VISIBILITY_ENUM(NAME, LIT)                                      \
+  case TokenKind::en_##NAME: {                                                 \
+    Flag = ShaderVisibility::NAME;                                             \
+    break;                                                                     \
+  }
+#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
+  default:
+    llvm_unreachable("Switch for an expected token was not provided");
+    return true;
+  }
+
+  return false;
+}
+
+RootSignatureToken RootSignatureParser::PeekNextToken() {
+  RootSignatureToken Token; // Defaults to invalid kind
+  if (CurTok != LastTok)
+    Token = *(CurTok + 1);
+  return Token;
+}
+
+bool RootSignatureParser::ConsumeNextToken() {
+  if (CurTok == LastTok)
+    return ReportError(); // Report unexpected end of tokens error
+  CurTok++;
+  return false;
+}
+
+bool RootSignatureParser::PeekExpectedToken(TokenKind Expected) {
+  return PeekExpectedToken(ArrayRef{Expected});
+}
+
+bool RootSignatureParser::PeekExpectedToken(ArrayRef<TokenKind> AnyExpected) {
+  RootSignatureToken Token = PeekNextToken();
+  if (Token.Kind == TokenKind::invalid)
+    return true;
+  for (auto Expected : AnyExpected) {
+    if (Token.Kind == Expected)
+      return false;
+  }
+  return true;
+}
+
+bool RootSignatureParser::ConsumeExpectedToken(TokenKind Expected) {
+  return ConsumeExpectedToken(ArrayRef{Expected});
+}
+
+bool RootSignatureParser::ConsumeExpectedToken(
+    ArrayRef<TokenKind> AnyExpected) {
+  if (ConsumeNextToken())
+    return true;
+  for (auto Expected : AnyExpected) {
+    if (CurTok->Kind == Expected)
+      return false;
+  }
+  return ReportError(); // Report unexpected token kind error
+}
+
+bool RootSignatureParser::TryConsumeExpectedToken(TokenKind Expected) {
+  return TryConsumeExpectedToken(ArrayRef{Expected});
+}
+
+bool RootSignatureParser::TryConsumeExpectedToken(
+    ArrayRef<TokenKind> AnyExpected) {
+  if (PeekExpectedToken(AnyExpected))
+    return true;
+  return ConsumeNextToken();
+}
+
 } // namespace root_signature
 } // namespace hlsl
+} // namespace llvm
diff --git a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
index c430b0657dacf5..db538653322009 100644
--- a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
+++ b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
@@ -127,4 +127,89 @@ TEST_F(ParseHLSLRootSignatureTest, LexValidTokensTest) {
   delete PP;
 }
 
+TEST_F(ParseHLSLRootSignatureTest, ParseValidDTClausesTest) {
+  const llvm::StringLiteral Source = R"cc(
+    DescriptorTable(
+      CBV(b0),
+      SRV(t42, numDescriptors = 4, offset = 32),
+      Sampler(s987, space = 2, flags = 0),
+      UAV(u987234,
+        flags = Descriptors_Volatile | Data_Volatile
+              | Data_Static_While_Set_At_Execute | Data_Static
+              | Descriptors_Static_Keeping_Buffer_Bounds_Checks
+      ),
+      visibility = Shader_Visibility_Pixel
+    )
+  )cc";
+
+  TrivialModuleLoader ModLoader;
+  Preprocessor *PP = CreatePP(Source, ModLoader);
+  auto TokLoc = SourceLocation();
+
+  RootSignatureLexer Lexer(Source, TokLoc, *PP);
+
+  SmallVector<RootSignatureToken> Tokens;
+  ASSERT_FALSE(Lexer.Lex(Tokens));
+
+  SmallVector<RootElement> Elements;
+  RootSignatureParser Parser(Elements, Tokens);
+
+  ASSERT_FALSE(Parser.Parse());
+  ASSERT_EQ((int)Elements.size(), 5);
+
+  // Test default values are set correctly
+  RootElement Elem = Elements[0];
+  ASSERT_EQ(Elem.Tag, RootElement::ElementType::DescriptorTableClause);
+  ASSERT_EQ(Elem.Clause.Type, ClauseType::CBV);
+  ASSERT_EQ(Elem.Clause.Register.ViewType, RegisterType::BReg);
+  ASSERT_EQ(Elem.Clause.Register.Number, (uint32_t)0);
+  ASSERT_EQ(Elem.Clause.NumDescriptors, (uint32_t)1);
+  ASSERT_EQ(Elem.Clause.Space, (uint32_t)0);
+  ASSERT_EQ(Elem.Clause.Offset, (uint32_t)DescriptorTableOffsetAppend);
+  ASSERT_EQ(Elem.Clause.Flags,
+            DescriptorRangeFlags::DataStaticWhileSetAtExecute);
+
+  // Test optionally specified 'numDescriptors' and 'offset' parameters
+  Elem = Elements[1];
+  ASSERT_EQ(Elem.Tag, RootElement::ElementType::DescriptorTableClause);
+  ASSERT_EQ(Elem.Clause.Type, ClauseType::SRV);
+  ASSERT_EQ(Elem.Clause.Register.ViewType, RegisterType::TReg);
+  ASSERT_EQ(Elem.Clause.Register.Number, (uint32_t)42);
+  ASSERT_EQ(Elem.Clause.NumDescriptors, (uint32_t)4);
+  ASSERT_EQ(Elem.Clause.Space, (uint32_t)0);
+  ASSERT_EQ(Elem.Clause.Offset, (uint32_t)32);
+  ASSERT_EQ(Elem.Clause.Flags,
+            DescriptorRangeFlags::DataStaticWhileSetAtExecute);
+
+  // Test specified 'space' and the '0' flag in 'flags'
+  Elem = Elements[2];
+  ASSERT_EQ(Elem.Tag, RootElement::ElementType::DescriptorTableClause);
+  ASSERT_EQ(Elem.Clause.Type, ClauseType::Sampler);
+  ASSERT_EQ(Elem.Clause.Register.ViewType, RegisterType::SReg);
+  ASSERT_EQ(Elem.Clause.Register.Number, (uint32_t)987);
+  ASSERT_EQ(Elem.Clause.NumDescriptors, (uint32_t)1);
+  ASSERT_EQ(Elem.Clause.Space, (uint32_t)2);
+  ASSERT_EQ(Elem.Clause.Offset, (uint32_t)DescriptorTableOffsetAppend);
+  ASSERT_EQ(Elem.Clause.Flags, DescriptorRangeFlags::None);
+
+  // Test that we can specify all valid flags
+  Elem = Elements[3];
+  ASSERT_EQ(Elem.Tag, RootElement::ElementType::DescriptorTableClause);
+  ASSERT_EQ(Elem.Clause.Type, ClauseType::UAV);
+  ASSERT_EQ(Elem.Clause.Register.ViewType, RegisterType::UReg);
+  ASSERT_EQ(Elem.Clause.Register.Number, (uint32_t)987234);
+  ASSERT_EQ(Elem.Clause.NumDescriptors, (uint32_t)1);
+  ASSERT_EQ(Elem.Clause.Space, (uint32_t)0);
+  ASSERT_EQ(Elem.Clause.Offset, (uint32_t)DescriptorTableOffsetAppend);
+  ASSERT_EQ(Elem.Clause.Flags, DescriptorRangeFlags::ValidFlags);
+
+  // Test generated DescriptorTable start has correct values
+  Elem = Elements[4];
+  ASSERT_EQ(Elem.Tag, RootElement::ElementType::DescriptorTable);
+  ASSERT_EQ(Elem.Table.NumClauses, (uint32_t)4);
+  ASSERT_EQ(Elem.Table.Visibility, ShaderVisibility::Pixel);
+
+  delete PP;
+}
+
 } // anonymous namespace
diff --git a/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
new file mode 100644
index 00000000000000..7707180f8a4942
--- /dev/null
+++ b/llvm/include/llvm/Frontend/HLSL/HLSLRootSignature.h
@@ -0,0 +1,140 @@
+//===- HLSLRootSignature.h - HLSL Root Signature helper objects -----------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file This file contains helper objects for working with HLSL Root
+/// Signatures.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_FRONTEND_HLSL_HLSLROOTSIGNATURE_H
+#define LLVM_FRONTEND_HLSL_HLSLROOTSIGNATURE_H
+
+#include <stdint.h>
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Endian.h"
+
+namespace llvm {
+namespace hlsl {
+namespace root_signature {
+
+// This is a copy from DebugInfo/CodeView/CodeView.h
+#define RS_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(Class)                            \
+  inline Class operator|(Class a, Class b) {                                   \
+    return static_cast<Class>(llvm::to_underlying(a) |                         \
+                              llvm::to_underlying(b));                         \
+  }                                                                            \
+  inline Class operator&(Class a, Class b) {                                   \
+    return static_cast<Class>(llvm::to_underlying(a) &                         \
+                              llvm::to_underlying(b));                         \
+  }                                                                            \
+  inline Class operator~(Class a) {                                            \
+    return static_cast<Class>(~llvm::to_underlying(a));                        \
+  }                                                                            \
+  inline Class &operator|=(Class &a, Class b) {                                \
+    a = a | b;                                                                 \
+    return a;                                                                  \
+  }                                                                            \
+  inline Class &operator&=(Class &a, Class b) {                                \
+    a = a & b;                                                                 \
+    return a;                                                                  \
+  }
+
+// Definition of the various enumerations and flags
+enum class DescriptorRangeFlags : unsigned {
+  None = 0,
+  DescriptorsVolatile = 0x1,
+  DataVolatile = 0x2,
+  DataStaticWhileSetAtExecute = 0x4,
+  DataStatic = 0x8,
+  DescriptorsStaticKeepingBufferBoundsChecks = 0x10000,
+  ValidFlags = 0x1000f,
+  ValidSamplerFlags = DescriptorsVolatile,
+};
+RS_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(DescriptorRangeFlags)
+
+enum class ShaderVisibility {
+  All = 0,
+  Vertex = 1,
+  Hull = 2,
+  Domain = 3,
+  Geometry = 4,
+  Pixel = 5,
+  Amplification = 6,
+  Mesh = 7,
+};
+
+// Definitions of the in-memory data layout structures
+
+// Models the different registers: bReg | tReg | uReg | sReg
+enum class RegisterType { BReg, TReg, UReg, SReg };
+struct Register {
+  RegisterType ViewType;
+  uint32_t Number;
+};
+
+static const uint32_t DescriptorTableOffsetAppend = 0xffffffff;
+// Models DTClause : CBV | SRV | UAV | Sampler by collecting like parameters
+enum class ClauseType { CBV, SRV, UAV, Sampler };
+struct DescriptorTableClause {
+  ClauseType Type;
+  Register Register;
+  uint32_t NumDescriptors = 1;
+  uint32_t Space = 0;
+  uint32_t Offset = DescriptorTableOffsetAppend;
+  DescriptorRangeFlags Flags;
+
+  DescriptorTableClause(ClauseType Type) : Type(Type) {
+    switch (Type) {
+    case ClauseType::CBV:
+      Flags = DescriptorRangeFlags::DataStaticWhileSetAtExecute;
+      break;
+    case ClauseType::SRV:
+      Flags = DescriptorRangeFlags::DataStaticWhileSetAtExecute;
+      break;
+    case ClauseType::UAV:
+      Flags = DescriptorRangeFlags::DataVolatile;
+      break;
+    case ClauseType::Sampler:
+      Flags = DescriptorRangeFlags::None;
+      break;
+    }
+  }
+};
+
+// Models the end of a descriptor table and stores its visibility
+struct DescriptorTable {
+  ShaderVisibility Visibility = ShaderVisibility::All;
+  uint32_t NumClauses = 0; // The number of clauses in the table
+};
+
+// Models RootElement : DescriptorTable | DescriptorTableClause
+struct RootElement {
+  enum class ElementType {
+    DescriptorTable,
+    DescriptorTableClause,
+  };
+
+  ElementType Tag;
+  union {
+    DescriptorTable Table;
+    DescriptorTableClause Clause;
+  };
+
+  // Constructors
+  RootElement(DescriptorTable Table)
+      : Tag(ElementType::DescriptorTable), Table(Table) {}
+  RootElement(DescriptorTableClause Clause)
+      : Tag(ElementType::DescriptorTableClause), Clause(Clause) {}
+};
+
+} // namespace root_signature
+} // namespace hlsl
+} // namespace llvm
+
+#endif // LLVM_FRONTEND_HLSL_HLSLROOTSIGNATURE_H

>From ab390a2f7bea0505ce009db7ab2fc14ad21eea7a Mon Sep 17 00:00:00 2001
From: Finn Plummer <canadienfinn at gmail.com>
Date: Tue, 14 Jan 2025 23:21:02 +0000
Subject: [PATCH 3/3] add extra default visibility testcase

- also catches a small error for no clauses edge case
---
 clang/lib/Parse/ParseHLSLRootSignature.cpp           |  2 +-
 clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp | 11 +++++++++--
 2 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/clang/lib/Parse/ParseHLSLRootSignature.cpp b/clang/lib/Parse/ParseHLSLRootSignature.cpp
index 9f12ff12866dcd..e1bd006eac48b5 100644
--- a/clang/lib/Parse/ParseHLSLRootSignature.cpp
+++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp
@@ -218,7 +218,7 @@ bool RootSignatureParser::ParseDescriptorTable() {
     HasComma = !TryConsumeExpectedToken(TokenKind::pu_comma);
   }
 
-  if (HasComma)
+  if (HasComma && Table.NumClauses != 0)
     return ReportError(); // report 'comma' denotes a required extra item
 
   if (ConsumeExpectedToken(TokenKind::pu_r_paren))
diff --git a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
index db538653322009..2167a64ae8c635 100644
--- a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
+++ b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
@@ -139,7 +139,8 @@ TEST_F(ParseHLSLRootSignatureTest, ParseValidDTClausesTest) {
               | Descriptors_Static_Keeping_Buffer_Bounds_Checks
       ),
       visibility = Shader_Visibility_Pixel
-    )
+    ),
+    DescriptorTable()
   )cc";
 
   TrivialModuleLoader ModLoader;
@@ -155,7 +156,7 @@ TEST_F(ParseHLSLRootSignatureTest, ParseValidDTClausesTest) {
   RootSignatureParser Parser(Elements, Tokens);
 
   ASSERT_FALSE(Parser.Parse());
-  ASSERT_EQ((int)Elements.size(), 5);
+  ASSERT_EQ((int)Elements.size(), 6);
 
   // Test default values are set correctly
   RootElement Elem = Elements[0];
@@ -209,6 +210,12 @@ TEST_F(ParseHLSLRootSignatureTest, ParseValidDTClausesTest) {
   ASSERT_EQ(Elem.Table.NumClauses, (uint32_t)4);
   ASSERT_EQ(Elem.Table.Visibility, ShaderVisibility::Pixel);
 
+  // Test generated DescriptorTable start has correct default values
+  Elem = Elements[5];
+  ASSERT_EQ(Elem.Tag, RootElement::ElementType::DescriptorTable);
+  ASSERT_EQ(Elem.Table.NumClauses, (uint32_t)0);
+  ASSERT_EQ(Elem.Table.Visibility, ShaderVisibility::All);
+
   delete PP;
 }
 



More information about the llvm-commits mailing list