clang 22.0.0git
ParseHLSLRootSignature.cpp
Go to the documentation of this file.
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
12#include "clang/Parse/Parser.h"
13#include "clang/Sema/Sema.h"
14
15using namespace llvm::hlsl::rootsig;
16
17namespace clang {
18namespace hlsl {
19
21
23 TokenKind::kw_RootFlags,
24 TokenKind::kw_CBV,
25 TokenKind::kw_UAV,
26 TokenKind::kw_SRV,
27 TokenKind::kw_DescriptorTable,
28 TokenKind::kw_StaticSampler,
29};
30
32 llvm::dxbc::RootSignatureVersion Version, StringLiteral *Signature,
33 Preprocessor &PP)
34 : Version(Version), Signature(Signature), Lexer(Signature->getString()),
35 PP(PP), CurToken(0) {}
36
38 // Iterate as many RootSignatureElements as possible, until we hit the
39 // end of the stream
40 bool HadError = false;
41 bool HasRootFlags = false;
42 while (!peekExpectedToken(TokenKind::end_of_stream)) {
43 if (tryConsumeExpectedToken(TokenKind::kw_RootFlags)) {
44 if (HasRootFlags) {
45 reportDiag(diag::err_hlsl_rootsig_repeat_param)
46 << TokenKind::kw_RootFlags;
47 HadError = true;
48 skipUntilExpectedToken(RootElementKeywords);
49 continue;
50 }
51 HasRootFlags = true;
52
53 SourceLocation ElementLoc = getTokenLocation(CurToken);
54 auto Flags = parseRootFlags();
55 if (!Flags.has_value()) {
56 HadError = true;
57 skipUntilExpectedToken(RootElementKeywords);
58 continue;
59 }
60
61 Elements.emplace_back(ElementLoc, *Flags);
62 } else if (tryConsumeExpectedToken(TokenKind::kw_RootConstants)) {
63 SourceLocation ElementLoc = getTokenLocation(CurToken);
64 auto Constants = parseRootConstants();
65 if (!Constants.has_value()) {
66 HadError = true;
67 skipUntilExpectedToken(RootElementKeywords);
68 continue;
69 }
70 Elements.emplace_back(ElementLoc, *Constants);
71 } else if (tryConsumeExpectedToken(TokenKind::kw_DescriptorTable)) {
72 SourceLocation ElementLoc = getTokenLocation(CurToken);
73 auto Table = parseDescriptorTable();
74 if (!Table.has_value()) {
75 HadError = true;
76 // We are within a DescriptorTable, we will do our best to recover
77 // by skipping until we encounter the expected closing ')'.
78 skipUntilClosedParens();
79 consumeNextToken();
80 skipUntilExpectedToken(RootElementKeywords);
81 continue;
82 }
83 Elements.emplace_back(ElementLoc, *Table);
84 } else if (tryConsumeExpectedToken(
85 {TokenKind::kw_CBV, TokenKind::kw_SRV, TokenKind::kw_UAV})) {
86 SourceLocation ElementLoc = getTokenLocation(CurToken);
87 auto Descriptor = parseRootDescriptor();
88 if (!Descriptor.has_value()) {
89 HadError = true;
90 skipUntilExpectedToken(RootElementKeywords);
91 continue;
92 }
93 Elements.emplace_back(ElementLoc, *Descriptor);
94 } else if (tryConsumeExpectedToken(TokenKind::kw_StaticSampler)) {
95 SourceLocation ElementLoc = getTokenLocation(CurToken);
96 auto Sampler = parseStaticSampler();
97 if (!Sampler.has_value()) {
98 HadError = true;
99 skipUntilExpectedToken(RootElementKeywords);
100 continue;
101 }
102 Elements.emplace_back(ElementLoc, *Sampler);
103 } else {
104 HadError = true;
105 consumeNextToken(); // let diagnostic be at the start of invalid token
106 reportDiag(diag::err_hlsl_invalid_token)
107 << /*parameter=*/0 << /*param of*/ TokenKind::kw_RootSignature;
108 skipUntilExpectedToken(RootElementKeywords);
109 continue;
110 }
111
112 if (!tryConsumeExpectedToken(TokenKind::pu_comma)) {
113 // ',' denotes another element, otherwise, expected to be at end of stream
114 break;
115 }
116 }
117
118 return HadError ||
119 consumeExpectedToken(TokenKind::end_of_stream,
120 diag::err_expected_either, TokenKind::pu_comma);
121}
122
123template <typename FlagType>
124static FlagType maybeOrFlag(std::optional<FlagType> Flags, FlagType Flag) {
125 if (!Flags.has_value())
126 return Flag;
127
128 return static_cast<FlagType>(llvm::to_underlying(Flags.value()) |
129 llvm::to_underlying(Flag));
130}
131
132std::optional<llvm::dxbc::RootFlags> RootSignatureParser::parseRootFlags() {
133 assert(CurToken.TokKind == TokenKind::kw_RootFlags &&
134 "Expects to only be invoked starting at given keyword");
135
136 if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
137 CurToken.TokKind))
138 return std::nullopt;
139
140 std::optional<llvm::dxbc::RootFlags> Flags = llvm::dxbc::RootFlags::None;
141
142 // Handle valid empty case
143 if (tryConsumeExpectedToken(TokenKind::pu_r_paren))
144 return Flags;
145
146 // Handle the edge-case of '0' to specify no flags set
147 if (tryConsumeExpectedToken(TokenKind::int_literal)) {
148 if (!verifyZeroFlag()) {
149 reportDiag(diag::err_hlsl_rootsig_non_zero_flag);
150 return std::nullopt;
151 }
152 } else {
153 // Otherwise, parse as many flags as possible
154 TokenKind Expected[] = {
155#define ROOT_FLAG_ENUM(NAME, LIT) TokenKind::en_##NAME,
156#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
157 };
158
159 do {
160 if (tryConsumeExpectedToken(Expected)) {
161 switch (CurToken.TokKind) {
162#define ROOT_FLAG_ENUM(NAME, LIT) \
163 case TokenKind::en_##NAME: \
164 Flags = maybeOrFlag<llvm::dxbc::RootFlags>(Flags, \
165 llvm::dxbc::RootFlags::NAME); \
166 break;
167#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
168 default:
169 llvm_unreachable("Switch for consumed enum token was not provided");
170 }
171 } else {
172 consumeNextToken(); // consume token to point at invalid token
173 reportDiag(diag::err_hlsl_invalid_token)
174 << /*value=*/1 << /*value of*/ TokenKind::kw_RootFlags;
175 return std::nullopt;
176 }
177 } while (tryConsumeExpectedToken(TokenKind::pu_or));
178 }
179
180 if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_either,
181 TokenKind::pu_comma))
182 return std::nullopt;
183
184 return Flags;
185}
186
187std::optional<RootConstants> RootSignatureParser::parseRootConstants() {
188 assert(CurToken.TokKind == TokenKind::kw_RootConstants &&
189 "Expects to only be invoked starting at given keyword");
190
191 if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
192 CurToken.TokKind))
193 return std::nullopt;
194
195 RootConstants Constants;
196
197 auto Params = parseRootConstantParams();
198 if (!Params.has_value())
199 return std::nullopt;
200
201 if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_either,
202 TokenKind::pu_comma))
203 return std::nullopt;
204
205 // Check mandatory parameters where provided
206 if (!Params->Num32BitConstants.has_value()) {
207 reportDiag(diag::err_hlsl_rootsig_missing_param)
208 << TokenKind::kw_num32BitConstants;
209 return std::nullopt;
210 }
211
212 Constants.Num32BitConstants = Params->Num32BitConstants.value();
213
214 if (!Params->Reg.has_value()) {
215 reportDiag(diag::err_hlsl_rootsig_missing_param) << TokenKind::bReg;
216 return std::nullopt;
217 }
218
219 Constants.Reg = Params->Reg.value();
220
221 // Fill in optional parameters
222 if (Params->Visibility.has_value())
223 Constants.Visibility = Params->Visibility.value();
224
225 if (Params->Space.has_value())
226 Constants.Space = Params->Space.value();
227
228 return Constants;
229}
230
231std::optional<RootDescriptor> RootSignatureParser::parseRootDescriptor() {
232 assert((CurToken.TokKind == TokenKind::kw_CBV ||
233 CurToken.TokKind == TokenKind::kw_SRV ||
234 CurToken.TokKind == TokenKind::kw_UAV) &&
235 "Expects to only be invoked starting at given keyword");
236
237 TokenKind DescriptorKind = CurToken.TokKind;
238
239 if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
240 CurToken.TokKind))
241 return std::nullopt;
242
243 RootDescriptor Descriptor;
244 TokenKind ExpectedReg;
245 switch (DescriptorKind) {
246 default:
247 llvm_unreachable("Switch for consumed token was not provided");
248 case TokenKind::kw_CBV:
249 Descriptor.Type = ResourceClass::CBuffer;
250 ExpectedReg = TokenKind::bReg;
251 break;
252 case TokenKind::kw_SRV:
253 Descriptor.Type = ResourceClass::SRV;
254 ExpectedReg = TokenKind::tReg;
255 break;
256 case TokenKind::kw_UAV:
257 Descriptor.Type = ResourceClass::UAV;
258 ExpectedReg = TokenKind::uReg;
259 break;
260 }
261 Descriptor.setDefaultFlags(Version);
262
263 auto Params = parseRootDescriptorParams(DescriptorKind, ExpectedReg);
264 if (!Params.has_value())
265 return std::nullopt;
266
267 if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_either,
268 TokenKind::pu_comma))
269 return std::nullopt;
270
271 // Check mandatory parameters were provided
272 if (!Params->Reg.has_value()) {
273 reportDiag(diag::err_hlsl_rootsig_missing_param) << ExpectedReg;
274 return std::nullopt;
275 }
276
277 Descriptor.Reg = Params->Reg.value();
278
279 // Fill in optional values
280 if (Params->Space.has_value())
281 Descriptor.Space = Params->Space.value();
282
283 if (Params->Visibility.has_value())
284 Descriptor.Visibility = Params->Visibility.value();
285
286 if (Params->Flags.has_value())
287 Descriptor.Flags = Params->Flags.value();
288
289 return Descriptor;
290}
291
292std::optional<DescriptorTable> RootSignatureParser::parseDescriptorTable() {
293 assert(CurToken.TokKind == TokenKind::kw_DescriptorTable &&
294 "Expects to only be invoked starting at given keyword");
295
296 if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
297 CurToken.TokKind))
298 return std::nullopt;
299
300 DescriptorTable Table;
301 std::optional<llvm::dxbc::ShaderVisibility> Visibility;
302
303 // Iterate as many Clauses as possible, until we hit ')'
304 while (!peekExpectedToken(TokenKind::pu_r_paren)) {
305 if (tryConsumeExpectedToken({TokenKind::kw_CBV, TokenKind::kw_SRV,
306 TokenKind::kw_UAV, TokenKind::kw_Sampler})) {
307 // DescriptorTableClause - CBV, SRV, UAV, or Sampler
308 SourceLocation ElementLoc = getTokenLocation(CurToken);
309 auto Clause = parseDescriptorTableClause();
310 if (!Clause.has_value()) {
311 // We are within a DescriptorTableClause, we will do our best to recover
312 // by skipping until we encounter the expected closing ')'
313 skipUntilExpectedToken(TokenKind::pu_r_paren);
314 consumeNextToken();
315 return std::nullopt;
316 }
317 Elements.emplace_back(ElementLoc, *Clause);
318 Table.NumClauses++;
319 } else if (tryConsumeExpectedToken(TokenKind::kw_visibility)) {
320 // visibility = SHADER_VISIBILITY
321 if (Visibility.has_value()) {
322 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
323 return std::nullopt;
324 }
325
326 if (consumeExpectedToken(TokenKind::pu_equal))
327 return std::nullopt;
328
329 Visibility = parseShaderVisibility(TokenKind::kw_visibility);
330 if (!Visibility.has_value())
331 return std::nullopt;
332 } else {
333 consumeNextToken(); // let diagnostic be at the start of invalid token
334 reportDiag(diag::err_hlsl_invalid_token)
335 << /*parameter=*/0 << /*param of*/ TokenKind::kw_DescriptorTable;
336 return std::nullopt;
337 }
338
339 // ',' denotes another element, otherwise, expected to be at ')'
340 if (!tryConsumeExpectedToken(TokenKind::pu_comma))
341 break;
342 }
343
344 if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_either,
345 TokenKind::pu_comma))
346 return std::nullopt;
347
348 // Fill in optional visibility
349 if (Visibility.has_value())
350 Table.Visibility = Visibility.value();
351
352 return Table;
353}
354
355std::optional<DescriptorTableClause>
356RootSignatureParser::parseDescriptorTableClause() {
357 assert((CurToken.TokKind == TokenKind::kw_CBV ||
358 CurToken.TokKind == TokenKind::kw_SRV ||
359 CurToken.TokKind == TokenKind::kw_UAV ||
360 CurToken.TokKind == TokenKind::kw_Sampler) &&
361 "Expects to only be invoked starting at given keyword");
362
363 TokenKind ParamKind = CurToken.TokKind;
364
365 if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
366 CurToken.TokKind))
367 return std::nullopt;
368
369 DescriptorTableClause Clause;
370 TokenKind ExpectedReg;
371 switch (ParamKind) {
372 default:
373 llvm_unreachable("Switch for consumed token was not provided");
374 case TokenKind::kw_CBV:
375 Clause.Type = ResourceClass::CBuffer;
376 ExpectedReg = TokenKind::bReg;
377 break;
378 case TokenKind::kw_SRV:
379 Clause.Type = ResourceClass::SRV;
380 ExpectedReg = TokenKind::tReg;
381 break;
382 case TokenKind::kw_UAV:
383 Clause.Type = ResourceClass::UAV;
384 ExpectedReg = TokenKind::uReg;
385 break;
386 case TokenKind::kw_Sampler:
387 Clause.Type = ResourceClass::Sampler;
388 ExpectedReg = TokenKind::sReg;
389 break;
390 }
391 Clause.setDefaultFlags(Version);
392
393 auto Params = parseDescriptorTableClauseParams(ParamKind, ExpectedReg);
394 if (!Params.has_value())
395 return std::nullopt;
396
397 if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_either,
398 TokenKind::pu_comma))
399 return std::nullopt;
400
401 // Check mandatory parameters were provided
402 if (!Params->Reg.has_value()) {
403 reportDiag(diag::err_hlsl_rootsig_missing_param) << ExpectedReg;
404 return std::nullopt;
405 }
406
407 Clause.Reg = Params->Reg.value();
408
409 // Fill in optional values
410 if (Params->NumDescriptors.has_value())
411 Clause.NumDescriptors = Params->NumDescriptors.value();
412
413 if (Params->Space.has_value())
414 Clause.Space = Params->Space.value();
415
416 if (Params->Offset.has_value())
417 Clause.Offset = Params->Offset.value();
418
419 if (Params->Flags.has_value())
420 Clause.Flags = Params->Flags.value();
421
422 return Clause;
423}
424
425std::optional<StaticSampler> RootSignatureParser::parseStaticSampler() {
426 assert(CurToken.TokKind == TokenKind::kw_StaticSampler &&
427 "Expects to only be invoked starting at given keyword");
428
429 if (consumeExpectedToken(TokenKind::pu_l_paren, diag::err_expected_after,
430 CurToken.TokKind))
431 return std::nullopt;
432
433 StaticSampler Sampler;
434
435 auto Params = parseStaticSamplerParams();
436 if (!Params.has_value())
437 return std::nullopt;
438
439 if (consumeExpectedToken(TokenKind::pu_r_paren, diag::err_expected_either,
440 TokenKind::pu_comma))
441 return std::nullopt;
442
443 // Check mandatory parameters were provided
444 if (!Params->Reg.has_value()) {
445 reportDiag(diag::err_hlsl_rootsig_missing_param) << TokenKind::sReg;
446 return std::nullopt;
447 }
448
449 Sampler.Reg = Params->Reg.value();
450
451 // Fill in optional values
452 if (Params->Filter.has_value())
453 Sampler.Filter = Params->Filter.value();
454
455 if (Params->AddressU.has_value())
456 Sampler.AddressU = Params->AddressU.value();
457
458 if (Params->AddressV.has_value())
459 Sampler.AddressV = Params->AddressV.value();
460
461 if (Params->AddressW.has_value())
462 Sampler.AddressW = Params->AddressW.value();
463
464 if (Params->MipLODBias.has_value())
465 Sampler.MipLODBias = Params->MipLODBias.value();
466
467 if (Params->MaxAnisotropy.has_value())
468 Sampler.MaxAnisotropy = Params->MaxAnisotropy.value();
469
470 if (Params->CompFunc.has_value())
471 Sampler.CompFunc = Params->CompFunc.value();
472
473 if (Params->BorderColor.has_value())
474 Sampler.BorderColor = Params->BorderColor.value();
475
476 if (Params->MinLOD.has_value())
477 Sampler.MinLOD = Params->MinLOD.value();
478
479 if (Params->MaxLOD.has_value())
480 Sampler.MaxLOD = Params->MaxLOD.value();
481
482 if (Params->Space.has_value())
483 Sampler.Space = Params->Space.value();
484
485 if (Params->Visibility.has_value())
486 Sampler.Visibility = Params->Visibility.value();
487
488 if (Params->Flags.has_value())
489 Sampler.Flags = Params->Flags.value();
490
491 return Sampler;
492}
493
494// Parameter arguments (eg. `bReg`, `space`, ...) can be specified in any
495// order and only exactly once. The following methods will parse through as
496// many arguments as possible reporting an error if a duplicate is seen.
497std::optional<RootSignatureParser::ParsedConstantParams>
498RootSignatureParser::parseRootConstantParams() {
499 assert(CurToken.TokKind == TokenKind::pu_l_paren &&
500 "Expects to only be invoked starting at given token");
501
502 ParsedConstantParams Params;
503 while (!peekExpectedToken(TokenKind::pu_r_paren)) {
504 if (tryConsumeExpectedToken(TokenKind::kw_num32BitConstants)) {
505 // `num32BitConstants` `=` POS_INT
506 if (Params.Num32BitConstants.has_value()) {
507 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
508 return std::nullopt;
509 }
510
511 if (consumeExpectedToken(TokenKind::pu_equal))
512 return std::nullopt;
513
514 auto Num32BitConstants = parseUIntParam();
515 if (!Num32BitConstants.has_value())
516 return std::nullopt;
517 Params.Num32BitConstants = Num32BitConstants;
518 } else if (tryConsumeExpectedToken(TokenKind::bReg)) {
519 // `b` POS_INT
520 if (Params.Reg.has_value()) {
521 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
522 return std::nullopt;
523 }
524 auto Reg = parseRegister();
525 if (!Reg.has_value())
526 return std::nullopt;
527 Params.Reg = Reg;
528 } else if (tryConsumeExpectedToken(TokenKind::kw_space)) {
529 // `space` `=` POS_INT
530 if (Params.Space.has_value()) {
531 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
532 return std::nullopt;
533 }
534
535 if (consumeExpectedToken(TokenKind::pu_equal))
536 return std::nullopt;
537
538 auto Space = parseUIntParam();
539 if (!Space.has_value())
540 return std::nullopt;
541 Params.Space = Space;
542 } else if (tryConsumeExpectedToken(TokenKind::kw_visibility)) {
543 // `visibility` `=` SHADER_VISIBILITY
544 if (Params.Visibility.has_value()) {
545 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
546 return std::nullopt;
547 }
548
549 if (consumeExpectedToken(TokenKind::pu_equal))
550 return std::nullopt;
551
552 auto Visibility = parseShaderVisibility(TokenKind::kw_visibility);
553 if (!Visibility.has_value())
554 return std::nullopt;
555 Params.Visibility = Visibility;
556 } else {
557 consumeNextToken(); // let diagnostic be at the start of invalid token
558 reportDiag(diag::err_hlsl_invalid_token)
559 << /*parameter=*/0 << /*param of*/ TokenKind::kw_RootConstants;
560 return std::nullopt;
561 }
562
563 // ',' denotes another element, otherwise, expected to be at ')'
564 if (!tryConsumeExpectedToken(TokenKind::pu_comma))
565 break;
566 }
567
568 return Params;
569}
570
571std::optional<RootSignatureParser::ParsedRootDescriptorParams>
572RootSignatureParser::parseRootDescriptorParams(TokenKind DescKind,
573 TokenKind RegType) {
574 assert(CurToken.TokKind == TokenKind::pu_l_paren &&
575 "Expects to only be invoked starting at given token");
576
577 ParsedRootDescriptorParams Params;
578 while (!peekExpectedToken(TokenKind::pu_r_paren)) {
579 if (tryConsumeExpectedToken(RegType)) {
580 // ( `b` | `t` | `u`) POS_INT
581 if (Params.Reg.has_value()) {
582 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
583 return std::nullopt;
584 }
585 auto Reg = parseRegister();
586 if (!Reg.has_value())
587 return std::nullopt;
588 Params.Reg = Reg;
589 } else if (tryConsumeExpectedToken(TokenKind::kw_space)) {
590 // `space` `=` POS_INT
591 if (Params.Space.has_value()) {
592 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
593 return std::nullopt;
594 }
595
596 if (consumeExpectedToken(TokenKind::pu_equal))
597 return std::nullopt;
598
599 auto Space = parseUIntParam();
600 if (!Space.has_value())
601 return std::nullopt;
602 Params.Space = Space;
603 } else if (tryConsumeExpectedToken(TokenKind::kw_visibility)) {
604 // `visibility` `=` SHADER_VISIBILITY
605 if (Params.Visibility.has_value()) {
606 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
607 return std::nullopt;
608 }
609
610 if (consumeExpectedToken(TokenKind::pu_equal))
611 return std::nullopt;
612
613 auto Visibility = parseShaderVisibility(TokenKind::kw_visibility);
614 if (!Visibility.has_value())
615 return std::nullopt;
616 Params.Visibility = Visibility;
617 } else if (tryConsumeExpectedToken(TokenKind::kw_flags)) {
618 // `flags` `=` ROOT_DESCRIPTOR_FLAGS
619 if (Params.Flags.has_value()) {
620 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
621 return std::nullopt;
622 }
623
624 if (consumeExpectedToken(TokenKind::pu_equal))
625 return std::nullopt;
626
627 auto Flags = parseRootDescriptorFlags(TokenKind::kw_flags);
628 if (!Flags.has_value())
629 return std::nullopt;
630 Params.Flags = Flags;
631 } else {
632 consumeNextToken(); // let diagnostic be at the start of invalid token
633 reportDiag(diag::err_hlsl_invalid_token)
634 << /*parameter=*/0 << /*param of*/ DescKind;
635 return std::nullopt;
636 }
637
638 // ',' denotes another element, otherwise, expected to be at ')'
639 if (!tryConsumeExpectedToken(TokenKind::pu_comma))
640 break;
641 }
642
643 return Params;
644}
645
646std::optional<RootSignatureParser::ParsedClauseParams>
647RootSignatureParser::parseDescriptorTableClauseParams(TokenKind ClauseKind,
648 TokenKind RegType) {
649 assert(CurToken.TokKind == TokenKind::pu_l_paren &&
650 "Expects to only be invoked starting at given token");
651
652 ParsedClauseParams Params;
653 while (!peekExpectedToken(TokenKind::pu_r_paren)) {
654 if (tryConsumeExpectedToken(RegType)) {
655 // ( `b` | `t` | `u` | `s`) POS_INT
656 if (Params.Reg.has_value()) {
657 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
658 return std::nullopt;
659 }
660 auto Reg = parseRegister();
661 if (!Reg.has_value())
662 return std::nullopt;
663 Params.Reg = Reg;
664 } else if (tryConsumeExpectedToken(TokenKind::kw_numDescriptors)) {
665 // `numDescriptors` `=` POS_INT | unbounded
666 if (Params.NumDescriptors.has_value()) {
667 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
668 return std::nullopt;
669 }
670
671 if (consumeExpectedToken(TokenKind::pu_equal))
672 return std::nullopt;
673
674 std::optional<uint32_t> NumDescriptors;
675 if (tryConsumeExpectedToken(TokenKind::en_unbounded))
676 NumDescriptors = NumDescriptorsUnbounded;
677 else {
678 NumDescriptors = parseUIntParam();
679 if (!NumDescriptors.has_value())
680 return std::nullopt;
681 }
682
683 Params.NumDescriptors = NumDescriptors;
684 } else if (tryConsumeExpectedToken(TokenKind::kw_space)) {
685 // `space` `=` POS_INT
686 if (Params.Space.has_value()) {
687 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
688 return std::nullopt;
689 }
690
691 if (consumeExpectedToken(TokenKind::pu_equal))
692 return std::nullopt;
693
694 auto Space = parseUIntParam();
695 if (!Space.has_value())
696 return std::nullopt;
697 Params.Space = Space;
698 } else if (tryConsumeExpectedToken(TokenKind::kw_offset)) {
699 // `offset` `=` POS_INT | DESCRIPTOR_RANGE_OFFSET_APPEND
700 if (Params.Offset.has_value()) {
701 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
702 return std::nullopt;
703 }
704
705 if (consumeExpectedToken(TokenKind::pu_equal))
706 return std::nullopt;
707
708 std::optional<uint32_t> Offset;
709 if (tryConsumeExpectedToken(TokenKind::en_DescriptorRangeOffsetAppend))
710 Offset = DescriptorTableOffsetAppend;
711 else {
712 Offset = parseUIntParam();
713 if (!Offset.has_value())
714 return std::nullopt;
715 }
716
717 Params.Offset = Offset;
718 } else if (tryConsumeExpectedToken(TokenKind::kw_flags)) {
719 // `flags` `=` DESCRIPTOR_RANGE_FLAGS
720 if (Params.Flags.has_value()) {
721 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
722 return std::nullopt;
723 }
724
725 if (consumeExpectedToken(TokenKind::pu_equal))
726 return std::nullopt;
727
728 auto Flags = parseDescriptorRangeFlags(TokenKind::kw_flags);
729 if (!Flags.has_value())
730 return std::nullopt;
731 Params.Flags = Flags;
732 } else {
733 consumeNextToken(); // let diagnostic be at the start of invalid token
734 reportDiag(diag::err_hlsl_invalid_token)
735 << /*parameter=*/0 << /*param of*/ ClauseKind;
736 return std::nullopt;
737 }
738
739 // ',' denotes another element, otherwise, expected to be at ')'
740 if (!tryConsumeExpectedToken(TokenKind::pu_comma))
741 break;
742 }
743
744 return Params;
745}
746
747std::optional<RootSignatureParser::ParsedStaticSamplerParams>
748RootSignatureParser::parseStaticSamplerParams() {
749 assert(CurToken.TokKind == TokenKind::pu_l_paren &&
750 "Expects to only be invoked starting at given token");
751
752 ParsedStaticSamplerParams Params;
753 while (!peekExpectedToken(TokenKind::pu_r_paren)) {
754 if (tryConsumeExpectedToken(TokenKind::sReg)) {
755 // `s` POS_INT
756 if (Params.Reg.has_value()) {
757 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
758 return std::nullopt;
759 }
760 auto Reg = parseRegister();
761 if (!Reg.has_value())
762 return std::nullopt;
763 Params.Reg = Reg;
764 } else if (tryConsumeExpectedToken(TokenKind::kw_filter)) {
765 // `filter` `=` FILTER
766 if (Params.Filter.has_value()) {
767 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
768 return std::nullopt;
769 }
770
771 if (consumeExpectedToken(TokenKind::pu_equal))
772 return std::nullopt;
773
774 auto Filter = parseSamplerFilter(TokenKind::kw_filter);
775 if (!Filter.has_value())
776 return std::nullopt;
777 Params.Filter = Filter;
778 } else if (tryConsumeExpectedToken(TokenKind::kw_addressU)) {
779 // `addressU` `=` TEXTURE_ADDRESS
780 if (Params.AddressU.has_value()) {
781 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
782 return std::nullopt;
783 }
784
785 if (consumeExpectedToken(TokenKind::pu_equal))
786 return std::nullopt;
787
788 auto AddressU = parseTextureAddressMode(TokenKind::kw_addressU);
789 if (!AddressU.has_value())
790 return std::nullopt;
791 Params.AddressU = AddressU;
792 } else if (tryConsumeExpectedToken(TokenKind::kw_addressV)) {
793 // `addressV` `=` TEXTURE_ADDRESS
794 if (Params.AddressV.has_value()) {
795 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
796 return std::nullopt;
797 }
798
799 if (consumeExpectedToken(TokenKind::pu_equal))
800 return std::nullopt;
801
802 auto AddressV = parseTextureAddressMode(TokenKind::kw_addressV);
803 if (!AddressV.has_value())
804 return std::nullopt;
805 Params.AddressV = AddressV;
806 } else if (tryConsumeExpectedToken(TokenKind::kw_addressW)) {
807 // `addressW` `=` TEXTURE_ADDRESS
808 if (Params.AddressW.has_value()) {
809 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
810 return std::nullopt;
811 }
812
813 if (consumeExpectedToken(TokenKind::pu_equal))
814 return std::nullopt;
815
816 auto AddressW = parseTextureAddressMode(TokenKind::kw_addressW);
817 if (!AddressW.has_value())
818 return std::nullopt;
819 Params.AddressW = AddressW;
820 } else if (tryConsumeExpectedToken(TokenKind::kw_mipLODBias)) {
821 // `mipLODBias` `=` NUMBER
822 if (Params.MipLODBias.has_value()) {
823 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
824 return std::nullopt;
825 }
826
827 if (consumeExpectedToken(TokenKind::pu_equal))
828 return std::nullopt;
829
830 auto MipLODBias = parseFloatParam();
831 if (!MipLODBias.has_value())
832 return std::nullopt;
833 Params.MipLODBias = MipLODBias;
834 } else if (tryConsumeExpectedToken(TokenKind::kw_maxAnisotropy)) {
835 // `maxAnisotropy` `=` POS_INT
836 if (Params.MaxAnisotropy.has_value()) {
837 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
838 return std::nullopt;
839 }
840
841 if (consumeExpectedToken(TokenKind::pu_equal))
842 return std::nullopt;
843
844 auto MaxAnisotropy = parseUIntParam();
845 if (!MaxAnisotropy.has_value())
846 return std::nullopt;
847 Params.MaxAnisotropy = MaxAnisotropy;
848 } else if (tryConsumeExpectedToken(TokenKind::kw_comparisonFunc)) {
849 // `comparisonFunc` `=` COMPARISON_FUNC
850 if (Params.CompFunc.has_value()) {
851 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
852 return std::nullopt;
853 }
854
855 if (consumeExpectedToken(TokenKind::pu_equal))
856 return std::nullopt;
857
858 auto CompFunc = parseComparisonFunc(TokenKind::kw_comparisonFunc);
859 if (!CompFunc.has_value())
860 return std::nullopt;
861 Params.CompFunc = CompFunc;
862 } else if (tryConsumeExpectedToken(TokenKind::kw_borderColor)) {
863 // `borderColor` `=` STATIC_BORDER_COLOR
864 if (Params.BorderColor.has_value()) {
865 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
866 return std::nullopt;
867 }
868
869 if (consumeExpectedToken(TokenKind::pu_equal))
870 return std::nullopt;
871
872 auto BorderColor = parseStaticBorderColor(TokenKind::kw_borderColor);
873 if (!BorderColor.has_value())
874 return std::nullopt;
875 Params.BorderColor = BorderColor;
876 } else if (tryConsumeExpectedToken(TokenKind::kw_minLOD)) {
877 // `minLOD` `=` NUMBER
878 if (Params.MinLOD.has_value()) {
879 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
880 return std::nullopt;
881 }
882
883 if (consumeExpectedToken(TokenKind::pu_equal))
884 return std::nullopt;
885
886 auto MinLOD = parseFloatParam();
887 if (!MinLOD.has_value())
888 return std::nullopt;
889 Params.MinLOD = MinLOD;
890 } else if (tryConsumeExpectedToken(TokenKind::kw_maxLOD)) {
891 // `maxLOD` `=` NUMBER
892 if (Params.MaxLOD.has_value()) {
893 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
894 return std::nullopt;
895 }
896
897 if (consumeExpectedToken(TokenKind::pu_equal))
898 return std::nullopt;
899
900 auto MaxLOD = parseFloatParam();
901 if (!MaxLOD.has_value())
902 return std::nullopt;
903 Params.MaxLOD = MaxLOD;
904 } else if (tryConsumeExpectedToken(TokenKind::kw_space)) {
905 // `space` `=` POS_INT
906 if (Params.Space.has_value()) {
907 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
908 return std::nullopt;
909 }
910
911 if (consumeExpectedToken(TokenKind::pu_equal))
912 return std::nullopt;
913
914 auto Space = parseUIntParam();
915 if (!Space.has_value())
916 return std::nullopt;
917 Params.Space = Space;
918 } else if (tryConsumeExpectedToken(TokenKind::kw_visibility)) {
919 // `visibility` `=` SHADER_VISIBILITY
920 if (Params.Visibility.has_value()) {
921 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
922 return std::nullopt;
923 }
924
925 if (consumeExpectedToken(TokenKind::pu_equal))
926 return std::nullopt;
927
928 auto Visibility = parseShaderVisibility(TokenKind::kw_visibility);
929 if (!Visibility.has_value())
930 return std::nullopt;
931 Params.Visibility = Visibility;
932 } else if (tryConsumeExpectedToken(TokenKind::kw_flags)) {
933 // `flags` `=` STATIC_SAMPLE_FLAGS
934 if (Params.Flags.has_value()) {
935 reportDiag(diag::err_hlsl_rootsig_repeat_param) << CurToken.TokKind;
936 return std::nullopt;
937 }
938
939 if (consumeExpectedToken(TokenKind::pu_equal))
940 return std::nullopt;
941
942 auto Flags = parseStaticSamplerFlags(TokenKind::kw_flags);
943 if (!Flags.has_value())
944 return std::nullopt;
945 Params.Flags = Flags;
946 } else {
947 consumeNextToken(); // let diagnostic be at the start of invalid token
948 reportDiag(diag::err_hlsl_invalid_token)
949 << /*parameter=*/0 << /*param of*/ TokenKind::kw_StaticSampler;
950 return std::nullopt;
951 }
952
953 // ',' denotes another element, otherwise, expected to be at ')'
954 if (!tryConsumeExpectedToken(TokenKind::pu_comma))
955 break;
956 }
957
958 return Params;
959}
960
961std::optional<uint32_t> RootSignatureParser::parseUIntParam() {
962 assert(CurToken.TokKind == TokenKind::pu_equal &&
963 "Expects to only be invoked starting at given keyword");
964 tryConsumeExpectedToken(TokenKind::pu_plus);
965 if (consumeExpectedToken(TokenKind::int_literal, diag::err_expected_after,
966 CurToken.TokKind))
967 return std::nullopt;
968 return handleUIntLiteral();
969}
970
971std::optional<Register> RootSignatureParser::parseRegister() {
972 assert((CurToken.TokKind == TokenKind::bReg ||
973 CurToken.TokKind == TokenKind::tReg ||
974 CurToken.TokKind == TokenKind::uReg ||
975 CurToken.TokKind == TokenKind::sReg) &&
976 "Expects to only be invoked starting at given keyword");
977
978 Register Reg;
979 switch (CurToken.TokKind) {
980 default:
981 llvm_unreachable("Switch for consumed token was not provided");
982 case TokenKind::bReg:
983 Reg.ViewType = RegisterType::BReg;
984 break;
985 case TokenKind::tReg:
986 Reg.ViewType = RegisterType::TReg;
987 break;
988 case TokenKind::uReg:
989 Reg.ViewType = RegisterType::UReg;
990 break;
991 case TokenKind::sReg:
992 Reg.ViewType = RegisterType::SReg;
993 break;
994 }
995
996 auto Number = handleUIntLiteral();
997 if (!Number.has_value())
998 return std::nullopt; // propogate NumericLiteralParser error
999
1000 Reg.Number = *Number;
1001 return Reg;
1002}
1003
1004std::optional<float> RootSignatureParser::parseFloatParam() {
1005 assert(CurToken.TokKind == TokenKind::pu_equal &&
1006 "Expects to only be invoked starting at given keyword");
1007 // Consume sign modifier
1008 bool Signed =
1009 tryConsumeExpectedToken({TokenKind::pu_plus, TokenKind::pu_minus});
1010 bool Negated = Signed && CurToken.TokKind == TokenKind::pu_minus;
1011
1012 // DXC will treat a postive signed integer as unsigned
1013 if (!Negated && tryConsumeExpectedToken(TokenKind::int_literal)) {
1014 std::optional<uint32_t> UInt = handleUIntLiteral();
1015 if (!UInt.has_value())
1016 return std::nullopt;
1017 return float(UInt.value());
1018 }
1019
1020 if (Negated && tryConsumeExpectedToken(TokenKind::int_literal)) {
1021 std::optional<int32_t> Int = handleIntLiteral(Negated);
1022 if (!Int.has_value())
1023 return std::nullopt;
1024 return float(Int.value());
1025 }
1026
1027 if (tryConsumeExpectedToken(TokenKind::float_literal)) {
1028 std::optional<float> Float = handleFloatLiteral(Negated);
1029 if (!Float.has_value())
1030 return std::nullopt;
1031 return Float.value();
1032 }
1033
1034 return std::nullopt;
1035}
1036
1037std::optional<llvm::dxbc::ShaderVisibility>
1038RootSignatureParser::parseShaderVisibility(TokenKind Context) {
1039 assert(CurToken.TokKind == TokenKind::pu_equal &&
1040 "Expects to only be invoked starting at given keyword");
1041
1042 TokenKind Expected[] = {
1043#define SHADER_VISIBILITY_ENUM(NAME, LIT) TokenKind::en_##NAME,
1044#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1045 };
1046
1047 if (!tryConsumeExpectedToken(Expected)) {
1048 consumeNextToken(); // consume token to point at invalid token
1049 reportDiag(diag::err_hlsl_invalid_token)
1050 << /*value=*/1 << /*value of*/ Context;
1051 return std::nullopt;
1052 }
1053
1054 switch (CurToken.TokKind) {
1055#define SHADER_VISIBILITY_ENUM(NAME, LIT) \
1056 case TokenKind::en_##NAME: \
1057 return llvm::dxbc::ShaderVisibility::NAME; \
1058 break;
1059#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1060 default:
1061 llvm_unreachable("Switch for consumed enum token was not provided");
1062 }
1063
1064 return std::nullopt;
1065}
1066
1067std::optional<llvm::dxbc::SamplerFilter>
1068RootSignatureParser::parseSamplerFilter(TokenKind Context) {
1069 assert(CurToken.TokKind == TokenKind::pu_equal &&
1070 "Expects to only be invoked starting at given keyword");
1071
1072 TokenKind Expected[] = {
1073#define FILTER_ENUM(NAME, LIT) TokenKind::en_##NAME,
1074#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1075 };
1076
1077 if (!tryConsumeExpectedToken(Expected)) {
1078 consumeNextToken(); // consume token to point at invalid token
1079 reportDiag(diag::err_hlsl_invalid_token)
1080 << /*value=*/1 << /*value of*/ Context;
1081 return std::nullopt;
1082 }
1083
1084 switch (CurToken.TokKind) {
1085#define FILTER_ENUM(NAME, LIT) \
1086 case TokenKind::en_##NAME: \
1087 return llvm::dxbc::SamplerFilter::NAME; \
1088 break;
1089#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1090 default:
1091 llvm_unreachable("Switch for consumed enum token was not provided");
1092 }
1093
1094 return std::nullopt;
1095}
1096
1097std::optional<llvm::dxbc::TextureAddressMode>
1098RootSignatureParser::parseTextureAddressMode(TokenKind Context) {
1099 assert(CurToken.TokKind == TokenKind::pu_equal &&
1100 "Expects to only be invoked starting at given keyword");
1101
1102 TokenKind Expected[] = {
1103#define TEXTURE_ADDRESS_MODE_ENUM(NAME, LIT) TokenKind::en_##NAME,
1104#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1105 };
1106
1107 if (!tryConsumeExpectedToken(Expected)) {
1108 consumeNextToken(); // consume token to point at invalid token
1109 reportDiag(diag::err_hlsl_invalid_token)
1110 << /*value=*/1 << /*value of*/ Context;
1111 return std::nullopt;
1112 }
1113
1114 switch (CurToken.TokKind) {
1115#define TEXTURE_ADDRESS_MODE_ENUM(NAME, LIT) \
1116 case TokenKind::en_##NAME: \
1117 return llvm::dxbc::TextureAddressMode::NAME; \
1118 break;
1119#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1120 default:
1121 llvm_unreachable("Switch for consumed enum token was not provided");
1122 }
1123
1124 return std::nullopt;
1125}
1126
1127std::optional<llvm::dxbc::ComparisonFunc>
1128RootSignatureParser::parseComparisonFunc(TokenKind Context) {
1129 assert(CurToken.TokKind == TokenKind::pu_equal &&
1130 "Expects to only be invoked starting at given keyword");
1131
1132 TokenKind Expected[] = {
1133#define COMPARISON_FUNC_ENUM(NAME, LIT) TokenKind::en_##NAME,
1134#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1135 };
1136
1137 if (!tryConsumeExpectedToken(Expected)) {
1138 consumeNextToken(); // consume token to point at invalid token
1139 reportDiag(diag::err_hlsl_invalid_token)
1140 << /*value=*/1 << /*value of*/ Context;
1141 return std::nullopt;
1142 }
1143
1144 switch (CurToken.TokKind) {
1145#define COMPARISON_FUNC_ENUM(NAME, LIT) \
1146 case TokenKind::en_##NAME: \
1147 return llvm::dxbc::ComparisonFunc::NAME; \
1148 break;
1149#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1150 default:
1151 llvm_unreachable("Switch for consumed enum token was not provided");
1152 }
1153
1154 return std::nullopt;
1155}
1156
1157std::optional<llvm::dxbc::StaticBorderColor>
1158RootSignatureParser::parseStaticBorderColor(TokenKind Context) {
1159 assert(CurToken.TokKind == TokenKind::pu_equal &&
1160 "Expects to only be invoked starting at given keyword");
1161
1162 TokenKind Expected[] = {
1163#define STATIC_BORDER_COLOR_ENUM(NAME, LIT) TokenKind::en_##NAME,
1164#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1165 };
1166
1167 if (!tryConsumeExpectedToken(Expected)) {
1168 consumeNextToken(); // consume token to point at invalid token
1169 reportDiag(diag::err_hlsl_invalid_token)
1170 << /*value=*/1 << /*value of*/ Context;
1171 return std::nullopt;
1172 }
1173
1174 switch (CurToken.TokKind) {
1175#define STATIC_BORDER_COLOR_ENUM(NAME, LIT) \
1176 case TokenKind::en_##NAME: \
1177 return llvm::dxbc::StaticBorderColor::NAME; \
1178 break;
1179#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1180 default:
1181 llvm_unreachable("Switch for consumed enum token was not provided");
1182 }
1183
1184 return std::nullopt;
1185}
1186
1187std::optional<llvm::dxbc::RootDescriptorFlags>
1188RootSignatureParser::parseRootDescriptorFlags(TokenKind Context) {
1189 assert(CurToken.TokKind == TokenKind::pu_equal &&
1190 "Expects to only be invoked starting at given keyword");
1191
1192 // Handle the edge-case of '0' to specify no flags set
1193 if (tryConsumeExpectedToken(TokenKind::int_literal)) {
1194 if (!verifyZeroFlag()) {
1195 reportDiag(diag::err_hlsl_rootsig_non_zero_flag);
1196 return std::nullopt;
1197 }
1198 return llvm::dxbc::RootDescriptorFlags::None;
1199 }
1200
1201 TokenKind Expected[] = {
1202#define ROOT_DESCRIPTOR_FLAG_ENUM(NAME, LIT) TokenKind::en_##NAME,
1203#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1204 };
1205
1206 std::optional<llvm::dxbc::RootDescriptorFlags> Flags;
1207
1208 do {
1209 if (tryConsumeExpectedToken(Expected)) {
1210 switch (CurToken.TokKind) {
1211#define ROOT_DESCRIPTOR_FLAG_ENUM(NAME, LIT) \
1212 case TokenKind::en_##NAME: \
1213 Flags = maybeOrFlag<llvm::dxbc::RootDescriptorFlags>( \
1214 Flags, llvm::dxbc::RootDescriptorFlags::NAME); \
1215 break;
1216#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1217 default:
1218 llvm_unreachable("Switch for consumed enum token was not provided");
1219 }
1220 } else {
1221 consumeNextToken(); // consume token to point at invalid token
1222 reportDiag(diag::err_hlsl_invalid_token)
1223 << /*value=*/1 << /*value of*/ Context;
1224 return std::nullopt;
1225 }
1226 } while (tryConsumeExpectedToken(TokenKind::pu_or));
1227
1228 return Flags;
1229}
1230
1231std::optional<llvm::dxbc::DescriptorRangeFlags>
1232RootSignatureParser::parseDescriptorRangeFlags(TokenKind Context) {
1233 assert(CurToken.TokKind == TokenKind::pu_equal &&
1234 "Expects to only be invoked starting at given keyword");
1235
1236 // Handle the edge-case of '0' to specify no flags set
1237 if (tryConsumeExpectedToken(TokenKind::int_literal)) {
1238 if (!verifyZeroFlag()) {
1239 reportDiag(diag::err_hlsl_rootsig_non_zero_flag);
1240 return std::nullopt;
1241 }
1242 return llvm::dxbc::DescriptorRangeFlags::None;
1243 }
1244
1245 TokenKind Expected[] = {
1246#define DESCRIPTOR_RANGE_FLAG_ENUM(NAME, LIT, ON) TokenKind::en_##NAME,
1247#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1248 };
1249
1250 std::optional<llvm::dxbc::DescriptorRangeFlags> Flags;
1251
1252 do {
1253 if (tryConsumeExpectedToken(Expected)) {
1254 switch (CurToken.TokKind) {
1255#define DESCRIPTOR_RANGE_FLAG_ENUM(NAME, LIT, ON) \
1256 case TokenKind::en_##NAME: \
1257 Flags = maybeOrFlag<llvm::dxbc::DescriptorRangeFlags>( \
1258 Flags, llvm::dxbc::DescriptorRangeFlags::NAME); \
1259 break;
1260#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1261 default:
1262 llvm_unreachable("Switch for consumed enum token was not provided");
1263 }
1264 } else {
1265 consumeNextToken(); // consume token to point at invalid token
1266 reportDiag(diag::err_hlsl_invalid_token)
1267 << /*value=*/1 << /*value of*/ Context;
1268 return std::nullopt;
1269 }
1270 } while (tryConsumeExpectedToken(TokenKind::pu_or));
1271
1272 return Flags;
1273}
1274
1275std::optional<llvm::dxbc::StaticSamplerFlags>
1276RootSignatureParser::parseStaticSamplerFlags(TokenKind Context) {
1277 assert(CurToken.TokKind == TokenKind::pu_equal &&
1278 "Expects to only be invoked starting at given keyword");
1279
1280 // Handle the edge-case of '0' to specify no flags set
1281 if (tryConsumeExpectedToken(TokenKind::int_literal)) {
1282 if (!verifyZeroFlag()) {
1283 reportDiag(diag::err_hlsl_rootsig_non_zero_flag);
1284 return std::nullopt;
1285 }
1286 return llvm::dxbc::StaticSamplerFlags::None;
1287 }
1288
1289 TokenKind Expected[] = {
1290#define STATIC_SAMPLER_FLAG_ENUM(NAME, LIT) TokenKind::en_##NAME,
1291#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1292 };
1293
1294 std::optional<llvm::dxbc::StaticSamplerFlags> Flags;
1295
1296 do {
1297 if (tryConsumeExpectedToken(Expected)) {
1298 switch (CurToken.TokKind) {
1299#define STATIC_SAMPLER_FLAG_ENUM(NAME, LIT) \
1300 case TokenKind::en_##NAME: \
1301 Flags = maybeOrFlag<llvm::dxbc::StaticSamplerFlags>( \
1302 Flags, llvm::dxbc::StaticSamplerFlags::NAME); \
1303 break;
1304#include "clang/Lex/HLSLRootSignatureTokenKinds.def"
1305 default:
1306 llvm_unreachable("Switch for consumed enum token was not provided");
1307 }
1308 } else {
1309 consumeNextToken(); // consume token to point at invalid token
1310 reportDiag(diag::err_hlsl_invalid_token)
1311 << /*value=*/1 << /*value of*/ Context;
1312 return std::nullopt;
1313 }
1314 } while (tryConsumeExpectedToken(TokenKind::pu_or));
1315
1316 return Flags;
1317}
1318
1319std::optional<uint32_t> RootSignatureParser::handleUIntLiteral() {
1320 // Parse the numeric value and do semantic checks on its specification
1321 clang::NumericLiteralParser Literal(
1322 CurToken.NumSpelling, getTokenLocation(CurToken), PP.getSourceManager(),
1323 PP.getLangOpts(), PP.getTargetInfo(), PP.getDiagnostics());
1324 if (Literal.hadError)
1325 return std::nullopt; // Error has already been reported so just return
1326
1327 assert(Literal.isIntegerLiteral() &&
1328 "NumSpelling can only consist of digits");
1329
1330 llvm::APSInt Val(32, /*IsUnsigned=*/true);
1331 if (Literal.GetIntegerValue(Val)) {
1332 // Report that the value has overflowed
1333 reportDiag(diag::err_hlsl_number_literal_overflow)
1334 << /*integer type*/ 0 << /*is signed*/ 0;
1335 return std::nullopt;
1336 }
1337
1338 return Val.getExtValue();
1339}
1340
1341std::optional<int32_t> RootSignatureParser::handleIntLiteral(bool Negated) {
1342 // Parse the numeric value and do semantic checks on its specification
1343 clang::NumericLiteralParser Literal(
1344 CurToken.NumSpelling, getTokenLocation(CurToken), PP.getSourceManager(),
1345 PP.getLangOpts(), PP.getTargetInfo(), PP.getDiagnostics());
1346 if (Literal.hadError)
1347 return std::nullopt; // Error has already been reported so just return
1348
1349 assert(Literal.isIntegerLiteral() &&
1350 "NumSpelling can only consist of digits");
1351
1352 llvm::APSInt Val(32, /*IsUnsigned=*/true);
1353 // GetIntegerValue will overwrite Val from the parsed Literal and return
1354 // true if it overflows as a 32-bit unsigned int
1355 bool Overflowed = Literal.GetIntegerValue(Val);
1356
1357 // So we then need to check that it doesn't overflow as a 32-bit signed int:
1358 int64_t MaxNegativeMagnitude = -int64_t(std::numeric_limits<int32_t>::min());
1359 Overflowed |= (Negated && MaxNegativeMagnitude < Val.getExtValue());
1360
1361 int64_t MaxPositiveMagnitude = int64_t(std::numeric_limits<int32_t>::max());
1362 Overflowed |= (!Negated && MaxPositiveMagnitude < Val.getExtValue());
1363
1364 if (Overflowed) {
1365 // Report that the value has overflowed
1366 reportDiag(diag::err_hlsl_number_literal_overflow)
1367 << /*integer type*/ 0 << /*is signed*/ 1;
1368 return std::nullopt;
1369 }
1370
1371 if (Negated)
1372 Val = -Val;
1373
1374 return int32_t(Val.getExtValue());
1375}
1376
1377std::optional<float> RootSignatureParser::handleFloatLiteral(bool Negated) {
1378 // Parse the numeric value and do semantic checks on its specification
1379 clang::NumericLiteralParser Literal(
1380 CurToken.NumSpelling, getTokenLocation(CurToken), PP.getSourceManager(),
1381 PP.getLangOpts(), PP.getTargetInfo(), PP.getDiagnostics());
1382 if (Literal.hadError)
1383 return std::nullopt; // Error has already been reported so just return
1384
1385 assert(Literal.isFloatingLiteral() &&
1386 "NumSpelling consists only of [0-9.ef+-]. Any malformed NumSpelling "
1387 "will be caught and reported by NumericLiteralParser.");
1388
1389 // DXC used `strtod` to convert the token string to a float which corresponds
1390 // to:
1391 auto DXCSemantics = llvm::APFloat::Semantics::S_IEEEdouble;
1392 auto DXCRoundingMode = llvm::RoundingMode::NearestTiesToEven;
1393
1394 llvm::APFloat Val(llvm::APFloat::EnumToSemantics(DXCSemantics));
1395 llvm::APFloat::opStatus Status(Literal.GetFloatValue(Val, DXCRoundingMode));
1396
1397 // Note: we do not error when opStatus::opInexact by itself as this just
1398 // denotes that rounding occured but not that it is invalid
1399 assert(!(Status & llvm::APFloat::opStatus::opInvalidOp) &&
1400 "NumSpelling consists only of [0-9.ef+-]. Any malformed NumSpelling "
1401 "will be caught and reported by NumericLiteralParser.");
1402
1403 assert(!(Status & llvm::APFloat::opStatus::opDivByZero) &&
1404 "It is not possible for a division to be performed when "
1405 "constructing an APFloat from a string");
1406
1407 if (Status & llvm::APFloat::opStatus::opUnderflow) {
1408 // Report that the value has underflowed
1409 reportDiag(diag::err_hlsl_number_literal_underflow);
1410 return std::nullopt;
1411 }
1412
1413 if (Status & llvm::APFloat::opStatus::opOverflow) {
1414 // Report that the value has overflowed
1415 reportDiag(diag::err_hlsl_number_literal_overflow) << /*float type*/ 1;
1416 return std::nullopt;
1417 }
1418
1419 if (Negated)
1420 Val = -Val;
1421
1422 double DoubleVal = Val.convertToDouble();
1423 double FloatMax = double(std::numeric_limits<float>::max());
1424 if (FloatMax < DoubleVal || DoubleVal < -FloatMax) {
1425 // Report that the value has overflowed
1426 reportDiag(diag::err_hlsl_number_literal_overflow) << /*float type*/ 1;
1427 return std::nullopt;
1428 }
1429
1430 return static_cast<float>(DoubleVal);
1431}
1432
1433bool RootSignatureParser::verifyZeroFlag() {
1434 assert(CurToken.TokKind == TokenKind::int_literal);
1435 auto X = handleUIntLiteral();
1436 return X.has_value() && X.value() == 0;
1437}
1438
1439bool RootSignatureParser::peekExpectedToken(TokenKind Expected) {
1440 return peekExpectedToken(ArrayRef{Expected});
1441}
1442
1443bool RootSignatureParser::peekExpectedToken(ArrayRef<TokenKind> AnyExpected) {
1444 RootSignatureToken Result = Lexer.peekNextToken();
1445 return llvm::is_contained(AnyExpected, Result.TokKind);
1446}
1447
1448bool RootSignatureParser::consumeExpectedToken(TokenKind Expected,
1449 unsigned DiagID,
1450 TokenKind Context) {
1451 if (tryConsumeExpectedToken(Expected))
1452 return false;
1453
1454 // Report unexpected token kind error
1455 DiagnosticBuilder DB = reportDiag(DiagID);
1456 switch (DiagID) {
1457 case diag::err_expected:
1458 DB << Expected;
1459 break;
1460 case diag::err_expected_either:
1461 DB << Expected << Context;
1462 break;
1463 case diag::err_expected_after:
1464 DB << Context << Expected;
1465 break;
1466 default:
1467 break;
1468 }
1469 return true;
1470}
1471
1472bool RootSignatureParser::tryConsumeExpectedToken(TokenKind Expected) {
1473 return tryConsumeExpectedToken(ArrayRef{Expected});
1474}
1475
1476bool RootSignatureParser::tryConsumeExpectedToken(
1477 ArrayRef<TokenKind> AnyExpected) {
1478 // If not the expected token just return
1479 if (!peekExpectedToken(AnyExpected))
1480 return false;
1481 consumeNextToken();
1482 return true;
1483}
1484
1485bool RootSignatureParser::skipUntilExpectedToken(TokenKind Expected) {
1486 return skipUntilExpectedToken(ArrayRef{Expected});
1487}
1488
1489bool RootSignatureParser::skipUntilExpectedToken(
1490 ArrayRef<TokenKind> AnyExpected) {
1491
1492 while (!peekExpectedToken(AnyExpected)) {
1493 if (peekExpectedToken(TokenKind::end_of_stream))
1494 return false;
1495 consumeNextToken();
1496 }
1497
1498 return true;
1499}
1500
1501bool RootSignatureParser::skipUntilClosedParens(uint32_t NumParens) {
1502 TokenKind ParenKinds[] = {
1503 TokenKind::pu_l_paren,
1504 TokenKind::pu_r_paren,
1505 };
1506 while (skipUntilExpectedToken(ParenKinds)) {
1507 consumeNextToken();
1508 if (CurToken.TokKind == TokenKind::pu_r_paren)
1509 NumParens--;
1510 else
1511 NumParens++;
1512 if (NumParens == 0)
1513 return true;
1514 }
1515
1516 return false;
1517}
1518
1519SourceLocation RootSignatureParser::getTokenLocation(RootSignatureToken Tok) {
1520 return Signature->getLocationOfByte(Tok.LocOffset, PP.getSourceManager(),
1521 PP.getLangOpts(), PP.getTargetInfo());
1522}
1523
1525 llvm::dxbc::RootSignatureVersion Version,
1526 StringLiteral *Signature) {
1527 // Construct our identifier
1528 auto [DeclIdent, Found] =
1529 Actions.HLSL().ActOnStartRootSignatureDecl(Signature->getString());
1530 // If we haven't found an already defined DeclIdent then parse the root
1531 // signature string and construct the in-memory elements
1532 if (!Found) {
1533 // Invoke the root signature parser to construct the in-memory constructs
1534 hlsl::RootSignatureParser Parser(Version, Signature,
1535 Actions.getPreprocessor());
1536 if (Parser.parse())
1537 return nullptr;
1538
1539 // Construct the declaration.
1541 Signature->getBeginLoc(), DeclIdent, Parser.getElements());
1542 }
1543
1544 return DeclIdent;
1545}
1546
1547void HandleRootSignatureTarget(Sema &S, StringRef EntryRootSig) {
1548 ASTConsumer *Consumer = &S.getASTConsumer();
1549
1550 // Minimally initalize the parser. This does a couple things:
1551 // - initializes Sema scope handling
1552 // - invokes HLSLExternalSemaSource
1553 // - invokes the preprocessor to lex the macros in the file
1554 std::unique_ptr<Parser> P(new Parser(S.getPreprocessor(), S, true));
1556
1557 bool HaveLexer = S.getPreprocessor().getCurrentLexer();
1558 if (HaveLexer) {
1559 P->Initialize();
1561
1562 // Skim through the file to parse to find the define
1563 while (P->getCurToken().getKind() != tok::eof)
1564 P->ConsumeAnyToken();
1565
1566 HLSLRootSignatureDecl *SignatureDecl =
1569
1570 if (SignatureDecl)
1571 Consumer->HandleTopLevelDecl(DeclGroupRef(SignatureDecl));
1572 else
1573 S.getDiagnostics().Report(diag::err_hlsl_rootsignature_entry)
1574 << EntryRootSig;
1575 }
1576
1577 Consumer->HandleTranslationUnit(S.getASTContext());
1578}
1579
1580} // namespace hlsl
1581} // namespace clang
Token Tok
The Token.
#define X(type, name)
Definition Value.h:97
__device__ double
__device__ float
ASTConsumer - This is an abstract interface that should be implemented by clients that read ASTs.
Definition ASTConsumer.h:34
virtual void HandleTranslationUnit(ASTContext &Ctx)
HandleTranslationUnit - This method is called when the ASTs for entire translation unit have been par...
Definition ASTConsumer.h:67
virtual bool HandleTopLevelDecl(DeclGroupRef D)
HandleTopLevelDecl - Handle the specified top-level declaration.
TranslationUnitDecl * getTranslationUnitDecl() const
DiagnosticBuilder Report(SourceLocation Loc, unsigned DiagID)
Issue the message to the client.
One of these records is kept for each identifier that is lexed.
Parser - This implements a parser for the C family of languages.
Definition Parser.h:171
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
PreprocessorLexer * getCurrentLexer() const
Return the current lexer being lexed from.
void EnterMainSourceFile()
Enter the specified FileID as the main source file, which implicitly adds the builtin defines etc.
HLSLRootSignatureDecl * lookupRootSignatureOverrideDecl(DeclContext *DC) const
std::pair< IdentifierInfo *, bool > ActOnStartRootSignatureDecl(StringRef Signature)
Computes the unique Root Signature identifier from the given signature, then lookup if there is a pre...
void ActOnFinishRootSignatureDecl(SourceLocation Loc, IdentifierInfo *DeclIdent, ArrayRef< hlsl::RootSignatureElement > Elements)
Creates the Root Signature decl of the parsed Root Signature elements onto the AST and push it onto c...
Sema - This implements semantic analysis and AST building for C.
Definition Sema.h:854
Preprocessor & getPreprocessor() const
Definition Sema.h:924
DiagnosticsEngine & getDiagnostics() const
Definition Sema.h:922
ASTContext & getASTContext() const
Definition Sema.h:925
ASTConsumer & getASTConsumer() const
Definition Sema.h:926
SemaHLSL & HLSL()
Definition Sema.h:1455
void ActOnStartOfTranslationUnit()
This is called before the very first declaration in the translation unit is parsed.
Definition Sema.cpp:1165
Encodes a location in the source.
StringLiteral - This represents a string literal expression, e.g.
Definition Expr.h:1799
SourceLocation getBeginLoc() const LLVM_READONLY
Definition Expr.h:1973
StringRef getString() const
Definition Expr.h:1867
bool parse()
Consumes tokens from the Lexer and constructs the in-memory representations of the RootElements.
RootSignatureParser(llvm::dxbc::RootSignatureVersion Version, StringLiteral *Signature, Preprocessor &PP)
uint32_t Literal
Literals are represented as positive integers.
Definition CNFFormula.h:35
void HandleRootSignatureTarget(Sema &S, StringRef EntryRootSig)
IdentifierInfo * ParseHLSLRootSignature(Sema &Actions, llvm::dxbc::RootSignatureVersion Version, StringLiteral *Signature)
static FlagType maybeOrFlag(std::optional< FlagType > Flags, FlagType Flag)
static const TokenKind RootElementKeywords[]
RootSignatureToken::Kind TokenKind
llvm::cl::opt< std::string > Filter
The JSON file list parser is used to communicate input to InstallAPI.
@ Result
The result type of a method or function.
Definition TypeBase.h:905
Visibility
Describes the different kinds of visibility that a declaration may have.
Definition Visibility.h:34
long int64_t