Skip to content

Commit e4b9486

Browse files
authored
[HLSL][RootSignature] Implement parsing of a DescriptorTable with empty clauses (#133302)
- defines the Parser class and an initial set of helper methods to support consuming tokens. functionality is demonstrated through a simple empty descriptor table test case - defines an initial in-memory representation of a DescriptorTable - implements a test harness that will be used to validate the correct diagnostics are generated. it will construct a dummy pre-processor with diagnostics consumer to do so Implements the first part of #126569
1 parent 8b06da1 commit e4b9486

11 files changed

+620
-13
lines changed

clang/include/clang/Basic/DiagnosticParseKinds.td

+4
Original file line numberDiff line numberDiff line change
@@ -1830,4 +1830,8 @@ def err_hlsl_virtual_function
18301830
def err_hlsl_virtual_inheritance
18311831
: Error<"virtual inheritance is unsupported in HLSL">;
18321832

1833+
// HLSL Root Siganture diagnostic messages
1834+
def err_hlsl_unexpected_end_of_params
1835+
: Error<"expected %0 to denote end of parameters, or, another valid parameter of %1">;
1836+
18331837
} // end of Parser diagnostics

clang/include/clang/Lex/HLSLRootSignatureTokenKinds.def

+12-11
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,16 @@
1414
//===----------------------------------------------------------------------===//
1515

1616
#ifndef TOK
17-
#define TOK(X)
17+
#define TOK(X, SPELLING)
1818
#endif
1919
#ifndef PUNCTUATOR
20-
#define PUNCTUATOR(X,Y) TOK(pu_ ## X)
20+
#define PUNCTUATOR(X,Y) TOK(pu_ ## X, Y)
2121
#endif
2222
#ifndef KEYWORD
23-
#define KEYWORD(X) TOK(kw_ ## X)
23+
#define KEYWORD(X) TOK(kw_ ## X, #X)
2424
#endif
2525
#ifndef ENUM
26-
#define ENUM(NAME, LIT) TOK(en_ ## NAME)
26+
#define ENUM(NAME, LIT) TOK(en_ ## NAME, LIT)
2727
#endif
2828

2929
// Defines the various types of enum
@@ -49,15 +49,15 @@
4949
#endif
5050

5151
// General Tokens:
52-
TOK(invalid)
53-
TOK(end_of_stream)
54-
TOK(int_literal)
52+
TOK(invalid, "invalid identifier")
53+
TOK(end_of_stream, "end of stream")
54+
TOK(int_literal, "integer literal")
5555

5656
// Register Tokens:
57-
TOK(bReg)
58-
TOK(tReg)
59-
TOK(uReg)
60-
TOK(sReg)
57+
TOK(bReg, "b register")
58+
TOK(tReg, "t register")
59+
TOK(uReg, "u register")
60+
TOK(sReg, "s register")
6161

6262
// Punctuators:
6363
PUNCTUATOR(l_paren, '(')
@@ -69,6 +69,7 @@ PUNCTUATOR(plus, '+')
6969
PUNCTUATOR(minus, '-')
7070

7171
// RootElement Keywords:
72+
KEYWORD(RootSignature) // used only for diagnostic messaging
7273
KEYWORD(DescriptorTable)
7374

7475
// DescriptorTable Keywords:

clang/include/clang/Lex/LexHLSLRootSignature.h

+14-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#ifndef LLVM_CLANG_LEX_LEXHLSLROOTSIGNATURE_H
1414
#define LLVM_CLANG_LEX_LEXHLSLROOTSIGNATURE_H
1515

16+
#include "clang/Basic/Diagnostic.h"
1617
#include "clang/Basic/SourceLocation.h"
1718

