Skip to content

Commit bee90e6

Browse files
committed
[HLSL] Implement parsing of RootFlags
- Define the Parser class that will contain all the parsing methods in ParseHLSLRootSignature.h - Implement the dispatch behaviour of Parse and ParseRootElement in ParseHLSLRootSignature.cpp - Define the general in-memory datastructure of a RootElement that will be a union of the various RootElement types - Implement the ParseRootFlags methods in ParseHLSLRootSignature - Define the in-memory represenation of the RootFlag and adds it to the RootElement structure - Add testing of valid inputs to ParseHLSLRootSignatureTest.cpp
1 parent f739aa4 commit bee90e6

File tree

6 files changed

+350
-0
lines changed

6 files changed

+350
-0
lines changed
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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_SEMA_PARSEHLSLROOTSIGNATURE_H
14+
#define LLVM_CLANG_SEMA_PARSEHLSLROOTSIGNATURE_H
15+
16+
#include "llvm/ADT/SmallVector.h"
17+
#include "llvm/ADT/StringRef.h"
18+
#include "llvm/ADT/StringSwitch.h"
19+
20+
#include "llvm/Frontend/HLSL/HLSLRootSignature.h"
21+
22+
namespace llvm {
23+
namespace hlsl {
24+
namespace root_signature {
25+
26+
class Parser {
27+
public:
28+
Parser(StringRef Signature, SmallVector<RootElement> *Elements)
29+
: Buffer(Signature), Elements(Elements) {}
30+
31+
// Consumes the internal buffer as a list of root elements and will
32+
// emplace their in-memory representation onto the back of Elements.
33+
//
34+
// It will consume until it successfully reaches the end of the buffer,
35+
// or until the first error is encountered. The return value denotes if
36+
// there was a failure.
37+
bool Parse();
38+
39+
private:
40+
bool ReportError();
41+
42+
// RootElements parse methods
43+
bool ParseRootElement();
44+
45+
bool ParseRootFlags();
46+
// Enum methods
47+
template <typename EnumType>
48+
bool ParseEnum(SmallVector<std::pair<StringLiteral, EnumType>> Mapping,
49+
EnumType &Enum);
50+
bool ParseRootFlag(RootFlags &Flag);
51+
52+
// Internal state used when parsing
53+
StringRef Buffer;
54+
SmallVector<RootElement> *Elements;
55+
56+
StringRef Token;
57+
};
58+
59+
} // namespace root_signature
60+
} // namespace hlsl
61+
} // namespace llvm
62+
63+
#endif // LLVM_CLANG_SEMA_PARSEHLSLROOTSIGNATURE_H

