ssa_rewrite_pass.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  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_SSA_REWRITE_PASS_H_
  15. #define SOURCE_OPT_SSA_REWRITE_PASS_H_
  16. #include <queue>
  17. #include <string>
  18. #include <unordered_map>
  19. #include <unordered_set>
  20. #include <utility>
  21. #include <vector>
  22. #include "source/opt/basic_block.h"
  23. #include "source/opt/ir_context.h"
  24. #include "source/opt/mem_pass.h"
  25. namespace spvtools {
  26. namespace opt {
  27. // Utility class for passes that need to rewrite a function into SSA. This
  28. // converts load/store operations on function-local variables into SSA IDs,
  29. // which allows them to be the target of optimizing transformations.
  30. //
  31. // Store and load operations to these variables are converted into
  32. // operations on SSA IDs. Phi instructions are added when needed. See the
  33. // SSA construction paper for algorithmic details
  34. // (https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6)
  35. class SSARewriter {
  36. public:
  37. SSARewriter(MemPass* pass) : pass_(pass) {}
  38. // Rewrites SSA-target variables in function |fp| into SSA. This is the
  39. // entry point for the SSA rewrite algorithm. SSA-target variables are
  40. // locally defined variables that meet the criteria set by IsSSATargetVar.
  41. //
  42. // Returns whether the function was modified or not, and whether or not the
  43. // rewrite was successful.
  44. Pass::Status RewriteFunctionIntoSSA(Function* fp);
  45. private:
  46. class PhiCandidate {
  47. public:
  48. explicit PhiCandidate(uint32_t var, uint32_t result, BasicBlock* block)
  49. : var_id_(var),
  50. result_id_(result),
  51. bb_(block),
  52. phi_args_(),
  53. copy_of_(0),
  54. is_complete_(false),
  55. users_() {}
  56. uint32_t var_id() const { return var_id_; }
  57. uint32_t result_id() const { return result_id_; }
  58. BasicBlock* bb() const { return bb_; }
  59. std::vector<uint32_t>& phi_args() { return phi_args_; }
  60. const std::vector<uint32_t>& phi_args() const { return phi_args_; }
  61. uint32_t copy_of() const { return copy_of_; }
  62. bool is_complete() const { return is_complete_; }
  63. std::vector<uint32_t>& users() { return users_; }
  64. const std::vector<uint32_t>& users() const { return users_; }
  65. // Marks this phi candidate as a trivial copy of |orig_id|.
  66. void MarkCopyOf(uint32_t orig_id) { copy_of_ = orig_id; }
  67. // Marks this phi candidate as incomplete.
  68. void MarkIncomplete() { is_complete_ = false; }
  69. // Marks this phi candidate as complete.
  70. void MarkComplete() { is_complete_ = true; }
  71. // Returns true if this Phi candidate is ready to be emitted.
  72. bool IsReady() const { return is_complete() && copy_of() == 0; }
  73. // Pretty prints this Phi candidate into a string and returns it. |cfg| is
  74. // needed to lookup basic block predecessors.
  75. std::string PrettyPrint(const CFG* cfg) const;
  76. // Registers |operand_id| as a user of this Phi candidate.
  77. void AddUser(uint32_t operand_id) { users_.push_back(operand_id); }
  78. private:
  79. // Variable ID that this Phi is merging.
  80. uint32_t var_id_;
  81. // SSA ID generated by this Phi (i.e., this is the result ID of the eventual
  82. // Phi instruction).
  83. uint32_t result_id_;
  84. // Basic block to hold this Phi.
  85. BasicBlock* bb_;
  86. // Vector of operands for every predecessor block of |bb|. This vector is
  87. // organized so that the Ith slot contains the argument coming from the Ith
  88. // predecessor of |bb|.
  89. std::vector<uint32_t> phi_args_;
  90. // If this Phi is a trivial copy of another Phi, this is the ID of the
  91. // original. If this is 0, it means that this is not a trivial Phi.
  92. uint32_t copy_of_;
  93. // False, if this Phi candidate has no arguments or at least one argument is
  94. // %0.
  95. bool is_complete_;
  96. // List of all users for this Phi instruction. Each element is the result ID
  97. // of the load instruction replaced by this Phi, or the result ID of a Phi
  98. // candidate that has this Phi in its list of operands.
  99. std::vector<uint32_t> users_;
  100. };
  101. // Type used to keep track of store operations in each basic block.
  102. typedef std::unordered_map<BasicBlock*,
  103. std::unordered_map<uint32_t, uint32_t>>
  104. BlockDefsMap;
  105. // Generates all the SSA rewriting decisions for basic block |bb|. This
  106. // populates the Phi candidate table (|phi_candidate_|) and the load
  107. // replacement table (|load_replacement_). Returns true if successful.
  108. bool GenerateSSAReplacements(BasicBlock* bb);
  109. // Seals block |bb|. Sealing a basic block means |bb| and all its
  110. // predecessors of |bb| have been scanned for loads/stores.
  111. void SealBlock(BasicBlock* bb);
  112. // Returns true if |bb| has been sealed.
  113. bool IsBlockSealed(BasicBlock* bb) { return sealed_blocks_.count(bb) != 0; }
  114. // Returns the Phi candidate with result ID |id| if it exists in the table
  115. // |phi_candidates_|. If no such Phi candidate exists, it returns nullptr.
  116. PhiCandidate* GetPhiCandidate(uint32_t id) {
  117. auto it = phi_candidates_.find(id);
  118. return (it != phi_candidates_.end()) ? &it->second : nullptr;
  119. }
  120. // Replaces all the users of Phi candidate |phi_cand| to be users of
  121. // |repl_id|.
  122. void ReplacePhiUsersWith(const PhiCandidate& phi_cand, uint32_t repl_id);
  123. // Returns the value ID that should replace the load ID in the given
  124. // replacement pair |repl|. The replacement is a pair (|load_id|, |val_id|).
  125. // If |val_id| is itself replaced by another value in the table, this function
  126. // will look the replacement for |val_id| until it finds one that is not
  127. // itself replaced. For instance, given:
  128. //
  129. // %34 = OpLoad %float %f1
  130. // OpStore %t %34
  131. // %36 = OpLoad %float %t
  132. //
  133. // Assume that %f1 is reached by a Phi candidate %42, the load
  134. // replacement table will have the following entries:
  135. //
  136. // %34 -> %42
  137. // %36 -> %34
  138. //
  139. // So, when looking for the replacement for %36, we should not use
  140. // %34. Rather, we should use %42. To do this, the chain of
  141. // replacements must be followed until we reach an element that has
  142. // no replacement.
  143. uint32_t GetReplacement(std::pair<uint32_t, uint32_t> repl);
  144. // Returns the argument at index |ix| from |phi_candidate|. If argument |ix|
  145. // comes from a trivial Phi, it follows the copy-of chain from that trivial
  146. // Phi until it finds the original Phi candidate.
  147. //
  148. // This is only valid after all Phi candidates have been completed. It can
  149. // only be called when generating the IR for these Phis.
  150. uint32_t GetPhiArgument(const PhiCandidate* phi_candidate, uint32_t ix);
  151. // Applies all the SSA replacement decisions. This replaces loads/stores to
  152. // SSA target variables with their corresponding SSA IDs, and inserts Phi
  153. // instructions for them.
  154. bool ApplyReplacements();
  155. // Registers a definition for variable |var_id| in basic block |bb| with
  156. // value |val_id|.
  157. void WriteVariable(uint32_t var_id, BasicBlock* bb, uint32_t val_id) {
  158. defs_at_block_[bb][var_id] = val_id;
  159. if (auto* pc = GetPhiCandidate(val_id)) {
  160. pc->AddUser(bb->id());
  161. }
  162. }
  163. // Returns the value of |var_id| at |bb| if |defs_at_block_| contains it.
  164. // Otherwise, returns 0.
  165. uint32_t GetValueAtBlock(uint32_t var_id, BasicBlock* bb);
  166. // Processes the store operation |inst| in basic block |bb|. This extracts
  167. // the variable ID being stored into, determines whether the variable is an
  168. // SSA-target variable, and, if it is, it stores its value in the
  169. // |defs_at_block_| map.
  170. void ProcessStore(Instruction* inst, BasicBlock* bb);
  171. // Processes the load operation |inst| in basic block |bb|. This extracts
  172. // the variable ID being stored into, determines whether the variable is an
  173. // SSA-target variable, and, if it is, it reads its reaching definition by
  174. // calling |GetReachingDef|. Returns true if successful.
  175. bool ProcessLoad(Instruction* inst, BasicBlock* bb);
  176. // Reads the current definition for variable |var_id| in basic block |bb|.
  177. // If |var_id| is not defined in block |bb| it walks up the predecessors of
  178. // |bb|, creating new Phi candidates along the way, if needed.
  179. //
  180. // It returns the value for |var_id| from the RHS of the current reaching
  181. // definition for |var_id|.
  182. uint32_t GetReachingDef(uint32_t var_id, BasicBlock* bb);
  183. // Adds arguments to |phi_candidate| by getting the reaching definition of
  184. // |phi_candidate|'s variable on each of the predecessors of its basic
  185. // block. After populating the argument list, it determines whether all its
  186. // arguments are the same. If so, it returns the ID of the argument that
  187. // this Phi copies.
  188. uint32_t AddPhiOperands(PhiCandidate* phi_candidate);
  189. // Creates a Phi candidate instruction for variable |var_id| in basic block
  190. // |bb|.
  191. //
  192. // Since the rewriting algorithm may remove Phi candidates when it finds
  193. // them to be trivial, we avoid the expense of creating actual Phi
  194. // instructions by keeping a pool of Phi candidates (|phi_candidates_|)
  195. // during rewriting.
  196. //
  197. // Once the candidate Phi is created, it returns its ID.
  198. PhiCandidate& CreatePhiCandidate(uint32_t var_id, BasicBlock* bb);
  199. // Attempts to remove a trivial Phi candidate |phi_cand|. Trivial Phis are
  200. // those that only reference themselves and one other value |val| any number
  201. // of times. This will try to remove any other Phis that become trivial
  202. // after |phi_cand| is removed.
  203. //
  204. // If |phi_cand| is trivial, it returns the SSA ID for the value that should
  205. // replace it. Otherwise, it returns the SSA ID for |phi_cand|.
  206. uint32_t TryRemoveTrivialPhi(PhiCandidate* phi_cand);
  207. // Finalizes |phi_candidate| by replacing every argument that is still %0
  208. // with its reaching definition.
  209. void FinalizePhiCandidate(PhiCandidate* phi_candidate);
  210. // Finalizes processing of Phi candidates. Once the whole function has been
  211. // scanned for loads and stores, the CFG will still have some incomplete and
  212. // trivial Phis. This will add missing arguments and remove trivial Phi
  213. // candidates.
  214. void FinalizePhiCandidates();
  215. // Prints the table of Phi candidates to std::cerr.
  216. void PrintPhiCandidates() const;
  217. // Prints the load replacement table to std::cerr.
  218. void PrintReplacementTable() const;
  219. // Map holding the value of every SSA-target variable at every basic block
  220. // where the variable is stored. defs_at_block_[block][var_id] = val_id
  221. // means that there is a store or Phi instruction for variable |var_id| at
  222. // basic block |block| with value |val_id|.
  223. BlockDefsMap defs_at_block_;
  224. // Map, indexed by Phi ID, holding all the Phi candidates created during SSA
  225. // rewriting. |phi_candidates_[id]| returns the Phi candidate whose result
  226. // is |id|.
  227. std::unordered_map<uint32_t, PhiCandidate> phi_candidates_;
  228. // Queue of incomplete Phi candidates. These are Phi candidates created at
  229. // unsealed blocks. They need to be completed before they are instantiated
  230. // in ApplyReplacements.
  231. std::queue<PhiCandidate*> incomplete_phis_;
  232. // List of completed Phi candidates. These are the only candidates that
  233. // will become real Phi instructions.
  234. std::vector<PhiCandidate*> phis_to_generate_;
  235. // SSA replacement table. This maps variable IDs, resulting from a load
  236. // operation, to the value IDs that will replace them after SSA rewriting.
  237. // After all the rewriting decisions are made, a final scan through the IR
  238. // is done to replace all uses of the original load ID with the value ID.
  239. std::unordered_map<uint32_t, uint32_t> load_replacement_;
  240. // Set of blocks that have been sealed already.
  241. std::unordered_set<BasicBlock*> sealed_blocks_;
  242. // Memory pass requesting the SSA rewriter.
  243. MemPass* pass_;
  244. };
  245. class SSARewritePass : public MemPass {
  246. public:
  247. SSARewritePass() = default;
  248. const char* name() const override { return "ssa-rewrite"; }
  249. Status Process() override;
  250. };
  251. } // namespace opt
  252. } // namespace spvtools
  253. #endif // SOURCE_OPT_SSA_REWRITE_PASS_H_