[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