| // Copyright (c) 2018 Google LLC. | 
 | // | 
 | // Licensed under the Apache License, Version 2.0 (the "License"); | 
 | // you may not use this file except in compliance with the License. | 
 | // You may obtain a copy of the License at | 
 | // | 
 | //     http://www.apache.org/licenses/LICENSE-2.0 | 
 | // | 
 | // Unless required by applicable law or agreed to in writing, software | 
 | // distributed under the License is distributed on an "AS IS" BASIS, | 
 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | // See the License for the specific language governing permissions and | 
 | // limitations under the License. | 
 |  | 
 | #ifndef SOURCE_OPT_LOOP_UTILS_H_ | 
 | #define SOURCE_OPT_LOOP_UTILS_H_ | 
 |  | 
 | #include <list> | 
 | #include <memory> | 
 | #include <unordered_map> | 
 | #include <vector> | 
 |  | 
 | #include "source/opt/ir_context.h" | 
 | #include "source/opt/loop_descriptor.h" | 
 |  | 
 | namespace spvtools { | 
 |  | 
 | namespace opt { | 
 |  | 
 | // Class to gather some metrics about a Region Of Interest (ROI). | 
 | // So far it counts the number of instructions in a ROI (excluding debug | 
 | // and label instructions) per basic block and in total. | 
 | struct CodeMetrics { | 
 |   void Analyze(const Loop& loop); | 
 |  | 
 |   // The number of instructions per basic block in the ROI. | 
 |   std::unordered_map<uint32_t, size_t> block_sizes_; | 
 |  | 
 |   // Number of instruction in the ROI. | 
 |   size_t roi_size_; | 
 | }; | 
 |  | 
 | // LoopUtils is used to encapsulte loop optimizations and from the passes which | 
 | // use them. Any pass which needs a loop optimization should do it through this | 
 | // or through a pass which is using this. | 
 | class LoopUtils { | 
 |  public: | 
 |   // Holds a auxiliary results of the loop cloning procedure. | 
 |   struct LoopCloningResult { | 
 |     using ValueMapTy = std::unordered_map<uint32_t, uint32_t>; | 
 |     using BlockMapTy = std::unordered_map<uint32_t, BasicBlock*>; | 
 |     using PtrMap = std::unordered_map<Instruction*, Instruction*>; | 
 |  | 
 |     PtrMap ptr_map_; | 
 |  | 
 |     // Mapping between the original loop ids and the new one. | 
 |     ValueMapTy value_map_; | 
 |     // Mapping between original loop blocks to the cloned one. | 
 |     BlockMapTy old_to_new_bb_; | 
 |     // Mapping between the cloned loop blocks to original one. | 
 |     BlockMapTy new_to_old_bb_; | 
 |     // List of cloned basic block. | 
 |     std::vector<std::unique_ptr<BasicBlock>> cloned_bb_; | 
 |   }; | 
 |  | 
 |   LoopUtils(IRContext* context, Loop* loop) | 
 |       : context_(context), | 
 |         loop_desc_( | 
 |             context->GetLoopDescriptor(loop->GetHeaderBlock()->GetParent())), | 
 |         loop_(loop), | 
 |         function_(*loop_->GetHeaderBlock()->GetParent()) {} | 
 |  | 
 |   // The converts the current loop to loop closed SSA form. | 
 |   // In the loop closed SSA, all loop exiting values go through a dedicated Phi | 
 |   // instruction. For instance: | 
 |   // | 
 |   // for (...) { | 
 |   //   A1 = ... | 
 |   //   if (...) | 
 |   //     A2 = ... | 
 |   //   A = phi A1, A2 | 
 |   // } | 
 |   // ... = op A ... | 
 |   // | 
 |   // Becomes | 
 |   // | 
 |   // for (...) { | 
 |   //   A1 = ... | 
 |   //   if (...) | 
 |   //     A2 = ... | 
 |   //   A = phi A1, A2 | 
 |   // } | 
 |   // C = phi A | 
 |   // ... = op C ... | 
 |   // | 
 |   // This makes some loop transformations (such as loop unswitch) simpler | 
 |   // (removes the needs to take care of exiting variables). | 
 |   void MakeLoopClosedSSA(); | 
 |  | 
 |   // Create dedicate exit basic block. This ensure all exit basic blocks has the | 
 |   // loop as sole predecessors. | 
 |   // By construction, structured control flow already has a dedicated exit | 
 |   // block. | 
 |   // Preserves: CFG, def/use and instruction to block mapping. | 
 |   void CreateLoopDedicatedExits(); | 
 |  | 
 |   // Clone |loop_| and remap its instructions. Newly created blocks | 
 |   // will be added to the |cloning_result.cloned_bb_| list, correctly ordered to | 
 |   // be inserted into a function. | 
 |   // It is assumed that |ordered_loop_blocks| is compatible with the result of | 
 |   // |Loop::ComputeLoopStructuredOrder|. If the preheader and merge block are in | 
 |   // the list they will also be cloned. If not, the resulting loop will share | 
 |   // them with the original loop. | 
 |   // The function preserves the def/use, cfg and instr to block analyses. | 
 |   // The cloned loop nest will be added to the loop descriptor and will have | 
 |   // ownership. | 
 |   Loop* CloneLoop(LoopCloningResult* cloning_result, | 
 |                   const std::vector<BasicBlock*>& ordered_loop_blocks) const; | 
 |   // Clone |loop_| and remap its instructions, as above. Overload to compute | 
 |   // loop block ordering within method rather than taking in as parameter. | 
 |   Loop* CloneLoop(LoopCloningResult* cloning_result) const; | 
 |  | 
 |   // Clone the |loop_| and make the new loop branch to the second loop on exit. | 
 |   Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); | 
 |  | 
 |   // Perfom a partial unroll of |loop| by given |factor|. This will copy the | 
 |   // body of the loop |factor| times. So a |factor| of one would give a new loop | 
 |   // with the original body plus one unrolled copy body. | 
 |   bool PartiallyUnroll(size_t factor); | 
 |  | 
 |   // Fully unroll |loop|. | 
 |   bool FullyUnroll(); | 
 |  | 
 |   // This function validates that |loop| meets the assumptions made by the | 
 |   // implementation of the loop unroller. As the implementation accommodates | 
 |   // more types of loops this function can reduce its checks. | 
 |   // | 
 |   // The conditions checked to ensure the loop can be unrolled are as follows: | 
 |   // 1. That the loop is in structured order. | 
 |   // 2. That the continue block is a branch to the header. | 
 |   // 3. That the only phi used in the loop is the induction variable. | 
 |   //  TODO(stephen@codeplay.com): This is a temporary mesure, after the loop is | 
 |   //  converted into LCSAA form and has a single entry and exit we can rewrite | 
 |   //  the other phis. | 
 |   // 4. That this is an inner most loop, or that loops contained within this | 
 |   // loop have already been fully unrolled. | 
 |   // 5. That each instruction in the loop is only used within the loop. | 
 |   // (Related to the above phi condition). | 
 |   bool CanPerformUnroll(); | 
 |  | 
 |   // Maintains the loop descriptor object after the unroll functions have been | 
 |   // called, otherwise the analysis should be invalidated. | 
 |   void Finalize(); | 
 |  | 
 |   // Returns the context associate to |loop_|. | 
 |   IRContext* GetContext() { return context_; } | 
 |   // Returns the loop descriptor owning |loop_|. | 
 |   LoopDescriptor* GetLoopDescriptor() { return loop_desc_; } | 
 |   // Returns the loop on which the object operates on. | 
 |   Loop* GetLoop() const { return loop_; } | 
 |   // Returns the function that |loop_| belong to. | 
 |   Function* GetFunction() const { return &function_; } | 
 |  | 
 |  private: | 
 |   IRContext* context_; | 
 |   LoopDescriptor* loop_desc_; | 
 |   Loop* loop_; | 
 |   Function& function_; | 
 |  | 
 |   // Populates the loop nest of |new_loop| according to |loop_| nest. | 
 |   void PopulateLoopNest(Loop* new_loop, | 
 |                         const LoopCloningResult& cloning_result) const; | 
 |  | 
 |   // Populates |new_loop| descriptor according to |old_loop|'s one. | 
 |   void PopulateLoopDesc(Loop* new_loop, Loop* old_loop, | 
 |                         const LoopCloningResult& cloning_result) const; | 
 | }; | 
 |  | 
 | }  // namespace opt | 
 | }  // namespace spvtools | 
 |  | 
 | #endif  // SOURCE_OPT_LOOP_UTILS_H_ |