[llvm-branch-commits] [clang] [llvm] [HLSL][RootSignature] Implement Parsing of Descriptor Tables (PR #122982)
Finn Plummer via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Jan 17 16:12:05 PST 2025
https://github.com/inbelic updated https://github.com/llvm/llvm-project/pull/122982
>From 58ef8ad2d3d9bfa008745b35f1514222c13b773a 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 1/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 | 327 ++++++++++++++++++
.../Parse/ParseHLSLRootSignatureTest.cpp | 85 +++++
.../llvm/Frontend/HLSL/HLSLRootSignature.h | 140 ++++++++
4 files changed, 620 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 fac4a92f1920be..c5e6dd112c6fae 100644
--- a/clang/lib/Parse/ParseHLSLRootSignature.cpp
+++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp
@@ -148,6 +148,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 42c57731ddb804..6a2ab179edae33 100644
--- a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
+++ b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
@@ -130,4 +130,89 @@ TEST_F(ParseHLSLRootSignatureTest, LexValidTokensTest) {
CheckTokens(Tokens, Expected);
}
+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 5efadf0390d3fbb33ea41b2bf2335d6c1b078333 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 2/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 c5e6dd112c6fae..ce62526c6d88bc 100644
--- a/clang/lib/Parse/ParseHLSLRootSignature.cpp
+++ b/clang/lib/Parse/ParseHLSLRootSignature.cpp
@@ -219,7 +219,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 6a2ab179edae33..d13b70d9428078 100644
--- a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
+++ b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
@@ -142,7 +142,8 @@ TEST_F(ParseHLSLRootSignatureTest, ParseValidDTClausesTest) {
| Descriptors_Static_Keeping_Buffer_Bounds_Checks
),
visibility = Shader_Visibility_Pixel
- )
+ ),
+ DescriptorTable()
)cc";
TrivialModuleLoader ModLoader;
@@ -158,7 +159,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];
@@ -212,6 +213,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;
}
>From cac95d10450c3dc4976d83ff8015b25a6718c3ec Mon Sep 17 00:00:00 2001
From: Finn Plummer <canadienfinn at gmail.com>
Date: Sat, 18 Jan 2025 00:11:40 +0000
Subject: [PATCH 3/3] rebase onto unique_ptr change
---
clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
index d13b70d9428078..68446b88e977dc 100644
--- a/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
+++ b/clang/unittests/Parse/ParseHLSLRootSignatureTest.cpp
@@ -147,7 +147,7 @@ TEST_F(ParseHLSLRootSignatureTest, ParseValidDTClausesTest) {
)cc";
TrivialModuleLoader ModLoader;
- Preprocessor *PP = CreatePP(Source, ModLoader);
+ auto PP = CreatePP(Source, ModLoader);
auto TokLoc = SourceLocation();
RootSignatureLexer Lexer(Source, TokLoc, *PP);
@@ -218,8 +218,6 @@ TEST_F(ParseHLSLRootSignatureTest, ParseValidDTClausesTest) {
ASSERT_EQ(Elem.Tag, RootElement::ElementType::DescriptorTable);
ASSERT_EQ(Elem.Table.NumClauses, (uint32_t)0);
ASSERT_EQ(Elem.Table.Visibility, ShaderVisibility::All);
-
- delete PP;
}
} // anonymous namespace
More information about the llvm-branch-commits
mailing list