1819
#include "llvm/ADT/SmallVector.h"
@@ -24,7 +25,7 @@ namespace hlsl {
2425

2526
struct RootSignatureToken {
2627
enum Kind {
27-
#define TOK(X) X,
28+
#define TOK(X, SPELLING) X,
2829
#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
2930
};
3031

@@ -43,6 +44,18 @@ struct RootSignatureToken {
4344
};
4445
using TokenKind = enum RootSignatureToken::Kind;
4546

47+
inline const DiagnosticBuilder &operator<<(const DiagnosticBuilder &DB,
48+
const TokenKind Kind) {
49+
switch (Kind) {
50+
#define TOK(X, SPELLING) \
51+
case TokenKind::X: \
52+
DB << SPELLING; \
53+
break;
54+
#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
55+
}
56+
return DB;
57+
}
58+
4659
class RootSignatureLexer {
4760
public:
4861
RootSignatureLexer(StringRef Signature, clang::SourceLocation SourceLoc)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
//===--- ParseHLSLRootSignature.h -------------------------------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file defines the RootSignatureParser interface.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H
14+
#define LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H
15+
16+
#include "clang/Basic/DiagnosticParse.h"
17+
#include "clang/Lex/LexHLSLRootSignature.h"
18+
#include "clang/Lex/Preprocessor.h"
19+
20+
#include "llvm/ADT/SmallVector.h"
21+
#include "llvm/ADT/StringRef.h"
22+
23+
#include "llvm/Frontend/HLSL/HLSLRootSignature.h"
24+
25+
namespace clang {
26+
namespace hlsl {
27+
28+
class RootSignatureParser {
29+
public:
30+
RootSignatureParser(SmallVector<llvm::hlsl::rootsig::RootElement> &Elements,
31+
RootSignatureLexer &Lexer, clang::Preprocessor &PP);
32+
33+
/// Consumes tokens from the Lexer and constructs the in-memory
34+
/// representations of the RootElements. Tokens are consumed until an
35+
/// error is encountered or the end of the buffer.
36+
///
37+
/// Returns true if a parsing error is encountered.
38+
bool parse();
39+
40+
private:
41+
DiagnosticsEngine &getDiags() { return PP.getDiagnostics(); }
42+
43+
// All private Parse.* methods follow a similar pattern:
44+
// - Each method will start with an assert to denote what the CurToken is
45+
// expected to be and will parse from that token forward
46+
//
47+
// - Therefore, it is the callers responsibility to ensure that you are
48+
// at the correct CurToken. This should be done with the pattern of:
49+
//
50+
// if (TryConsumeExpectedToken(TokenKind))
51+
// if (Parse.*())
52+
// return true;
53+
//
54+
// or,
55+
//
56+
// if (ConsumeExpectedToken(TokenKind, ...))
57+
// return true;
58+
// if (Parse.*())
59+
// return true;
60+
//
61+
// - All methods return true if a parsing error is encountered. It is the
62+
// callers responsibility to propogate this error up, or deal with it
63+
// otherwise
64+
//
65+
// - An error will be raised if the proceeding tokens are not what is
66+
// expected, or, there is a lexing error
67+
68+
/// Root Element parse methods:
69+
bool parseDescriptorTable();
70+
bool parseDescriptorTableClause();
71+
72+
/// Invoke the Lexer to consume a token and update CurToken with the result
73+
void consumeNextToken() { CurToken = Lexer.ConsumeToken(); }
74+
75+
/// Return true if the next token one of the expected kinds
76+
bool peekExpectedToken(TokenKind Expected);
77+
bool peekExpectedToken(ArrayRef<TokenKind> AnyExpected);
78+
79+
/// Consumes the next token and report an error if it is not of the expected
80+
/// kind.
81+
///
82+
/// Returns true if there was an error reported.
83+
bool consumeExpectedToken(TokenKind Expected,
84+
unsigned DiagID = diag::err_expected,
85+
TokenKind Context = TokenKind::invalid);
86+
87+
/// Peek if the next token is of the expected kind and if it is then consume
88+
/// it.
89+
///
90+
/// Returns true if it successfully matches the expected kind and the token
91+
/// was consumed.
92+
bool tryConsumeExpectedToken(TokenKind Expected);
93+
bool tryConsumeExpectedToken(ArrayRef<TokenKind> Expected);
94+
95+
private:
96+
SmallVector<llvm::hlsl::rootsig::RootElement> &Elements;
97+
RootSignatureLexer &Lexer;
98+
99+
clang::Preprocessor &PP;
100+
101+
RootSignatureToken CurToken;
102+
};
103+
104+
} // namespace hlsl
105+
} // namespace clang
106+
107+
#endif // LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H

clang/lib/Parse/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ add_clang_library(clangParse
1414
ParseExpr.cpp
1515
ParseExprCXX.cpp
1616
ParseHLSL.cpp
17+
ParseHLSLRootSignature.cpp
1718
ParseInit.cpp
1819
ParseObjc.cpp
1920
ParseOpenMP.cpp
+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//=== ParseHLSLRootSignature.cpp - Parse Root Signature -------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "clang/Parse/ParseHLSLRootSignature.h"
10+
11+
#include "llvm/Support/raw_ostream.h"
12+
13+
using namespace llvm::hlsl::rootsig;
14+
15+
namespace clang {
16+
namespace hlsl {
17+
18+
RootSignatureParser::RootSignatureParser(SmallVector<RootElement> &Elements,
19+
RootSignatureLexer &Lexer,
20+
Preprocessor &PP)
21+
: Elements(Elements), Lexer(Lexer), PP(PP), CurToken(SourceLocation()) {}
22+
23+
bool RootSignatureParser::parse() {
24+
// Iterate as many RootElements as possible
25+
while (tryConsumeExpectedToken(TokenKind::kw_DescriptorTable)) {
26+
// Dispatch onto parser method.
27+
// We guard against the unreachable here as we just ensured that CurToken
28+
// will be one of the kinds in the while condition
29+
switch (CurToken.Kind) {
30+
case TokenKind::kw_DescriptorTable:
31+
if (parseDescriptorTable())
32+
return true;
33+
break;
34+
default:
35+
llvm_unreachable("Switch for consumed token was not provided");
36+
}
37+
38+
if (!tryConsumeExpectedToken(TokenKind::pu_comma))
39+
break;
40+
}
41+
42+
if (!tryConsumeExpectedToken(TokenKind::end_of_stream)) {
43+
getDiags().Report(CurToken.TokLoc, diag::err_hlsl_unexpected_end_of_params)
44+
<< /*expected=*/TokenKind::end_of_stream
45+
<< /*param of=*/TokenKind::kw_RootSignature;
46+
return true;
47+
}
48+
return false;
49+
}
50+
51+
bool RootSignatureParser::parseDescriptorTable() {
52+
assert(CurToken.Kind == TokenKind::kw_DescriptorTable &&
53+
"Expects to only be invoked starting at given keyword");
54+
55+
DescriptorTable Table;
56+
57+
if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
58+
CurToken.Kind))
59+
return true;
60+
61+
// Iterate as many Clauses as possible
62+
while (tryConsumeExpectedToken({TokenKind::kw_CBV, TokenKind::kw_SRV,
63+
TokenKind::kw_UAV, TokenKind::kw_Sampler})) {
64+
if (parseDescriptorTableClause())
65+
return true;
66+
67+
Table.NumClauses++;
68+
69+
if (!tryConsumeExpectedToken(TokenKind::pu_comma))
70+
break;
71+
}
72+
73+
if (!tryConsumeExpectedToken(TokenKind::pu_r_paren)) {
74+
getDiags().Report(CurToken.TokLoc, diag::err_hlsl_unexpected_end_of_params)
75+
<< /*expected=*/TokenKind::pu_r_paren
76+
<< /*param of=*/TokenKind::kw_DescriptorTable;
77+
return true;
78+
}
79+
80+
Elements.push_back(Table);
81+
return false;
82+
}
83+
84+
bool RootSignatureParser::parseDescriptorTableClause() {
85+
assert((CurToken.Kind == TokenKind::kw_CBV ||
86+
CurToken.Kind == TokenKind::kw_SRV ||
87+
CurToken.Kind == TokenKind::kw_UAV ||
88+
CurToken.Kind == TokenKind::kw_Sampler) &&
89+
"Expects to only be invoked starting at given keyword");
90+
91+
DescriptorTableClause Clause;
92+
switch (CurToken.Kind) {
93+
default:
94+
llvm_unreachable("Switch for consumed token was not provided");
95+
case TokenKind::kw_CBV:
96+
Clause.Type = ClauseType::CBuffer;
97+
break;
98+
case TokenKind::kw_SRV:
99+
Clause.Type = ClauseType::SRV;
100+
break;
101+
case TokenKind::kw_UAV:
102+
Clause.Type = ClauseType::UAV;
103+
break;
104+
case TokenKind::kw_Sampler:
105+
Clause.Type = ClauseType::Sampler;
106+
break;
107+
}
108+
109+
if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
110+
CurToken.Kind))
111+
return true;
112+
113+
if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_after,
114+
CurToken.Kind))
115+
return true;
116+
117+
Elements.push_back(Clause);
118+
return false;
119+
}
120+
121+
bool RootSignatureParser::peekExpectedToken(TokenKind Expected) {
122+
return peekExpectedToken(ArrayRef{Expected});
123+
}
124+
125+
bool RootSignatureParser::peekExpectedToken(ArrayRef<TokenKind> AnyExpected) {
126+
RootSignatureToken Result = Lexer.PeekNextToken();
127+
return llvm::is_contained(AnyExpected, Result.Kind);
128+
}
129+
130+
bool RootSignatureParser::consumeExpectedToken(TokenKind Expected,
131+
unsigned DiagID,
132+
TokenKind Context) {
133+
if (tryConsumeExpectedToken(Expected))
134+
return false;
135+
136+
// Report unexpected token kind error
137+
DiagnosticBuilder DB = getDiags().Report(CurToken.TokLoc, DiagID);
138+
switch (DiagID) {
139+
case diag::err_expected:
140+
DB << Expected;
141+
break;
142+
case diag::err_expected_either:
143+
case diag::err_expected_after:
144+
DB << Expected << Context;
145+
break;
146+
default:
147+
break;
148+
}
149+
return true;
150+
}
151+
152+
bool RootSignatureParser::tryConsumeExpectedToken(TokenKind Expected) {
153+
return tryConsumeExpectedToken(ArrayRef{Expected});
154+
}
155+
156+
bool RootSignatureParser::tryConsumeExpectedToken(
157+
ArrayRef<TokenKind> AnyExpected) {
158+
// If not the expected token just return
159+
if (!peekExpectedToken(AnyExpected))
160+
return false;
161+
consumeNextToken();
162+
return true;
163+
}
164+
165+
} // namespace hlsl
166+
} // namespace clang

clang/unittests/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ endfunction()
2525

2626
add_subdirectory(Basic)
2727
add_subdirectory(Lex)
28+
add_subdirectory(Parse)
2829
add_subdirectory(Driver)
2930
if(CLANG_ENABLE_STATIC_ANALYZER)
3031
add_subdirectory(Analysis)

0 commit comments

Comments
 (0)