Skip to content

Commit 98deff6

Browse files
committed
[HLSL][RootSignature] Initial Lexer Definition with puncuators
- Defines the RootSignatureLexer class - Defines the test harness required for testing - Implements the punctuator tokens and tests functionality
1 parent f739aa4 commit 98deff6

File tree

8 files changed

+365
-0
lines changed

8 files changed

+365
-0
lines changed

clang/include/clang/Basic/DiagnosticLexKinds.td

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,4 +1017,10 @@ Error<"'#pragma unsafe_buffer_usage' was not ended">;
10171017

10181018
def err_pp_pragma_unsafe_buffer_usage_syntax :
10191019
Error<"expected 'begin' or 'end'">;
1020+
1021+
// HLSL Root Signature Lexing Errors
1022+
let CategoryName = "Root Signature Lexical Issue" in {
1023+
def err_hlsl_invalid_token: Error<"unable to lex a valid Root Signature token">;
1024+
}
1025+
10201026
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
//===--- HLSLRootSignature.def - Tokens and Enum Database -------*- C++ -*-===//
2+
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file defines the TokenKinds used in the Root Signature DSL. This
11+
// includes keywords, enums and a small subset of punctuators. Users of this
12+
// file must optionally #define the TOK, KEYWORD, ENUM or specific ENUM macros
13+
// to make use of this file.
14+
//
15+
//===----------------------------------------------------------------------===//
16+
17+
#ifndef TOK
18+
#define TOK(X)
19+
#endif
20+
#ifndef PUNCTUATOR
21+
#define PUNCTUATOR(X,Y) TOK(pu_ ## X)
22+
#endif
23+
24+
// General Tokens:
25+
TOK(invalid)
26+
27+
// Punctuators:
28+
PUNCTUATOR(l_paren, '(')
29+
PUNCTUATOR(r_paren, ')')
30+
PUNCTUATOR(comma, ',')
31+
PUNCTUATOR(or, '|')
32+
PUNCTUATOR(equal, '=')
33+
34+
#undef PUNCTUATOR
35+
#undef TOK
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
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 ParseHLSLRootSignature interface.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H
14+
#define LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H
15+
16+
#include "clang/Basic/DiagnosticLex.h"
17+
#include "clang/Lex/Preprocessor.h"
18+
19+
#include "llvm/ADT/SmallVector.h"
20+
#include "llvm/ADT/StringRef.h"
21+
22+
namespace clang {
23+
namespace hlsl {
24+
25+
struct RootSignatureToken {
26+
enum Kind {
27+
#define TOK(X) X,
28+
#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
29+
};
30+
31+
Kind Kind = Kind::invalid;
32+
33+
// Retain the SouceLocation of the token for diagnostics
34+
clang::SourceLocation TokLoc;
35+
36+
// Constructors
37+
RootSignatureToken(clang::SourceLocation TokLoc) : TokLoc(TokLoc) {}
38+
};
39+
using TokenKind = enum RootSignatureToken::Kind;
40+
41+
class RootSignatureLexer {
42+
public:
43+
RootSignatureLexer(StringRef Signature, clang::SourceLocation SourceLoc,
44+
clang::Preprocessor &PP)
45+
: Buffer(Signature), SourceLoc(SourceLoc), PP(PP) {}
46+
47+
// Consumes the internal buffer as a list of tokens and will emplace them
48+
// onto the given tokens.
49+
//
50+
// It will consume until it successfully reaches the end of the buffer,
51+
// or, until the first error is encountered. The return value denotes if
52+
// there was a failure.
53+
bool Lex(SmallVector<RootSignatureToken> &Tokens);
54+
55+
private:
56+
// Internal buffer to iterate over
57+
StringRef Buffer;
58+
59+
// Passed down parameters from Sema
60+
clang::SourceLocation SourceLoc;
61+
clang::Preprocessor &PP;
62+
63+
// Consumes the internal buffer for a single token.
64+
//
65+
// The return value denotes if there was a failure.
66+
bool LexToken(RootSignatureToken &Token);
67+
68+
// Advance the buffer by the specified number of characters. Updates the
69+
// SourceLocation appropriately.
70+
void AdvanceBuffer(unsigned NumCharacters = 1) {
71+
Buffer = Buffer.drop_front(NumCharacters);
72+
SourceLoc = SourceLoc.getLocWithOffset(NumCharacters);
73+
}
74+
};
75+
76+
} // namespace hlsl
77+
} // namespace clang
78+
79+
#endif // LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H

