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_LOOP_UTILS_H_ 16 #define SOURCE_OPT_LOOP_UTILS_H_ 17 18 #include <list> 19 #include <memory> 20 #include <unordered_map> 21 #include <vector> 22 23 #include "source/opt/ir_context.h" 24 #include "source/opt/loop_descriptor.h" 25 26 namespace spvtools { 27 28 namespace opt { 29 30 // Class to gather some metrics about a Region Of Interest (ROI). 31 // So far it counts the number of instructions in a ROI (excluding debug 32 // and label instructions) per basic block and in total. 33 struct CodeMetrics { 34 void Analyze(const Loop& loop); 35 36 // The number of instructions per basic block in the ROI. 37 std::unordered_map<uint32_t, size_t> block_sizes_; 38 39 // Number of instruction in the ROI. 40 size_t roi_size_; 41 }; 42 43 // LoopUtils is used to encapsulte loop optimizations and from the passes which 44 // use them. Any pass which needs a loop optimization should do it through this 45 // or through a pass which is using this. 46 class LoopUtils { 47 public: 48 // Holds a auxiliary results of the loop cloning procedure. 49 struct LoopCloningResult { 50 using ValueMapTy = std::unordered_map<uint32_t, uint32_t>; 51 using BlockMapTy = std::unordered_map<uint32_t, BasicBlock*>; 52 using PtrMap = std::unordered_map<Instruction*, Instruction*>; 53 54 PtrMap ptr_map_; 55 56 // Mapping between the original loop ids and the new one. 57 ValueMapTy value_map_; 58 // Mapping between original loop blocks to the cloned one. 59 BlockMapTy old_to_new_bb_; 60 // Mapping between the cloned loop blocks to original one. 61 BlockMapTy new_to_old_bb_; 62 // List of cloned basic block. 63 std::vector<std::unique_ptr<BasicBlock>> cloned_bb_; 64 }; 65 LoopUtils(IRContext * context,Loop * loop)66 LoopUtils(IRContext* context, Loop* loop) 67 : context_(context), 68 loop_desc_( 69 context->GetLoopDescriptor(loop->GetHeaderBlock()->GetParent())), 70 loop_(loop), 71 function_(*loop_->GetHeaderBlock()->GetParent()) {} 72 73 // The converts the current loop to loop closed SSA form. 74 // In the loop closed SSA, all loop exiting values go through a dedicated Phi 75 // instruction. For instance: 76 // 77 // for (...) { 78 // A1 = ... 79 // if (...) 80 // A2 = ... 81 // A = phi A1, A2 82 // } 83 // ... = op A ... 84 // 85 // Becomes 86 // 87 // for (...) { 88 // A1 = ... 89 // if (...) 90 // A2 = ... 91 // A = phi A1, A2 92 // } 93 // C = phi A 94 // ... = op C ... 95 // 96 // This makes some loop transformations (such as loop unswitch) simpler 97 // (removes the needs to take care of exiting variables). 98 void MakeLoopClosedSSA(); 99 100 // Create dedicate exit basic block. This ensure all exit basic blocks has the 101 // loop as sole predecessors. 102 // By construction, structured control flow already has a dedicated exit 103 // block. 104 // Preserves: CFG, def/use and instruction to block mapping. 105 void CreateLoopDedicatedExits(); 106 107 // Clone |loop_| and remap its instructions. Newly created blocks 108 // will be added to the |cloning_result.cloned_bb_| list, correctly ordered to 109 // be inserted into a function. 110 // It is assumed that |ordered_loop_blocks| is compatible with the result of 111 // |Loop::ComputeLoopStructuredOrder|. If the preheader and merge block are in 112 // the list they will also be cloned. If not, the resulting loop will share 113 // them with the original loop. 114 // The function preserves the def/use, cfg and instr to block analyses. 115 // The cloned loop nest will be added to the loop descriptor and will have 116 // ownership. 117 Loop* CloneLoop(LoopCloningResult* cloning_result, 118 const std::vector<BasicBlock*>& ordered_loop_blocks) const; 119 // Clone |loop_| and remap its instructions, as above. Overload to compute 120 // loop block ordering within method rather than taking in as parameter. 121 Loop* CloneLoop(LoopCloningResult* cloning_result) const; 122 123 // Clone the |loop_| and make the new loop branch to the second loop on exit. 124 Loop* CloneAndAttachLoopToHeader(LoopCloningResult* cloning_result); 125 126 // Perfom a partial unroll of |loop| by given |factor|. This will copy the 127 // body of the loop |factor| times. So a |factor| of one would give a new loop 128 // with the original body plus one unrolled copy body. 129 bool PartiallyUnroll(size_t factor); 130 131 // Fully unroll |loop|. 132 bool FullyUnroll(); 133 134 // This function validates that |loop| meets the assumptions made by the 135 // implementation of the loop unroller. As the implementation accommodates 136 // more types of loops this function can reduce its checks. 137 // 138 // The conditions checked to ensure the loop can be unrolled are as follows: 139 // 1. That the loop is in structured order. 140 // 2. That the continue block is a branch to the header. 141 // 3. That the only phi used in the loop is the induction variable. 142 // TODO(stephen@codeplay.com): This is a temporary mesure, after the loop is 143 // converted into LCSAA form and has a single entry and exit we can rewrite 144 // the other phis. 145 // 4. That this is an inner most loop, or that loops contained within this 146 // loop have already been fully unrolled. 147 // 5. That each instruction in the loop is only used within the loop. 148 // (Related to the above phi condition). 149 bool CanPerformUnroll(); 150 151 // Maintains the loop descriptor object after the unroll functions have been 152 // called, otherwise the analysis should be invalidated. 153 void Finalize(); 154 155 // Returns the context associate to |loop_|. GetContext()156 IRContext* GetContext() { return context_; } 157 // Returns the loop descriptor owning |loop_|. GetLoopDescriptor()158 LoopDescriptor* GetLoopDescriptor() { return loop_desc_; } 159 // Returns the loop on which the object operates on. GetLoop()160 Loop* GetLoop() const { return loop_; } 161 // Returns the function that |loop_| belong to. GetFunction()162 Function* GetFunction() const { return &function_; } 163 164 private: 165 IRContext* context_; 166 LoopDescriptor* loop_desc_; 167 Loop* loop_; 168 Function& function_; 169 170 // Populates the loop nest of |new_loop| according to |loop_| nest. 171 void PopulateLoopNest(Loop* new_loop, 172 const LoopCloningResult& cloning_result) const; 173 174 // Populates |new_loop| descriptor according to |old_loop|'s one. 175 void PopulateLoopDesc(Loop* new_loop, Loop* old_loop, 176 const LoopCloningResult& cloning_result) const; 177 }; 178 179 } // namespace opt 180 } // namespace spvtools 181 182 #endif // SOURCE_OPT_LOOP_UTILS_H_ 183