AzslcUtils.h 57 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325
  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 "AzslcMangling.h"
  10. #include "AzslcCommon.h"
  11. #include "AzslcException.h"
  12. #include "DiagnosticStream.h"
  13. #include "antlr4-runtime.h"
  14. #include "generated/azslLexer.h"
  15. #include "generated/azslParserBaseListener.h"
  16. #include "generated/azslParser.h"
  17. #include "AzslcPredefinedTypes.h"
  18. #include "ReflectableEnums.h"
  19. #include "ReflectableEnumsUtils.h"
  20. #if defined(_WIN32) || defined(_WIN64)
  21. # define AzslcStrnicmp _strnicmp
  22. #elif defined(__APPLE__)
  23. # define AzslcStrnicmp strncasecmp
  24. #elif defined (__linux__) || defined(__unix__)
  25. # include <strings.h>
  26. # define AzslcStrnicmp strncasecmp
  27. #endif
  28. namespace AZ::ShaderCompiler
  29. {
  30. extern DiagnosticStream verboseCout;
  31. extern DiagnosticStream warningCout;
  32. extern Endl azEndl;
  33. using AstType = azslParser::TypeContext; // all usertypes and predefined, but cannot be Void
  34. using AstTypeofNode = azslParser::TypeofExpressionContext;
  35. using AstPredefinedTypeNode = azslParser::PredefinedTypeContext;
  36. using AstClassDeclNode = azslParser::ClassDefinitionContext;
  37. using AstStructDeclNode = azslParser::StructDefinitionContext;
  38. using AstEnumDeclNode = azslParser::EnumDefinitionContext;
  39. using AstInterfaceDeclNode = azslParser::InterfaceDefinitionContext;
  40. using AstSRGSemanticDeclNode = azslParser::SrgSemanticContext;
  41. using AstSRGSemanticMemberDeclNode = azslParser::SrgSemanticMemberDeclarationContext;
  42. using AstSRGDeclNode = azslParser::SrgDefinitionContext;
  43. using AstSRGMemberNode = azslParser::SrgMemberDeclarationContext;
  44. using AstNamedVarDecl = azslParser::NamedVariableDeclaratorContext;
  45. using AstUnnamedVarDecl = azslParser::UnnamedVariableDeclaratorContext;
  46. using AstFuncSig = azslParser::LeadingTypeFunctionSignatureContext;
  47. using AstIdExpr = azslParser::IdExpressionContext;
  48. using AstExpr = azslParser::ExpressionContext;
  49. using AstMemberAccess = azslParser::MemberAccessExpressionContext;
  50. using AstVarInitializer = azslParser::VariableInitializerContext;
  51. inline constexpr ssize_t operator ""_ssz(unsigned long long n)
  52. {
  53. return n;
  54. }
  55. inline bool EqualNoCase(string_view str1, string_view str2)
  56. {
  57. if (str1.length() != str2.length())
  58. {
  59. return false;
  60. }
  61. return AzslcStrnicmp(str1.data(), str2.data(), str2.length()) == 0;
  62. }
  63. inline bool StartsWithNoCase(string_view str1, string_view str2)
  64. {
  65. if (str1.length() < str2.length())
  66. {
  67. return false;
  68. }
  69. return AzslcStrnicmp(str1.data(), str2.data(), str2.length()) == 0;
  70. }
  71. // this format is the Microsoft standard for error list parsing "file(line,column): message"
  72. inline string DiagLine(size_t line)
  73. {
  74. using namespace std::string_literals;
  75. return AzslcException::s_lineFinder->GetVirtualFileName(line) + "("s + std::to_string(line) + "):";
  76. }
  77. inline string DiagLine(optional<int> line)
  78. {
  79. return line ? DiagLine(static_cast<size_t>(*line)) : string{};
  80. }
  81. inline string DiagLine(optional<size_t> line)
  82. {
  83. return line ? DiagLine(*line) : string{};
  84. }
  85. inline string DiagLine(antlr4::Token* token)
  86. {
  87. return DiagLine(token->getLine());
  88. }
  89. inline string DiagLine(tree::TerminalNode* astNode)
  90. {
  91. return DiagLine(astNode->getSymbol());
  92. }
  93. //! low level version with everything parameterizable
  94. template<typename... Types>
  95. inline void PrintWarning(DiagnosticStream& stream, Warn::EnumType level, optional<size_t> lineNumber, optional<size_t> column, Types&&... messageBits)
  96. {
  97. stream << PushLevel{} << level
  98. << AzslcException::MakeErrorMessage(lineNumber ? AzslcException::s_lineFinder->GetVirtualFileName(*lineNumber) : "",
  99. lineNumber ? ToString(AzslcException::s_lineFinder->GetVirtualLineNumber(*lineNumber)) : "", column ? ToString(*column) : "",
  100. "", false, "", ConcatString(messageBits..., "\n"))
  101. << PopLevel{};
  102. }
  103. //! version for clients with only, maybe, a line number
  104. template<typename... Types>
  105. inline void PrintWarning(Warn::EnumType level, optional<size_t> line, Types&&... messageBits)
  106. {
  107. PrintWarning(warningCout, level, line, none, messageBits...);
  108. }
  109. //! version for clients with a token (richest, preferred way)
  110. template<typename... Types>
  111. inline void PrintWarning(Warn::EnumType level, antlr4::Token* token, Types&&... messageBits)
  112. {
  113. PrintWarning(warningCout, level, token->getLine(), token->getCharPositionInLine() + 1, messageBits...);
  114. }
  115. inline AstTypeofNode* ExtractTypeofAstNode(AstType* ctx)
  116. {
  117. return ctx->typeofExpression();
  118. }
  119. template<typename AnyOther>
  120. inline AstTypeofNode* ExtractTypeofAstNode(AnyOther*)
  121. {
  122. return nullptr;
  123. }
  124. using ConstNumericVal = variant<monostate, int32_t, uint32_t, float>;
  125. //! Extracts <float> from <ConstNumericVal>. When not possible it will throw if <defval> is not set or fall back to <defval> if set.
  126. inline float ExtractValueAsFloat(const ConstNumericVal& var, optional<float> defval = none)
  127. {
  128. if (holds_alternative<monostate>(var))
  129. {
  130. if (defval == none)
  131. {
  132. throw std::logic_error{ "Constant value did not hold anything. Set defval if you want a fallback option." };
  133. }
  134. else
  135. {
  136. return *defval;
  137. }
  138. }
  139. else if (holds_alternative<int32_t>(var))
  140. {
  141. auto intVal = get<int32_t>(var);
  142. // Casting to float has precision loss: https://onlinegdb.com/By0AJTUVE
  143. if (intVal > 16777216 || intVal < -16777216)
  144. {
  145. PrintWarning(Warn::W3, none, "warning: Casting integer ", intVal, " to float, will result in ", static_cast<float>(intVal));
  146. }
  147. return static_cast<float>(intVal);
  148. }
  149. else if (holds_alternative<uint32_t>(var))
  150. {
  151. auto uintVal = get<uint32_t>(var);
  152. // Same comment
  153. if (uintVal > 16777216)
  154. {
  155. PrintWarning(Warn::W3, none, "warning: Casting integer ", uintVal, " to float, will result in ", static_cast<float>(uintVal));
  156. }
  157. return static_cast<float>(uintVal);
  158. }
  159. assert(holds_alternative<float>(var));
  160. return get<float>(var);
  161. }
  162. inline int64_t ExtractValueAsInt64(const ConstNumericVal& var, optional<int64_t> defval = none)
  163. {
  164. if (holds_alternative<monostate>(var))
  165. {
  166. if (defval == none)
  167. {
  168. throw std::logic_error{ "Constant value did not hold anything. Set defval if you want a fallback option." };
  169. }
  170. else
  171. {
  172. return *defval;
  173. }
  174. }
  175. else if (holds_alternative<float>(var))
  176. {
  177. // Casting from float has precision loss: https://onlinegdb.com/By0AJTUVE
  178. auto floatVal = get<float>(var);
  179. auto intVal = static_cast<int64_t>(floatVal);
  180. PrintWarning(Warn::W3, none, "warning: Casting float ", floatVal, " to integer, will result in ", intVal);
  181. return intVal;
  182. }
  183. else if (holds_alternative<int32_t>(var))
  184. {
  185. return get<int32_t>(var);
  186. }
  187. return static_cast<int64_t>(get<uint32_t>(var));
  188. }
  189. template<class T>
  190. inline T ExtractValueAs(const ConstNumericVal& var, optional<T> defval = none)
  191. {
  192. if (holds_alternative<monostate>(var))
  193. {
  194. if (defval == none)
  195. {
  196. throw std::logic_error{ "Constant value did not hold anything. Set defval if you want a fallback option." };
  197. }
  198. else
  199. {
  200. return *defval;
  201. }
  202. }
  203. else if (holds_alternative<float>(var))
  204. {
  205. // Casting from float has precision loss: https://onlinegdb.com/By0AJTUVE
  206. auto floatVal = get<float>(var);
  207. auto intVal = static_cast<int64_t>(floatVal);
  208. PrintWarning(Warn::W3, none, "warning: Casting float ", floatVal, " to integer, will result in ", intVal);
  209. return static_cast<T>(intVal);
  210. }
  211. else if (holds_alternative<int32_t>(var))
  212. {
  213. return static_cast<T>(get<int32_t>(var));
  214. }
  215. return static_cast<T>(get<uint32_t>(var));
  216. }
  217. //! Safe way to call ExtractValueAsInt64 which returns false instead of throwing
  218. inline bool TryGetConstExprValueAsInt64(const ConstNumericVal& foldedIdentifier, int64_t& returnValue) noexcept(true)
  219. {
  220. if (holds_alternative<monostate>(foldedIdentifier))
  221. {
  222. return false;
  223. }
  224. returnValue = ExtractValueAsInt64(foldedIdentifier); // Won't throw because we have already checked holds_alternative<monostate>
  225. return true;
  226. }
  227. MAKE_REFLECTABLE_ENUM_POWER (StorageFlag,
  228. Static, Const, Unsigned, RowMajor, ColumnMajor, Extern, Inline, Rootconstant, Option, Precise, Groupshared, Uniform, Volatile, Globallycoherent, In, Out, InOut, Enumerator, Other
  229. );
  230. inline Streamable& operator << (Streamable& out, StorageFlag::EnumType sf)
  231. {
  232. return out << ToLower(StorageFlag::ToStr(sf));
  233. }
  234. using Modifiers = Flag<StorageFlag>;
  235. struct TypeQualifiers
  236. {
  237. Modifiers m_flag;
  238. vector<string> m_others; // For qualifiers we didn't add to the enum
  239. string GetDisplayName() const
  240. {
  241. vector<StorageFlag> bag;
  242. auto end = std::copy_if(StorageFlag::Enumerate{}.begin(), StorageFlag::Enumerate{}.end(), std::back_inserter(bag),
  243. [&](auto sf) -> bool { return (m_flag & sf) && (sf & ~StorageFlag::Other); });
  244. // Join will call operator<< on StorageFlag::EnumType for stringification
  245. return string{Trim(Join(bag.begin(), bag.end(), " ") + " " + Join(m_others.begin(), m_others.end(), " "))};
  246. }
  247. void OrMerge(const TypeQualifiers& src)
  248. {
  249. m_flag |= src.m_flag;
  250. StableMerge(m_others, src.m_others); // stable is key to not disturb emission tests accross platforms that could hash differently if using unordered_sets.
  251. }
  252. };
  253. struct ArrayDimensions
  254. {
  255. const bool IsArray() const
  256. {
  257. return !m_dimensions.empty();
  258. }
  259. const bool IsUnbounded() const
  260. {
  261. // For now m_tex[][4][5] won't be supported.
  262. // But m_tex[] is supported.
  263. return (m_dimensions.size() == 1) && (m_dimensions[0] == Unbounded);
  264. }
  265. //! Have all dimensions been statically resolved ?
  266. const bool AreAllDimsFullyConstantFolded() const
  267. {
  268. return std::all_of(m_dimensions.begin(), m_dimensions.end(), [](int d) { return (d >= 0); });
  269. }
  270. int GetDimensionAt_OrDefault(int dimensionIndex, int defaultValueIfNoSuchDim) const
  271. {
  272. return m_dimensions.size() > dimensionIndex ? m_dimensions[dimensionIndex] : defaultValueIfNoSuchDim;
  273. }
  274. //! pretty print a representation of an array variable declarator suffix in C-like form
  275. //! {} is ""
  276. //! {unbounded} is "[]"
  277. //! {unknown} is "[?]"
  278. //! {1,2} is "[1][2]"
  279. string ToString(const string& prefix = "[", const string& separator = "][", const string& suffix = "]") const
  280. {
  281. vector<string> asStrs;
  282. TransformCopy(m_dimensions, asStrs,
  283. [](int d) -> string { return d == Unknown ? "<unrecognized-expr>" : d == Unbounded ? "" : std::to_string(d); });
  284. return asStrs.empty() ? "" : prefix + Join(asStrs.begin(), asStrs.end(), separator) + suffix;
  285. }
  286. void PushBack(int newDimension)
  287. {
  288. m_dimensions.push_back(newDimension);
  289. }
  290. void Clear()
  291. {
  292. m_dimensions.clear();
  293. }
  294. bool Empty() const
  295. {
  296. return m_dimensions.empty();
  297. }
  298. auto begin() const
  299. {
  300. return m_dimensions.begin();
  301. }
  302. auto end() const
  303. {
  304. return m_dimensions.end();
  305. }
  306. friend bool operator == (const ArrayDimensions& lhs, const ArrayDimensions& rhs)
  307. {
  308. return lhs.m_dimensions == rhs.m_dimensions;
  309. }
  310. friend bool operator != (const ArrayDimensions& lhs, const ArrayDimensions& rhs)
  311. {
  312. return !operator==(lhs,rhs);
  313. }
  314. //! value taken by dimensions that couldn't be constant-folded (variable initializer)
  315. static constexpr int Unknown = -1;
  316. //! dimension with an empty bracket []
  317. static constexpr int Unbounded = -2;
  318. //! multi array dimensions. e.g. a[2][3] will be a vector of {2,3}
  319. //! a[][3] (or a[var][3]) will be a vector of {unknown,3}
  320. //! (note: if `var` is a statically foldable const expression, then it has a chance to resolved to its initial value)
  321. //! empty {} means "not an array"
  322. vector<int> m_dimensions;
  323. };
  324. //! OutputFormat
  325. //! Specifies the pixel output format hint for the render target
  326. //!
  327. //! None - hints that the target is unused
  328. //! R32 - one channel (R), can be Float32, Uint32 or Sint32
  329. //! R32G32 - two channels (RG), can be Float32, Uint32 or Sint32
  330. //! R32A32- two channels (RA), can be Float32, Uint32 or Sint32
  331. //! R16G16B16A16_FLOAT - Default. Four or less channels (RGBA), Float16
  332. //! R16G16B16A16_UNORM - four or less channels (RGBA), Unorm16
  333. //! R16G16B16A16_SNORM - four or less channels (RGBA), Snorm16
  334. //! R16G16B16A16_UINT - four or less channels (RGBA), Uint16
  335. //! R16G16B16A16_SINT - four or less channels (RGBA), Sint16
  336. //! R32G32B32A32 - four channels (RGBA), can be Float32, Uint32 or Sint32
  337. MAKE_REFLECTABLE_ENUM(OutputFormat,
  338. None, R32, R32G32, R32A32, R16G16B16A16_FLOAT, R16G16B16A16_UNORM, R16G16B16A16_SNORM, R16G16B16A16_UINT, R16G16B16A16_SINT, R32G32B32A32
  339. );
  340. namespace Packing
  341. {
  342. constexpr uint32_t s_bytesPerRegister = 16;
  343. constexpr uint32_t s_bytesPerComponent = 4;
  344. enum class Layout : uint32_t
  345. {
  346. CStylePacking, //!< Dense packing with no padding. Also known as Scalar. Default for structured buffers in DirectX.
  347. DirectXPacking, //!< DirectX style packing, default for cbuffer layouts in DirectX
  348. RelaxedDirectXPacking, //!< As DirectX standard, but assumes dense array packing and invariant to row-column major (best fit)
  349. RelaxedStd140Packing, //!< Vector-relaxed OpenGL std140, default for Uniform buffer packing for Vulkan (base alignment)
  350. RelaxedStd430Packing, //!< Vector-relaxed OpenGL std430, default for Storage buffer packing for Vulkan (scalar alignment)
  351. StrictStd140Packing, //!< Strict OpenGL std140, default for Uniform buffer packing for OpenGL
  352. StrictStd430Packing, //!< Strict OpenGL std430, default for Storage buffer packing for OpenGL
  353. DirectXStoragePacking = CStylePacking, //!< DirectX style packing for Storage buffers. That's actually Scalar
  354. };
  355. //! Used together with Layout. Defines what alignment rules the next chunk should follow
  356. enum class Alignment : uint32_t
  357. {
  358. asVectorStart,
  359. asVectorEnd,
  360. asMatrixStart,
  361. asMatrixEnd,
  362. asStructStart,
  363. asStructEnd,
  364. asArrayStart,
  365. asArrayEnd,
  366. };
  367. // Some standards (std140/std430) have extended alignment rules for structures, which dictate that
  368. // the alignment inside a struct behaves like base rather than scalar.
  369. // For all other cases the alignment doesn't change so we can return the input.
  370. static Layout GetExtendedLayout(const Layout& scalarLayout)
  371. {
  372. if (scalarLayout == Layout::RelaxedStd430Packing)
  373. {
  374. return Layout::RelaxedStd140Packing;
  375. }
  376. else if (scalarLayout == Layout::StrictStd430Packing)
  377. {
  378. return Layout::StrictStd140Packing;
  379. }
  380. return scalarLayout;
  381. }
  382. static uint32_t AlignUp(const uint32_t value, const uint32_t alignment)
  383. {
  384. if (alignment <= 1)
  385. {
  386. return value;
  387. }
  388. uint32_t mask = alignment - 1;
  389. return (value + mask) & ~mask;
  390. }
  391. static uint32_t AlignStructToLargestMember(Layout layout, uint32_t currentSize, uint32_t memberSize)
  392. {
  393. if (memberSize == 0)
  394. {
  395. return currentSize;
  396. }
  397. if (layout == Layout::RelaxedStd140Packing ||
  398. layout == Layout::RelaxedStd430Packing ||
  399. layout == Layout::StrictStd140Packing ||
  400. layout == Layout::StrictStd430Packing)
  401. {
  402. // Alignment starts as s_bytesPerComponent, can only double (power of two) and cannot exceed s_bytesPerRegister
  403. auto alignment = s_bytesPerComponent;
  404. while (alignment < memberSize && alignment < s_bytesPerRegister)
  405. {
  406. alignment *= 2;
  407. }
  408. return AlignUp(currentSize, alignment);
  409. }
  410. return currentSize;
  411. }
  412. // Checks if the parameters correspond to either a Vector or a Matrix collapsed to a Vector.
  413. static bool IsVectorAligned(const Alignment& alignment, uint32_t rows, uint32_t cols)
  414. {
  415. if (alignment == Alignment::asVectorStart ||
  416. alignment == Alignment::asVectorEnd)
  417. {
  418. return true;
  419. }
  420. if (alignment == Alignment::asMatrixStart ||
  421. alignment == Alignment::asMatrixEnd)
  422. {
  423. if (rows <= 1 || cols <= 1)
  424. {
  425. return true;
  426. }
  427. }
  428. return false;
  429. }
  430. //! Aligns the offset for the next structure
  431. //! Vulkan : https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#interfaces-resources-layout
  432. //! https://github.com/microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst#memory-layout-rules
  433. //! OpenGL : https://www.khronos.org/registry/OpenGL/specs/gl/glspec45.core.pdf#page=159
  434. //! https://github.com/Microsoft/DirectXShaderCompiler/blob/master/docs/SPIR-V.rst
  435. static uint32_t AlignOffset(Layout layout, uint32_t currentSize, const Alignment& alignment, uint32_t rows, uint32_t cols)
  436. {
  437. switch (layout)
  438. {
  439. case Layout::CStylePacking:
  440. return currentSize;
  441. case Layout::DirectXPacking:
  442. case Layout::RelaxedDirectXPacking:
  443. if (alignment == Alignment::asMatrixStart ||
  444. alignment == Alignment::asArrayStart ||
  445. alignment == Alignment::asStructStart)
  446. {
  447. return AlignUp(currentSize, s_bytesPerRegister);
  448. }
  449. // Vectors, matrix endings or array endings don't require special alignment
  450. return currentSize;
  451. case Layout::RelaxedStd140Packing:
  452. if (alignment == Alignment::asArrayStart ||
  453. alignment == Alignment::asStructStart ||
  454. alignment == Alignment::asArrayEnd ||
  455. alignment == Alignment::asStructEnd )
  456. {
  457. return AlignUp(currentSize, s_bytesPerRegister);
  458. }
  459. if (IsVectorAligned(alignment, rows, cols))
  460. {
  461. // Matrices with only one row or only one column collapse to vectors and don't require alignment if the size fits the remaining register
  462. // const auto elements = std::max(rows, cols);
  463. // According to the base alignment packing rules vectors with 3 elements align as 4-element vectors with strict std140.
  464. // dxc doesn't seem to follow that standard with relaxed std140.
  465. return currentSize;
  466. }
  467. if (alignment == Alignment::asMatrixStart ||
  468. alignment == Alignment::asMatrixEnd)
  469. {
  470. return AlignUp(currentSize, s_bytesPerRegister);
  471. }
  472. return currentSize;
  473. case Layout::RelaxedStd430Packing:
  474. case Layout::StrictStd140Packing:
  475. case Layout::StrictStd430Packing:
  476. // The relaxed std430 (= scalar packing) for Vulkan doesn't dictate any special rules for matrix, array or struct
  477. // start or end decorations, but individual case are handled uniquely
  478. // dxc is also not without exceptions: https://github.com/microsoft/DirectXShaderCompiler/issues/2639
  479. // so fully handling complex structs might take more effort
  480. return currentSize;
  481. default:
  482. throw std::runtime_error{ "PackNextChunk: Unknown format should be handled properly!" };
  483. break;
  484. }
  485. return 0; // Prevents warning C4715
  486. }
  487. //! Packs the next chunk of data to the current offset and returns the new offset
  488. //! Alignment - dictates the alignment rules to follow for packing nextChunkSize
  489. static uint32_t PackNextChunk(Layout layout, uint32_t nextChunkSize, uint32_t& offset)
  490. {
  491. switch (layout)
  492. {
  493. case Layout::CStylePacking:
  494. return offset + nextChunkSize;
  495. case Layout::DirectXPacking:
  496. case Layout::RelaxedDirectXPacking:
  497. case Layout::RelaxedStd140Packing:
  498. case Layout::StrictStd140Packing:
  499. {
  500. // Rule - sizes within 16 bytes should not cross the 16-byte boundary
  501. const auto remaining = offset % s_bytesPerRegister;
  502. if (remaining > 0 && remaining + nextChunkSize > s_bytesPerRegister)
  503. {
  504. offset = AlignUp(offset, s_bytesPerRegister);
  505. }
  506. return offset + nextChunkSize;
  507. }
  508. case Layout::RelaxedStd430Packing:
  509. case Layout::StrictStd430Packing:
  510. return offset + nextChunkSize;
  511. default:
  512. throw std::runtime_error{ "PackNextChunk: Unknown format should be handled properly!" };
  513. break;
  514. }
  515. return 0; // Prevents warning C4715
  516. }
  517. //! Packs the base size into an array of certain dimensions
  518. //! Dimensions can be 0, in which case it returns the baseSize, because there is no array rules to follow
  519. //! out Alignment - The minimum alignment required to pack this structure
  520. static uint32_t PackIntoArray(Layout layout, uint32_t baseSize, const ArrayDimensions& dimensions)
  521. {
  522. if (dimensions.Empty())
  523. {
  524. return baseSize;
  525. }
  526. // Sanity check. We can't do this earlier as some array dimensions are not resolvable at compile time,
  527. // but still valid at runtime.
  528. if (!dimensions.AreAllDimsFullyConstantFolded())
  529. {
  530. throw std::runtime_error{"Layout packing failed for unresolved array dimensions. Please check previous warnings!"};
  531. }
  532. switch (layout)
  533. {
  534. case Layout::CStylePacking:
  535. case Layout::RelaxedDirectXPacking:
  536. case Layout::RelaxedStd430Packing:
  537. case Layout::StrictStd430Packing:
  538. case Layout::StrictStd140Packing:
  539. {
  540. uint32_t totalElements = 1;
  541. for (auto dimension : dimensions)
  542. {
  543. totalElements *= dimension;
  544. }
  545. return baseSize * totalElements;
  546. }
  547. case Layout::DirectXPacking:
  548. {
  549. // DirectX elements always start on a new register
  550. const auto unalignedSize = baseSize;
  551. baseSize = AlignUp(baseSize, s_bytesPerRegister);
  552. uint32_t totalElements = 1;
  553. for (auto dimension : dimensions)
  554. {
  555. totalElements *= dimension;
  556. }
  557. return baseSize * (totalElements - 1) + unalignedSize; // The last element doesn't occupy its full register
  558. }
  559. case Layout::RelaxedStd140Packing:
  560. {
  561. baseSize = AlignUp(baseSize, s_bytesPerRegister);
  562. uint32_t totalElements = 1;
  563. for (auto dimension : dimensions)
  564. {
  565. totalElements *= dimension;
  566. }
  567. return baseSize * totalElements;
  568. }
  569. default:
  570. throw std::runtime_error{ "PackNextChunk: Unknown format should be handled properly!" };
  571. break;
  572. }
  573. return 0; // Prevents warning C4715
  574. }
  575. //! Packs the base size as a vector or a matrix
  576. //! Rows and/or cols can be 0. However, if Rows is greater than 0, Cols cannot be 0.
  577. //! Note! Regardless of major, matrices are defined as rows-by-columns! Rows == 0 means it's not a matrix
  578. static uint32_t PackAsVectorMatrix(Layout layout, uint32_t baseSize, uint32_t rows, uint32_t cols, bool rowMajor)
  579. {
  580. if (cols <= 1 && rows <= 1)
  581. { // This is neither - return
  582. return baseSize;
  583. }
  584. // Sanity check - if it's a matrix then rows > 0 and cols must be in the [1..4] range
  585. if (rows > 0)
  586. {
  587. // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-per-component-math
  588. assert(rows <= 4);
  589. assert(cols >= 1 && cols <= 4);
  590. }
  591. switch (layout)
  592. {
  593. case Layout::CStylePacking:
  594. return baseSize
  595. * (cols == 0 ? 1 : cols)
  596. * (rows == 0 ? 1 : rows);
  597. case Layout::DirectXPacking:
  598. if (rows > 0)
  599. { // It's a matrix
  600. if (rowMajor && rows > 1 && cols == 1)
  601. {
  602. // This is a very special case which we can't replicate the same way dxc packs the data
  603. // Additionally, this results in a lot of wasted space, so it's explicitly forbidden
  604. throw std::runtime_error{ "PackAsVectorMatrix: row_major packing for 2x1, 3x1 and 4x1 matrix types is not allowed!" };
  605. }
  606. const auto fullSize = ((rowMajor) ? rows : cols) * s_bytesPerRegister; // Use Rows or Cols to calculate the size
  607. // Note! A matrix does not occupy the entire last register if its number of elements is less than 4
  608. const auto adjustSize = (4 - ((rowMajor) ? cols : rows)) * s_bytesPerComponent; // Use the opposite (Cols or Rows)
  609. return fullSize - adjustSize;
  610. }
  611. // It's a vector
  612. return cols * s_bytesPerComponent;
  613. case Layout::RelaxedDirectXPacking:
  614. if (rows > 0)
  615. { // It's a matrix
  616. auto major = (rows < cols) ? rows : cols; // Take the smaller fit
  617. return major * s_bytesPerRegister;
  618. }
  619. // It's a vector
  620. return cols * s_bytesPerComponent;
  621. case Layout::RelaxedStd140Packing:
  622. case Layout::RelaxedStd430Packing:
  623. case Layout::StrictStd140Packing:
  624. case Layout::StrictStd430Packing:
  625. if (rows > 0)
  626. { // It's a matrix
  627. if (rows == 1)
  628. {
  629. // It collapses it as a vector
  630. return cols * s_bytesPerComponent;
  631. }
  632. if (cols == 1)
  633. {
  634. // It collapses it as a vector
  635. return rows * s_bytesPerComponent;
  636. }
  637. auto matrixStride = (layout == Layout::RelaxedStd430Packing && rows == 2) ? 2 * s_bytesPerComponent : s_bytesPerRegister;
  638. auto matrixElements = rowMajor ? rows : cols;
  639. if (rowMajor)
  640. {
  641. std::swap(matrixStride, matrixElements);
  642. }
  643. return matrixStride * matrixElements;
  644. }
  645. // It's a vector
  646. return cols * s_bytesPerComponent;
  647. }
  648. return 0; // Prevents warning C4715
  649. }
  650. inline uint32_t PackedSizeof(int indexInAzslPredefined_Scalar)
  651. {
  652. // the array is generated but it's expected to look like:
  653. // {"bool", "double", "dword", "float", "half", "int", "int16_t", "int32_t", "int64_t", "uint", "uint16_t", "uint32_t", "uint64_t"}
  654. // just update that code if it changes one day, the assert will pop.
  655. if (indexInAzslPredefined_Scalar == 1 || indexInAzslPredefined_Scalar == 8 || indexInAzslPredefined_Scalar == 12)
  656. {
  657. assert(string_view{"double"} == AZ::ShaderCompiler::Predefined::Scalar[1]);
  658. assert(string_view{"int64_t"} == AZ::ShaderCompiler::Predefined::Scalar[8]);
  659. assert(string_view{"uint64_t"} == AZ::ShaderCompiler::Predefined::Scalar[12]);
  660. // Shader packing reference:
  661. // https://docs.microsoft.com/en-us/windows/desktop/direct3dhlsl/dx-graphics-hlsl-packing-rules
  662. return 8;
  663. }
  664. else if (indexInAzslPredefined_Scalar == 4 || indexInAzslPredefined_Scalar == 6 || indexInAzslPredefined_Scalar == 10)
  665. {
  666. // https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing
  667. // extract: "with -enable-16bit-types: HLSL half type maps to native 16-bit float16_t type"
  668. // "native 16-bit types have storage size of 16-bits (as expected)"
  669. assert(string_view{"half"} == AZ::ShaderCompiler::Predefined::Scalar[4]);
  670. assert(string_view{"int16_t"} == AZ::ShaderCompiler::Predefined::Scalar[6]);
  671. assert(string_view{"uint16_t"} == AZ::ShaderCompiler::Predefined::Scalar[10]);
  672. return 2;
  673. }
  674. else if (indexInAzslPredefined_Scalar < 0)
  675. {
  676. return 0; // non-predefined case, surely meaning UDT.
  677. }
  678. assert(indexInAzslPredefined_Scalar < AZ::ShaderCompiler::Predefined::Scalar.size()); // #craefulgang.
  679. return 4; // bool is 32 too.
  680. }
  681. };
  682. struct TokensLocation
  683. {
  684. misc::Interval m_expressionSpan; // eg for `void F()` -> type id-expr brace brace -> m_expressionSpan concerns id-expr. for `nested::leaf` -> all 3 tokens are in the span
  685. ssize_t m_focusedTokenId; // eg for `void F()` -> `F` is the focused token. for id-expr, each nested identifier has a seenat. in `nested::leaf` -> 2 TokensLocation. one with 'nested' as focus, one with 'leaf' as focus; both with the same expression span.
  686. size_t m_line;
  687. size_t m_charPos;
  688. };
  689. // Most contexts (all?) will duck-type into this function
  690. template <typename ContextT>
  691. TokensLocation MakeTokensLocation(ContextT* ctx, const Token* focusedToken)
  692. {
  693. TokensLocation tl;
  694. tl.m_expressionSpan = const_cast<std::remove_const_t<ContextT>*>(ctx)->getSourceInterval();
  695. tl.m_focusedTokenId = static_cast<ssize_t>(focusedToken->getTokenIndex());
  696. tl.m_line = focusedToken->getLine();
  697. tl.m_charPos = focusedToken->getCharPositionInLine();
  698. return tl;
  699. };
  700. // version for single token
  701. inline TokensLocation MakeTokensLocation(const Token* tok)
  702. {
  703. TokensLocation tl;
  704. tl.m_expressionSpan = misc::Interval{tok->getTokenIndex(), tok->getTokenIndex()};
  705. tl.m_focusedTokenId = static_cast<ssize_t>(tok->getTokenIndex());
  706. tl.m_line = tok->getLine();
  707. tl.m_charPos = tok->getCharPositionInLine();
  708. return tl;
  709. };
  710. struct Seenat
  711. {
  712. IdentifierUID m_referredDefinition;
  713. TokensLocation m_where; // location in original stream, of a token group that can refer to a symbol.
  714. // (it's an interval because of nested-name-specifiers)
  715. };
  716. // before being able to clean that up with C++20 concepts, let's factorize the SFINAE expression to filter the accepted iterator types by an expected trait
  717. #define SFINAE_IS_SAME(DependentTypeToCompare, ExpectedType)\
  718. enable_if_t< is_same_v<typename DependentTypeToCompare, ExpectedType> >* = nullptr
  719. // Create a yaml element list for 'any collection' of Seenat (passed as a pseudo range)
  720. // - {line: x, col: y}
  721. template <typename SeenatRangeIter, SFINAE_IS_SAME(SeenatRangeIter::value_type, Seenat) >
  722. string ToYaml(SeenatRangeIter begin, SeenatRangeIter end, string_view indent)
  723. {
  724. string s;
  725. for (SeenatRangeIter it = begin; it != end; ++it)
  726. {
  727. s += indent;
  728. s += "- {line: " + std::to_string(it->m_where.m_line);
  729. s += ", col: " + std::to_string(it->m_where.m_charPos + 1); // +1 because charPos starts at 0. most editors start at 1
  730. s += "}\n";
  731. }
  732. return s;
  733. }
  734. // version for pseudo-range of IdentifierUID elements
  735. template <typename UIDRangeIter, SFINAE_IS_SAME(UIDRangeIter::value_type, IdentifierUID) >
  736. string ToYaml(UIDRangeIter begin, UIDRangeIter end, string_view indent)
  737. {
  738. string s;
  739. for (UIDRangeIter it = begin; it != end; ++it)
  740. {
  741. s += indent;
  742. s += "- {name: '" + it->m_name + "'}\n";
  743. }
  744. return s;
  745. }
  746. #undef SFINAE_IS_SAME
  747. // like PathPart but more specialized for grammatical elements specificity of an idExpression
  748. struct IdExpressionPart
  749. {
  750. string GetText() const
  751. {
  752. return m_token->getText();
  753. }
  754. bool IsScopeToken() const
  755. {
  756. return m_type == GlobalScopeOperator || m_type == ScopeResolutionOperator;
  757. }
  758. // Azslc Intermediate Representation uses slash separators for scopes
  759. // This function can help constructing internal "mangled" forms of qualified symbol names
  760. string GetAZIRMangledText() const
  761. {
  762. return IsScopeToken() ? "/" : GetText();
  763. }
  764. enum Type
  765. {
  766. NestedNameSpecifier, // nns::leaf (nns)
  767. ScopeResolutionOperator, // nns::leaf (::)
  768. GlobalScopeOperator, // ::leaf (leading ::)
  769. LoneUnqualifiedId, // leaf (leaf)
  770. QualifiedLeaf // nns::leaf (leaf)
  771. };
  772. Token* m_token;
  773. Type m_type;
  774. };
  775. template <typename Functor>
  776. void ForEachIdExpressionPart(AstIdExpr* ctx, Functor action)
  777. {
  778. static_assert( is_invocable<Functor, IdExpressionPart>::value, "please pass function objects that takes IdExpressionPart as argument" );
  779. if (ctx->unqualifiedId())
  780. {
  781. Token* loneLeaf = ctx->unqualifiedId()->Identifier()->getSymbol();
  782. action(IdExpressionPart{loneLeaf, IdExpressionPart::LoneUnqualifiedId});
  783. }
  784. else
  785. {
  786. Token* globalSROToken = ctx->qualifiedId()->nestedNameSpecifier()->GlobalSROToken;
  787. if (globalSROToken)
  788. {
  789. action(IdExpressionPart{globalSROToken, IdExpressionPart::GlobalScopeOperator});
  790. }
  791. for (auto* identifier : ctx->qualifiedId()->nestedNameSpecifier()->Identifier())
  792. {
  793. action(IdExpressionPart{identifier->getSymbol(), IdExpressionPart::NestedNameSpecifier});
  794. auto* nextToken = identifier->getSymbol()->getTokenSource()->nextToken().get();
  795. action(IdExpressionPart{nextToken, IdExpressionPart::ScopeResolutionOperator});
  796. }
  797. Token* last = ctx->qualifiedId()->unqualifiedId()->Identifier()->getSymbol();
  798. action(IdExpressionPart{last, IdExpressionPart::QualifiedLeaf});
  799. }
  800. }
  801. // This is a helper to make client code look smoother (free of internal detail about branching in non-null tree paths...)
  802. // It will reconstruct the mangled string for the identifier by looping over nested name elements.
  803. // The return type is Unqualified, even if a global SRO token is present.
  804. // When a global SRO token is present (leading :: in an idExression) then the identifier
  805. // is necessarily fully qualified (FQ).
  806. // We assume all identifiers are relative (non FQ), but in this one case,
  807. // we know that it's FQ.
  808. // This is too little a case to go out of our way and construct a QualifiedName.
  809. // Even if that sounds cool and clean, it breaks the principle of canonicality.
  810. // The QualifiedName and UnqualifiedName types are made to make compile time assignment
  811. // incompatible.
  812. // But the distinction here would have to be made at runtime.
  813. // Effectively changing the return type to a variant<UnqualifiedName, QualifiedName>
  814. // .. losing the compile time advantage of the whole original design choice.
  815. // Not only that, but propagating this information upward will pollute the clients, who
  816. // will feel compelled to preserve this information like it was a precious bit of entropy.
  817. // Except.. it is not precious. We can treat a FQ symbol as unqualified,
  818. // because all names will have to go through the MakeFullyQualified cruncher anyway.
  819. // (and that cruncher is canonical enough to return identity when it sees a FQ symbol)
  820. // Making this choice, we streamline treatment of names and simplify code.
  821. inline UnqualifiedName ExtractNameFromIdExpression(azslParser::IdExpressionContext* ctx)
  822. {
  823. std::stringstream ss; // accumulator
  824. ForEachIdExpressionPart(ctx, [&ss](const IdExpressionPart& part) { ss << part.GetAZIRMangledText(); });
  825. return UnqualifiedName{ss.str()};
  826. }
  827. template <typename TemplateContext>
  828. UnqualifiedName ExtractNameFromAnyContextWithName(TemplateContext* ctx)
  829. {
  830. return UnqualifiedName{ctx->Name->getText()};
  831. }
  832. template <typename ParentType>
  833. bool Is3ParentRuleOfType(antlr4::ParserRuleContext* ctx)
  834. {
  835. if (ctx == nullptr || ctx->parent == nullptr || ctx->parent->parent == nullptr) // input canonicalization
  836. {
  837. return false;
  838. }
  839. auto threeUp = ctx->parent->parent->parent;
  840. return dynamic_cast<ParentType>(threeUp);
  841. }
  842. // is def
  843. inline bool IsParentRuleAFunctionDefinition(azslParser::FunctionParamContext* ctx)
  844. {
  845. return Is3ParentRuleOfType<azslParser::HlslFunctionDefinitionContext*>(ctx);
  846. }
  847. // is decl
  848. inline bool IsParentRuleAFunctionDeclaration(azslParser::FunctionParamContext* ctx)
  849. {
  850. return Is3ParentRuleOfType<azslParser::HlslFunctionDeclarationContext*>(ctx);
  851. }
  852. inline bool IsRHSOfMemberAccess(tree::ParseTree* ctx)
  853. {
  854. auto* asMemberAccess = As<AstMemberAccess*>(ctx->parent);
  855. return asMemberAccess && asMemberAccess->Member == ctx;
  856. }
  857. //! Checks if your current rule is at distance 0 of a rule you want to target; (according to pointer arithmetic distance)
  858. //! example with target = ArrayAccessExpresison
  859. //! $ ((T)a::b.c)[x] from c: unqualifiedId -> idExpression -> (right of) MemberAccessExpression -> CastExpression -> ParenthesizedExpression -> ArrayAccessExpression
  860. //! all rules involved are 0-away (transparent) for pointer arithmetic, thus-> return true
  861. //! from b: unqualifiedId -> qualifiedId -> idExpression -> (left of) MemberAccessExpression
  862. //! stop here. because LHS of 'dot'. the "pointer-like" operation is offseted by RHS. thus -> return false
  863. //! $ (c + d)[x] from any of c or d: the link is broken here because '+' introduced a distance, the subscript applies on the temporary that results. thus -> return false
  864. //! returns: the found target, or nullptr if not found or not 0-away
  865. template< typename TargetRule >
  866. inline TargetRule* FindRuleInAstThatIs0AwayWrtPointerDistance(antlr4::ParserRuleContext* ctx)
  867. {
  868. // possible intermediates (that don't introduce a distance):
  869. using Nested = azslParser::NestedNameSpecifierContext;
  870. using Qualif = azslParser::QualifiedIdContext;
  871. using UnQualif = azslParser::UnqualifiedIdContext;
  872. using IdExpr = azslParser::IdentifierExpressionContext;
  873. using BraceExpr = azslParser::ParenthesizedExpressionContext;
  874. using Cast = azslParser::CastExpressionContext;
  875. //and AstIdExpr, AstMemberAccess too
  876. if (ctx == nullptr)
  877. {
  878. return nullptr;
  879. }
  880. if (TargetRule* callCtx = Is<TargetRule*>(ctx) ? As<TargetRule*>(ctx) : As<TargetRule*>(ctx->parent)) // goal
  881. {
  882. return callCtx;
  883. }
  884. bool zeroDist = DynamicTypeIsAnyOf<Nested, Qualif, UnQualif, IdExpr, BraceExpr, Cast, AstIdExpr>(ctx)
  885. || IsRHSOfMemberAccess(ctx);
  886. auto* parentAsParserRuleCtx = polymorphic_downcast<ParserRuleContext*>(ctx->parent);
  887. return zeroDist ? FindRuleInAstThatIs0AwayWrtPointerDistance<TargetRule>(parentAsParserRuleCtx) // if still valid, recurse
  888. : nullptr;
  889. }
  890. //! checks if your current rule is at distance 0 of a subscript []; according to pointer arithmetic distance.
  891. //! refer to comment of function FindRuleInAstThatIs0AwayWrtPointerDistance for examples
  892. inline bool IsNextToArrayAccessExpression(antlr4::ParserRuleContext* ctx)
  893. {
  894. using AAExpr = azslParser::ArrayAccessExpressionContext; // end condition
  895. return FindRuleInAstThatIs0AwayWrtPointerDistance<AAExpr>(ctx);
  896. }
  897. //! checks if your current rule is at distance 0 of a function call syntax f(); according to pointer arithmetic distance
  898. //! example: $ ((T)a::b.c)(x)
  899. //! returns: the argument list of the found call expression, if found. nullptr otherwise
  900. inline azslParser::ArgumentListContext* GetArgumentListIfBelongsToFunctionCall(antlr4::ParserRuleContext* ctx)
  901. {
  902. using Call = azslParser::FunctionCallExpressionContext; // end condition
  903. Call* found = FindRuleInAstThatIs0AwayWrtPointerDistance<Call>(ctx);
  904. return found ? found->argumentList() : nullptr;
  905. }
  906. //! try to find a specific context type that this context would be a child of.
  907. template <typename LookedUp>
  908. inline LookedUp* ExtractSpecificParent(antlr4::ParserRuleContext* ctx)
  909. {
  910. if (ctx == nullptr)
  911. {
  912. return nullptr;
  913. }
  914. auto* asCtx = As<LookedUp*>(ctx);
  915. if (asCtx) // goal
  916. {
  917. return asCtx;
  918. }
  919. return ctx->parent == nullptr ? nullptr : ExtractSpecificParent<LookedUp>(polymorphic_downcast<ParserRuleContext*>(ctx->parent));
  920. }
  921. //! try to find a variable initializer that this context would be a child of.
  922. inline AstVarInitializer* ExtractInitializerParent(antlr4::ParserRuleContext* ctx)
  923. {
  924. return ExtractSpecificParent<AstVarInitializer>(ctx);
  925. }
  926. inline azslParser::FunctionParamContext* ParamContextOverUnnamedVariableDeclarator(AstUnnamedVarDecl* ctx)
  927. {
  928. return As<azslParser::FunctionParamContext*>(ctx->parent);
  929. }
  930. inline azslParser::VariableDeclarationContext* VarDeclContextOverVariableDeclarator(AstNamedVarDecl* ctx)
  931. {
  932. return As<azslParser::VariableDeclarationContext*>(ctx->parent->parent);
  933. }
  934. inline azslParser::TypeContext* ExtractTypeFromUnnamedVariableDeclarator(AstUnnamedVarDecl* ctx, azslParser::FunctionParamContext** funcParamContextOut = nullptr)
  935. {
  936. auto* paramCtx = ParamContextOverUnnamedVariableDeclarator(ctx);
  937. if (paramCtx != nullptr)
  938. {
  939. if (funcParamContextOut)
  940. {
  941. *funcParamContextOut = paramCtx;
  942. }
  943. return paramCtx->type();
  944. }
  945. auto* varDeclCtx = VarDeclContextOverVariableDeclarator(As<AstNamedVarDecl*>(ctx->parent));
  946. if (varDeclCtx != nullptr)
  947. {
  948. return varDeclCtx->type();
  949. }
  950. return nullptr;
  951. }
  952. /// move up the AST into parent rules to ry to get a name. in case of function parameters, name can be omitted so this function may return null
  953. inline Token* ExtractVariableNameIdentifier(AstUnnamedVarDecl* ctx)
  954. {
  955. return Is<AstNamedVarDecl>(ctx->parent) ? polymorphic_downcast<AstNamedVarDecl*>(ctx->parent)->Name
  956. : polymorphic_downcast<azslParser::FunctionParamContext*>(ctx->parent)->Name;
  957. }
  958. inline ParserRuleContext* GetParentIfIsNamedVarDecl_OtherwiseIdentity(AstUnnamedVarDecl* ctx)
  959. {
  960. if (!ctx || !Is<AstNamedVarDecl>(ctx->parent))
  961. {
  962. return ctx;
  963. }
  964. return polymorphic_downcast<AstNamedVarDecl*>(ctx->parent);
  965. }
  966. inline string ExtractVariableNameSamplerBodyDeclaration(azslParser::SamplerBodyDeclarationContext* ctx)
  967. {
  968. // parent1 is variableInitializer ; parent2 is unnamedVariableDeclarator
  969. return ExtractVariableNameIdentifier(polymorphic_downcast<AstUnnamedVarDecl*>(ctx->parent->parent))->getText();
  970. }
  971. inline bool TypeIsSamplerComparisonState(AstUnnamedVarDecl* ctx)
  972. {
  973. auto* typeCtx = ExtractTypeFromUnnamedVariableDeclarator(ctx);
  974. return typeCtx->predefinedType() &&
  975. typeCtx->predefinedType()->samplerStatePredefinedType() &&
  976. typeCtx->predefinedType()->samplerStatePredefinedType()->SamplerComparisonState();
  977. }
  978. inline azslParser::StorageFlagsContext* ExtractStorageFlagsFromUnnamedVariableDeclarator(AstUnnamedVarDecl* ctx)
  979. {
  980. return ExtractTypeFromUnnamedVariableDeclarator(ctx)->storageFlags();
  981. }
  982. inline StorageFlag AsFlag(azslParser::StorageFlagContext* ctx)
  983. {
  984. return ctx->Const() ? StorageFlag::Const
  985. : ctx->Extern() ? StorageFlag::Extern
  986. : ctx->Groupshared() ? StorageFlag::Groupshared
  987. : ctx->Precise() ? StorageFlag::Precise
  988. : ctx->Static() ? StorageFlag::Static
  989. : ctx->Uniform() ? StorageFlag::Uniform
  990. : ctx->Volatile() ? StorageFlag::Volatile
  991. : ctx->Globallycoherent() ? StorageFlag::Globallycoherent
  992. : ctx->RowMajor() ? StorageFlag::RowMajor
  993. : ctx->ColumnMajor() ? StorageFlag::ColumnMajor
  994. : ctx->In() ? StorageFlag::In
  995. : ctx->Out() ? StorageFlag::Out
  996. : ctx->Inout() ? StorageFlag::InOut
  997. : ctx->Inline() ? StorageFlag::Inline
  998. : ctx->Option() ? StorageFlag::Option
  999. : ctx->Rootconstant() ? StorageFlag::Rootconstant
  1000. : ctx->Unsigned() ? StorageFlag::Unsigned
  1001. // Everything else can still be stored, but won't be checked in any special way:
  1002. // linear, centroid, noninterpolation, noperspective, sample, point, line, triangle, lineadk, triangleadj, indices, vertices, etc...
  1003. : StorageFlag::Other;
  1004. }
  1005. inline bool IsFlag(azslParser::StorageFlagContext* ctx, StorageFlag flag)
  1006. {
  1007. return AsFlag(ctx) == flag;
  1008. }
  1009. // Either just a string, or string + its original source node.
  1010. // keeping the source node allows for typeof(expression) resolution, and reversely keeping the name allows for clear strings during debugging.
  1011. // Ext for extended.
  1012. struct ExtractedTypeExt
  1013. {
  1014. UnqualifiedName m_name;
  1015. AstType* m_node = nullptr;
  1016. };
  1017. // composed type parts. 'Buffer<float4>' will have 'Buffer' as core and 'float4' as genericParam
  1018. struct ExtractedComposedType
  1019. {
  1020. ExtractedTypeExt m_core;
  1021. ExtractedTypeExt m_genericParam; // note: could be made recursive one day. use unique_ptr here
  1022. ArrayDimensions m_genericDimensions; // can expand generic dimensions too later. for MSTexture, IO patch, matrix<t,N,M> etc...
  1023. };
  1024. // Compose middleEnd configurations, wraps minimal user options from command line.
  1025. struct MiddleEndConfiguration
  1026. {
  1027. int m_rootConstantsMaxSize;
  1028. AZ::ShaderCompiler::Packing::Layout m_packConstantBuffers;
  1029. AZ::ShaderCompiler::Packing::Layout m_packDataBuffers;
  1030. bool m_isRowMajor;
  1031. bool m_padRootConstantCB;
  1032. bool m_skipAlignmentValidation;
  1033. };
  1034. ExtractedComposedType ExtractComposedTypeNamesFromAstContext(AstType* ctx, vector<tree::TerminalNode*>* genericDims = nullptr);
  1035. // Oftentimes a type rule looks like: `genericTexture: TextureKeyword '<' type '>'` , this function extracts `type`.
  1036. // Types parent of that ctx should have an AnalyzeClass that returns HasGenericParameter
  1037. // for more power, this function can return an AstPointer to a type rule directly in case of availability.
  1038. inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(AstPredefinedTypeNode* ctx, vector<tree::TerminalNode*>* genericDims = nullptr)
  1039. {
  1040. if (ctx->bufferPredefinedType())
  1041. { // use aggregate initialization syntax to construct the return type (tops 4 elements, but here we fillup 2 only)
  1042. return {ExtractedTypeExt{UnqualifiedName{ctx->bufferPredefinedType()->bufferType()->getText()}}, // m_core
  1043. ExtractedTypeExt{UnqualifiedName{ctx->bufferPredefinedType()->scalarOrVectorOrMatrixType()->getText()}}}; // m_genericParam
  1044. }
  1045. else if (ctx->genericMatrixPredefinedType())
  1046. {
  1047. auto intLit1 = ctx->genericMatrixPredefinedType()->IntegerLiteral(0);
  1048. auto intLit2 = ctx->genericMatrixPredefinedType()->IntegerLiteral(1);
  1049. if (genericDims && intLit1 && intLit2)
  1050. {
  1051. genericDims->push_back(intLit1);
  1052. genericDims->push_back(intLit2);
  1053. }
  1054. return {ExtractedTypeExt{UnqualifiedName{ctx->genericMatrixPredefinedType()->Matrix()->getText()}}, // m_core
  1055. ExtractedTypeExt{UnqualifiedName{ctx->genericMatrixPredefinedType()->scalarType()->getText()}}}; // m_genericParam
  1056. }
  1057. else if (ctx->streamOutputPredefinedType())
  1058. {
  1059. auto* core = ctx->streamOutputPredefinedType()->streamOutputObjectType();
  1060. AstType* genericCtx = ctx->streamOutputPredefinedType()->type();
  1061. return {ExtractedTypeExt{UnqualifiedName{core->getText()}}, // m_core
  1062. ExtractedTypeExt{UnqualifiedName{genericCtx->getText()}, genericCtx}}; // for the generic, since this is a type context, we will return it as a node, and let the client recurse if needed.
  1063. }
  1064. else if (ctx->structuredBufferPredefinedType())
  1065. {
  1066. auto* core = ctx->structuredBufferPredefinedType()->structuredBufferName();
  1067. AstType* genericCtx = ctx->structuredBufferPredefinedType()->type();
  1068. return {ExtractedTypeExt{UnqualifiedName{core->getText()}}, // m_core
  1069. ExtractedTypeExt{UnqualifiedName{genericCtx->getText()}, genericCtx}}; // same as above
  1070. }
  1071. else if (ctx->genericTexturePredefinedType())
  1072. {
  1073. return {ExtractedTypeExt{UnqualifiedName{ctx->genericTexturePredefinedType()->textureType()->getText()}}, // m_core
  1074. ExtractedTypeExt{UnqualifiedName{ctx->genericTexturePredefinedType()->scalarOrVectorType()->getText()}}}; // m_genericParam
  1075. }
  1076. else if (ctx->msTexturePredefinedType())
  1077. {
  1078. auto intLit = ctx->msTexturePredefinedType()->IntegerLiteral();
  1079. if (genericDims && intLit)
  1080. {
  1081. genericDims->push_back(intLit);
  1082. }
  1083. return {ExtractedTypeExt{UnqualifiedName{ctx->msTexturePredefinedType()->textureTypeMS()->getText()}}, // m_core
  1084. ExtractedTypeExt{UnqualifiedName{ctx->msTexturePredefinedType()->scalarOrVectorType()->getText()}}}; // m_genericParam
  1085. }
  1086. else if (ctx->genericVectorType())
  1087. {
  1088. auto intLit = ctx->genericVectorType()->IntegerLiteral();
  1089. if (genericDims && intLit)
  1090. {
  1091. genericDims->push_back(intLit);
  1092. }
  1093. return {ExtractedTypeExt{UnqualifiedName{ctx->genericVectorType()->Vector()->getText()}}, // m_core
  1094. ExtractedTypeExt{UnqualifiedName{ctx->genericVectorType()->scalarType()->getText()}}}; // m_genericParam
  1095. }
  1096. else if (ctx->constantBufferTemplated())
  1097. {
  1098. auto coreName = ctx->constantBufferTemplated()->CBCoreType->getText();
  1099. AstType* genericCtx = ctx->constantBufferTemplated()->GenericTypeName;
  1100. return {ExtractedTypeExt{UnqualifiedName{coreName}}, // m_core
  1101. ExtractedTypeExt{UnqualifiedName{genericCtx->getText()}, genericCtx}}; // same as previous 2 comments above
  1102. }
  1103. // all other rules are getable without artefacts for sure from a simple getText
  1104. return {ExtractedTypeExt{UnqualifiedName{ctx->getText()}}}; // only core, no generic part.
  1105. }
  1106. //! from a special rule: scalarOrVectorOrMatrixType
  1107. inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(azslParser::ScalarOrVectorOrMatrixTypeContext* ctx, vector<tree::TerminalNode*>* genericDims = nullptr)
  1108. {
  1109. return {ExtractedTypeExt{UnqualifiedName{ctx->getText()}}};
  1110. // for now there is no generic part, but mind it when you fix the grammar to support generic vector.
  1111. }
  1112. //! from userDefinedType context
  1113. inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(azslParser::UserDefinedTypeContext* ctx, vector<tree::TerminalNode*>* genericDims = nullptr)
  1114. {
  1115. if (ctx->idExpression())
  1116. {
  1117. return {ExtractedTypeExt{ExtractNameFromIdExpression(ctx->idExpression())}};
  1118. }
  1119. assert(ctx->anyStructuredTypeDefinition());
  1120. auto* anyStructLikeCtx = ctx->anyStructuredTypeDefinition();
  1121. auto structName = VisitFirstNonNull([](auto* ctx) { return ExtractNameFromAnyContextWithName(ctx); },
  1122. anyStructLikeCtx->classDefinition(),
  1123. anyStructLikeCtx->interfaceDefinition(),
  1124. anyStructLikeCtx->enumDefinition(),
  1125. anyStructLikeCtx->structDefinition());
  1126. return {ExtractedTypeExt{UnqualifiedName{structName}}};
  1127. }
  1128. //! from type context (next to highest level)
  1129. inline ExtractedComposedType ExtractComposedTypeNamesFromAstContext(AstType* ctx, vector<tree::TerminalNode*>* genericDims /*= nullptr*/)
  1130. {
  1131. if (ctx->userDefinedType())
  1132. {
  1133. return ExtractComposedTypeNamesFromAstContext(ctx->userDefinedType(), genericDims);
  1134. }
  1135. else if (ctx->predefinedType())
  1136. {
  1137. return ExtractComposedTypeNamesFromAstContext(ctx->predefinedType(), genericDims);
  1138. }
  1139. else if (ctx->Void())
  1140. {
  1141. assert(string_view{AZ::ShaderCompiler::Predefined::Void[0]} == ctx->Void()->getText());
  1142. return {UnqualifiedName{ctx->Void()->getText()}}; // "void"
  1143. }
  1144. // this could be a typeof, let's return the node for further resolve!
  1145. return {ExtractedTypeExt{UnqualifiedName{ctx->getText()}, ctx}};
  1146. }
  1147. //! Parse an HLSL semantic from a context into (semantic name, semantic index, is system value)
  1148. inline tuple<string, int, bool> ExtractHlslSemantic(azslParser::HlslSemanticContext* hlslSemantic)
  1149. {
  1150. // Semantic name and index
  1151. auto semanticName = hlslSemantic->getText();
  1152. size_t index = semanticName.find_last_not_of("0123456789") + 1;
  1153. int semanticIndex = (index == semanticName.length()) ?
  1154. 0 : std::stoi(semanticName.substr(index));
  1155. auto colon = semanticName.find_first_of(":");
  1156. auto firstChar = (colon == string::npos) ? 0 : colon + 1;
  1157. firstChar = semanticName.find_first_not_of(" ", firstChar);
  1158. semanticName = semanticName.substr(firstChar, index - firstChar);
  1159. bool isSystemValue = (hlslSemantic->Name->HLSLSemanticSystem() != nullptr);
  1160. return { semanticName, semanticIndex, isSystemValue };
  1161. }
  1162. inline bool HasStandardInitializer(AstVarInitializer* ctx)
  1163. {
  1164. return ctx && ctx->standardVariableInitializer();
  1165. }
  1166. inline bool HasStandardInitializer(AstUnnamedVarDecl* ctx)
  1167. {
  1168. return ctx && HasStandardInitializer(ctx->variableInitializer());
  1169. }
  1170. }