clang/lib/Parse/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
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
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
#include "clang/Parse/ParseHLSLRootSignature.h"
2+
3+
namespace clang {
4+
namespace hlsl {
5+
6+
// Lexer Definitions
7+
8+
bool RootSignatureLexer::Lex(SmallVector<RootSignatureToken> &Tokens) {
9+
// Discard any leading whitespace
10+
AdvanceBuffer(Buffer.take_while(isspace).size());
11+
12+
while (!Buffer.empty()) {
13+
// Record where this token is in the text for usage in parser diagnostics
14+
RootSignatureToken Result(SourceLoc);
15+
if (LexToken(Result))
16+
return true;
17+
18+
// Successfully Lexed the token so we can store it
19+
Tokens.push_back(Result);
20+
21+
// Discard any trailing whitespace
22+
AdvanceBuffer(Buffer.take_while(isspace).size());
23+
}
24+
25+
return false;
26+
}
27+
28+
bool RootSignatureLexer::LexToken(RootSignatureToken &Result) {
29+
char C = Buffer.front();
30+
31+
// Punctuators
32+
switch (C) {
33+
#define PUNCTUATOR(X, Y) \
34+
case Y: { \
35+
Result.Kind = TokenKind::pu_##X; \
36+
AdvanceBuffer(); \
37+
return false; \
38+
}
39+
#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
40+
default:
41+
break;
42+
}
43+
44+
// Unable to match on any token type
45+
PP.getDiagnostics().Report(Result.TokLoc, diag::err_hlsl_invalid_token);
46+
return true;
47+
}
48+
49+
} // namespace hlsl
50+
} // namespace clang

clang/unittests/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
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)