clang/lib/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ add_clang_library(clangSema
2424
JumpDiagnostics.cpp
2525
MultiplexExternalSemaSource.cpp
2626
ParsedAttr.cpp
27+
ParseHLSLRootSignature.cpp
2728
Scope.cpp
2829
ScopeInfo.cpp
2930
Sema.cpp
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
#include "clang/Sema/ParseHLSLRootSignature.h"
2+
3+
namespace llvm {
4+
namespace hlsl {
5+
namespace root_signature {
6+
7+
// TODO: Hook up with Sema to properly report semantic/validation errors
8+
bool Parser::ReportError() { return true; }
9+
10+
bool Parser::ParseRootFlags() {
11+
// Set to RootFlags::None and skip whitespace to catch when we have RootFlags(
12+
// )
13+
RootFlags Flags = RootFlags::None;
14+
Buffer = Buffer.drop_while(isspace);
15+
StringLiteral Prefix = "";
16+
17+
// Loop until we reach the end of the rootflags
18+
while (!Buffer.starts_with(")")) {
19+
// Trim expected | when more than 1 flag
20+
if (!Buffer.consume_front(Prefix))
21+
return ReportError();
22+
Prefix = "|";
23+
24+
// Remove any whitespace
25+
Buffer = Buffer.drop_while(isspace);
26+
27+
RootFlags CurFlag;
28+
if (ParseRootFlag(CurFlag))
29+
return ReportError();
30+
Flags |= CurFlag;
31+
32+
// Remove any whitespace
33+
Buffer = Buffer.drop_while(isspace);
34+
}
35+
36+
// Create and push the root element on the parsed elements
37+
Elements->push_back(RootElement(Flags));
38+
return false;
39+
}
40+
41+
template <typename EnumType>
42+
bool Parser::ParseEnum(SmallVector<std::pair<StringLiteral, EnumType>> Mapping,
43+
EnumType &Enum) {
44+
// Retrieve enum
45+
Token = Buffer.take_while([](char C) { return isalnum(C) || C == '_'; });
46+
Buffer = Buffer.drop_front(Token.size());
47+
48+
// Try to get the case-insensitive enum
49+
auto Switch = llvm::StringSwitch<std::optional<EnumType>>(Token);
50+
for (auto Pair : Mapping)
51+
Switch.CaseLower(Pair.first, Pair.second);
52+
auto MaybeEnum = Switch.Default(std::nullopt);
53+
if (!MaybeEnum)
54+
return true;
55+
Enum = *MaybeEnum;
56+
57+
return false;
58+
}
59+
60+
bool Parser::ParseRootFlag(RootFlags &Flag) {
61+
SmallVector<std::pair<StringLiteral, RootFlags>> Mapping = {
62+
{"0", RootFlags::None},
63+
{"ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT",
64+
RootFlags::AllowInputAssemblerInputLayout},
65+
{"DENY_VERTEX_SHADER_ROOT_ACCESS", RootFlags::DenyVertexShaderRootAccess},
66+
{"DENY_HULL_SHADER_ROOT_ACCESS", RootFlags::DenyHullShaderRootAccess},
67+
{"DENY_DOMAIN_SHADER_ROOT_ACCESS", RootFlags::DenyDomainShaderRootAccess},
68+
{"DENY_GEOMETRY_SHADER_ROOT_ACCESS",
69+
RootFlags::DenyGeometryShaderRootAccess},
70+
{"DENY_PIXEL_SHADER_ROOT_ACCESS", RootFlags::DenyPixelShaderRootAccess},
71+
{"ALLOW_STREAM_OUTPUT", RootFlags::AllowStreamOutput},
72+
{"LOCAL_ROOT_SIGNATURE", RootFlags::LocalRootSignature},
73+
{"DENY_AMPLIFICATION_SHADER_ROOT_ACCESS",
74+
RootFlags::DenyAmplificationShaderRootAccess},
75+
{"DENY_MESH_SHADER_ROOT_ACCESS", RootFlags::DenyMeshShaderRootAccess},
76+
{"CBV_SRV_UAV_HEAP_DIRECTLY_INDEXED",
77+
RootFlags::CBVSRVUAVHeapDirectlyIndexed},
78+
{"SAMPLER_HEAP_DIRECTLY_INDEXED", RootFlags::SamplerHeapDirectlyIndexed},
79+
{"AllowLowTierReservedHwCbLimit",
80+
RootFlags::AllowLowTierReservedHwCbLimit},
81+
};
82+
83+
return ParseEnum<RootFlags>(Mapping, Flag);
84+
}
85+
86+
bool Parser::ParseRootElement() {
87+
// Define different ParserMethods to use StringSwitch for dispatch
88+
enum class ParserMethod {
89+
ReportError,
90+
ParseRootFlags,
91+
};
92+
93+
// Retreive which method should be used
94+
auto Method = llvm::StringSwitch<ParserMethod>(Token)
95+
.Case("RootFlags", ParserMethod::ParseRootFlags)
96+
.Default(ParserMethod::ReportError);
97+
98+
// Dispatch on the correct method
99+
switch (Method) {
100+
case ParserMethod::ReportError:
101+
return ReportError();
102+
case ParserMethod::ParseRootFlags:
103+
return ParseRootFlags();
104+
}
105+
}
106+
107+
// Parser entry point function
108+
bool Parser::Parse() {
109+
StringLiteral Prefix = "";
110+
while (!Buffer.empty()) {
111+
// Trim expected comma when more than 1 root element
112+
if (!Buffer.consume_front(Prefix))
113+
return ReportError();
114+
Prefix = ",";
115+
116+
// Remove any whitespace
117+
Buffer = Buffer.drop_while(isspace);
118+
119+
// Retrieve the root element identifier
120+
auto Split = Buffer.split('(');
121+
Token = Split.first;
122+
Buffer = Split.second;
123+
124+
// Dispatch to the applicable root element parser
125+
if (ParseRootElement())
126+
return true;
127+
128+
// Then we can clean up the remaining ")"
129+
if (!Buffer.consume_front(")"))
130+
return ReportError();
131+
}
132+
133+
// All input has been correctly parsed
134+
return false;
135+
}
136+
137+
} // namespace root_signature
138+
} // namespace hlsl
139+
} // namespace llvm

clang/unittests/Sema/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ add_clang_unittest(SemaTests
77
ExternalSemaSourceTest.cpp
88
CodeCompleteTest.cpp
99
GslOwnerPointerInference.cpp
10+
ParseHLSLRootSignatureTest.cpp
1011
SemaLookupTest.cpp
1112
SemaNoloadLookupTest.cpp
1213
)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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/Sema/ParseHLSLRootSignature.h"
10+
#include "gtest/gtest.h"
11+
12+
using namespace llvm::hlsl::root_signature;
13+
14+
namespace {
15+
16+
TEST(ParseHLSLRootSignature, EmptyRootFlags) {
17+
llvm::StringRef RootFlagString = " RootFlags()";
18+
llvm::SmallVector<RootElement> RootElements;
19+
Parser Parser(RootFlagString, &RootElements);
20+
ASSERT_FALSE(Parser.Parse());
21+
ASSERT_EQ(RootElements.size(), (unsigned long)1);
22+
ASSERT_EQ(RootFlags::None, RootElements[0].Flags);
23+
}
24+
25+
TEST(ParseHLSLRootSignature, RootFlagsNone) {
26+
llvm::StringRef RootFlagString = " RootFlags(0)";
27+
llvm::SmallVector<RootElement> RootElements;
28+
Parser Parser(RootFlagString, &RootElements);
29+
ASSERT_FALSE(Parser.Parse());
30+
ASSERT_EQ(RootElements.size(), (unsigned long)1);
31+
ASSERT_EQ(RootFlags::None, RootElements[0].Flags);
32+
}
33+
34+
TEST(ParseHLSLRootSignature, ValidRootFlags) {
35+
// Test that the flags are all captured and that they are case insensitive
36+
llvm::StringRef RootFlagString = " RootFlags( "
37+
" ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT"
38+
"| deny_vertex_shader_root_access"
39+
"| DENY_HULL_SHADER_ROOT_ACCESS"
40+
"| deny_domain_shader_root_access"
41+
"| DENY_GEOMETRY_SHADER_ROOT_ACCESS"
42+
"| deny_pixel_shader_root_access"
43+
"| ALLOW_STREAM_OUTPUT"
44+
"| LOCAL_ROOT_SIGNATURE"
45+
"| deny_amplification_shader_root_access"
46+
"| DENY_MESH_SHADER_ROOT_ACCESS"
47+
"| cbv_srv_uav_heap_directly_indexed"
48+
"| SAMPLER_HEAP_DIRECTLY_INDEXED"
49+
"| AllowLowTierReservedHwCbLimit )";
50+
51+
llvm::SmallVector<RootElement> RootElements;
52+
Parser Parser(RootFlagString, &RootElements);
53+
ASSERT_FALSE(Parser.Parse());
54+
ASSERT_EQ(RootElements.size(), (unsigned long)1);
55+
ASSERT_EQ(RootFlags::ValidFlags, RootElements[0].Flags);
56+
}
57+
58+
} // anonymous namespace
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
//===- HLSLRootSignature.h - HLSL Root Signature helper objects -----------===//
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+
/// \file This file contains helper objects for working with HLSL Root
10+
/// Signatures.
11+
///
12+
//===----------------------------------------------------------------------===//
13+
14+
#ifndef LLVM_FRONTEND_HLSL_HLSLROOTSIGNATURE_H
15+
#define LLVM_FRONTEND_HLSL_HLSLROOTSIGNATURE_H
16+
17+
#include <stdint.h>
18+
19+
#include "llvm/Support/Endian.h"
20+
21+
namespace llvm {
22+
namespace hlsl {
23+
namespace root_signature {
24+
25+
// This is a copy from DebugInfo/CodeView/CodeView.h
26+
#define RS_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(Class) \
27+
inline Class operator|(Class a, Class b) { \
28+
return static_cast<Class>(llvm::to_underlying(a) | \
29+
llvm::to_underlying(b)); \
30+
} \
31+
inline Class operator&(Class a, Class b) { \
32+
return static_cast<Class>(llvm::to_underlying(a) & \
33+
llvm::to_underlying(b)); \
34+
} \
35+
inline Class operator~(Class a) { \
36+
return static_cast<Class>(~llvm::to_underlying(a)); \
37+
} \
38+
inline Class &operator|=(Class &a, Class b) { \
39+
a = a | b; \
40+
return a; \
41+
} \
42+
inline Class &operator&=(Class &a, Class b) { \
43+
a = a & b; \
44+
return a; \
45+
}
46+
47+
// Various enumerations and flags
48+
49+
enum class RootFlags : uint32_t {
50+
None = 0,
51+
AllowInputAssemblerInputLayout = 0x1,
52+
DenyVertexShaderRootAccess = 0x2,
53+
DenyHullShaderRootAccess = 0x4,
54+
DenyDomainShaderRootAccess = 0x8,
55+
DenyGeometryShaderRootAccess = 0x10,
56+
DenyPixelShaderRootAccess = 0x20,
57+
AllowStreamOutput = 0x40,
58+
LocalRootSignature = 0x80,
59+
DenyAmplificationShaderRootAccess = 0x100,
60+
DenyMeshShaderRootAccess = 0x200,
61+
CBVSRVUAVHeapDirectlyIndexed = 0x400,
62+
SamplerHeapDirectlyIndexed = 0x800,
63+
AllowLowTierReservedHwCbLimit = 0x80000000,
64+
ValidFlags = 0x80000fff
65+
};
66+
RS_DEFINE_ENUM_CLASS_FLAGS_OPERATORS(RootFlags)
67+
68+
// Define the in-memory layout structures
69+
70+
struct RootElement {
71+
enum class ElementType {
72+
RootFlags,
73+
};
74+
75+
ElementType Tag;
76+
union {
77+
RootFlags Flags;
78+
};
79+
80+
// Constructors
81+
RootElement(RootFlags Flags) : Tag(ElementType::RootFlags), Flags(Flags) {}
82+
};
83+
84+
} // namespace root_signature
85+
} // namespace hlsl
86+
} // namespace llvm
87+
88+
#endif // LLVM_FRONTEND_HLSL_HLSLROOTSIGNATURE_H

0 commit comments

Comments
 (0)