AzslcEmitter.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #pragma once
  9. #include "AzslcBackend.h"
  10. #include "AzslcSymbolTranslation.h"
  11. #include "AzslcCodeEmissionMutator.h"
  12. namespace Json
  13. {
  14. class Value;
  15. }
  16. namespace AZ::ShaderCompiler
  17. {
  18. enum class EmitFunctionAs
  19. {
  20. Declaration,
  21. Definition
  22. };
  23. struct CodeEmitter : Backend
  24. {
  25. CodeEmitter(IntermediateRepresentation* ir, TokenStream* tokens, std::ostream& out, PreprocessorLineDirectiveFinder* lineFinder)
  26. :
  27. Backend(ir, tokens),
  28. m_out(out),
  29. m_lineFinder(lineFinder)
  30. {}
  31. //! Create a companion database of mutations on the IR, through which the emitter backend can query symbols scope and names.
  32. //! The state of changes is stored in the AZ::ShaderCompiler::SymbolTranslation class
  33. //! @param options user configuration parsed from command line
  34. void SetupScopeMigrations(const Options& options);
  35. //! Execute code emission
  36. //! @param options user configuration parsed from command line
  37. void Run(const Options& options);
  38. //! For scope-migration-aware name emission of symbol names
  39. string GetTranslatedName(QualifiedNameView mangledName, UsageContext context, ssize_t tokenId = NotOverToken) const;
  40. string GetTranslatedName(const IdentifierUID& uid, UsageContext context, ssize_t tokenId = NotOverToken) const;
  41. string GetTranslatedName(const TypeRefInfo& typeRef, UsageContext context, ssize_t tokenId = NotOverToken) const;
  42. string GetTranslatedName(const ExtendedTypeInfo& extType, UsageContext context, const Options& options, Modifiers banned = {}, ssize_t tokenId = NotOverToken) const;
  43. //! Write the HLSL formatted shape of an attribute into a stream
  44. static void EmitAttribute(const AttributeInfo& attrInfo, Streamable& outstream);
  45. void SetCodeMutator(ICodeEmissionMutator* codeMutator) { m_codeMutator = codeMutator; }
  46. //! It would be nice that the clients don't push text through the passed "out" stream since it's not observed by the line counter;
  47. //! use this API in case of custom client text pushing.
  48. template< typename Streamable >
  49. CodeEmitter& operator << (Streamable&& s)
  50. {
  51. m_out << s;
  52. return *this;
  53. }
  54. protected:
  55. //! Emits the closest preprocessor generated "#line <int> <filepath>" directive located before
  56. //! @originalLineNumber. Keeps track of the best finds so the #line directives are not emitted more than once.
  57. void EmitPreprocessorLineDirective(size_t originalLineNumber);
  58. //! Emits the closest preprocessor generated "#line <int> <filepath>" directive located near
  59. //! @symbolName. See above, EmitPreprocessorLineDirective (size_t), for more details.
  60. void EmitPreprocessorLineDirective(QualifiedNameView symbolName);
  61. void EmitStruct(const ClassInfo& classInfo, string_view structName, const Options& options);
  62. void EmitAttribute(const AttributeInfo& attrInfo) const;
  63. void EmitFunction(const FunctionInfo& funcSub, const IdentifierUID& id, EmitFunctionAs entityConfiguration, const Options& options);
  64. void EmitTypeAlias(const IdentifierUID& uid, const TypeAliasInfo& aliasInfo, const Options& options) const;
  65. void EmitEnum(const IdentifierUID& uid, const ClassInfo& classInfo, const Options& options);
  66. //! Emits root constants
  67. void EmitRootConstants(const RootSigDesc& rootSig, const Options& options) const;
  68. //! Emits get function declarations for root constant members
  69. void EmitGetterFunctionDeclarationsForRootConstants(const IdentifierUID& uid) const;
  70. struct Except : std::initializer_list<string>
  71. {};
  72. //! Emit all attributes accumulated over a symbol. Omit an optional list of attributes passed as 2nd argument.
  73. void EmitAllAttachedAttributes(const IdentifierUID& uid, Except = {}) const;
  74. //! Emits get function definitions for root constants
  75. void EmitGetFunctionsForRootConstants(const ClassInfo& classInfo, string_view bufferName) const;
  76. //! That is a list of code elements we possibly want to emit (e.g when we emit a variable declaration)
  77. MAKE_REFLECTABLE_ENUM_POWER( VarDeclHas,
  78. InOutModifiers, // when we emit in the context of function parameters, HLSL in out keywords are important
  79. HlslSemantics, // : TEXCOORD sort of element
  80. Initializer, // = val; sort of element
  81. OptionDefine, // an option define is not a code element but an indicator that we're emitting a shader option
  82. NoType, // in the usual declaration 'Type varname', omit the type. this is used for enumerators
  83. NoModifiers // modifiers are storage flags (const, precise, rowmajor...)
  84. );
  85. using VarDeclHasFlag = Flag<VarDeclHas>;
  86. void EmitVariableDeclaration(const VarInfo&, const IdentifierUID& uid, const Options& options, VarDeclHasFlag declOptions) const;
  87. //! Iter must be an iterator over FunctionInfo::Parameter elements
  88. template <typename Iter>
  89. void EmitParameters(Iter begin, Iter end, const Options& options, bool withInitializer)
  90. {
  91. for (auto it = begin;
  92. it != end;
  93. ++it)
  94. {
  95. const FunctionInfo::Parameter& param = *it;
  96. auto* varInfo = m_ir->GetSymbolSubAs<VarInfo>(param.m_varId.GetName());
  97. if (varInfo)
  98. {
  99. auto flag = VarDeclHasFlag(VarDeclHas::InOutModifiers) | VarDeclHas::HlslSemantics;
  100. flag |= withInitializer ? VarDeclHas::Initializer : VarDeclHas::EnumType(0);
  101. EmitVariableDeclaration(*varInfo, param.m_varId, options, flag);
  102. }
  103. else
  104. {
  105. m_out << GetInputModifier(param.m_typeInfo.m_qualifiers) << " ";
  106. m_out << GetTranslatedName(param.m_typeInfo, UsageContext::ReferenceSite, options);
  107. if (!param.m_arrayRankSpecifiers.empty())
  108. {
  109. for (auto* rankCtx : param.m_arrayRankSpecifiers)
  110. {
  111. EmitTranspiledTokens(rankCtx->getSourceInterval());
  112. }
  113. }
  114. if (auto* semantic = param.m_semanticCtx)
  115. {
  116. m_out << " " + semantic->getText();
  117. }
  118. if (param.m_defaultValueExpression)
  119. {
  120. EmitTranspiledTokens(param.m_defaultValueExpression->getSourceInterval());
  121. }
  122. }
  123. bool lastIteration = it + 1 == end;
  124. if (!lastIteration)
  125. {
  126. m_out << ", ";
  127. }
  128. }
  129. }
  130. //! NotOverToken is for procedurally generated code, that doesn't have an original source in terms of token.
  131. static const ssize_t NotOverToken = -1;
  132. void EmitSRGCBUnified(const SRGInfo& srgInfo, IdentifierUID srgId, const Options& options, const RootSigDesc& rootSig);
  133. void EmitSRGCB(const IdentifierUID& cId, const Options& options, const RootSigDesc& rootSig) const;
  134. void EmitSRGSampler(const IdentifierUID& sId, const Options& options, const RootSigDesc& rootSig) const;
  135. void EmitSRGDataView(const IdentifierUID& tId, const Options& options, const RootSigDesc& rootSig) const;
  136. void EmitGetShaderKeyFunctionDeclaration(const IdentifierUID& getterUid, const TypeRefInfo& returnType) const;
  137. void EmitGetShaderKeyFunction(const IdentifierUID& shaderKeyUid, const IdentifierUID& getterUid, uint32_t size, uint32_t offset, string_view defaultValue, const TypeRefInfo& returnType) const;
  138. //! Will emit SRG content in the shape of HLSL transformed resource, e.g a constant buffer struct for the SRG variables.
  139. void EmitSRG(const SRGInfo& srgInfo, const IdentifierUID& srgId, const Options& options, const RootSigDesc& rootSig);
  140. //! Advanced logic (targeted transpilation transforms included) interval-as-text extractor from source token stream
  141. //! Will copy function body original tokens, skipping comments, reformatting if possible, and translating variable declarations when needed, as well as mutating reference names of migrated SRG contents.
  142. void EmitTranspiledTokens(misc::Interval interval, Streamable& output) const override;
  143. void EmitTranspiledTokens(misc::Interval interval) const { EmitTranspiledTokens(interval, m_out); }
  144. //! Move a symbol to a different scope. Currently used to strip SRGs of their symbols, so that SRGs are effectively erased.
  145. void MigrateASTSubTree(const IdentifierUID& azslSymbol, QualifiedNameView landingScope);
  146. //! Verifies if a symbol is in the global scope (in the IR "viewed" through the translation)
  147. bool IsTopLevelThroughTranslation(const IdentifierUID& uid) const;
  148. //! Emits a single shader option variable as a static const
  149. void EmitShaderVariantOptionVariableDeclaration(const IdentifierUID& symbolUid, const Options& options) const;
  150. //! Emits all shader option variable fallback getters
  151. void EmitShaderVariantOptionGetters(const Options& options) const;
  152. //! Stateful check to normalize redundant declarations
  153. bool AlreadyEmittedFunctionDeclaration(const IdentifierUID& uid) const;
  154. bool AlreadyEmittedFunctionDefinition(const IdentifierUID& uid) const;
  155. //! Helper function used during GetTextInStream().
  156. //! The function assumes @nodeFromToken comes from @token.
  157. //! It checks whether the token that is about to be written to the output
  158. //! is not an undefined SRG field/variable.
  159. //! Throws an exception if it is an undefined ShaderResourceGroup field/variable.
  160. void IfIsSrgMemberValidateIsDefined(antlr4::Token* token, TokenToAst::AstNode* nodeFromToken) const;
  161. SymbolTranslation m_translations;
  162. unordered_set<IdentifierUID> m_alreadyEmittedFunctionDeclarations;
  163. unordered_set<IdentifierUID> m_alreadyEmittedFunctionDefinitions;
  164. map<size_t, size_t> m_alreadyEmittedPreprocessorLineDirectives;
  165. IdentifierUID m_shaderVariantFallbackUid;
  166. //! If not null it will be used during code emission to produce
  167. //! the mutations.
  168. ICodeEmissionMutator* m_codeMutator = nullptr;
  169. //! We keep track here of the number of lines that have been emitted.
  170. //! Each symbol has an original line number (virtual and physical) where it appeared,
  171. //! and emission will also have line directives to remap errors from further tools to the original azsl.
  172. //! To avoid spamming the output with line directives, we can keep track of whether a deviation
  173. //! has been introduced since the last emitted line directive and the desired virtual line of the currently emitted code construct.
  174. mutable NewLineCounterStream m_out;
  175. PreprocessorLineDirectiveFinder* m_lineFinder;
  176. //! This is a readability function for class emission code. Serves for HLSL declarator of classes
  177. string EmitInheritanceList(const ClassInfo& clInfo);
  178. };
  179. }