clang/unittests/Parse/CMakeLists.txt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
set(LLVM_LINK_COMPONENTS
2+
Support
3+
)
4+
5+
add_clang_unittest(ParseTests
6+
ParseHLSLRootSignatureTest.cpp
7+
)
8+
9+
clang_target_link_libraries(ParseTests
10+
PRIVATE
11+
clangAST
12+
clangASTMatchers
13+
clangBasic
14+
clangFrontend
15+
clangParse
16+
clangSema
17+
clangSerialization
18+
clangTooling
19+
)
20+
21+
target_link_libraries(ParseTests
22+
PRIVATE
23+
LLVMTestingAnnotations
24+
LLVMTestingSupport
25+
clangTesting
26+
)
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
//=== ParseHLSLRootSignatureTest.cpp - Parse Root Signature tests ---------===//
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/Basic/Diagnostic.h"
10+
#include "clang/Basic/DiagnosticOptions.h"
11+
#include "clang/Basic/FileManager.h"
12+
#include "clang/Basic/LangOptions.h"
13+
#include "clang/Basic/SourceLocation.h"
14+
#include "clang/Basic/SourceManager.h"
15+
#include "clang/Basic/TargetInfo.h"
16+
#include "clang/Lex/HeaderSearch.h"
17+
#include "clang/Lex/HeaderSearchOptions.h"
18+
#include "clang/Lex/Lexer.h"
19+
#include "clang/Lex/ModuleLoader.h"
20+
#include "clang/Lex/Preprocessor.h"
21+
#include "clang/Lex/PreprocessorOptions.h"
22+
23+
#include "clang/Parse/ParseHLSLRootSignature.h"
24+
#include "gtest/gtest.h"
25+
26+
using namespace clang;
27+
28+
namespace {
29+
30+
// Diagnostic helper for helper tests
31+
class ExpectedDiagConsumer : public DiagnosticConsumer {
32+
virtual void anchor() {}
33+
34+
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
35+
const Diagnostic &Info) override {
36+
if (!FirstDiag || !ExpectedDiagID.has_value()) {
37+
Satisfied = false;
38+
return;
39+
}
40+
FirstDiag = false;
41+
42+
Satisfied = ExpectedDiagID.value() == Info.getID();
43+
}
44+
45+
bool FirstDiag = true;
46+
bool Satisfied = false;
47+
std::optional<unsigned> ExpectedDiagID;
48+
49+
public:
50+
void SetNoDiag() {
51+
Satisfied = true;
52+
ExpectedDiagID = std::nullopt;
53+
}
54+
55+
void SetExpected(unsigned DiagID) {
56+
Satisfied = false;
57+
ExpectedDiagID = DiagID;
58+
}
59+
60+
bool IsSatisfied() { return Satisfied; }
61+
};
62+
63+
// The test fixture.
64+
class ParseHLSLRootSignatureTest : public ::testing::Test {
65+
protected:
66+
ParseHLSLRootSignatureTest()
67+
: FileMgr(FileMgrOpts), DiagID(new DiagnosticIDs()),
68+
Consumer(new ExpectedDiagConsumer()),
69+
Diags(DiagID, new DiagnosticOptions, Consumer),
70+
SourceMgr(Diags, FileMgr), TargetOpts(new TargetOptions) {
71+
TargetOpts->Triple = "x86_64-apple-darwin11.1.0";
72+
Target = TargetInfo::CreateTargetInfo(Diags, TargetOpts);
73+
}
74+
75+
std::unique_ptr<Preprocessor> CreatePP(StringRef Source,
76+
TrivialModuleLoader &ModLoader) {
77+
std::unique_ptr<llvm::MemoryBuffer> Buf =
78+
llvm::MemoryBuffer::getMemBuffer(Source);
79+
SourceMgr.setMainFileID(SourceMgr.createFileID(std::move(Buf)));
80+
81+
HeaderSearch HeaderInfo(std::make_shared<HeaderSearchOptions>(), SourceMgr,
82+
Diags, LangOpts, Target.get());
83+
std::unique_ptr<Preprocessor> PP = std::make_unique<Preprocessor>(
84+
std::make_shared<PreprocessorOptions>(), Diags, LangOpts, SourceMgr,
85+
HeaderInfo, ModLoader,
86+
/*IILookup =*/nullptr,
87+
/*OwnsHeaderSearch =*/false);
88+
PP->Initialize(*Target);
89+
PP->EnterMainSourceFile();
90+
return PP;
91+
}
92+
93+
void CheckTokens(SmallVector<hlsl::RootSignatureToken> &Computed,
94+
SmallVector<hlsl::TokenKind> &Expected) {
95+
ASSERT_EQ(Computed.size(), Expected.size());
96+
for (unsigned I = 0, E = Expected.size(); I != E; ++I) {
97+
ASSERT_EQ(Computed[I].Kind, Expected[I]);
98+
}
99+
}
100+
101+
FileSystemOptions FileMgrOpts;
102+
FileManager FileMgr;
103+
IntrusiveRefCntPtr<DiagnosticIDs> DiagID;
104+
ExpectedDiagConsumer *Consumer;
105+
DiagnosticsEngine Diags;
106+
SourceManager SourceMgr;
107+
LangOptions LangOpts;
108+
std::shared_ptr<TargetOptions> TargetOpts;
109+
IntrusiveRefCntPtr<TargetInfo> Target;
110+
};
111+
112+
// Valid Lexing Tests
113+
114+
TEST_F(ParseHLSLRootSignatureTest, ValidLexAllTokensTest) {
115+
// This test will check that we can lex all defined tokens as defined in
116+
// HLSLRootSignatureTokenKinds.def, plus some additional integer variations
117+
const llvm::StringLiteral Source = R"cc(
118+
(),|=
119+
)cc";
120+
121+
TrivialModuleLoader ModLoader;
122+
auto PP = CreatePP(Source, ModLoader);
123+
auto TokLoc = SourceLocation();
124+
125+
// Test no diagnostics produced
126+
Consumer->SetNoDiag();
127+
128+
hlsl::RootSignatureLexer Lexer(Source, TokLoc, *PP);
129+
130+
SmallVector<hlsl::RootSignatureToken> Tokens = {
131+
hlsl::RootSignatureToken(
132+
SourceLocation()) // invalid token for completeness
133+
};
134+
ASSERT_FALSE(Lexer.Lex(Tokens));
135+
ASSERT_TRUE(Consumer->IsSatisfied());
136+
137+
SmallVector<hlsl::TokenKind> Expected = {
138+
#define TOK(NAME) hlsl::TokenKind::NAME,
139+
#include "clang/Parse/HLSLRootSignatureTokenKinds.def"
140+
};
141+
142+
CheckTokens(Tokens, Expected);
143+
}
144+
145+
// Invalid Lexing Tests
146+
147+
TEST_F(ParseHLSLRootSignatureTest, InvalidLexIdentifierTest) {
148+
// This test will check that the lexing fails due to no valid token
149+
const llvm::StringLiteral Source = R"cc(
150+
notAToken
151+
)cc";
152+
153+
TrivialModuleLoader ModLoader;
154+
auto PP = CreatePP(Source, ModLoader);
155+
auto TokLoc = SourceLocation();
156+
157+
// Test correct diagnostic produced
158+
Consumer->SetExpected(diag::err_hlsl_invalid_token);
159+
160+
hlsl::RootSignatureLexer Lexer(Source, TokLoc, *PP);
161+
162+
SmallVector<hlsl::RootSignatureToken> Tokens;
163+
ASSERT_TRUE(Lexer.Lex(Tokens));
164+
ASSERT_TRUE(Consumer->IsSatisfied());
165+
}
166+
167+
} // anonymous namespace

0 commit comments

Comments
 (0)