InitListHandler.h 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. //===------- InitListHandler.h - Initializer List Handler -------*- C++ -*-===//
  2. //
  3. // The LLVM Compiler Infrastructure
  4. //
  5. // This file is distributed under the University of Illinois Open Source
  6. // License. See LICENSE.TXT for details.
  7. //===----------------------------------------------------------------------===//
  8. //
  9. // This file defines an initalizer list handler that takes in an InitListExpr
  10. // and emits the corresponding SPIR-V instructions for it.
  11. //
  12. //===----------------------------------------------------------------------===//
  13. #ifndef LLVM_CLANG_LIB_SPIRV_INITLISTHANDLER_H
  14. #define LLVM_CLANG_LIB_SPIRV_INITLISTHANDLER_H
  15. #include <deque>
  16. #include <utility>
  17. #include <vector>
  18. #include "clang/AST/Expr.h"
  19. #include "clang/Basic/Diagnostic.h"
  20. #include "clang/SPIRV/ModuleBuilder.h"
  21. #include "SPIRVEmitter.h"
  22. #include "TypeTranslator.h"
  23. namespace clang {
  24. namespace spirv {
  25. /// The class for handling initializer lists.
  26. ///
  27. /// Initializer lists in HLSL are very flexible; as long as the initializer
  28. /// list provides the exact number of elements required by the type of the
  29. /// object to be initialized, it will highly likely be accepted. To handle
  30. /// such flexibility, composite extraction, recursive composite construction,
  31. /// and proper casting is necessary for some cases. For example:
  32. ///
  33. /// float4x4 mat = {scalar, vec1, vec2, vec3, vec2, float2(1, 2), 3, vec4};
  34. /// The first element vector is composed of scalar, vec1, and vec2; the second
  35. /// element vector is composed of vec3 and the first element of vec2; etc.
  36. ///
  37. /// The AST is designed to omit the composite extraction and construction. It
  38. /// also does not have casts to the expected types and from lvalues to rvalues.
  39. ///
  40. /// Resulting from the above reasons, the logic for handling initalizer lists
  41. /// are complicated. So we have this dedicated class for it. It is built on
  42. /// top of the SPIRVEmitter class and calls into SPIRVEmitter for normal
  43. /// translation tasks. This gives better code structure.
  44. ///
  45. /// The logic for handling initalizer lists is largely the following:
  46. ///
  47. /// First we flatten() the given initalizer list recursively and put all non-
  48. /// initializer-list AST Exprs into the initializers queue. This handles curly
  49. /// braces of even wired forms like float2x2 mat = {{1.}, {2., {{3.}}}, 4.};
  50. ///
  51. /// Then we construct the final SPIR-V composite from the initializer list
  52. /// by traversing the type of the composite. This is done recursively in the
  53. /// depth first search manner, using the type of the composite as the root.
  54. ///
  55. /// When we reach a scalar type, we will try to decode a scalar value from the
  56. /// front of the initializers queue. This may trigger composite extraction
  57. /// since the front of the queue may be a vector/matrix. The leftover values
  58. /// after the extraction should be retained for the next decoding. Thus, we need
  59. /// another queue, scalars, to keep track of leftover unused scalar values.
  60. /// To adjust properly, when decoding values for a given type, we first try
  61. /// the scalar queue.
  62. ///
  63. /// When we reach a composite type, we will try to construct a composite using
  64. /// the scalar values previously extracted and retained in the scalars queue.
  65. /// To optimize, if we have no leftover scalars and a value of the same type at
  66. /// the front of the initializers queue, we use the value as a whole.
  67. ///
  68. /// If the composite type is vector or matrix, we decompose() it into scalars as
  69. /// explained above. If it is a struct or array type, the element type is not
  70. /// guaranteed to be scalars. But still, we need to split them into their
  71. /// elements. For such cases, we create faux MemberExpr or ArraySubscriptExpr
  72. /// AST nodes for all the elements and push them into the initializers queue.
  73. class InitListHandler {
  74. public:
  75. /// Constructs an InitListHandler which uses the given emitter for normal
  76. /// translation tasks. It will reuse the ModuleBuilder embedded in the given
  77. /// emitter.
  78. explicit InitListHandler(SPIRVEmitter &emitter);
  79. /// Processes the given InitListExpr and returns the <result-id> for the final
  80. /// SPIR-V value.
  81. uint32_t process(const InitListExpr *expr);
  82. private:
  83. /// \brief Wrapper method to create an error message and report it
  84. /// in the diagnostic engine associated with this consumer.
  85. template <unsigned N>
  86. DiagnosticBuilder emitError(const char (&message)[N], SourceLocation loc) {
  87. const auto diagId =
  88. diags.getCustomDiagID(clang::DiagnosticsEngine::Error, message);
  89. return diags.Report(loc, diagId);
  90. }
  91. /// Flattens the given InitListExpr and puts all non-InitListExpr AST nodes
  92. /// into initializers.
  93. void flatten(const InitListExpr *expr);
  94. /// Decomposes the given Expr and puts all elements into the end of the
  95. /// scalars queue.
  96. void decompose(const Expr *expr);
  97. void decomposeVector(uint32_t vec, QualType elemType, uint32_t size);
  98. /// If the next initializer is a struct, replaces it with MemberExprs to all
  99. /// its members and returns true. Otherwise, does nothing and return false.
  100. bool tryToSplitStruct();
  101. /// If the next initializer is a constant array, replaces it with MemberExprs
  102. /// to all its members and returns true. Otherwise, does nothing and return
  103. /// false.
  104. bool tryToSplitConstantArray();
  105. /// Emits the necessary SPIR-V instructions to create a SPIR-V value of the
  106. /// given type. The scalars and initializers queue will be used to fetch the
  107. /// next value.
  108. uint32_t createInitForType(QualType type, SourceLocation);
  109. uint32_t createInitForBuiltinType(QualType type, SourceLocation);
  110. uint32_t createInitForVectorType(QualType elemType, uint32_t count,
  111. SourceLocation);
  112. uint32_t createInitForMatrixType(QualType matrixType, SourceLocation);
  113. uint32_t createInitForStructType(QualType type);
  114. uint32_t createInitForConstantArrayType(QualType type, SourceLocation);
  115. uint32_t createInitForSamplerImageType(QualType type, SourceLocation);
  116. private:
  117. SPIRVEmitter &theEmitter;
  118. ModuleBuilder &theBuilder;
  119. TypeTranslator &typeTranslator;
  120. DiagnosticsEngine &diags;
  121. /// A queue keeping track of unused AST nodes for initializers. Since we will
  122. /// only comsume initializers from the head of the queue and will not add new
  123. /// initializers to the tail of the queue, we use a vector (containing the
  124. /// reverse of the original intializer list) here and manipulate its tail.
  125. /// This is more efficient than using deque.
  126. std::vector<const Expr *> initializers;
  127. /// A queue keeping track of previously extracted but unused scalars.
  128. /// Each element is a pair, with the first element as the SPIR-V <result-id>
  129. /// and the second element as the AST type of the scalar value.
  130. std::deque<std::pair<uint32_t, QualType>> scalars;
  131. };
  132. } // end namespace spirv
  133. } // end namespace clang
  134. #endif