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