//===--- ParseHLSLRootSignature.h -------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines the RootSignatureParser interface. // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H #define LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H #include "clang/AST/Expr.h" #include "clang/Basic/DiagnosticParse.h" #include "clang/Lex/LexHLSLRootSignature.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/SemaHLSL.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/Frontend/HLSL/HLSLRootSignature.h" namespace clang { namespace hlsl { class RootSignatureParser { public: RootSignatureParser(llvm::dxbc::RootSignatureVersion Version, StringLiteral *Signature, Preprocessor &PP); /// Consumes tokens from the Lexer and constructs the in-memory /// representations of the RootElements. Tokens are consumed until an /// error is encountered or the end of the buffer. /// /// Returns true if a parsing error is encountered. bool parse(); /// Return all elements that have been parsed. ArrayRef getElements() { return Elements; } private: DiagnosticsEngine &getDiags() { return PP.getDiagnostics(); } // All private parse.* methods follow a similar pattern: // - Each method will start with an assert to denote what the CurToken is // expected to be and will parse from that token forward // // - Therefore, it is the callers responsibility to ensure that you are // at the correct CurToken. This should be done with the pattern of: // // if (tryConsumeExpectedToken(RootSignatureToken::Kind)) { // auto ParsedObject = parse.*(); // if (!ParsedObject.has_value()) // return std::nullopt; // ... // } // // or, // // if (consumeExpectedToken(RootSignatureToken::Kind, ...)) // return std::nullopt; // auto ParsedObject = parse.*(); // if (!ParsedObject.has_value()) // return std::nullopt; // ... // // - All methods return std::nullopt if a parsing error is encountered. It // is the callers responsibility to propogate this error up, or deal with it // otherwise // // - An error will be raised if the proceeding tokens are not what is // expected, or, there is a lexing error /// Root Element parse methods: std::optional parseRootFlags(); std::optional parseRootConstants(); std::optional parseRootDescriptor(); std::optional parseDescriptorTable(); std::optional parseDescriptorTableClause(); std::optional parseStaticSampler(); /// Parameter arguments (eg. `bReg`, `space`, ...) can be specified in any /// order and only exactly once. The following methods define a /// `Parsed.*Params` struct to denote the current state of parsed params struct ParsedConstantParams { std::optional Reg; std::optional Num32BitConstants; std::optional Space; std::optional Visibility; }; std::optional parseRootConstantParams(); struct ParsedRootDescriptorParams { std::optional Reg; std::optional Space; std::optional Visibility; std::optional Flags; }; std::optional parseRootDescriptorParams(RootSignatureToken::Kind DescKind, RootSignatureToken::Kind RegType); struct ParsedClauseParams { std::optional Reg; std::optional NumDescriptors; std::optional Space; std::optional Offset; std::optional Flags; }; std::optional parseDescriptorTableClauseParams(RootSignatureToken::Kind ClauseKind, RootSignatureToken::Kind RegType); struct ParsedStaticSamplerParams { std::optional Reg; std::optional Filter; std::optional AddressU; std::optional AddressV; std::optional AddressW; std::optional MipLODBias; std::optional MaxAnisotropy; std::optional CompFunc; std::optional BorderColor; std::optional MinLOD; std::optional MaxLOD; std::optional Space; std::optional Visibility; std::optional Flags; }; std::optional parseStaticSamplerParams(); // Common parsing methods std::optional parseUIntParam(); std::optional parseRegister(); std::optional parseFloatParam(); /// Parsing methods of various enums std::optional parseShaderVisibility(RootSignatureToken::Kind Context); std::optional parseSamplerFilter(RootSignatureToken::Kind Context); std::optional parseTextureAddressMode(RootSignatureToken::Kind Context); std::optional parseComparisonFunc(RootSignatureToken::Kind Context); std::optional parseStaticBorderColor(RootSignatureToken::Kind Context); std::optional parseRootDescriptorFlags(RootSignatureToken::Kind Context); std::optional parseDescriptorRangeFlags(RootSignatureToken::Kind Context); std::optional parseStaticSamplerFlags(RootSignatureToken::Kind Context); /// Use NumericLiteralParser to convert CurToken.NumSpelling into a unsigned /// 32-bit integer std::optional handleUIntLiteral(); /// Use NumericLiteralParser to convert CurToken.NumSpelling into a signed /// 32-bit integer std::optional handleIntLiteral(bool Negated); /// Use NumericLiteralParser to convert CurToken.NumSpelling into a float /// /// This matches the behaviour of DXC, which is as follows: /// - convert the spelling with `strtod` /// - check for a float overflow /// - cast the double to a float /// The behaviour of `strtod` is replicated using: /// Semantics: llvm::APFloat::Semantics::S_IEEEdouble /// RoundingMode: llvm::RoundingMode::NearestTiesToEven std::optional handleFloatLiteral(bool Negated); /// Flags may specify the value of '0' to denote that there should be no /// flags set. /// /// Return true if the current int_literal token is '0', otherwise false bool verifyZeroFlag(); /// Invoke the Lexer to consume a token and update CurToken with the result void consumeNextToken() { CurToken = Lexer.consumeToken(); } /// Return true if the next token one of the expected kinds bool peekExpectedToken(RootSignatureToken::Kind Expected); bool peekExpectedToken(ArrayRef AnyExpected); /// Consumes the next token and report an error if it is not of the expected /// kind. /// /// Returns true if there was an error reported. bool consumeExpectedToken( RootSignatureToken::Kind Expected, unsigned DiagID = diag::err_expected, RootSignatureToken::Kind Context = RootSignatureToken::Kind::invalid); /// Peek if the next token is of the expected kind and if it is then consume /// it. /// /// Returns true if it successfully matches the expected kind and the token /// was consumed. bool tryConsumeExpectedToken(RootSignatureToken::Kind Expected); bool tryConsumeExpectedToken(ArrayRef Expected); /// Consume tokens until the expected token has been peeked to be next /// or we have reached the end of the stream. Note that this means the /// expected token will be the next token not CurToken. /// /// Returns true if it found a token of the given type. bool skipUntilExpectedToken(RootSignatureToken::Kind Expected); bool skipUntilExpectedToken(ArrayRef Expected); /// Consume tokens until we reach a closing right paren, ')', or, until we /// have reached the end of the stream. This will place the current token /// to be the end of stream or the right paren. /// /// Returns true if it is closed before the end of stream. bool skipUntilClosedParens(uint32_t NumParens = 1); /// Convert the token's offset in the signature string to its SourceLocation /// /// This allows to currently retrieve the location for multi-token /// StringLiterals SourceLocation getTokenLocation(RootSignatureToken Tok); /// Construct a diagnostics at the location of the current token DiagnosticBuilder reportDiag(unsigned DiagID) { return getDiags().Report(getTokenLocation(CurToken), DiagID); } private: llvm::dxbc::RootSignatureVersion Version; SmallVector Elements; StringLiteral *Signature; RootSignatureLexer Lexer; Preprocessor &PP; RootSignatureToken CurToken; }; IdentifierInfo *ParseHLSLRootSignature(Sema &Actions, llvm::dxbc::RootSignatureVersion Version, StringLiteral *Signature); void HandleRootSignatureTarget(Sema &S, StringRef EntryRootSig); } // namespace hlsl } // namespace clang #endif // LLVM_CLANG_PARSE_PARSEHLSLROOTSIGNATURE_H