[HLSL][RootSignature] Add parsing for empty RootDescriptors (#140147)

- define the RootDescriptor in-memory struct containing its type
- add test harness for testing

First part of https://github.com/llvm/llvm-project/issues/126577
This commit is contained in:
Finn Plummer 2025-05-21 14:40:13 -07:00 committed by GitHub
parent 4f869e0f5c
commit b499f7f2b2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 91 additions and 4 deletions

View File

@ -73,6 +73,7 @@ private:
/// Root Element parse methods:
std::optional<llvm::hlsl::rootsig::RootFlags> parseRootFlags();
std::optional<llvm::hlsl::rootsig::RootConstants> parseRootConstants();
std::optional<llvm::hlsl::rootsig::RootDescriptor> parseRootDescriptor();
std::optional<llvm::hlsl::rootsig::DescriptorTable> parseDescriptorTable();
std::optional<llvm::hlsl::rootsig::DescriptorTableClause>
parseDescriptorTableClause();

View File

@ -47,6 +47,14 @@ bool RootSignatureParser::parse() {
return true;
Elements.push_back(*Table);
}
if (tryConsumeExpectedToken(
{TokenKind::kw_CBV, TokenKind::kw_SRV, TokenKind::kw_UAV})) {
auto Descriptor = parseRootDescriptor();
if (!Descriptor.has_value())
return true;
Elements.push_back(*Descriptor);
}
} while (tryConsumeExpectedToken(TokenKind::pu_comma));
return consumeExpectedToken(TokenKind::end_of_stream,
@ -155,6 +163,41 @@ std::optional<RootConstants> RootSignatureParser::parseRootConstants() {
return Constants;
}
std::optional<RootDescriptor> RootSignatureParser::parseRootDescriptor() {
assert((CurToken.TokKind == TokenKind::kw_CBV ||
CurToken.TokKind == TokenKind::kw_SRV ||
CurToken.TokKind == TokenKind::kw_UAV) &&
"Expects to only be invoked starting at given keyword");
TokenKind DescriptorKind = CurToken.TokKind;
if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
CurToken.TokKind))
return std::nullopt;
RootDescriptor Descriptor;
switch (DescriptorKind) {
default:
llvm_unreachable("Switch for consumed token was not provided");
case TokenKind::kw_CBV:
Descriptor.Type = DescriptorType::CBuffer;
break;
case TokenKind::kw_SRV:
Descriptor.Type = DescriptorType::SRV;
break;
case TokenKind::kw_UAV:
Descriptor.Type = DescriptorType::UAV;
break;
}
if (consumeExpectedToken(TokenKind::pu_r_paren,
diag::err_hlsl_unexpected_end_of_params,
/*param of=*/TokenKind::kw_RootConstants))
return std::nullopt;
return Descriptor;
}
std::optional<DescriptorTable> RootSignatureParser::parseDescriptorTable() {
assert(CurToken.TokKind == TokenKind::kw_DescriptorTable &&
"Expects to only be invoked starting at given keyword");

View File

@ -344,6 +344,43 @@ TEST_F(ParseHLSLRootSignatureTest, ValidParseRootFlagsTest) {
ASSERT_TRUE(Consumer->isSatisfied());
}
TEST_F(ParseHLSLRootSignatureTest, ValidParseRootDescriptorsTest) {
const llvm::StringLiteral Source = R"cc(
CBV(),
SRV(),
UAV()
)cc";
TrivialModuleLoader ModLoader;
auto PP = createPP(Source, ModLoader);
auto TokLoc = SourceLocation();
hlsl::RootSignatureLexer Lexer(Source, TokLoc);
SmallVector<RootElement> Elements;
hlsl::RootSignatureParser Parser(Elements, Lexer, *PP);
// Test no diagnostics produced
Consumer->setNoDiag();
ASSERT_FALSE(Parser.parse());
ASSERT_EQ(Elements.size(), 3u);
RootElement Elem = Elements[0];
ASSERT_TRUE(std::holds_alternative<RootDescriptor>(Elem));
ASSERT_EQ(std::get<RootDescriptor>(Elem).Type, DescriptorType::CBuffer);
Elem = Elements[1];
ASSERT_TRUE(std::holds_alternative<RootDescriptor>(Elem));
ASSERT_EQ(std::get<RootDescriptor>(Elem).Type, DescriptorType::SRV);
Elem = Elements[2];
ASSERT_TRUE(std::holds_alternative<RootDescriptor>(Elem));
ASSERT_EQ(std::get<RootDescriptor>(Elem).Type, DescriptorType::UAV);
ASSERT_TRUE(Consumer->isSatisfied());
}
TEST_F(ParseHLSLRootSignatureTest, ValidTrailingCommaTest) {
// This test will checks we can handling trailing commas ','
const llvm::StringLiteral Source = R"cc(

View File

@ -85,6 +85,12 @@ struct RootConstants {
ShaderVisibility Visibility = ShaderVisibility::All;
};
using DescriptorType = llvm::dxil::ResourceClass;
// Models RootDescriptor : CBV | SRV | UAV, by collecting like parameters
struct RootDescriptor {
DescriptorType Type;
};
// Models the end of a descriptor table and stores its visibility
struct DescriptorTable {
ShaderVisibility Visibility = ShaderVisibility::All;
@ -125,8 +131,8 @@ struct DescriptorTableClause {
void dump(raw_ostream &OS) const;
};
/// Models RootElement : RootFlags | RootConstants | DescriptorTable
/// | DescriptorTableClause
/// Models RootElement : RootFlags | RootConstants | RootDescriptor
/// | DescriptorTable | DescriptorTableClause
///
/// A Root Signature is modeled in-memory by an array of RootElements. These
/// aim to map closely to their DSL grammar reprsentation defined in the spec.
@ -140,8 +146,8 @@ struct DescriptorTableClause {
/// The DescriptorTable is modelled by having its Clauses as the previous
/// RootElements in the array, and it holds a data member for the Visibility
/// parameter.
using RootElement = std::variant<RootFlags, RootConstants, DescriptorTable,
DescriptorTableClause>;
using RootElement = std::variant<RootFlags, RootConstants, RootDescriptor,
DescriptorTable, DescriptorTableClause>;
void dumpRootElements(raw_ostream &OS, ArrayRef<RootElement> Elements);