copy_prop_arrays.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. // Copyright (c) 2018 Google LLC.
  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_COPY_PROP_ARRAYS_H_
  15. #define SOURCE_OPT_COPY_PROP_ARRAYS_H_
  16. #include <memory>
  17. #include <vector>
  18. #include "source/opt/mem_pass.h"
  19. namespace spvtools {
  20. namespace opt {
  21. // This pass implements a simple array copy propagation. It does not do a full
  22. // array data flow. It looks for simple cases that meet the following
  23. // conditions:
  24. //
  25. // 1) The source must never be stored to.
  26. // 2) The target must be stored to exactly once.
  27. // 3) The store to the target must be a store to the entire array, and be a
  28. // copy of the entire source.
  29. // 4) All loads of the target must be dominated by the store.
  30. //
  31. // The hard part is keeping all of the types correct. We do not want to
  32. // have to do too large a search to update everything, which may not be
  33. // possible, so we give up if we see any instruction that might be hard to
  34. // update.
  35. class CopyPropagateArrays : public MemPass {
  36. public:
  37. const char* name() const override { return "copy-propagate-arrays"; }
  38. Status Process() override;
  39. IRContext::Analysis GetPreservedAnalyses() override {
  40. return IRContext::kAnalysisDefUse | IRContext::kAnalysisCFG |
  41. IRContext::kAnalysisInstrToBlockMapping |
  42. IRContext::kAnalysisLoopAnalysis | IRContext::kAnalysisDecorations |
  43. IRContext::kAnalysisDominatorAnalysis | IRContext::kAnalysisNameMap |
  44. IRContext::kAnalysisConstants | IRContext::kAnalysisTypes;
  45. }
  46. private:
  47. // Represents one index in the OpAccessChain instruction. It can be either
  48. // an instruction's result_id (OpConstant by ex), or a immediate value.
  49. // Immediate values are used to prepare the final access chain without
  50. // creating OpConstant instructions until done.
  51. struct AccessChainEntry {
  52. bool is_result_id;
  53. union {
  54. uint32_t result_id;
  55. uint32_t immediate;
  56. };
  57. bool operator!=(const AccessChainEntry& other) const {
  58. return other.is_result_id != is_result_id || other.result_id != result_id;
  59. }
  60. };
  61. // The class used to identify a particular memory object. This memory object
  62. // will be owned by a particular variable, meaning that the memory is part of
  63. // that variable. It could be the entire variable or a member of the
  64. // variable.
  65. class MemoryObject {
  66. public:
  67. // Construction a memory object that is owned by |var_inst|. The iterator
  68. // |begin| and |end| traverse a container of integers that identify which
  69. // member of |var_inst| this memory object will represent. These integers
  70. // are interpreted the same way they would be in an |OpAccessChain|
  71. // instruction.
  72. template <class iterator>
  73. MemoryObject(Instruction* var_inst, iterator begin, iterator end);
  74. // Change |this| to now point to the member identified by |access_chain|
  75. // (starting from the current member). The elements in |access_chain| are
  76. // interpreted the same as the indices in the |OpAccessChain|
  77. // instruction.
  78. void PushIndirection(const std::vector<AccessChainEntry>& access_chain);
  79. // Change |this| to now represent the first enclosing object to which it
  80. // belongs. (Remove the last element off the access_chain). It is invalid
  81. // to call this function if |this| does not represent a member of its owner.
  82. void PopIndirection() {
  83. assert(IsMember());
  84. access_chain_.pop_back();
  85. }
  86. // Returns true if |this| represents a member of its owner, and not the
  87. // entire variable.
  88. bool IsMember() const { return !access_chain_.empty(); }
  89. // Returns the number of members in the object represented by |this|. If
  90. // |this| does not represent a composite type or the number of components is
  91. // not known at compile time, the return value will be 0.
  92. uint32_t GetNumberOfMembers();
  93. // Returns the owning variable that the memory object is contained in.
  94. Instruction* GetVariable() const { return variable_inst_; }
  95. // Returns a vector of integers that can be used to access the specific
  96. // member that |this| represents starting from the owning variable. These
  97. // values are to be interpreted the same way the indices are in an
  98. // |OpAccessChain| instruction.
  99. const std::vector<AccessChainEntry>& AccessChain() const {
  100. return access_chain_;
  101. }
  102. // Converts all immediate values in the AccessChain their OpConstant
  103. // equivalent.
  104. void BuildConstants();
  105. // Returns the type id of the pointer type that can be used to point to this
  106. // memory object.
  107. uint32_t GetPointerTypeId(const CopyPropagateArrays* pass) const {
  108. analysis::DefUseManager* def_use_mgr =
  109. GetVariable()->context()->get_def_use_mgr();
  110. analysis::TypeManager* type_mgr =
  111. GetVariable()->context()->get_type_mgr();
  112. Instruction* var_pointer_inst =
  113. def_use_mgr->GetDef(GetVariable()->type_id());
  114. uint32_t member_type_id = pass->GetMemberTypeId(
  115. var_pointer_inst->GetSingleWordInOperand(1), GetAccessIds());
  116. uint32_t member_pointer_type_id = type_mgr->FindPointerToType(
  117. member_type_id, static_cast<spv::StorageClass>(
  118. var_pointer_inst->GetSingleWordInOperand(0)));
  119. return member_pointer_type_id;
  120. }
  121. // Returns the storage class of the memory object.
  122. spv::StorageClass GetStorageClass() const {
  123. analysis::TypeManager* type_mgr =
  124. GetVariable()->context()->get_type_mgr();
  125. const analysis::Pointer* pointer_type =
  126. type_mgr->GetType(GetVariable()->type_id())->AsPointer();
  127. return pointer_type->storage_class();
  128. }
  129. // Returns true if |other| represents memory that is contains inside of the
  130. // memory represented by |this|.
  131. bool Contains(MemoryObject* other);
  132. private:
  133. // The variable that owns this memory object.
  134. Instruction* variable_inst_;
  135. // The access chain to reach the particular member the memory object
  136. // represents. It should be interpreted the same way the indices in an
  137. // |OpAccessChain| are interpreted.
  138. std::vector<AccessChainEntry> access_chain_;
  139. std::vector<uint32_t> GetAccessIds() const;
  140. };
  141. // Returns the memory object being stored to |var_inst| in the store
  142. // instruction |store_inst|, if one exists, that can be used in place of
  143. // |var_inst| in all of the loads of |var_inst|. This code is conservative
  144. // and only identifies very simple cases. If no such memory object can be
  145. // found, the return value is |nullptr|.
  146. std::unique_ptr<CopyPropagateArrays::MemoryObject> FindSourceObjectIfPossible(
  147. Instruction* var_inst, Instruction* store_inst);
  148. // Replaces all loads of |var_inst| with a load from |source| instead.
  149. // |insertion_pos| is a position where it is possible to construct the
  150. // address of |source| and also dominates all of the loads of |var_inst|.
  151. void PropagateObject(Instruction* var_inst, MemoryObject* source,
  152. Instruction* insertion_pos);
  153. // Returns true if all of the references to |ptr_inst| can be rewritten and
  154. // are dominated by |store_inst|.
  155. bool HasValidReferencesOnly(Instruction* ptr_inst, Instruction* store_inst);
  156. // Returns a memory object that at one time was equivalent to the value in
  157. // |result|. If no such memory object exists, the return value is |nullptr|.
  158. std::unique_ptr<MemoryObject> GetSourceObjectIfAny(uint32_t result);
  159. // Returns the memory object that is loaded by |load_inst|. If a memory
  160. // object cannot be identified, the return value is |nullptr|. The opcode of
  161. // |load_inst| must be |OpLoad|.
  162. std::unique_ptr<MemoryObject> BuildMemoryObjectFromLoad(
  163. Instruction* load_inst);
  164. // Returns the memory object that at some point was equivalent to the result
  165. // of |extract_inst|. If a memory object cannot be identified, the return
  166. // value is |nullptr|. The opcode of |extract_inst| must be
  167. // |OpCompositeExtract|.
  168. std::unique_ptr<MemoryObject> BuildMemoryObjectFromExtract(
  169. Instruction* extract_inst);
  170. // Returns the memory object that at some point was equivalent to the result
  171. // of |construct_inst|. If a memory object cannot be identified, the return
  172. // value is |nullptr|. The opcode of |constuct_inst| must be
  173. // |OpCompositeConstruct|.
  174. std::unique_ptr<MemoryObject> BuildMemoryObjectFromCompositeConstruct(
  175. Instruction* conststruct_inst);
  176. // Returns the memory object that at some point was equivalent to the result
  177. // of |insert_inst|. If a memory object cannot be identified, the return
  178. // value is |nullptr|. The opcode of |insert_inst| must be
  179. // |OpCompositeInsert|. This function looks for a series of
  180. // |OpCompositeInsert| instructions that insert the elements one at a time in
  181. // order from beginning to end.
  182. std::unique_ptr<MemoryObject> BuildMemoryObjectFromInsert(
  183. Instruction* insert_inst);
  184. // Return true if the given entry can represent the given value.
  185. bool IsAccessChainIndexValidAndEqualTo(const AccessChainEntry& entry,
  186. uint32_t value) const;
  187. // Return true if |type_id| is a pointer type whose pointee type is an array.
  188. bool IsPointerToArrayType(uint32_t type_id);
  189. // Return true if |inst| is one of the InterpolateAt* GLSL.std.450 extended
  190. // instructions.
  191. bool IsInterpolationInstruction(Instruction* inst);
  192. // Returns true if there are not stores using |ptr_inst| or something derived
  193. // from it.
  194. bool HasNoStores(Instruction* ptr_inst);
  195. // Creates an |OpAccessChain| instruction whose result is a pointer the memory
  196. // represented by |source|. The new instruction will be placed before
  197. // |insertion_point|. |insertion_point| must be part of a function. Returns
  198. // the new instruction.
  199. Instruction* BuildNewAccessChain(Instruction* insertion_point,
  200. MemoryObject* source) const;
  201. // Rewrites all uses of |original_ptr| to use |new_pointer_inst| updating
  202. // types of other instructions as needed. This function should not be called
  203. // if |CanUpdateUses(original_ptr_inst, new_pointer_inst->type_id())| returns
  204. // false.
  205. void UpdateUses(Instruction* original_ptr_inst,
  206. Instruction* new_pointer_inst);
  207. // Return true if |UpdateUses| is able to change all of the uses of
  208. // |original_ptr_inst| to |type_id| and still have valid code.
  209. bool CanUpdateUses(Instruction* original_ptr_inst, uint32_t type_id);
  210. // Returns a store to |var_inst| that writes to the entire variable, and is
  211. // the only store that does so. Note it does not look through OpAccessChain
  212. // instruction, so partial stores are not considered.
  213. Instruction* FindStoreInstruction(const Instruction* var_inst) const;
  214. // Return the type id of the member of the type |id| access using
  215. // |access_chain|. The elements of |access_chain| are to be interpreted the
  216. // same way the indexes are used in an |OpCompositeExtract| instruction.
  217. uint32_t GetMemberTypeId(uint32_t id,
  218. const std::vector<uint32_t>& access_chain) const;
  219. // If the result of inst is stored to a variable, add that variable to the
  220. // worklist.
  221. void AddUsesToWorklist(Instruction* inst);
  222. // OpVariable worklist. An instruction is added to this list if we would like
  223. // to run copy propagation on it.
  224. std::queue<Instruction*> worklist_;
  225. };
  226. } // namespace opt
  227. } // namespace spvtools
  228. #endif // SOURCE_OPT_COPY_PROP_ARRAYS_H_