Skip to content

Commit b41b86a

Browse files
inbelicllvm-beanz
andauthored
[HLSL][RootSignature] Implement Lexing of DescriptorTables (#122981)
For the sake of scope, we will let the lexing of floating literals be deferred until needed for Static Samplers. Other than that this pr should allow us to simply define new enumerations/keywords in `RootSignatureTokenKinds.def` for when they are used in the parser. We could have defined all of these keywords here, but for the sake of correctness in review we will let them be split up. - Define `RootSignatureLexer` and provide a public `LexToken` method for external use - Define the file `RootSignatureTokenKinds` to define required tokens and allow for future custom keywords/enums - Implement the internal methods required to parse the different types of tokens (integers, flag enums, puncuators...) - Add test harness for unit testing and the respective unit tests for lexing the tokens Resolves #126563 --------- Co-authored-by: Chris B <beanz@abolishcrlf.org>
1 parent 708dc65 commit b41b86a

File tree

6 files changed

+486
-0
lines changed

6 files changed

+486
-0
lines changed
+123
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
//===--- HLSLRootSignature.def - Tokens and Enum Database -------*- 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 TokenKinds used in the Root Signature DSL. This
10+
// includes keywords, enums and a small subset of punctuators. Users of this
11+
// file must optionally #define the TOK, KEYWORD, ENUM or specific ENUM macros
12+
// to make use of this file.
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef TOK
17+
#define TOK(X)
18+
#endif
19+
#ifndef PUNCTUATOR
20+
#define PUNCTUATOR(X,Y) TOK(pu_ ## X)
21+
#endif
22+
#ifndef KEYWORD
23+
#define KEYWORD(X) TOK(kw_ ## X)
24+
#endif
25+
#ifndef ENUM
26+
#define ENUM(NAME, LIT) TOK(en_ ## NAME)
27+
#endif
28+
29+
// Defines the various types of enum
30+
#ifndef DESCRIPTOR_RANGE_OFFSET_ENUM
31+
#define DESCRIPTOR_RANGE_OFFSET_ENUM(NAME, LIT) ENUM(NAME, LIT)
32+
#endif
33+
#ifndef ROOT_DESCRIPTOR_FLAG_ENUM
34+
#define ROOT_DESCRIPTOR_FLAG_ENUM(NAME, LIT) ENUM(NAME, LIT)
35+
#endif
36+
// Note: ON denotes that the flag is unique from the above Root Descriptor
37+
// Flags. This is required to avoid token kind enum conflicts.
38+
#ifndef DESCRIPTOR_RANGE_FLAG_ENUM_OFF
39+
#define DESCRIPTOR_RANGE_FLAG_ENUM_OFF(NAME, LIT)
40+
#endif
41+
#ifndef DESCRIPTOR_RANGE_FLAG_ENUM_ON
42+
#define DESCRIPTOR_RANGE_FLAG_ENUM_ON(NAME, LIT) ENUM(NAME, LIT)
43+
#endif
44+
#ifndef DESCRIPTOR_RANGE_FLAG_ENUM
45+
#define DESCRIPTOR_RANGE_FLAG_ENUM(NAME, LIT, ON) DESCRIPTOR_RANGE_FLAG_ENUM_##ON(NAME, LIT)
46+
#endif
47+
#ifndef SHADER_VISIBILITY_ENUM
48+
#define SHADER_VISIBILITY_ENUM(NAME, LIT) ENUM(NAME, LIT)
49+
#endif
50+
51+
// General Tokens:
52+
TOK(invalid)
53+
TOK(end_of_stream)
54+
TOK(int_literal)
55+
56+
// Register Tokens:
57+
TOK(bReg)
58+
TOK(tReg)
59+
TOK(uReg)
60+
TOK(sReg)
61+
62+
// Punctuators:
63+
PUNCTUATOR(l_paren, '(')
64+
PUNCTUATOR(r_paren, ')')
65+
PUNCTUATOR(comma, ',')
66+
PUNCTUATOR(or, '|')
67+
PUNCTUATOR(equal, '=')
68+
PUNCTUATOR(plus, '+')
69+
PUNCTUATOR(minus, '-')
70+
71+
// RootElement Keywords:
72+
KEYWORD(DescriptorTable)
73+
74+
// DescriptorTable Keywords:
75+
KEYWORD(CBV)
76+
KEYWORD(SRV)
77+
KEYWORD(UAV)
78+
KEYWORD(Sampler)
79+
80+
// General Parameter Keywords:
81+
KEYWORD(space)
82+
KEYWORD(visibility)
83+
KEYWORD(flags)
84+
85+
// View Parameter Keywords:
86+
KEYWORD(numDescriptors)
87+
KEYWORD(offset)
88+
89+
// Descriptor Range Offset Enum:
90+
DESCRIPTOR_RANGE_OFFSET_ENUM(DescriptorRangeOffsetAppend, "DESCRIPTOR_RANGE_OFFSET_APPEND")
91+
92+
// Root Descriptor Flag Enums:
93+
ROOT_DESCRIPTOR_FLAG_ENUM(DataVolatile, "DATA_VOLATILE")
94+
ROOT_DESCRIPTOR_FLAG_ENUM(DataStaticWhileSetAtExecute, "DATA_STATIC_WHILE_SET_AT_EXECUTE")
95+
ROOT_DESCRIPTOR_FLAG_ENUM(DataStatic, "DATA_STATIC")
96+
97+
// Descriptor Range Flag Enums:
98+
DESCRIPTOR_RANGE_FLAG_ENUM(DescriptorsVolatile, "DESCRIPTORS_VOLATILE", ON)
99+
DESCRIPTOR_RANGE_FLAG_ENUM(DataVolatile, "DATA_VOLATILE", OFF)
100+
DESCRIPTOR_RANGE_FLAG_ENUM(DataStaticWhileSetAtExecute, "DATA_STATIC_WHILE_SET_AT_EXECUTE", OFF)
101+
DESCRIPTOR_RANGE_FLAG_ENUM(DataStatic, "DATA_STATIC", OFF)
102+
DESCRIPTOR_RANGE_FLAG_ENUM(DescriptorsStaticKeepingBufferBoundsChecks, "DESCRIPTORS_STATIC_KEEPING_BUFFER_BOUNDS_CHECKS", ON)
103+
104+
// Shader Visibiliy Enums:
105+
SHADER_VISIBILITY_ENUM(All, "SHADER_VISIBILITY_ALL")
106+
SHADER_VISIBILITY_ENUM(Vertex, "SHADER_VISIBILITY_VERTEX")
107+
SHADER_VISIBILITY_ENUM(Hull, "SHADER_VISIBILITY_HULL")
108+
SHADER_VISIBILITY_ENUM(Domain, "SHADER_VISIBILITY_DOMAIN")
109+
SHADER_VISIBILITY_ENUM(Geometry, "SHADER_VISIBILITY_GEOMETRY")
110+
SHADER_VISIBILITY_ENUM(Pixel, "SHADER_VISIBILITY_PIXEL")
111+
SHADER_VISIBILITY_ENUM(Amplification, "SHADER_VISIBILITY_AMPLIFICATION")
112+
SHADER_VISIBILITY_ENUM(Mesh, "SHADER_VISIBILITY_MESH")
113+
114+
#undef SHADER_VISIBILITY_ENUM
115+
#undef DESCRIPTOR_RANGE_FLAG_ENUM
116+
#undef DESCRIPTOR_RANGE_FLAG_ENUM_OFF
117+
#undef DESCRIPTOR_RANGE_FLAG_ENUM_ON
118+
#undef ROOT_DESCRIPTOR_FLAG_ENUM
119+
#undef DESCRIPTOR_RANGE_OFFSET_ENUM
120+
#undef ENUM
121+
#undef KEYWORD
122+
#undef PUNCTUATOR
123+
#undef TOK

Diff for: clang/include/clang/Lex/LexHLSLRootSignature.h

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
//===--- LexHLSLRootSignature.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 LexHLSLRootSignature interface.
10+
//
11+
//===----------------------------------------------------------------------===//
12+
13+
#ifndef LLVM_CLANG_LEX_LEXHLSLROOTSIGNATURE_H
14+
#define LLVM_CLANG_LEX_LEXHLSLROOTSIGNATURE_H
15+
16+
#include "clang/Basic/SourceLocation.h"
17+
18+
#include "llvm/ADT/SmallVector.h"
19+
#include "llvm/ADT/StringRef.h"
20+
#include "llvm/ADT/StringSwitch.h"
21+
22+
namespace clang {
23+
namespace hlsl {
24+
25+
struct RootSignatureToken {
26+
enum Kind {
27+
#define TOK(X) X,
28+
#include "clang/Lex/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+
// Retain spelling of an numeric constant to be parsed later
37+
StringRef NumSpelling;
38+
39+
// Constructors
40+
RootSignatureToken(clang::SourceLocation TokLoc) : TokLoc(TokLoc) {}
41+
RootSignatureToken(enum Kind Kind, clang::SourceLocation TokLoc)
42+
: Kind(Kind), TokLoc(TokLoc) {}
43+
};
44+
using TokenKind = enum RootSignatureToken::Kind;
45+
46+
class RootSignatureLexer {
47+
public:
48+
RootSignatureLexer(StringRef Signature, clang::SourceLocation SourceLoc)
49+
: Buffer(Signature), SourceLoc(SourceLoc) {}
50+
51+
/// Consumes and returns the next token.
52+
RootSignatureToken ConsumeToken();
53+
54+
/// Returns the token that proceeds CurToken
55+
RootSignatureToken PeekNextToken();
56+
57+
bool EndOfBuffer() {
58+
AdvanceBuffer(Buffer.take_while(isspace).size());
59+
return Buffer.empty();
60+
}
61+
62+
private:
63+
// Internal buffer to iterate over
64+
StringRef Buffer;
65+
66+
// Current peek state
67+
std::optional<RootSignatureToken> NextToken = std::nullopt;
68+
69+
// Passed down parameters from Sema
70+
clang::SourceLocation SourceLoc;
71+
72+
/// Consumes the buffer and returns the lexed token.
73+
RootSignatureToken LexToken();
74+
75+
/// Advance the buffer by the specified number of characters.
76+
/// Updates the SourceLocation appropriately.
77+
void AdvanceBuffer(unsigned NumCharacters = 1) {
78+
Buffer = Buffer.drop_front(NumCharacters);
79+
SourceLoc = SourceLoc.getLocWithOffset(NumCharacters);
80+
}
81+
};
82+
83+
} // namespace hlsl
84+
} // namespace clang
85+
86+
#endif // LLVM_CLANG_LEX_PARSEHLSLROOTSIGNATURE_H

Diff for: clang/lib/Lex/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ add_clang_library(clangLex
1111
HeaderSearch.cpp
1212
InitHeaderSearch.cpp
1313
Lexer.cpp
14+
LexHLSLRootSignature.cpp
1415
LiteralSupport.cpp
1516
MacroArgs.cpp
1617
MacroInfo.cpp

Diff for: clang/lib/Lex/LexHLSLRootSignature.cpp

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
#include "clang/Lex/LexHLSLRootSignature.h"
2+
3+
namespace clang {
4+
namespace hlsl {
5+
6+
// Lexer Definitions
7+
8+
static bool IsNumberChar(char C) {
9+
// TODO(#126565): extend for float support exponents
10+
return isdigit(C); // integer support
11+
}
12+
13+
RootSignatureToken RootSignatureLexer::LexToken() {
14+
// Discard any leading whitespace
15+
AdvanceBuffer(Buffer.take_while(isspace).size());
16+
17+
if (EndOfBuffer())
18+
return RootSignatureToken(TokenKind::end_of_stream, SourceLoc);
19+
20+
// Record where this token is in the text for usage in parser diagnostics
21+
RootSignatureToken Result(SourceLoc);
22+
23+
char C = Buffer.front();
24+
25+
// Punctuators
26+
switch (C) {
27+
#define PUNCTUATOR(X, Y) \
28+
case Y: { \
29+
Result.Kind = TokenKind::pu_##X; \
30+
AdvanceBuffer(); \
31+
return Result; \
32+
}
33+
#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
34+
default:
35+
break;
36+
}
37+
38+
// Integer literal
39+
if (isdigit(C)) {
40+
Result.Kind = TokenKind::int_literal;
41+
Result.NumSpelling = Buffer.take_while(IsNumberChar);
42+
AdvanceBuffer(Result.NumSpelling.size());
43+
return Result;
44+
}
45+
46+
// All following tokens require at least one additional character
47+
if (Buffer.size() <= 1) {
48+
Result = RootSignatureToken(TokenKind::invalid, SourceLoc);
49+
return Result;
50+
}
51+
52+
// Peek at the next character to deteremine token type
53+
char NextC = Buffer[1];
54+
55+
// Registers: [tsub][0-9+]
56+
if ((C == 't' || C == 's' || C == 'u' || C == 'b') && isdigit(NextC)) {
57+
// Convert character to the register type.
58+
switch (C) {
59+
case 'b':
60+
Result.Kind = TokenKind::bReg;
61+
break;
62+
case 't':
63+
Result.Kind = TokenKind::tReg;
64+
break;
65+
case 'u':
66+
Result.Kind = TokenKind::uReg;
67+
break;
68+
case 's':
69+
Result.Kind = TokenKind::sReg;
70+
break;
71+
default:
72+
llvm_unreachable("Switch for an expected token was not provided");
73+
}
74+
75+
AdvanceBuffer();
76+
77+
// Lex the integer literal
78+
Result.NumSpelling = Buffer.take_while(IsNumberChar);
79+
AdvanceBuffer(Result.NumSpelling.size());
80+
81+
return Result;
82+
}
83+
84+
// Keywords and Enums:
85+
StringRef TokSpelling =
86+
Buffer.take_while([](char C) { return isalnum(C) || C == '_'; });
87+
88+
// Define a large string switch statement for all the keywords and enums
89+
auto Switch = llvm::StringSwitch<TokenKind>(TokSpelling);
90+
#define KEYWORD(NAME) Switch.Case(#NAME, TokenKind::kw_##NAME);
91+
#define ENUM(NAME, LIT) Switch.CaseLower(LIT, TokenKind::en_##NAME);
92+
#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
93+
94+
// Then attempt to retreive a string from it
95+
Result.Kind = Switch.Default(TokenKind::invalid);
96+
AdvanceBuffer(TokSpelling.size());
97+
return Result;
98+
}
99+
100+
RootSignatureToken RootSignatureLexer::ConsumeToken() {
101+
// If we previously peeked then just return the previous value over
102+
if (NextToken && NextToken->Kind != TokenKind::end_of_stream) {
103+
RootSignatureToken Result = *NextToken;
104+
NextToken = std::nullopt;
105+
return Result;
106+
}
107+
return LexToken();
108+
}
109+
110+
RootSignatureToken RootSignatureLexer::PeekNextToken() {
111+
// Already peeked from the current token
112+
if (NextToken)
113+
return *NextToken;
114+
115+
NextToken = LexToken();
116+
return *NextToken;
117+
}
118+
119+
} // namespace hlsl
120+
} // namespace clang

Diff for: clang/unittests/Lex/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_clang_unittest(LexTests
77
HeaderMapTest.cpp
88
HeaderSearchTest.cpp
99
LexerTest.cpp
10+
LexHLSLRootSignatureTest.cpp
1011
ModuleDeclStateTest.cpp
1112
PPCallbacksTest.cpp
1213
PPConditionalDirectiveRecordTest.cpp

0 commit comments

Comments
 (0)