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: |
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: |
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 | |
65 | uint32_t var_id() const { return var_id_; } |
66 | uint32_t result_id() const { return result_id_; } |
67 | BasicBlock* bb() const { return bb_; } |
68 | std::vector<uint32_t>& phi_args() { return phi_args_; } |
69 | const std::vector<uint32_t>& phi_args() const { return phi_args_; } |
70 | uint32_t copy_of() const { return copy_of_; } |
71 | bool is_complete() const { return is_complete_; } |
72 | std::vector<uint32_t>& users() { return users_; } |
73 | const std::vector<uint32_t>& users() const { return users_; } |
74 | |
75 | // Marks this phi candidate as a trivial copy of |orig_id|. |
76 | void MarkCopyOf(uint32_t orig_id) { copy_of_ = orig_id; } |
77 | |
78 | // Marks this phi candidate as incomplete. |
79 | void MarkIncomplete() { is_complete_ = false; } |
80 | |
81 | // Marks this phi candidate as complete. |
82 | void MarkComplete() { is_complete_ = true; } |
83 | |
84 | // Returns true if this Phi candidate is ready to be emitted. |
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. |
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. |
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. |
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|. |
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 | |
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 | |