• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // This file implements the SSA rewriting algorithm proposed in
16 //
17 //      Simple and Efficient Construction of Static Single Assignment Form.
18 //      Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013)
19 //      In: Jhala R., De Bosschere K. (eds)
20 //      Compiler Construction. CC 2013.
21 //      Lecture Notes in Computer Science, vol 7791.
22 //      Springer, Berlin, Heidelberg
23 //
24 //      https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
25 //
26 // In contrast to common eager algorithms based on dominance and dominance
27 // frontier information, this algorithm works backwards from load operations.
28 //
29 // When a target variable is loaded, it queries the variable's reaching
30 // definition.  If the reaching definition is unknown at the current location,
31 // it searches backwards in the CFG, inserting Phi instructions at join points
32 // in the CFG along the way until it finds the desired store instruction.
33 //
34 // The algorithm avoids repeated lookups using memoization.
35 //
36 // For reducible CFGs, which are a superset of the structured CFGs in SPIRV,
37 // this algorithm is proven to produce minimal SSA.  That is, it inserts the
38 // minimal number of Phi instructions required to ensure the SSA property, but
39 // some Phi instructions may be dead
40 // (https://en.wikipedia.org/wiki/Static_single_assignment_form).
41 
42 #include "source/opt/ssa_rewrite_pass.h"
43 
44 #include <memory>
45 #include <sstream>
46 
47 #include "source/opcode.h"
48 #include "source/opt/cfg.h"
49 #include "source/opt/mem_pass.h"
50 #include "source/opt/types.h"
51 #include "source/util/make_unique.h"
52 
53 // Debug logging (0: Off, 1-N: Verbosity level).  Replace this with the
54 // implementation done for
55 // https://github.com/KhronosGroup/SPIRV-Tools/issues/1351
56 // #define SSA_REWRITE_DEBUGGING_LEVEL 3
57 
58 #ifdef SSA_REWRITE_DEBUGGING_LEVEL
59 #include <ostream>
60 #else
61 #define SSA_REWRITE_DEBUGGING_LEVEL 0
62 #endif
63 
64 namespace spvtools {
65 namespace opt {
66 
67 namespace {
68 const uint32_t kStoreValIdInIdx = 1;
69 const uint32_t kVariableInitIdInIdx = 1;
70 }  // namespace
71 
PrettyPrint(const CFG * cfg) const72 std::string SSARewriter::PhiCandidate::PrettyPrint(const CFG* cfg) const {
73   std::ostringstream str;
74   str << "%" << result_id_ << " = Phi[%" << var_id_ << ", BB %" << bb_->id()
75       << "](";
76   if (phi_args_.size() > 0) {
77     uint32_t arg_ix = 0;
78     for (uint32_t pred_label : cfg->preds(bb_->id())) {
79       uint32_t arg_id = phi_args_[arg_ix++];
80       str << "[%" << arg_id << ", bb(%" << pred_label << ")] ";
81     }
82   }
83   str << ")";
84   if (copy_of_ != 0) {
85     str << "  [COPY OF " << copy_of_ << "]";
86   }
87   str << ((is_complete_) ? "  [COMPLETE]" : "  [INCOMPLETE]");
88 
89   return str.str();
90 }
91 
CreatePhiCandidate(uint32_t var_id,BasicBlock * bb)92 SSARewriter::PhiCandidate& SSARewriter::CreatePhiCandidate(uint32_t var_id,
93                                                            BasicBlock* bb) {
94   // TODO(1841): Handle id overflow.
95   uint32_t phi_result_id = pass_->context()->TakeNextId();
96   auto result = phi_candidates_.emplace(
97       phi_result_id, PhiCandidate(var_id, phi_result_id, bb));
98   PhiCandidate& phi_candidate = result.first->second;
99   return phi_candidate;
100 }
101 
ReplacePhiUsersWith(const PhiCandidate & phi_to_remove,uint32_t repl_id)102 void SSARewriter::ReplacePhiUsersWith(const PhiCandidate& phi_to_remove,
103                                       uint32_t repl_id) {
104   for (uint32_t user_id : phi_to_remove.users()) {
105     PhiCandidate* user_phi = GetPhiCandidate(user_id);
106     BasicBlock* bb = pass_->context()->get_instr_block(user_id);
107     if (user_phi) {
108       // If the user is a Phi candidate, replace all arguments that refer to
109       // |phi_to_remove.result_id()| with |repl_id|.
110       for (uint32_t& arg : user_phi->phi_args()) {
111         if (arg == phi_to_remove.result_id()) {
112           arg = repl_id;
113         }
114       }
115     } else if (bb->id() == user_id) {
116       // The phi candidate is the definition of the variable at basic block
117       // |bb|.  We must change this to the replacement.
118       WriteVariable(phi_to_remove.var_id(), bb, repl_id);
119     } else {
120       // For regular loads, traverse the |load_replacement_| table looking for
121       // instances of |phi_to_remove|.
122       for (auto& it : load_replacement_) {
123         if (it.second == phi_to_remove.result_id()) {
124           it.second = repl_id;
125         }
126       }
127     }
128   }
129 }
130 
TryRemoveTrivialPhi(PhiCandidate * phi_candidate)131 uint32_t SSARewriter::TryRemoveTrivialPhi(PhiCandidate* phi_candidate) {
132   uint32_t same_id = 0;
133   for (uint32_t arg_id : phi_candidate->phi_args()) {
134     if (arg_id == same_id || arg_id == phi_candidate->result_id()) {
135       // This is a self-reference operand or a reference to the same value ID.
136       continue;
137     }
138     if (same_id != 0) {
139       // This Phi candidate merges at least two values.  Therefore, it is not
140       // trivial.
141       assert(phi_candidate->copy_of() == 0 &&
142              "Phi candidate transitioning from copy to non-copy.");
143       return phi_candidate->result_id();
144     }
145     same_id = arg_id;
146   }
147 
148   // The previous logic has determined that this Phi candidate |phi_candidate|
149   // is trivial.  It is essentially the copy operation phi_candidate->phi_result
150   // = Phi(same, same, same, ...).  Since it is not necessary, we can re-route
151   // all the users of |phi_candidate->phi_result| to all its users, and remove
152   // |phi_candidate|.
153 
154   // Mark the Phi candidate as a trivial copy of |same_id|, so it won't be
155   // generated.
156   phi_candidate->MarkCopyOf(same_id);
157 
158   assert(same_id != 0 && "Completed Phis cannot have %0 in their arguments");
159 
160   // Since |phi_candidate| always produces |same_id|, replace all the users of
161   // |phi_candidate| with |same_id|.
162   ReplacePhiUsersWith(*phi_candidate, same_id);
163 
164   return same_id;
165 }
166 
AddPhiOperands(PhiCandidate * phi_candidate)167 uint32_t SSARewriter::AddPhiOperands(PhiCandidate* phi_candidate) {
168   assert(phi_candidate->phi_args().size() == 0 &&
169          "Phi candidate already has arguments");
170 
171   bool found_0_arg = false;
172   for (uint32_t pred : pass_->cfg()->preds(phi_candidate->bb()->id())) {
173     BasicBlock* pred_bb = pass_->cfg()->block(pred);
174 
175     // If |pred_bb| is not sealed, use %0 to indicate that
176     // |phi_candidate| needs to be completed after the whole CFG has
177     // been processed.
178     //
179     // Note that we cannot call GetReachingDef() in these cases
180     // because this would generate an empty Phi candidate in
181     // |pred_bb|.  When |pred_bb| is later processed, a new definition
182     // for |phi_candidate->var_id_| will be lost because
183     // |phi_candidate| will still be reached by the empty Phi.
184     //
185     // Consider:
186     //
187     //       BB %23:
188     //           %38 = Phi[%i](%int_0[%1], %39[%25])
189     //
190     //           ...
191     //
192     //       BB %25: [Starts unsealed]
193     //       %39 = Phi[%i]()
194     //       %34 = ...
195     //       OpStore %i %34    -> Currdef(%i) at %25 is %34
196     //       OpBranch %23
197     //
198     // When we first create the Phi in %38, we add an operandless Phi in
199     // %39 to hold the unknown reaching def for %i.
200     //
201     // But then, when we go to complete %39 at the end.  The reaching def
202     // for %i in %25's predecessor is %38 itself.  So we miss the fact
203     // that %25 has a def for %i that should be used.
204     //
205     // By making the argument %0, we make |phi_candidate| incomplete,
206     // which will cause it to be completed after the whole CFG has
207     // been scanned.
208     uint32_t arg_id = IsBlockSealed(pred_bb)
209                           ? GetReachingDef(phi_candidate->var_id(), pred_bb)
210                           : 0;
211     phi_candidate->phi_args().push_back(arg_id);
212 
213     if (arg_id == 0) {
214       found_0_arg = true;
215     } else {
216       // If this argument is another Phi candidate, add |phi_candidate| to the
217       // list of users for the defining Phi.
218       PhiCandidate* defining_phi = GetPhiCandidate(arg_id);
219       if (defining_phi && defining_phi != phi_candidate) {
220         defining_phi->AddUser(phi_candidate->result_id());
221       }
222     }
223   }
224 
225   // If we could not fill-in all the arguments of this Phi, mark it incomplete
226   // so it gets completed after the whole CFG has been processed.
227   if (found_0_arg) {
228     phi_candidate->MarkIncomplete();
229     incomplete_phis_.push(phi_candidate);
230     return phi_candidate->result_id();
231   }
232 
233   // Try to remove |phi_candidate|, if it's trivial.
234   uint32_t repl_id = TryRemoveTrivialPhi(phi_candidate);
235   if (repl_id == phi_candidate->result_id()) {
236     // |phi_candidate| is complete and not trivial.  Add it to the
237     // list of Phi candidates to generate.
238     phi_candidate->MarkComplete();
239     phis_to_generate_.push_back(phi_candidate);
240   }
241 
242   return repl_id;
243 }
244 
GetValueAtBlock(uint32_t var_id,BasicBlock * bb)245 uint32_t SSARewriter::GetValueAtBlock(uint32_t var_id, BasicBlock* bb) {
246   assert(bb != nullptr);
247   const auto& bb_it = defs_at_block_.find(bb);
248   if (bb_it != defs_at_block_.end()) {
249     const auto& current_defs = bb_it->second;
250     const auto& var_it = current_defs.find(var_id);
251     if (var_it != current_defs.end()) {
252       return var_it->second;
253     }
254   }
255   return 0;
256 }
257 
GetReachingDef(uint32_t var_id,BasicBlock * bb)258 uint32_t SSARewriter::GetReachingDef(uint32_t var_id, BasicBlock* bb) {
259   // If |var_id| has a definition in |bb|, return it.
260   uint32_t val_id = GetValueAtBlock(var_id, bb);
261   if (val_id != 0) return val_id;
262 
263   // Otherwise, look up the value for |var_id| in |bb|'s predecessors.
264   auto& predecessors = pass_->cfg()->preds(bb->id());
265   if (predecessors.size() == 1) {
266     // If |bb| has exactly one predecessor, we look for |var_id|'s definition
267     // there.
268     val_id = GetReachingDef(var_id, pass_->cfg()->block(predecessors[0]));
269   } else if (predecessors.size() > 1) {
270     // If there is more than one predecessor, this is a join block which may
271     // require a Phi instruction.  This will act as |var_id|'s current
272     // definition to break potential cycles.
273     PhiCandidate& phi_candidate = CreatePhiCandidate(var_id, bb);
274 
275     // Set the value for |bb| to avoid an infinite recursion.
276     WriteVariable(var_id, bb, phi_candidate.result_id());
277     val_id = AddPhiOperands(&phi_candidate);
278   }
279 
280   // If we could not find a store for this variable in the path from the root
281   // of the CFG, the variable is not defined, so we use undef.
282   if (val_id == 0) {
283     val_id = pass_->GetUndefVal(var_id);
284     if (val_id == 0) {
285       return 0;
286     }
287   }
288 
289   WriteVariable(var_id, bb, val_id);
290 
291   return val_id;
292 }
293 
SealBlock(BasicBlock * bb)294 void SSARewriter::SealBlock(BasicBlock* bb) {
295   auto result = sealed_blocks_.insert(bb);
296   (void)result;
297   assert(result.second == true &&
298          "Tried to seal the same basic block more than once.");
299 }
300 
ProcessStore(Instruction * inst,BasicBlock * bb)301 void SSARewriter::ProcessStore(Instruction* inst, BasicBlock* bb) {
302   auto opcode = inst->opcode();
303   assert((opcode == SpvOpStore || opcode == SpvOpVariable) &&
304          "Expecting a store or a variable definition instruction.");
305 
306   uint32_t var_id = 0;
307   uint32_t val_id = 0;
308   if (opcode == SpvOpStore) {
309     (void)pass_->GetPtr(inst, &var_id);
310     val_id = inst->GetSingleWordInOperand(kStoreValIdInIdx);
311   } else if (inst->NumInOperands() >= 2) {
312     var_id = inst->result_id();
313     val_id = inst->GetSingleWordInOperand(kVariableInitIdInIdx);
314   }
315   if (pass_->IsTargetVar(var_id)) {
316     WriteVariable(var_id, bb, val_id);
317     pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable(
318         inst, var_id, val_id, inst);
319 
320 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
321     std::cerr << "\tFound store '%" << var_id << " = %" << val_id << "': "
322               << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
323               << "\n";
324 #endif
325   }
326 }
327 
ProcessLoad(Instruction * inst,BasicBlock * bb)328 bool SSARewriter::ProcessLoad(Instruction* inst, BasicBlock* bb) {
329   // Get the pointer that we are using to load from.
330   uint32_t var_id = 0;
331   (void)pass_->GetPtr(inst, &var_id);
332 
333   // Get the immediate reaching definition for |var_id|.
334   //
335   // In the presence of variable pointers, the reaching definition may be
336   // another pointer.  For example, the following fragment:
337   //
338   //  %2 = OpVariable %_ptr_Input_float Input
339   // %11 = OpVariable %_ptr_Function__ptr_Input_float Function
340   //       OpStore %11 %2
341   // %12 = OpLoad %_ptr_Input_float %11
342   // %13 = OpLoad %float %12
343   //
344   // corresponds to the pseudo-code:
345   //
346   // layout(location = 0) in flat float *%2
347   // float %13;
348   // float *%12;
349   // float **%11;
350   // *%11 = %2;
351   // %12 = *%11;
352   // %13 = *%12;
353   //
354   // which ultimately, should correspond to:
355   //
356   // %13 = *%2;
357   //
358   // During rewriting, the pointer %12 is found to be replaceable by %2 (i.e.,
359   // load_replacement_[12] is 2). However, when processing the load
360   // %13 = *%12, the type of %12's reaching definition is another float
361   // pointer (%2), instead of a float value.
362   //
363   // When this happens, we need to continue looking up the reaching definition
364   // chain until we get to a float value or a non-target var (i.e. a variable
365   // that cannot be SSA replaced, like %2 in this case since it is a function
366   // argument).
367   analysis::DefUseManager* def_use_mgr = pass_->context()->get_def_use_mgr();
368   analysis::TypeManager* type_mgr = pass_->context()->get_type_mgr();
369   analysis::Type* load_type = type_mgr->GetType(inst->type_id());
370   uint32_t val_id = 0;
371   bool found_reaching_def = false;
372   while (!found_reaching_def) {
373     if (!pass_->IsTargetVar(var_id)) {
374       // If the variable we are loading from is not an SSA target (globals,
375       // function parameters), do nothing.
376       return true;
377     }
378 
379     val_id = GetReachingDef(var_id, bb);
380     if (val_id == 0) {
381       return false;
382     }
383 
384     // If the reaching definition is a pointer type different than the type of
385     // the instruction we are analyzing, then it must be a reference to another
386     // pointer (otherwise, this would be invalid SPIRV).  We continue
387     // de-referencing it by making |val_id| be |var_id|.
388     //
389     // NOTE: if there is no reaching definition instruction, it means |val_id|
390     // is an undef.
391     Instruction* reaching_def_inst = def_use_mgr->GetDef(val_id);
392     if (reaching_def_inst &&
393         !type_mgr->GetType(reaching_def_inst->type_id())->IsSame(load_type)) {
394       var_id = val_id;
395     } else {
396       found_reaching_def = true;
397     }
398   }
399 
400   // Schedule a replacement for the result of this load instruction with
401   // |val_id|. After all the rewriting decisions are made, every use of
402   // this load will be replaced with |val_id|.
403   uint32_t load_id = inst->result_id();
404   assert(load_replacement_.count(load_id) == 0);
405   load_replacement_[load_id] = val_id;
406   PhiCandidate* defining_phi = GetPhiCandidate(val_id);
407   if (defining_phi) {
408     defining_phi->AddUser(load_id);
409   }
410 
411 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
412   std::cerr << "\tFound load: "
413             << inst->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
414             << " (replacement for %" << load_id << " is %" << val_id << ")\n";
415 #endif
416 
417   return true;
418 }
419 
PrintPhiCandidates() const420 void SSARewriter::PrintPhiCandidates() const {
421   std::cerr << "\nPhi candidates:\n";
422   for (const auto& phi_it : phi_candidates_) {
423     std::cerr << "\tBB %" << phi_it.second.bb()->id() << ": "
424               << phi_it.second.PrettyPrint(pass_->cfg()) << "\n";
425   }
426   std::cerr << "\n";
427 }
428 
PrintReplacementTable() const429 void SSARewriter::PrintReplacementTable() const {
430   std::cerr << "\nLoad replacement table\n";
431   for (const auto& it : load_replacement_) {
432     std::cerr << "\t%" << it.first << " -> %" << it.second << "\n";
433   }
434   std::cerr << "\n";
435 }
436 
GenerateSSAReplacements(BasicBlock * bb)437 bool SSARewriter::GenerateSSAReplacements(BasicBlock* bb) {
438 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
439   std::cerr << "Generating SSA replacements for block: " << bb->id() << "\n";
440   std::cerr << bb->PrettyPrint(SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
441             << "\n";
442 #endif
443 
444   for (auto& inst : *bb) {
445     auto opcode = inst.opcode();
446     if (opcode == SpvOpStore || opcode == SpvOpVariable) {
447       ProcessStore(&inst, bb);
448     } else if (inst.opcode() == SpvOpLoad) {
449       if (!ProcessLoad(&inst, bb)) {
450         return false;
451       }
452     }
453   }
454 
455   // Seal |bb|. This means that all the stores in it have been scanned and
456   // it's ready to feed them into its successors.
457   SealBlock(bb);
458 
459 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
460   PrintPhiCandidates();
461   PrintReplacementTable();
462   std::cerr << "\n\n";
463 #endif
464   return true;
465 }
466 
GetReplacement(std::pair<uint32_t,uint32_t> repl)467 uint32_t SSARewriter::GetReplacement(std::pair<uint32_t, uint32_t> repl) {
468   uint32_t val_id = repl.second;
469   auto it = load_replacement_.find(val_id);
470   while (it != load_replacement_.end()) {
471     val_id = it->second;
472     it = load_replacement_.find(val_id);
473   }
474   return val_id;
475 }
476 
GetPhiArgument(const PhiCandidate * phi_candidate,uint32_t ix)477 uint32_t SSARewriter::GetPhiArgument(const PhiCandidate* phi_candidate,
478                                      uint32_t ix) {
479   assert(phi_candidate->IsReady() &&
480          "Tried to get the final argument from an incomplete/trivial Phi");
481 
482   uint32_t arg_id = phi_candidate->phi_args()[ix];
483   while (arg_id != 0) {
484     PhiCandidate* phi_user = GetPhiCandidate(arg_id);
485     if (phi_user == nullptr || phi_user->IsReady()) {
486       // If the argument is not a Phi or it's a Phi candidate ready to be
487       // emitted, return it.
488       return arg_id;
489     }
490     arg_id = phi_user->copy_of();
491   }
492 
493   assert(false &&
494          "No Phi candidates in the copy-of chain are ready to be generated");
495 
496   return 0;
497 }
498 
ApplyReplacements()499 bool SSARewriter::ApplyReplacements() {
500   bool modified = false;
501 
502 #if SSA_REWRITE_DEBUGGING_LEVEL > 2
503   std::cerr << "\n\nApplying replacement decisions to IR\n\n";
504   PrintPhiCandidates();
505   PrintReplacementTable();
506   std::cerr << "\n\n";
507 #endif
508 
509   // Add Phi instructions from completed Phi candidates.
510   std::vector<Instruction*> generated_phis;
511   for (const PhiCandidate* phi_candidate : phis_to_generate_) {
512 #if SSA_REWRITE_DEBUGGING_LEVEL > 2
513     std::cerr << "Phi candidate: " << phi_candidate->PrettyPrint(pass_->cfg())
514               << "\n";
515 #endif
516 
517     assert(phi_candidate->is_complete() &&
518            "Tried to instantiate a Phi instruction from an incomplete Phi "
519            "candidate");
520 
521     auto* local_var = pass_->get_def_use_mgr()->GetDef(phi_candidate->var_id());
522 
523     // Build the vector of operands for the new OpPhi instruction.
524     uint32_t type_id = pass_->GetPointeeTypeId(local_var);
525     std::vector<Operand> phi_operands;
526     uint32_t arg_ix = 0;
527     std::unordered_map<uint32_t, uint32_t> already_seen;
528     for (uint32_t pred_label : pass_->cfg()->preds(phi_candidate->bb()->id())) {
529       uint32_t op_val_id = GetPhiArgument(phi_candidate, arg_ix++);
530       if (already_seen.count(pred_label) == 0) {
531         phi_operands.push_back(
532             {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {op_val_id}});
533         phi_operands.push_back(
534             {spv_operand_type_t::SPV_OPERAND_TYPE_ID, {pred_label}});
535         already_seen[pred_label] = op_val_id;
536       } else {
537         // It is possible that there are two edges from the same parent block.
538         // Since the OpPhi can have only one entry for each parent, we have to
539         // make sure the two edges are consistent with each other.
540         assert(already_seen[pred_label] == op_val_id &&
541                "Inconsistent value for duplicate edges.");
542       }
543     }
544 
545     // Generate a new OpPhi instruction and insert it in its basic
546     // block.
547     std::unique_ptr<Instruction> phi_inst(
548         new Instruction(pass_->context(), SpvOpPhi, type_id,
549                         phi_candidate->result_id(), phi_operands));
550     generated_phis.push_back(phi_inst.get());
551     pass_->get_def_use_mgr()->AnalyzeInstDef(&*phi_inst);
552     pass_->context()->set_instr_block(&*phi_inst, phi_candidate->bb());
553     auto insert_it = phi_candidate->bb()->begin();
554     insert_it = insert_it.InsertBefore(std::move(phi_inst));
555     pass_->context()->get_decoration_mgr()->CloneDecorations(
556         phi_candidate->var_id(), phi_candidate->result_id(),
557         {SpvDecorationRelaxedPrecision});
558 
559     // Add DebugValue for the new OpPhi instruction.
560     insert_it->SetDebugScope(local_var->GetDebugScope());
561     pass_->context()->get_debug_info_mgr()->AddDebugValueForVariable(
562         &*insert_it, phi_candidate->var_id(), phi_candidate->result_id(),
563         &*insert_it);
564 
565     modified = true;
566   }
567 
568   // Scan uses for all inserted Phi instructions. Do this separately from the
569   // registration of the Phi instruction itself to avoid trying to analyze
570   // uses of Phi instructions that have not been registered yet.
571   for (Instruction* phi_inst : generated_phis) {
572     pass_->get_def_use_mgr()->AnalyzeInstUse(&*phi_inst);
573   }
574 
575 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
576   std::cerr << "\n\nReplacing the result of load instructions with the "
577                "corresponding SSA id\n\n";
578 #endif
579 
580   // Apply replacements from the load replacement table.
581   for (auto& repl : load_replacement_) {
582     uint32_t load_id = repl.first;
583     uint32_t val_id = GetReplacement(repl);
584     Instruction* load_inst =
585         pass_->context()->get_def_use_mgr()->GetDef(load_id);
586 
587 #if SSA_REWRITE_DEBUGGING_LEVEL > 2
588     std::cerr << "\t"
589               << load_inst->PrettyPrint(
590                      SPV_BINARY_TO_TEXT_OPTION_FRIENDLY_NAMES)
591               << "  (%" << load_id << " -> %" << val_id << ")\n";
592 #endif
593 
594     // Remove the load instruction and replace all the uses of this load's
595     // result with |val_id|.  Kill any names or decorates using the load's
596     // result before replacing to prevent incorrect replacement in those
597     // instructions.
598     pass_->context()->KillNamesAndDecorates(load_id);
599     pass_->context()->ReplaceAllUsesWith(load_id, val_id);
600     pass_->context()->KillInst(load_inst);
601     modified = true;
602   }
603 
604   return modified;
605 }
606 
FinalizePhiCandidate(PhiCandidate * phi_candidate)607 void SSARewriter::FinalizePhiCandidate(PhiCandidate* phi_candidate) {
608   assert(phi_candidate->phi_args().size() > 0 &&
609          "Phi candidate should have arguments");
610 
611   uint32_t ix = 0;
612   for (uint32_t pred : pass_->cfg()->preds(phi_candidate->bb()->id())) {
613     BasicBlock* pred_bb = pass_->cfg()->block(pred);
614     uint32_t& arg_id = phi_candidate->phi_args()[ix++];
615     if (arg_id == 0) {
616       // If |pred_bb| is still not sealed, it means it's unreachable. In this
617       // case, we just use Undef as an argument.
618       arg_id = IsBlockSealed(pred_bb)
619                    ? GetReachingDef(phi_candidate->var_id(), pred_bb)
620                    : pass_->GetUndefVal(phi_candidate->var_id());
621     }
622   }
623 
624   // This candidate is now completed.
625   phi_candidate->MarkComplete();
626 
627   // If |phi_candidate| is not trivial, add it to the list of Phis to
628   // generate.
629   if (TryRemoveTrivialPhi(phi_candidate) == phi_candidate->result_id()) {
630     // If we could not remove |phi_candidate|, it means that it is complete
631     // and not trivial. Add it to the list of Phis to generate.
632     assert(!phi_candidate->copy_of() && "A completed Phi cannot be trivial.");
633     phis_to_generate_.push_back(phi_candidate);
634   }
635 }
636 
FinalizePhiCandidates()637 void SSARewriter::FinalizePhiCandidates() {
638 #if SSA_REWRITE_DEBUGGING_LEVEL > 1
639   std::cerr << "Finalizing Phi candidates:\n\n";
640   PrintPhiCandidates();
641   std::cerr << "\n";
642 #endif
643 
644   // Now, complete the collected candidates.
645   while (incomplete_phis_.size() > 0) {
646     PhiCandidate* phi_candidate = incomplete_phis_.front();
647     incomplete_phis_.pop();
648     FinalizePhiCandidate(phi_candidate);
649   }
650 }
651 
RewriteFunctionIntoSSA(Function * fp)652 Pass::Status SSARewriter::RewriteFunctionIntoSSA(Function* fp) {
653 #if SSA_REWRITE_DEBUGGING_LEVEL > 0
654   std::cerr << "Function before SSA rewrite:\n"
655             << fp->PrettyPrint(0) << "\n\n\n";
656 #endif
657 
658   // Collect variables that can be converted into SSA IDs.
659   pass_->CollectTargetVars(fp);
660 
661   // Generate all the SSA replacements and Phi candidates. This will
662   // generate incomplete and trivial Phis.
663   bool succeeded = pass_->cfg()->WhileEachBlockInReversePostOrder(
664       fp->entry().get(), [this](BasicBlock* bb) {
665         if (!GenerateSSAReplacements(bb)) {
666           return false;
667         }
668         return true;
669       });
670 
671   if (!succeeded) {
672     return Pass::Status::Failure;
673   }
674 
675   // Remove trivial Phis and add arguments to incomplete Phis.
676   FinalizePhiCandidates();
677 
678   // Finally, apply all the replacements in the IR.
679   bool modified = ApplyReplacements();
680 
681 #if SSA_REWRITE_DEBUGGING_LEVEL > 0
682   std::cerr << "\n\n\nFunction after SSA rewrite:\n"
683             << fp->PrettyPrint(0) << "\n";
684 #endif
685 
686   return modified ? Pass::Status::SuccessWithChange
687                   : Pass::Status::SuccessWithoutChange;
688 }
689 
Process()690 Pass::Status SSARewritePass::Process() {
691   Status status = Status::SuccessWithoutChange;
692   for (auto& fn : *get_module()) {
693     if (fn.IsDeclaration()) {
694       continue;
695     }
696     status =
697         CombineStatus(status, SSARewriter(this).RewriteFunctionIntoSSA(&fn));
698     // Kill DebugDeclares for target variables.
699     for (auto var_id : seen_target_vars_) {
700       context()->get_debug_info_mgr()->KillDebugDeclares(var_id);
701     }
702     if (status == Status::Failure) {
703       break;
704     }
705   }
706   return status;
707 }
708 
709 }  // namespace opt
710 }  // namespace spvtools
711