scalar_replacement_pass.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. // Copyright (c) 2017 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. #ifndef SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
  15. #define SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_
  16. #include <cassert>
  17. #include <cstdio>
  18. #include <memory>
  19. #include <queue>
  20. #include <unordered_map>
  21. #include <unordered_set>
  22. #include <vector>
  23. #include "source/opt/function.h"
  24. #include "source/opt/mem_pass.h"
  25. #include "source/opt/type_manager.h"
  26. namespace spvtools {
  27. namespace opt {
  28. // Documented in optimizer.hpp
  29. class ScalarReplacementPass : public MemPass {
  30. private:
  31. static constexpr uint32_t kDefaultLimit = 0;
  32. public:
  33. ScalarReplacementPass(uint32_t limit = kDefaultLimit)
  34. : max_num_elements_(limit) {
  35. const auto num_to_write = snprintf(
  36. name_, sizeof(name_), "scalar-replacement=%u", max_num_elements_);
  37. assert(size_t(num_to_write) < sizeof(name_));
  38. (void)num_to_write; // Mark as unused
  39. #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
  40. // ClusterFuzz/OSS-Fuzz is likely to yield examples with very large arrays.
  41. // This can cause timeouts and memouts during fuzzing that
  42. // are not classed as bugs. To avoid this noise, we set the
  43. // max_num_elements_ to a smaller value for fuzzing.
  44. max_num_elements_ =
  45. (max_num_elements_ > 0 && max_num_elements_ < 100 ? max_num_elements_
  46. : 100);
  47. #endif
  48. }
  49. const char* name() const override { return name_; }
  50. // Attempts to scalarize all appropriate function scope variables. Returns
  51. // SuccessWithChange if any change is made.
  52. Status Process() override;
  53. IRContext::Analysis GetPreservedAnalyses() override {
  54. return IRContext::kAnalysisDefUse |
  55. IRContext::kAnalysisInstrToBlockMapping |
  56. IRContext::kAnalysisDecorations | IRContext::kAnalysisCombinators |
  57. IRContext::kAnalysisCFG | IRContext::kAnalysisNameMap |
  58. IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
  59. }
  60. private:
  61. // Small container for tracking statistics about variables.
  62. //
  63. // TODO(alanbaker): Develop some useful heuristics to tune this pass.
  64. struct VariableStats {
  65. uint32_t num_partial_accesses;
  66. uint32_t num_full_accesses;
  67. };
  68. // Attempts to scalarize all appropriate function scope variables in
  69. // |function|. Returns SuccessWithChange if any changes are mode.
  70. Status ProcessFunction(Function* function);
  71. // Returns true if |varInst| can be scalarized.
  72. //
  73. // Examines the use chain of |varInst| to verify all uses are valid for
  74. // scalarization.
  75. bool CanReplaceVariable(const Instruction* varInst) const;
  76. // Returns true if |typeInst| is an acceptable type to scalarize.
  77. //
  78. // Allows all aggregate types except runtime arrays. Additionally, checks the
  79. // that the number of elements that would be scalarized is within bounds.
  80. bool CheckType(const Instruction* typeInst) const;
  81. // Returns true if all the decorations for |varInst| are acceptable for
  82. // scalarization.
  83. bool CheckAnnotations(const Instruction* varInst) const;
  84. // Returns true if all the decorations for |typeInst| are acceptable for
  85. // scalarization.
  86. bool CheckTypeAnnotations(const Instruction* typeInst) const;
  87. // Returns true if the uses of |inst| are acceptable for scalarization.
  88. //
  89. // Recursively checks all the uses of |inst|. For |inst| specifically, only
  90. // allows spv::Op::OpAccessChain, spv::Op::OpInBoundsAccessChain,
  91. // spv::Op::OpLoad and spv::Op::OpStore. Access chains must have the first
  92. // index be a compile-time constant. Subsequent uses of access chains
  93. // (including other access chains) are checked in a more relaxed manner.
  94. bool CheckUses(const Instruction* inst) const;
  95. // Helper function for the above |CheckUses|.
  96. //
  97. // This version tracks some stats about the current OpVariable. These stats
  98. // are used to drive heuristics about when to scalarize.
  99. bool CheckUses(const Instruction* inst, VariableStats* stats) const;
  100. // Relaxed helper function for |CheckUses|.
  101. bool CheckUsesRelaxed(const Instruction* inst) const;
  102. // Transfers appropriate decorations from |source| to |replacements|.
  103. void TransferAnnotations(const Instruction* source,
  104. std::vector<Instruction*>* replacements);
  105. // Scalarizes |inst| and updates its uses.
  106. //
  107. // |inst| must be an OpVariable. It is replaced with an OpVariable for each
  108. // for element of the composite type. Uses of |inst| are updated as
  109. // appropriate. If the replacement variables are themselves scalarizable, they
  110. // get added to |worklist| for further processing. If any replacement
  111. // variable ends up with no uses it is erased. Returns
  112. // - Status::SuccessWithoutChange if the variable could not be replaced.
  113. // - Status::SuccessWithChange if it made replacements.
  114. // - Status::Failure if it couldn't create replacement variables.
  115. Pass::Status ReplaceVariable(Instruction* inst,
  116. std::queue<Instruction*>* worklist);
  117. // Returns the underlying storage type for |inst|.
  118. //
  119. // |inst| must be an OpVariable. Returns the type that is pointed to by
  120. // |inst|.
  121. Instruction* GetStorageType(const Instruction* inst) const;
  122. // Returns true if the load can be scalarized.
  123. //
  124. // |inst| must be an OpLoad. Returns true if |index| is the pointer operand of
  125. // |inst| and the load is not from volatile memory.
  126. bool CheckLoad(const Instruction* inst, uint32_t index) const;
  127. // Returns true if the store can be scalarized.
  128. //
  129. // |inst| must be an OpStore. Returns true if |index| is the pointer operand
  130. // of |inst| and the store is not to volatile memory.
  131. bool CheckStore(const Instruction* inst, uint32_t index) const;
  132. // Returns true if the DebugDeclare can be scalarized at |index|.
  133. bool CheckDebugDeclare(uint32_t index) const;
  134. // Returns true if |index| is the pointer operand of an OpImageTexelPointer
  135. // instruction.
  136. bool CheckImageTexelPointer(uint32_t index) const;
  137. // Creates a variable of type |typeId| from the |index|'th element of
  138. // |varInst|. The new variable is added to |replacements|. If the variable
  139. // could not be created, then |nullptr| is appended to |replacements|.
  140. void CreateVariable(uint32_t typeId, Instruction* varInst, uint32_t index,
  141. std::vector<Instruction*>* replacements);
  142. // Populates |replacements| with a new OpVariable for each element of |inst|.
  143. // Returns true if the replacement variables were successfully created.
  144. //
  145. // |inst| must be an OpVariable of a composite type. New variables are
  146. // initialized the same as the corresponding index in |inst|. |replacements|
  147. // will contain a variable for each element of the composite with matching
  148. // indexes (i.e. the 0'th element of |inst| is the 0'th entry of
  149. // |replacements|).
  150. bool CreateReplacementVariables(Instruction* inst,
  151. std::vector<Instruction*>* replacements);
  152. // Returns the array length for |arrayInst|.
  153. uint64_t GetArrayLength(const Instruction* arrayInst) const;
  154. // Returns the number of elements in |type|.
  155. //
  156. // |type| must be a vector or matrix type.
  157. uint64_t GetNumElements(const Instruction* type) const;
  158. // Returns true if |id| is a specialization constant.
  159. //
  160. // |id| must be registered definition.
  161. bool IsSpecConstant(uint32_t id) const;
  162. // Returns an id for a pointer to |id|.
  163. uint32_t GetOrCreatePointerType(uint32_t id);
  164. // Creates the initial value for the |index| element of |source| in |newVar|.
  165. //
  166. // If there is an initial value for |source| for element |index|, it is
  167. // appended as an operand on |newVar|. If the initial value is OpUndef, no
  168. // initial value is added to |newVar|.
  169. void GetOrCreateInitialValue(Instruction* source, uint32_t index,
  170. Instruction* newVar);
  171. // Replaces the load to the entire composite.
  172. //
  173. // Generates a load for each replacement variable and then creates a new
  174. // composite by combining all of the loads.
  175. //
  176. // |load| must be a load. Returns true if successful.
  177. bool ReplaceWholeLoad(Instruction* load,
  178. const std::vector<Instruction*>& replacements);
  179. // Replaces the store to the entire composite.
  180. //
  181. // Generates a composite extract and store for each element in the scalarized
  182. // variable from the original store data input. Returns true if successful.
  183. bool ReplaceWholeStore(Instruction* store,
  184. const std::vector<Instruction*>& replacements);
  185. // Replaces the DebugDeclare to the entire composite.
  186. //
  187. // Generates a DebugValue with Deref operation for each element in the
  188. // scalarized variable from the original DebugDeclare. Returns true if
  189. // successful.
  190. bool ReplaceWholeDebugDeclare(Instruction* dbg_decl,
  191. const std::vector<Instruction*>& replacements);
  192. // Replaces the DebugValue to the entire composite.
  193. //
  194. // Generates a DebugValue for each element in the scalarized variable from
  195. // the original DebugValue. Returns true if successful.
  196. bool ReplaceWholeDebugValue(Instruction* dbg_value,
  197. const std::vector<Instruction*>& replacements);
  198. // Replaces an access chain to the composite variable with either a direct use
  199. // of the appropriate replacement variable or another access chain with the
  200. // replacement variable as the base and one fewer indexes. Returns true if
  201. // successful.
  202. bool ReplaceAccessChain(Instruction* chain,
  203. const std::vector<Instruction*>& replacements);
  204. // Returns a set containing the which components of the result of |inst| are
  205. // potentially used. If the return value is |nullptr|, then every components
  206. // is possibly used.
  207. std::unique_ptr<std::unordered_set<int64_t>> GetUsedComponents(
  208. Instruction* inst);
  209. // Returns an instruction defining an undefined value type |type_id|.
  210. Instruction* GetUndef(uint32_t type_id);
  211. // Maps storage type to a pointer type enclosing that type.
  212. std::unordered_map<uint32_t, uint32_t> pointee_to_pointer_;
  213. // Maps type id to OpConstantNull for that type.
  214. std::unordered_map<uint32_t, uint32_t> type_to_null_;
  215. // Returns the number of elements in the variable |var_inst|.
  216. uint64_t GetMaxLegalIndex(const Instruction* var_inst) const;
  217. // Returns true if |length| is larger than limit on the size of the variable
  218. // that we will be willing to split.
  219. bool IsLargerThanSizeLimit(uint64_t length) const;
  220. // Copies all relevant decorations from `from` to `to`. This includes
  221. // decorations applied to the variable, and to the members of the type.
  222. // It is assumed that `to` is a variable that is intended to replace the
  223. // `member_index`th member of `from`.
  224. void CopyDecorationsToVariable(Instruction* from, Instruction* to,
  225. uint32_t member_index);
  226. // Copies pointer related decoration from `from` to `to` if they exist.
  227. void CopyPointerDecorationsToVariable(Instruction* from, Instruction* to);
  228. // Copies decorations that are needed from the `member_index` of `from` to
  229. // `to, if there was one.
  230. void CopyNecessaryMemberDecorationsToVariable(Instruction* from,
  231. Instruction* to,
  232. uint32_t member_index);
  233. // Limit on the number of members in an object that will be replaced.
  234. // 0 means there is no limit.
  235. uint32_t max_num_elements_;
  236. // This has to be big enough to fit "scalar-replacement=" followed by a
  237. // uint32_t number written in decimal (so 10 digits), and then a
  238. // terminating nul.
  239. char name_[30];
  240. };
  241. } // namespace opt
  242. } // namespace spvtools
  243. #endif // SOURCE_OPT_SCALAR_REPLACEMENT_PASS_H_