-
Notifications
You must be signed in to change notification settings - Fork 14.8k
[HLSL][RootSignature] Implement parsing of a DescriptorTable with empty clauses #133302
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
3094705
d744cd1
a0999c7
14c0479
9f07795
49cc48e
d87707f
d944fff
2123282
318f1ec
2af4de6
78c3837
262eeed
2eeda54
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
//===--- 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/Basic/DiagnosticParse.h" | ||
#include "clang/Lex/LexHLSLRootSignature.h" | ||
#include "clang/Lex/Preprocessor.h" | ||
|
||
#include "llvm/ADT/SmallVector.h" | ||
#include "llvm/ADT/StringRef.h" | ||
|
||
#include "llvm/Frontend/HLSL/HLSLRootSignature.h" | ||
|
||
namespace clang { | ||
namespace hlsl { | ||
|
||
class RootSignatureParser { | ||
public: | ||
RootSignatureParser(SmallVector<llvm::hlsl::rootsig::RootElement> &Elements, | ||
RootSignatureLexer &Lexer, clang::Preprocessor &PP); | ||
|
||
/// Consumes tokens from the Lexer and constructs the in-memory | ||
/// representations of the RootElements. Tokens are consumed until an | ||
/// error is encountered or the end of the buffer. | ||
/// | ||
/// Returns true if a parsing error is encountered. | ||
bool Parse(); | ||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
private: | ||
DiagnosticsEngine &Diags() { return PP.getDiagnostics(); } | ||
|
||
// All private Parse.* methods follow a similar pattern: | ||
// - Each method will start with an assert to denote what the CurToken is | ||
// expected to be and will parse from that token forward | ||
// | ||
// - Therefore, it is the callers responsibility to ensure that you are | ||
// at the correct CurToken. This should be done with the pattern of: | ||
// | ||
// if (TryConsumeExpectedToken(TokenKind)) | ||
// if (Parse.*()) | ||
// return true; | ||
// | ||
// or, | ||
// | ||
// if (ConsumeExpectedToken(TokenKind, ...)) | ||
// return true; | ||
// if (Parse.*()) | ||
// return true; | ||
// | ||
// - All methods return true if a parsing error is encountered. It is the | ||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// callers responsibility to propogate this error up, or deal with it | ||
// otherwise | ||
// | ||
// - An error will be raised if the proceeding tokens are not what is | ||
// expected, or, there is a lexing error | ||
|
||
/// Root Element parse methods: | ||
bool ParseDescriptorTable(); | ||
bool ParseDescriptorTableClause(); | ||
|
||
/// Invoke the Lexer to consume a token and update CurToken with the result | ||
void ConsumeNextToken() { CurToken = Lexer.ConsumeToken(); } | ||
|
||
/// Return true if the next token one of the expected kinds | ||
bool PeekExpectedToken(TokenKind Expected); | ||
bool PeekExpectedToken(ArrayRef<TokenKind> AnyExpected); | ||
|
||
/// Consumes the next token and report an error if it is not of the expected | ||
/// kind. | ||
/// | ||
/// Returns true if there was an error reported. | ||
bool ConsumeExpectedToken(TokenKind Expected, | ||
unsigned DiagID = diag::err_expected, | ||
TokenKind Context = TokenKind::invalid); | ||
bool ConsumeExpectedToken(ArrayRef<TokenKind> AnyExpected, | ||
unsigned DiagID = diag::err_expected, | ||
TokenKind Context = TokenKind::invalid); | ||
|
||
/// Peek if the next token is of the expected kind and if it is then consume | ||
/// it. | ||
/// | ||
/// Returns true if it successfully matches the expected kind and the token | ||
/// was consumed. | ||
bool TryConsumeExpectedToken(TokenKind Expected); | ||
bool TryConsumeExpectedToken(ArrayRef<TokenKind> Expected); | ||
|
||
private: | ||
SmallVector<llvm::hlsl::rootsig::RootElement> &Elements; | ||
RootSignatureLexer &Lexer; | ||
|
||
clang::Preprocessor &PP; | ||
|
||
RootSignatureToken CurToken; | ||
}; | ||
|
||
} // namespace hlsl | ||
} // namespace clang | ||
|
||
#endif // LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H |
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,188 @@ | ||
#include "clang/Parse/ParseHLSLRootSignature.h" | ||
|
||
#include "llvm/Support/raw_ostream.h" | ||
|
||
using namespace llvm::hlsl::rootsig; | ||
|
||
namespace clang { | ||
namespace hlsl { | ||
|
||
static std::string FormatTokenKinds(ArrayRef<TokenKind> Kinds) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can simplify this while also making it more efficient by implementing a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Updated to define the |
||
std::string TokenString; | ||
llvm::raw_string_ostream Out(TokenString); | ||
bool First = true; | ||
for (auto Kind : Kinds) { | ||
if (!First) | ||
Out << ", "; | ||
switch (Kind) { | ||
#define TOK(X, SPELLING) \ | ||
case TokenKind::X: \ | ||
Out << SPELLING; \ | ||
break; | ||
#include "clang/Lex/HLSLRootSignatureTokenKinds.def" | ||
} | ||
First = false; | ||
} | ||
|
||
return TokenString; | ||
} | ||
|
||
// Parser Definitions | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: can remove |
||
|
||
RootSignatureParser::RootSignatureParser(SmallVector<RootElement> &Elements, | ||
RootSignatureLexer &Lexer, | ||
Preprocessor &PP) | ||
: Elements(Elements), Lexer(Lexer), PP(PP), CurToken(SourceLocation()) {} | ||
|
||
bool RootSignatureParser::Parse() { | ||
// Iterate as many RootElements as possible | ||
while (TryConsumeExpectedToken(TokenKind::kw_DescriptorTable)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: Something that makes me confuse with that approach is, how will that look once you add more RootElements? Will that become something like: while (
TryConsumeExpectedToken(TokenKind::kw_DescriptorTable) ||
TryConsumeExpectedToken(TokenKind::kw_RootDescriptor) ||
TryConsumeExpectedToken(TokenKind::kw_RootConstant)) {
...
} My expectation for that code would be something like: do{
// Parsing logic here
} while(TryConsumeExpectedToken(TokenKind::pu_comma)); There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, it will look like how you mentioned in the other comment:
|
||
bool Error = false; | ||
// Dispatch onto parser method. | ||
// We guard against the unreachable here as we just ensured that CurToken | ||
// will be one of the kinds in the while condition | ||
switch (CurToken.Kind) { | ||
case TokenKind::kw_DescriptorTable: | ||
Error = ParseDescriptorTable(); | ||
break; | ||
default: | ||
llvm_unreachable("Switch for consumed token was not provided"); | ||
} | ||
|
||
if (Error) | ||
return true; | ||
|
||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (!TryConsumeExpectedToken(TokenKind::pu_comma)) | ||
break; | ||
} | ||
|
||
return ConsumeExpectedToken(TokenKind::end_of_stream, diag::err_expected); | ||
} | ||
|
||
bool RootSignatureParser::ParseDescriptorTable() { | ||
assert(CurToken.Kind == TokenKind::kw_DescriptorTable && | ||
"Expects to only be invoked starting at given keyword"); | ||
|
||
DescriptorTable Table; | ||
|
||
if (ConsumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after, | ||
CurToken.Kind)) | ||
return true; | ||
|
||
// Iterate as many Clauses as possible | ||
while (TryConsumeExpectedToken({TokenKind::kw_CBV, TokenKind::kw_SRV, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I imagine the above loop would be something like this, once you add more Root Elements? |
||
TokenKind::kw_UAV, TokenKind::kw_Sampler})) { | ||
if (ParseDescriptorTableClause()) | ||
return true; | ||
|
||
Table.NumClauses++; | ||
|
||
if (!TryConsumeExpectedToken(TokenKind::pu_comma)) | ||
break; | ||
} | ||
|
||
if (ConsumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_after, | ||
CurToken.Kind)) | ||
return true; | ||
|
||
Elements.push_back(Table); | ||
return false; | ||
} | ||
|
||
bool RootSignatureParser::ParseDescriptorTableClause() { | ||
assert((CurToken.Kind == TokenKind::kw_CBV || | ||
CurToken.Kind == TokenKind::kw_SRV || | ||
CurToken.Kind == TokenKind::kw_UAV || | ||
CurToken.Kind == TokenKind::kw_Sampler) && | ||
"Expects to only be invoked starting at given keyword"); | ||
|
||
DescriptorTableClause Clause; | ||
switch (CurToken.Kind) { | ||
default: | ||
break; // Unreachable given Try + assert pattern | ||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
case TokenKind::kw_CBV: | ||
Clause.Type = ClauseType::CBuffer; | ||
break; | ||
case TokenKind::kw_SRV: | ||
Clause.Type = ClauseType::SRV; | ||
break; | ||
case TokenKind::kw_UAV: | ||
Clause.Type = ClauseType::UAV; | ||
break; | ||
case TokenKind::kw_Sampler: | ||
Clause.Type = ClauseType::Sampler; | ||
break; | ||
} | ||
|
||
if (ConsumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after, | ||
CurToken.Kind)) | ||
return true; | ||
|
||
if (ConsumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_after, | ||
CurToken.Kind)) | ||
return true; | ||
|
||
Elements.push_back(Clause); | ||
return false; | ||
} | ||
|
||
// Returns true when given token is one of the expected kinds | ||
static bool IsExpectedToken(TokenKind Kind, ArrayRef<TokenKind> AnyExpected) { | ||
for (auto Expected : AnyExpected) | ||
if (Kind == Expected) | ||
return true; | ||
return false; | ||
} | ||
|
||
bool RootSignatureParser::PeekExpectedToken(TokenKind Expected) { | ||
return PeekExpectedToken(ArrayRef{Expected}); | ||
} | ||
|
||
bool RootSignatureParser::PeekExpectedToken(ArrayRef<TokenKind> AnyExpected) { | ||
RootSignatureToken Result = Lexer.PeekNextToken(); | ||
return IsExpectedToken(Result.Kind, AnyExpected); | ||
inbelic marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
|
||
bool RootSignatureParser::ConsumeExpectedToken(TokenKind Expected, | ||
unsigned DiagID, | ||
TokenKind Context) { | ||
return ConsumeExpectedToken(ArrayRef{Expected}, DiagID, Context); | ||
} | ||
|
||
bool RootSignatureParser::ConsumeExpectedToken(ArrayRef<TokenKind> AnyExpected, | ||
unsigned DiagID, | ||
TokenKind Context) { | ||
if (TryConsumeExpectedToken(AnyExpected)) | ||
return false; | ||
|
||
// Report unexpected token kind error | ||
DiagnosticBuilder DB = Diags().Report(CurToken.TokLoc, DiagID); | ||
switch (DiagID) { | ||
case diag::err_expected: | ||
DB << FormatTokenKinds(AnyExpected); | ||
break; | ||
case diag::err_expected_either: | ||
case diag::err_expected_after: | ||
DB << FormatTokenKinds(AnyExpected) << FormatTokenKinds({Context}); | ||
break; | ||
default: | ||
break; | ||
} | ||
return true; | ||
} | ||
|
||
bool RootSignatureParser::TryConsumeExpectedToken(TokenKind Expected) { | ||
return TryConsumeExpectedToken(ArrayRef{Expected}); | ||
} | ||
|
||
bool RootSignatureParser::TryConsumeExpectedToken( | ||
ArrayRef<TokenKind> AnyExpected) { | ||
// If not the expected token just return | ||
if (!PeekExpectedToken(AnyExpected)) | ||
return false; | ||
ConsumeNextToken(); | ||
return true; | ||
} | ||
|
||
} // namespace hlsl | ||
} // namespace clang |
Uh oh!
There was an error while loading. Please reload this page.