| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2017 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include "superblock_cloner.h" | 
 | 18 |  | 
 | 19 | #include "common_dominator.h" | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 20 | #include "induction_var_range.h" | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 21 | #include "graph_checker.h" | 
 | 22 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 23 | #include <sstream> | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 24 |  | 
| Vladimir Marko | 0a51605 | 2019-10-14 13:00:44 +0000 | [diff] [blame] | 25 | namespace art { | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 26 |  | 
 | 27 | using HBasicBlockMap = SuperblockCloner::HBasicBlockMap; | 
 | 28 | using HInstructionMap = SuperblockCloner::HInstructionMap; | 
 | 29 | using HBasicBlockSet = SuperblockCloner::HBasicBlockSet; | 
 | 30 | using HEdgeSet = SuperblockCloner::HEdgeSet; | 
 | 31 |  | 
 | 32 | void HEdge::Dump(std::ostream& stream) const { | 
 | 33 |   stream << "(" << from_ << "->" << to_ << ")"; | 
 | 34 | } | 
 | 35 |  | 
 | 36 | // | 
 | 37 | // Static helper methods. | 
 | 38 | // | 
 | 39 |  | 
 | 40 | // Returns whether instruction has any uses (regular or environmental) outside the region, | 
 | 41 | // defined by basic block set. | 
 | 42 | static bool IsUsedOutsideRegion(const HInstruction* instr, const HBasicBlockSet& bb_set) { | 
 | 43 |   auto& uses = instr->GetUses(); | 
 | 44 |   for (auto use_node = uses.begin(), e = uses.end(); use_node != e; ++use_node) { | 
 | 45 |     HInstruction* user = use_node->GetUser(); | 
 | 46 |     if (!bb_set.IsBitSet(user->GetBlock()->GetBlockId())) { | 
 | 47 |       return true; | 
 | 48 |     } | 
 | 49 |   } | 
 | 50 |  | 
 | 51 |   auto& env_uses = instr->GetEnvUses(); | 
 | 52 |   for (auto use_node = env_uses.begin(), e = env_uses.end(); use_node != e; ++use_node) { | 
 | 53 |     HInstruction* user = use_node->GetUser()->GetHolder(); | 
 | 54 |     if (!bb_set.IsBitSet(user->GetBlock()->GetBlockId())) { | 
 | 55 |       return true; | 
 | 56 |     } | 
 | 57 |   } | 
 | 58 |  | 
 | 59 |   return false; | 
 | 60 | } | 
 | 61 |  | 
 | 62 | // Returns whether the phi's inputs are the same HInstruction. | 
 | 63 | static bool ArePhiInputsTheSame(const HPhi* phi) { | 
 | 64 |   HInstruction* first_input = phi->InputAt(0); | 
 | 65 |   for (size_t i = 1, e = phi->InputCount(); i < e; i++) { | 
 | 66 |     if (phi->InputAt(i) != first_input) { | 
 | 67 |       return false; | 
 | 68 |     } | 
 | 69 |   } | 
 | 70 |  | 
 | 71 |   return true; | 
 | 72 | } | 
 | 73 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 74 | // Returns whether two Edge sets are equal (ArenaHashSet doesn't have "Equal" method). | 
 | 75 | static bool EdgeHashSetsEqual(const HEdgeSet* set1, const HEdgeSet* set2) { | 
| Vladimir Marko | 54159c6 | 2018-06-20 14:30:08 +0100 | [diff] [blame] | 76 |   if (set1->size() != set2->size()) { | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 77 |     return false; | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 78 |   } | 
 | 79 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 80 |   for (auto e : *set1) { | 
| Vladimir Marko | 54159c6 | 2018-06-20 14:30:08 +0100 | [diff] [blame] | 81 |     if (set2->find(e) == set2->end()) { | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 82 |       return false; | 
 | 83 |     } | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 84 |   } | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 85 |   return true; | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 86 | } | 
 | 87 |  | 
 | 88 | // Calls HGraph::OrderLoopHeaderPredecessors for each loop in the graph. | 
 | 89 | static void OrderLoopsHeadersPredecessors(HGraph* graph) { | 
 | 90 |   for (HBasicBlock* block : graph->GetPostOrder()) { | 
 | 91 |     if (block->IsLoopHeader()) { | 
 | 92 |       graph->OrderLoopHeaderPredecessors(block); | 
 | 93 |     } | 
 | 94 |   } | 
 | 95 | } | 
 | 96 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 97 | // Performs DFS on the subgraph (specified by 'bb_set') starting from the specified block; while | 
 | 98 | // traversing the function removes basic blocks from the bb_set (instead of traditional DFS | 
 | 99 | // 'marking'). So what is left in the 'bb_set' after the traversal is not reachable from the start | 
 | 100 | // block. | 
 | 101 | static void TraverseSubgraphForConnectivity(HBasicBlock* block, HBasicBlockSet* bb_set) { | 
 | 102 |   DCHECK(bb_set->IsBitSet(block->GetBlockId())); | 
 | 103 |   bb_set->ClearBit(block->GetBlockId()); | 
 | 104 |  | 
 | 105 |   for (HBasicBlock* succ : block->GetSuccessors()) { | 
 | 106 |     if (bb_set->IsBitSet(succ->GetBlockId())) { | 
 | 107 |       TraverseSubgraphForConnectivity(succ, bb_set); | 
 | 108 |     } | 
 | 109 |   } | 
 | 110 | } | 
 | 111 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 112 | // | 
 | 113 | // Helpers for CloneBasicBlock. | 
 | 114 | // | 
 | 115 |  | 
 | 116 | void SuperblockCloner::ReplaceInputsWithCopies(HInstruction* copy_instr) { | 
 | 117 |   DCHECK(!copy_instr->IsPhi()); | 
 | 118 |   for (size_t i = 0, e = copy_instr->InputCount(); i < e; i++) { | 
 | 119 |     // Copy instruction holds the same input as the original instruction holds. | 
 | 120 |     HInstruction* orig_input = copy_instr->InputAt(i); | 
 | 121 |     if (!IsInOrigBBSet(orig_input->GetBlock())) { | 
 | 122 |       // Defined outside the subgraph. | 
 | 123 |       continue; | 
 | 124 |     } | 
 | 125 |     HInstruction* copy_input = GetInstrCopy(orig_input); | 
 | 126 |     // copy_instr will be registered as a user of copy_inputs after returning from this function: | 
 | 127 |     // 'copy_block->AddInstruction(copy_instr)'. | 
 | 128 |     copy_instr->SetRawInputAt(i, copy_input); | 
 | 129 |   } | 
 | 130 | } | 
 | 131 |  | 
 | 132 | void SuperblockCloner::DeepCloneEnvironmentWithRemapping(HInstruction* copy_instr, | 
 | 133 |                                                          const HEnvironment* orig_env) { | 
 | 134 |   if (orig_env->GetParent() != nullptr) { | 
 | 135 |     DeepCloneEnvironmentWithRemapping(copy_instr, orig_env->GetParent()); | 
 | 136 |   } | 
 | 137 |   HEnvironment* copy_env = new (arena_) HEnvironment(arena_, *orig_env, copy_instr); | 
 | 138 |  | 
 | 139 |   for (size_t i = 0; i < orig_env->Size(); i++) { | 
 | 140 |     HInstruction* env_input = orig_env->GetInstructionAt(i); | 
 | 141 |     if (env_input != nullptr && IsInOrigBBSet(env_input->GetBlock())) { | 
 | 142 |       env_input = GetInstrCopy(env_input); | 
 | 143 |       DCHECK(env_input != nullptr && env_input->GetBlock() != nullptr); | 
 | 144 |     } | 
 | 145 |     copy_env->SetRawEnvAt(i, env_input); | 
 | 146 |     if (env_input != nullptr) { | 
 | 147 |       env_input->AddEnvUseAt(copy_env, i); | 
 | 148 |     } | 
 | 149 |   } | 
 | 150 |   // InsertRawEnvironment assumes that instruction already has an environment that's why we use | 
 | 151 |   // SetRawEnvironment in the 'else' case. | 
 | 152 |   // As this function calls itself recursively with the same copy_instr - this copy_instr may | 
 | 153 |   // have partially copied chain of HEnvironments. | 
 | 154 |   if (copy_instr->HasEnvironment()) { | 
 | 155 |     copy_instr->InsertRawEnvironment(copy_env); | 
 | 156 |   } else { | 
 | 157 |     copy_instr->SetRawEnvironment(copy_env); | 
 | 158 |   } | 
 | 159 | } | 
 | 160 |  | 
 | 161 | // | 
 | 162 | // Helpers for RemapEdgesSuccessors. | 
 | 163 | // | 
 | 164 |  | 
 | 165 | void SuperblockCloner::RemapOrigInternalOrIncomingEdge(HBasicBlock* orig_block, | 
 | 166 |                                                        HBasicBlock* orig_succ) { | 
 | 167 |   DCHECK(IsInOrigBBSet(orig_succ)); | 
 | 168 |   HBasicBlock* copy_succ = GetBlockCopy(orig_succ); | 
 | 169 |  | 
 | 170 |   size_t this_index = orig_succ->GetPredecessorIndexOf(orig_block); | 
 | 171 |   size_t phi_input_count = 0; | 
 | 172 |   // This flag reflects whether the original successor has at least one phi and this phi | 
 | 173 |   // has been already processed in the loop. Used for validation purposes in DCHECK to check that | 
 | 174 |   // in the end all of the phis in the copy successor have the same number of inputs - the number | 
 | 175 |   // of copy successor's predecessors. | 
 | 176 |   bool first_phi_met = false; | 
 | 177 |   for (HInstructionIterator it(orig_succ->GetPhis()); !it.Done(); it.Advance()) { | 
 | 178 |     HPhi* orig_phi = it.Current()->AsPhi(); | 
 | 179 |     HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi(); | 
 | 180 |     HInstruction* orig_phi_input = orig_phi->InputAt(this_index); | 
 | 181 |     // Remove corresponding input for original phi. | 
 | 182 |     orig_phi->RemoveInputAt(this_index); | 
 | 183 |     // Copy phi doesn't yet have either orig_block as predecessor or the input that corresponds | 
 | 184 |     // to orig_block, so add the input at the end of the list. | 
 | 185 |     copy_phi->AddInput(orig_phi_input); | 
 | 186 |     if (!first_phi_met) { | 
 | 187 |       phi_input_count = copy_phi->InputCount(); | 
 | 188 |       first_phi_met = true; | 
 | 189 |     } else { | 
 | 190 |       DCHECK_EQ(phi_input_count, copy_phi->InputCount()); | 
 | 191 |     } | 
 | 192 |   } | 
 | 193 |   // orig_block will be put at the end of the copy_succ's predecessors list; that corresponds | 
 | 194 |   // to the previously added phi inputs position. | 
 | 195 |   orig_block->ReplaceSuccessor(orig_succ, copy_succ); | 
 | 196 |   DCHECK(!first_phi_met || copy_succ->GetPredecessors().size() == phi_input_count); | 
 | 197 | } | 
 | 198 |  | 
 | 199 | void SuperblockCloner::AddCopyInternalEdge(HBasicBlock* orig_block, | 
 | 200 |                                            HBasicBlock* orig_succ) { | 
 | 201 |   DCHECK(IsInOrigBBSet(orig_succ)); | 
 | 202 |   HBasicBlock* copy_block = GetBlockCopy(orig_block); | 
 | 203 |   HBasicBlock* copy_succ = GetBlockCopy(orig_succ); | 
 | 204 |   copy_block->AddSuccessor(copy_succ); | 
 | 205 |  | 
 | 206 |   size_t orig_index = orig_succ->GetPredecessorIndexOf(orig_block); | 
 | 207 |   for (HInstructionIterator it(orig_succ->GetPhis()); !it.Done(); it.Advance()) { | 
 | 208 |     HPhi* orig_phi = it.Current()->AsPhi(); | 
 | 209 |     HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi(); | 
 | 210 |     HInstruction* orig_phi_input = orig_phi->InputAt(orig_index); | 
 | 211 |     copy_phi->AddInput(orig_phi_input); | 
 | 212 |   } | 
 | 213 | } | 
 | 214 |  | 
 | 215 | void SuperblockCloner::RemapCopyInternalEdge(HBasicBlock* orig_block, | 
 | 216 |                                              HBasicBlock* orig_succ) { | 
 | 217 |   DCHECK(IsInOrigBBSet(orig_succ)); | 
 | 218 |   HBasicBlock* copy_block = GetBlockCopy(orig_block); | 
 | 219 |   copy_block->AddSuccessor(orig_succ); | 
 | 220 |   DCHECK(copy_block->HasSuccessor(orig_succ)); | 
 | 221 |  | 
 | 222 |   size_t orig_index = orig_succ->GetPredecessorIndexOf(orig_block); | 
 | 223 |   for (HInstructionIterator it(orig_succ->GetPhis()); !it.Done(); it.Advance()) { | 
 | 224 |     HPhi* orig_phi = it.Current()->AsPhi(); | 
 | 225 |     HInstruction* orig_phi_input = orig_phi->InputAt(orig_index); | 
 | 226 |     orig_phi->AddInput(orig_phi_input); | 
 | 227 |   } | 
 | 228 | } | 
 | 229 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 230 | bool SuperblockCloner::IsRemapInfoForVersioning() const { | 
 | 231 |   return remap_incoming_->empty() && | 
 | 232 |          remap_orig_internal_->empty() && | 
 | 233 |          remap_copy_internal_->empty(); | 
 | 234 | } | 
 | 235 |  | 
 | 236 | void SuperblockCloner::CopyIncomingEdgesForVersioning() { | 
 | 237 |   for (uint32_t orig_block_id : orig_bb_set_.Indexes()) { | 
 | 238 |     HBasicBlock* orig_block = GetBlockById(orig_block_id); | 
 | 239 |     size_t incoming_edge_count = 0; | 
 | 240 |     for (HBasicBlock* orig_pred : orig_block->GetPredecessors()) { | 
 | 241 |       uint32_t orig_pred_id = orig_pred->GetBlockId(); | 
 | 242 |       if (IsInOrigBBSet(orig_pred_id)) { | 
 | 243 |         continue; | 
 | 244 |       } | 
 | 245 |  | 
 | 246 |       HBasicBlock* copy_block = GetBlockCopy(orig_block); | 
 | 247 |       // This corresponds to the requirement on the order of predecessors: all the incoming | 
 | 248 |       // edges must be seen before the internal ones. This is always true for natural loops. | 
 | 249 |       // TODO: remove this requirement. | 
 | 250 |       DCHECK_EQ(orig_block->GetPredecessorIndexOf(orig_pred), incoming_edge_count); | 
 | 251 |       for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) { | 
 | 252 |         HPhi* orig_phi = it.Current()->AsPhi(); | 
 | 253 |         HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi(); | 
 | 254 |         HInstruction* orig_phi_input = orig_phi->InputAt(incoming_edge_count); | 
 | 255 |         // Add the corresponding input of the original phi to the copy one. | 
 | 256 |         copy_phi->AddInput(orig_phi_input); | 
 | 257 |       } | 
 | 258 |       copy_block->AddPredecessor(orig_pred); | 
 | 259 |       incoming_edge_count++; | 
 | 260 |     } | 
 | 261 |   } | 
 | 262 | } | 
 | 263 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 264 | // | 
 | 265 | // Local versions of CF calculation/adjustment routines. | 
 | 266 | // | 
 | 267 |  | 
 | 268 | // TODO: merge with the original version in nodes.cc. The concern is that we don't want to affect | 
 | 269 | // the performance of the base version by checking the local set. | 
 | 270 | // TODO: this version works when updating the back edges info for natural loop-based local_set. | 
 | 271 | // Check which exactly types of subgraphs can be analysed or rename it to | 
 | 272 | // FindBackEdgesInTheNaturalLoop. | 
 | 273 | void SuperblockCloner::FindBackEdgesLocal(HBasicBlock* entry_block, ArenaBitVector* local_set) { | 
 | 274 |   ArenaBitVector visited(arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner); | 
 | 275 |   // "visited" must be empty on entry, it's an output argument for all visited (i.e. live) blocks. | 
 | 276 |   DCHECK_EQ(visited.GetHighestBitSet(), -1); | 
 | 277 |  | 
 | 278 |   // Nodes that we're currently visiting, indexed by block id. | 
 | 279 |   ArenaBitVector visiting(arena_, graph_->GetBlocks().size(), false, kArenaAllocGraphBuilder); | 
 | 280 |   // Number of successors visited from a given node, indexed by block id. | 
 | 281 |   ArenaVector<size_t> successors_visited(graph_->GetBlocks().size(), | 
 | 282 |                                          0u, | 
 | 283 |                                          arena_->Adapter(kArenaAllocGraphBuilder)); | 
 | 284 |   // Stack of nodes that we're currently visiting (same as marked in "visiting" above). | 
 | 285 |   ArenaVector<HBasicBlock*> worklist(arena_->Adapter(kArenaAllocGraphBuilder)); | 
 | 286 |   constexpr size_t kDefaultWorklistSize = 8; | 
 | 287 |   worklist.reserve(kDefaultWorklistSize); | 
 | 288 |  | 
 | 289 |   visited.SetBit(entry_block->GetBlockId()); | 
 | 290 |   visiting.SetBit(entry_block->GetBlockId()); | 
 | 291 |   worklist.push_back(entry_block); | 
 | 292 |  | 
 | 293 |   while (!worklist.empty()) { | 
 | 294 |     HBasicBlock* current = worklist.back(); | 
 | 295 |     uint32_t current_id = current->GetBlockId(); | 
 | 296 |     if (successors_visited[current_id] == current->GetSuccessors().size()) { | 
 | 297 |       visiting.ClearBit(current_id); | 
 | 298 |       worklist.pop_back(); | 
 | 299 |     } else { | 
 | 300 |       HBasicBlock* successor = current->GetSuccessors()[successors_visited[current_id]++]; | 
 | 301 |       uint32_t successor_id = successor->GetBlockId(); | 
 | 302 |       if (!local_set->IsBitSet(successor_id)) { | 
 | 303 |         continue; | 
 | 304 |       } | 
 | 305 |  | 
 | 306 |       if (visiting.IsBitSet(successor_id)) { | 
 | 307 |         DCHECK(ContainsElement(worklist, successor)); | 
 | 308 |         successor->AddBackEdgeWhileUpdating(current); | 
 | 309 |       } else if (!visited.IsBitSet(successor_id)) { | 
 | 310 |         visited.SetBit(successor_id); | 
 | 311 |         visiting.SetBit(successor_id); | 
 | 312 |         worklist.push_back(successor); | 
 | 313 |       } | 
 | 314 |     } | 
 | 315 |   } | 
 | 316 | } | 
 | 317 |  | 
 | 318 | void SuperblockCloner::RecalculateBackEdgesInfo(ArenaBitVector* outer_loop_bb_set) { | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 319 |   HBasicBlock* block_entry = nullptr; | 
 | 320 |  | 
 | 321 |   if (outer_loop_ == nullptr) { | 
 | 322 |     for (auto block : graph_->GetBlocks()) { | 
 | 323 |       if (block != nullptr) { | 
 | 324 |         outer_loop_bb_set->SetBit(block->GetBlockId()); | 
 | 325 |         HLoopInformation* info = block->GetLoopInformation(); | 
 | 326 |         if (info != nullptr) { | 
 | 327 |           info->ResetBasicBlockData(); | 
 | 328 |         } | 
 | 329 |       } | 
 | 330 |     } | 
 | 331 |     block_entry = graph_->GetEntryBlock(); | 
 | 332 |   } else { | 
 | 333 |     outer_loop_bb_set->Copy(&outer_loop_bb_set_); | 
 | 334 |     block_entry = outer_loop_->GetHeader(); | 
 | 335 |  | 
 | 336 |     // Add newly created copy blocks. | 
 | 337 |     for (auto entry : *bb_map_) { | 
 | 338 |       outer_loop_bb_set->SetBit(entry.second->GetBlockId()); | 
 | 339 |     } | 
 | 340 |  | 
 | 341 |     // Clear loop_info for the whole outer loop. | 
 | 342 |     for (uint32_t idx : outer_loop_bb_set->Indexes()) { | 
 | 343 |       HBasicBlock* block = GetBlockById(idx); | 
 | 344 |       HLoopInformation* info = block->GetLoopInformation(); | 
 | 345 |       if (info != nullptr) { | 
 | 346 |         info->ResetBasicBlockData(); | 
 | 347 |       } | 
 | 348 |     } | 
 | 349 |   } | 
 | 350 |  | 
 | 351 |   FindBackEdgesLocal(block_entry, outer_loop_bb_set); | 
 | 352 |  | 
 | 353 |   for (uint32_t idx : outer_loop_bb_set->Indexes()) { | 
 | 354 |     HBasicBlock* block = GetBlockById(idx); | 
 | 355 |     HLoopInformation* info = block->GetLoopInformation(); | 
 | 356 |     // Reset LoopInformation for regular blocks and old headers which are no longer loop headers. | 
 | 357 |     if (info != nullptr && | 
 | 358 |         (info->GetHeader() != block || info->NumberOfBackEdges() == 0)) { | 
 | 359 |       block->SetLoopInformation(nullptr); | 
 | 360 |     } | 
 | 361 |   } | 
 | 362 | } | 
 | 363 |  | 
 | 364 | // This is a modified version of HGraph::AnalyzeLoops. | 
 | 365 | GraphAnalysisResult SuperblockCloner::AnalyzeLoopsLocally(ArenaBitVector* outer_loop_bb_set) { | 
 | 366 |   // We iterate post order to ensure we visit inner loops before outer loops. | 
 | 367 |   // `PopulateRecursive` needs this guarantee to know whether a natural loop | 
 | 368 |   // contains an irreducible loop. | 
 | 369 |   for (HBasicBlock* block : graph_->GetPostOrder()) { | 
 | 370 |     if (!outer_loop_bb_set->IsBitSet(block->GetBlockId())) { | 
 | 371 |       continue; | 
 | 372 |     } | 
 | 373 |     if (block->IsLoopHeader()) { | 
 | 374 |       if (block->IsCatchBlock()) { | 
 | 375 |         // TODO: Dealing with exceptional back edges could be tricky because | 
 | 376 |         //       they only approximate the real control flow. Bail out for now. | 
 | 377 |         return kAnalysisFailThrowCatchLoop; | 
 | 378 |       } | 
 | 379 |       block->GetLoopInformation()->Populate(); | 
 | 380 |     } | 
 | 381 |   } | 
 | 382 |  | 
 | 383 |   for (HBasicBlock* block : graph_->GetPostOrder()) { | 
 | 384 |     if (!outer_loop_bb_set->IsBitSet(block->GetBlockId())) { | 
 | 385 |       continue; | 
 | 386 |     } | 
 | 387 |     if (block->IsLoopHeader()) { | 
 | 388 |       HLoopInformation* cur_loop = block->GetLoopInformation(); | 
 | 389 |       HLoopInformation* outer_loop = cur_loop->GetPreHeader()->GetLoopInformation(); | 
 | 390 |       if (outer_loop != nullptr) { | 
 | 391 |         outer_loop->PopulateInnerLoopUpwards(cur_loop); | 
 | 392 |       } | 
 | 393 |     } | 
 | 394 |   } | 
 | 395 |  | 
 | 396 |   return kAnalysisSuccess; | 
 | 397 | } | 
 | 398 |  | 
 | 399 | void SuperblockCloner::CleanUpControlFlow() { | 
 | 400 |   // TODO: full control flow clean up for now, optimize it. | 
 | 401 |   graph_->ClearDominanceInformation(); | 
 | 402 |  | 
 | 403 |   ArenaBitVector outer_loop_bb_set( | 
 | 404 |       arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner); | 
 | 405 |   RecalculateBackEdgesInfo(&outer_loop_bb_set); | 
 | 406 |  | 
 | 407 |   // TODO: do it locally. | 
 | 408 |   graph_->SimplifyCFG(); | 
 | 409 |   graph_->ComputeDominanceInformation(); | 
 | 410 |  | 
 | 411 |   // AnalyzeLoopsLocally requires a correct post-ordering information which was calculated just | 
 | 412 |   // before in ComputeDominanceInformation. | 
 | 413 |   GraphAnalysisResult result = AnalyzeLoopsLocally(&outer_loop_bb_set); | 
 | 414 |   DCHECK_EQ(result, kAnalysisSuccess); | 
 | 415 |  | 
 | 416 |   // TODO: do it locally | 
 | 417 |   OrderLoopsHeadersPredecessors(graph_); | 
 | 418 |  | 
 | 419 |   graph_->ComputeTryBlockInformation(); | 
 | 420 | } | 
 | 421 |  | 
 | 422 | // | 
 | 423 | // Helpers for ResolveDataFlow | 
 | 424 | // | 
 | 425 |  | 
 | 426 | void SuperblockCloner::ResolvePhi(HPhi* phi) { | 
 | 427 |   HBasicBlock* phi_block = phi->GetBlock(); | 
 | 428 |   for (size_t i = 0, e = phi->InputCount(); i < e; i++) { | 
 | 429 |     HInstruction* input = phi->InputAt(i); | 
 | 430 |     HBasicBlock* input_block = input->GetBlock(); | 
 | 431 |  | 
 | 432 |     // Originally defined outside the region. | 
 | 433 |     if (!IsInOrigBBSet(input_block)) { | 
 | 434 |       continue; | 
 | 435 |     } | 
 | 436 |     HBasicBlock* corresponding_block = phi_block->GetPredecessors()[i]; | 
 | 437 |     if (!IsInOrigBBSet(corresponding_block)) { | 
 | 438 |       phi->ReplaceInput(GetInstrCopy(input), i); | 
 | 439 |     } | 
 | 440 |   } | 
 | 441 | } | 
 | 442 |  | 
 | 443 | // | 
 | 444 | // Main algorithm methods. | 
 | 445 | // | 
 | 446 |  | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 447 | void SuperblockCloner::SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits) const { | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 448 |   DCHECK(exits->empty()); | 
 | 449 |   for (uint32_t block_id : orig_bb_set_.Indexes()) { | 
 | 450 |     HBasicBlock* block = GetBlockById(block_id); | 
 | 451 |     for (HBasicBlock* succ : block->GetSuccessors()) { | 
 | 452 |       if (!IsInOrigBBSet(succ)) { | 
 | 453 |         exits->push_back(succ); | 
 | 454 |       } | 
 | 455 |     } | 
 | 456 |   } | 
 | 457 | } | 
 | 458 |  | 
 | 459 | void SuperblockCloner::FindAndSetLocalAreaForAdjustments() { | 
 | 460 |   DCHECK(outer_loop_ == nullptr); | 
 | 461 |   ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner)); | 
 | 462 |   SearchForSubgraphExits(&exits); | 
 | 463 |  | 
 | 464 |   // For a reducible graph we need to update back-edges and dominance information only for | 
 | 465 |   // the outermost loop which is affected by the transformation - it can be found by picking | 
 | 466 |   // the common most outer loop of loops to which the subgraph exits blocks belong. | 
 | 467 |   // Note: it can a loop or the whole graph (outer_loop_ will be nullptr in this case). | 
 | 468 |   for (HBasicBlock* exit : exits) { | 
 | 469 |     HLoopInformation* loop_exit_loop_info = exit->GetLoopInformation(); | 
 | 470 |     if (loop_exit_loop_info == nullptr) { | 
 | 471 |       outer_loop_ = nullptr; | 
 | 472 |       break; | 
 | 473 |     } | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 474 |     if (outer_loop_ == nullptr) { | 
 | 475 |       // We should not use the initial outer_loop_ value 'nullptr' when finding the most outer | 
 | 476 |       // common loop. | 
 | 477 |       outer_loop_ = loop_exit_loop_info; | 
 | 478 |     } | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 479 |     outer_loop_ = FindCommonLoop(outer_loop_, loop_exit_loop_info); | 
 | 480 |   } | 
 | 481 |  | 
 | 482 |   if (outer_loop_ != nullptr) { | 
 | 483 |     // Save the loop population info as it will be changed later. | 
 | 484 |     outer_loop_bb_set_.Copy(&outer_loop_->GetBlocks()); | 
 | 485 |   } | 
 | 486 | } | 
 | 487 |  | 
 | 488 | void SuperblockCloner::RemapEdgesSuccessors() { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 489 |   // By this stage all the blocks have been copied, copy phis - created with no inputs; | 
 | 490 |   // no copy edges have been created so far. | 
 | 491 |   if (IsRemapInfoForVersioning()) { | 
 | 492 |     CopyIncomingEdgesForVersioning(); | 
 | 493 |   } | 
 | 494 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 495 |   // Redirect incoming edges. | 
 | 496 |   for (HEdge e : *remap_incoming_) { | 
 | 497 |     HBasicBlock* orig_block = GetBlockById(e.GetFrom()); | 
 | 498 |     HBasicBlock* orig_succ = GetBlockById(e.GetTo()); | 
 | 499 |     RemapOrigInternalOrIncomingEdge(orig_block, orig_succ); | 
 | 500 |   } | 
 | 501 |  | 
 | 502 |   // Redirect internal edges. | 
 | 503 |   for (uint32_t orig_block_id : orig_bb_set_.Indexes()) { | 
 | 504 |     HBasicBlock* orig_block = GetBlockById(orig_block_id); | 
 | 505 |  | 
 | 506 |     for (HBasicBlock* orig_succ : orig_block->GetSuccessors()) { | 
 | 507 |       uint32_t orig_succ_id = orig_succ->GetBlockId(); | 
 | 508 |  | 
 | 509 |       // Check for outgoing edge. | 
 | 510 |       if (!IsInOrigBBSet(orig_succ)) { | 
 | 511 |         HBasicBlock* copy_block = GetBlockCopy(orig_block); | 
 | 512 |         copy_block->AddSuccessor(orig_succ); | 
 | 513 |         continue; | 
 | 514 |       } | 
 | 515 |  | 
| Vladimir Marko | 54159c6 | 2018-06-20 14:30:08 +0100 | [diff] [blame] | 516 |       auto orig_redir = remap_orig_internal_->find(HEdge(orig_block_id, orig_succ_id)); | 
 | 517 |       auto copy_redir = remap_copy_internal_->find(HEdge(orig_block_id, orig_succ_id)); | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 518 |  | 
 | 519 |       // Due to construction all successors of copied block were set to original. | 
 | 520 |       if (copy_redir != remap_copy_internal_->end()) { | 
 | 521 |         RemapCopyInternalEdge(orig_block, orig_succ); | 
 | 522 |       } else { | 
 | 523 |         AddCopyInternalEdge(orig_block, orig_succ); | 
 | 524 |       } | 
 | 525 |  | 
 | 526 |       if (orig_redir != remap_orig_internal_->end()) { | 
 | 527 |         RemapOrigInternalOrIncomingEdge(orig_block, orig_succ); | 
 | 528 |       } | 
 | 529 |     } | 
 | 530 |   } | 
 | 531 | } | 
 | 532 |  | 
 | 533 | void SuperblockCloner::AdjustControlFlowInfo() { | 
 | 534 |   ArenaBitVector outer_loop_bb_set( | 
 | 535 |       arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner); | 
 | 536 |   RecalculateBackEdgesInfo(&outer_loop_bb_set); | 
 | 537 |  | 
 | 538 |   graph_->ClearDominanceInformation(); | 
 | 539 |   // TODO: Do it locally. | 
 | 540 |   graph_->ComputeDominanceInformation(); | 
 | 541 | } | 
 | 542 |  | 
 | 543 | // TODO: Current FastCase restriction guarantees that instructions' inputs are already mapped to | 
 | 544 | // the valid values; only phis' inputs must be adjusted. | 
 | 545 | void SuperblockCloner::ResolveDataFlow() { | 
 | 546 |   for (auto entry : *bb_map_) { | 
 | 547 |     HBasicBlock* orig_block = entry.first; | 
 | 548 |  | 
 | 549 |     for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) { | 
 | 550 |       HPhi* orig_phi = it.Current()->AsPhi(); | 
 | 551 |       HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi(); | 
 | 552 |       ResolvePhi(orig_phi); | 
 | 553 |       ResolvePhi(copy_phi); | 
 | 554 |     } | 
 | 555 |     if (kIsDebugBuild) { | 
 | 556 |       // Inputs of instruction copies must be already mapped to correspondent inputs copies. | 
 | 557 |       for (HInstructionIterator it(orig_block->GetInstructions()); !it.Done(); it.Advance()) { | 
 | 558 |         CheckInstructionInputsRemapping(it.Current()); | 
 | 559 |       } | 
 | 560 |     } | 
 | 561 |   } | 
 | 562 | } | 
 | 563 |  | 
 | 564 | // | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 565 | // Helpers for live-outs processing and Subgraph-closed SSA. | 
 | 566 | // | 
 | 567 |  | 
 | 568 | bool SuperblockCloner::CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs) const { | 
 | 569 |   DCHECK(live_outs->empty()); | 
 | 570 |   for (uint32_t idx : orig_bb_set_.Indexes()) { | 
 | 571 |     HBasicBlock* block = GetBlockById(idx); | 
 | 572 |  | 
 | 573 |     for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { | 
 | 574 |       HInstruction* instr = it.Current(); | 
 | 575 |       DCHECK(instr->IsClonable()); | 
 | 576 |  | 
 | 577 |       if (IsUsedOutsideRegion(instr, orig_bb_set_)) { | 
 | 578 |         live_outs->FindOrAdd(instr, instr); | 
 | 579 |       } | 
 | 580 |     } | 
 | 581 |  | 
 | 582 |     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { | 
 | 583 |       HInstruction* instr = it.Current(); | 
 | 584 |       if (!instr->IsClonable()) { | 
 | 585 |         return false; | 
 | 586 |       } | 
 | 587 |  | 
 | 588 |       if (IsUsedOutsideRegion(instr, orig_bb_set_)) { | 
 | 589 |         // TODO: Investigate why HNewInstance, HCheckCast has a requirement for the input. | 
 | 590 |         if (instr->IsLoadClass()) { | 
 | 591 |           return false; | 
 | 592 |         } | 
 | 593 |         live_outs->FindOrAdd(instr, instr); | 
 | 594 |       } | 
 | 595 |     } | 
 | 596 |   } | 
 | 597 |   return true; | 
 | 598 | } | 
 | 599 |  | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 600 | void SuperblockCloner::UpdateInductionRangeInfoOf( | 
 | 601 |       HInstruction* user, HInstruction* old_instruction, HInstruction* replacement) { | 
 | 602 |   if (induction_range_ != nullptr) { | 
 | 603 |     induction_range_->Replace(user, old_instruction, replacement); | 
 | 604 |   } | 
 | 605 | } | 
 | 606 |  | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 607 | void SuperblockCloner::ConstructSubgraphClosedSSA() { | 
 | 608 |   if (live_outs_.empty()) { | 
 | 609 |     return; | 
 | 610 |   } | 
 | 611 |  | 
 | 612 |   ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner)); | 
 | 613 |   SearchForSubgraphExits(&exits); | 
 | 614 |   if (exits.empty()) { | 
 | 615 |     DCHECK(live_outs_.empty()); | 
 | 616 |     return; | 
 | 617 |   } | 
 | 618 |  | 
 | 619 |   DCHECK_EQ(exits.size(), 1u); | 
 | 620 |   HBasicBlock* exit_block = exits[0]; | 
 | 621 |   // There should be no critical edges. | 
 | 622 |   DCHECK_EQ(exit_block->GetPredecessors().size(), 1u); | 
 | 623 |   DCHECK(exit_block->GetPhis().IsEmpty()); | 
 | 624 |  | 
 | 625 |   // For each live-out value insert a phi into the loop exit and replace all the value's uses | 
 | 626 |   // external to the loop with this phi. The phi will have the original value as its only input; | 
 | 627 |   // after copying is done FixSubgraphClosedSSAAfterCloning will add a corresponding copy of the | 
 | 628 |   // original value as the second input thus merging data flow from the original and copy parts of | 
 | 629 |   // the subgraph. Also update the record in the live_outs_ map from (value, value) to | 
 | 630 |   // (value, new_phi). | 
 | 631 |   for (auto live_out_it = live_outs_.begin(); live_out_it != live_outs_.end(); ++live_out_it) { | 
 | 632 |     HInstruction* value = live_out_it->first; | 
 | 633 |     HPhi* phi = new (arena_) HPhi(arena_, kNoRegNumber, 0, value->GetType()); | 
 | 634 |  | 
 | 635 |     if (value->GetType() == DataType::Type::kReference) { | 
 | 636 |       phi->SetReferenceTypeInfo(value->GetReferenceTypeInfo()); | 
 | 637 |     } | 
 | 638 |  | 
 | 639 |     exit_block->AddPhi(phi); | 
 | 640 |     live_out_it->second = phi; | 
 | 641 |  | 
 | 642 |     const HUseList<HInstruction*>& uses = value->GetUses(); | 
 | 643 |     for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) { | 
 | 644 |       HInstruction* user = it->GetUser(); | 
 | 645 |       size_t index = it->GetIndex(); | 
 | 646 |       // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput(). | 
 | 647 |       ++it; | 
 | 648 |       if (!IsInOrigBBSet(user->GetBlock())) { | 
 | 649 |         user->ReplaceInput(phi, index); | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 650 |         UpdateInductionRangeInfoOf(user, value, phi); | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 651 |       } | 
 | 652 |     } | 
 | 653 |  | 
 | 654 |     const HUseList<HEnvironment*>& env_uses = value->GetEnvUses(); | 
 | 655 |     for (auto it = env_uses.begin(), e = env_uses.end(); it != e; /* ++it below */) { | 
 | 656 |       HEnvironment* env = it->GetUser(); | 
 | 657 |       size_t index = it->GetIndex(); | 
 | 658 |       ++it; | 
 | 659 |       if (!IsInOrigBBSet(env->GetHolder()->GetBlock())) { | 
 | 660 |         env->ReplaceInput(phi, index); | 
 | 661 |       } | 
 | 662 |     } | 
 | 663 |  | 
 | 664 |     phi->AddInput(value); | 
 | 665 |   } | 
 | 666 | } | 
 | 667 |  | 
 | 668 | void SuperblockCloner::FixSubgraphClosedSSAAfterCloning() { | 
 | 669 |   for (auto it : live_outs_) { | 
 | 670 |     DCHECK(it.first != it.second); | 
 | 671 |     HInstruction* orig_value = it.first; | 
 | 672 |     HPhi* phi = it.second->AsPhi(); | 
 | 673 |     HInstruction* copy_value = GetInstrCopy(orig_value); | 
 | 674 |     // Copy edges are inserted after the original so we can just add new input to the phi. | 
 | 675 |     phi->AddInput(copy_value); | 
 | 676 |   } | 
 | 677 | } | 
 | 678 |  | 
 | 679 | // | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 680 | // Debug and logging methods. | 
 | 681 | // | 
 | 682 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 683 | // Debug function to dump graph' BasicBlocks info. | 
 | 684 | void DumpBB(HGraph* graph) { | 
 | 685 |   for (HBasicBlock* bb : graph->GetBlocks()) { | 
 | 686 |     if (bb == nullptr) { | 
 | 687 |       continue; | 
 | 688 |     } | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 689 |     std::ostringstream oss; | 
 | 690 |     oss << bb->GetBlockId(); | 
 | 691 |     oss << " <- "; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 692 |     for (HBasicBlock* pred : bb->GetPredecessors()) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 693 |       oss << pred->GetBlockId() << " "; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 694 |     } | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 695 |     oss << " -> "; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 696 |     for (HBasicBlock* succ : bb->GetSuccessors()) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 697 |       oss << succ->GetBlockId()  << " "; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 698 |     } | 
 | 699 |  | 
 | 700 |     if (bb->GetDominator()) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 701 |       oss << " dom " << bb->GetDominator()->GetBlockId(); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 702 |     } | 
 | 703 |  | 
 | 704 |     if (bb->GetLoopInformation()) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 705 |       oss <<  "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId(); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 706 |     } | 
 | 707 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 708 |     LOG(INFO) << oss.str(); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 709 |   } | 
 | 710 | } | 
 | 711 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 712 | void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) { | 
 | 713 |   DCHECK(!orig_instr->IsPhi()); | 
 | 714 |   HInstruction* copy_instr = GetInstrCopy(orig_instr); | 
 | 715 |   for (size_t i = 0, e = orig_instr->InputCount(); i < e; i++) { | 
 | 716 |     HInstruction* orig_input = orig_instr->InputAt(i); | 
 | 717 |     DCHECK(orig_input->GetBlock()->Dominates(orig_instr->GetBlock())); | 
 | 718 |  | 
 | 719 |     // If original input is defined outside the region then it will remain for both original | 
 | 720 |     // instruction and the copy after the transformation. | 
 | 721 |     if (!IsInOrigBBSet(orig_input->GetBlock())) { | 
 | 722 |       continue; | 
 | 723 |     } | 
 | 724 |     HInstruction* copy_input = GetInstrCopy(orig_input); | 
 | 725 |     DCHECK(copy_input->GetBlock()->Dominates(copy_instr->GetBlock())); | 
 | 726 |   } | 
 | 727 |  | 
 | 728 |   // Resolve environment. | 
 | 729 |   if (orig_instr->HasEnvironment()) { | 
 | 730 |     HEnvironment* orig_env = orig_instr->GetEnvironment(); | 
 | 731 |  | 
 | 732 |     for (size_t i = 0, e = orig_env->Size(); i < e; ++i) { | 
 | 733 |       HInstruction* orig_input = orig_env->GetInstructionAt(i); | 
 | 734 |  | 
 | 735 |       // If original input is defined outside the region then it will remain for both original | 
 | 736 |       // instruction and the copy after the transformation. | 
 | 737 |       if (orig_input == nullptr || !IsInOrigBBSet(orig_input->GetBlock())) { | 
 | 738 |         continue; | 
 | 739 |       } | 
 | 740 |  | 
 | 741 |       HInstruction* copy_input = GetInstrCopy(orig_input); | 
 | 742 |       DCHECK(copy_input->GetBlock()->Dominates(copy_instr->GetBlock())); | 
 | 743 |     } | 
 | 744 |   } | 
 | 745 | } | 
 | 746 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 747 | bool SuperblockCloner::CheckRemappingInfoIsValid() { | 
 | 748 |   for (HEdge edge : *remap_orig_internal_) { | 
 | 749 |     if (!IsEdgeValid(edge, graph_) || | 
 | 750 |         !IsInOrigBBSet(edge.GetFrom()) || | 
 | 751 |         !IsInOrigBBSet(edge.GetTo())) { | 
 | 752 |       return false; | 
 | 753 |     } | 
 | 754 |   } | 
 | 755 |  | 
 | 756 |   for (auto edge : *remap_copy_internal_) { | 
 | 757 |     if (!IsEdgeValid(edge, graph_) || | 
 | 758 |         !IsInOrigBBSet(edge.GetFrom()) || | 
 | 759 |         !IsInOrigBBSet(edge.GetTo())) { | 
 | 760 |       return false; | 
 | 761 |     } | 
 | 762 |   } | 
 | 763 |  | 
 | 764 |   for (auto edge : *remap_incoming_) { | 
 | 765 |     if (!IsEdgeValid(edge, graph_) || | 
 | 766 |         IsInOrigBBSet(edge.GetFrom()) || | 
 | 767 |         !IsInOrigBBSet(edge.GetTo())) { | 
 | 768 |       return false; | 
 | 769 |     } | 
 | 770 |   } | 
 | 771 |  | 
 | 772 |   return true; | 
 | 773 | } | 
 | 774 |  | 
 | 775 | void SuperblockCloner::VerifyGraph() { | 
 | 776 |   for (auto it : *hir_map_) { | 
 | 777 |     HInstruction* orig_instr = it.first; | 
 | 778 |     HInstruction* copy_instr = it.second; | 
 | 779 |     if (!orig_instr->IsPhi() && !orig_instr->IsSuspendCheck()) { | 
 | 780 |       DCHECK(it.first->GetBlock() != nullptr); | 
 | 781 |     } | 
 | 782 |     if (!copy_instr->IsPhi() && !copy_instr->IsSuspendCheck()) { | 
 | 783 |       DCHECK(it.second->GetBlock() != nullptr); | 
 | 784 |     } | 
 | 785 |   } | 
| Artem Serov | c8150b5 | 2019-07-31 18:28:00 +0100 | [diff] [blame] | 786 |   if (kSuperblockClonerVerify) { | 
 | 787 |     GraphChecker checker(graph_); | 
 | 788 |     checker.Run(); | 
 | 789 |     if (!checker.IsValid()) { | 
 | 790 |       for (const std::string& error : checker.GetErrors()) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 791 |         LOG(ERROR) << error; | 
| Artem Serov | c8150b5 | 2019-07-31 18:28:00 +0100 | [diff] [blame] | 792 |       } | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 793 |       LOG(FATAL) << "GraphChecker failed: superblock cloner"; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 794 |     } | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 795 |   } | 
 | 796 | } | 
 | 797 |  | 
 | 798 | void DumpBBSet(const ArenaBitVector* set) { | 
 | 799 |   for (uint32_t idx : set->Indexes()) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 800 |     LOG(INFO) << idx; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 801 |   } | 
 | 802 | } | 
 | 803 |  | 
 | 804 | void SuperblockCloner::DumpInputSets() { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 805 |   LOG(INFO) << "orig_bb_set:"; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 806 |   for (uint32_t idx : orig_bb_set_.Indexes()) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 807 |     LOG(INFO) << idx; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 808 |   } | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 809 |   LOG(INFO) << "remap_orig_internal:"; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 810 |   for (HEdge e : *remap_orig_internal_) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 811 |     LOG(INFO) << e; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 812 |   } | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 813 |   LOG(INFO) << "remap_copy_internal:"; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 814 |   for (auto e : *remap_copy_internal_) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 815 |     LOG(INFO) << e; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 816 |   } | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 817 |   LOG(INFO) << "remap_incoming:"; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 818 |   for (auto e : *remap_incoming_) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 819 |     LOG(INFO) << e; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 820 |   } | 
 | 821 | } | 
 | 822 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 823 | // | 
 | 824 | // Public methods. | 
 | 825 | // | 
 | 826 |  | 
 | 827 | SuperblockCloner::SuperblockCloner(HGraph* graph, | 
 | 828 |                                    const HBasicBlockSet* orig_bb_set, | 
 | 829 |                                    HBasicBlockMap* bb_map, | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 830 |                                    HInstructionMap* hir_map, | 
 | 831 |                                    InductionVarRange* induction_range) | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 832 |   : graph_(graph), | 
 | 833 |     arena_(graph->GetAllocator()), | 
 | 834 |     orig_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner), | 
 | 835 |     remap_orig_internal_(nullptr), | 
 | 836 |     remap_copy_internal_(nullptr), | 
 | 837 |     remap_incoming_(nullptr), | 
 | 838 |     bb_map_(bb_map), | 
 | 839 |     hir_map_(hir_map), | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 840 |     induction_range_(induction_range), | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 841 |     outer_loop_(nullptr), | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 842 |     outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner), | 
 | 843 |     live_outs_(std::less<HInstruction*>(), | 
 | 844 |         graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)) { | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 845 |   orig_bb_set_.Copy(orig_bb_set); | 
 | 846 | } | 
 | 847 |  | 
 | 848 | void SuperblockCloner::SetSuccessorRemappingInfo(const HEdgeSet* remap_orig_internal, | 
 | 849 |                                                  const HEdgeSet* remap_copy_internal, | 
 | 850 |                                                  const HEdgeSet* remap_incoming) { | 
 | 851 |   remap_orig_internal_ = remap_orig_internal; | 
 | 852 |   remap_copy_internal_ = remap_copy_internal; | 
 | 853 |   remap_incoming_ = remap_incoming; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 854 |   DCHECK(CheckRemappingInfoIsValid()); | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 855 | } | 
 | 856 |  | 
 | 857 | bool SuperblockCloner::IsSubgraphClonable() const { | 
 | 858 |   // TODO: Support irreducible graphs and graphs with try-catch. | 
 | 859 |   if (graph_->HasIrreducibleLoops() || graph_->HasTryCatch()) { | 
 | 860 |     return false; | 
 | 861 |   } | 
 | 862 |  | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 863 |   HInstructionMap live_outs( | 
 | 864 |       std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 865 |  | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 866 |   if (!CollectLiveOutsAndCheckClonable(&live_outs)) { | 
 | 867 |     return false; | 
 | 868 |   } | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 869 |  | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 870 |   ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner)); | 
 | 871 |   SearchForSubgraphExits(&exits); | 
 | 872 |  | 
 | 873 |   // The only loops with live-outs which are currently supported are loops with a single exit. | 
 | 874 |   if (!live_outs.empty() && exits.size() != 1) { | 
 | 875 |     return false; | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 876 |   } | 
 | 877 |  | 
 | 878 |   return true; | 
 | 879 | } | 
 | 880 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 881 | // Checks that loop unrolling/peeling/versioning is being conducted. | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 882 | bool SuperblockCloner::IsFastCase() const { | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 883 |   // Check that all the basic blocks belong to the same loop. | 
 | 884 |   bool flag = false; | 
 | 885 |   HLoopInformation* common_loop_info = nullptr; | 
 | 886 |   for (uint32_t idx : orig_bb_set_.Indexes()) { | 
 | 887 |     HBasicBlock* block = GetBlockById(idx); | 
 | 888 |     HLoopInformation* block_loop_info = block->GetLoopInformation(); | 
 | 889 |     if (!flag) { | 
 | 890 |       common_loop_info = block_loop_info; | 
 | 891 |     } else { | 
 | 892 |       if (block_loop_info != common_loop_info) { | 
 | 893 |         return false; | 
 | 894 |       } | 
 | 895 |     } | 
 | 896 |   } | 
 | 897 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 898 |   // Check that orig_bb_set_ corresponds to loop peeling/unrolling/versioning. | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 899 |   if (common_loop_info == nullptr || !orig_bb_set_.SameBitsSet(&common_loop_info->GetBlocks())) { | 
 | 900 |     return false; | 
 | 901 |   } | 
 | 902 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 903 |   if (IsRemapInfoForVersioning()) { | 
 | 904 |     return true; | 
 | 905 |   } | 
 | 906 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 907 |   bool peeling_or_unrolling = false; | 
 | 908 |   HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
 | 909 |   HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
 | 910 |   HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
 | 911 |  | 
 | 912 |  | 
 | 913 |   // Check whether remapping info corresponds to loop unrolling. | 
 | 914 |   CollectRemappingInfoForPeelUnroll(/* to_unroll*/ true, | 
 | 915 |                                     common_loop_info, | 
 | 916 |                                     &remap_orig_internal, | 
 | 917 |                                     &remap_copy_internal, | 
 | 918 |                                     &remap_incoming); | 
 | 919 |  | 
 | 920 |   peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) && | 
 | 921 |                           EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) && | 
 | 922 |                           EdgeHashSetsEqual(&remap_incoming, remap_incoming_); | 
 | 923 |  | 
| Vladimir Marko | 54159c6 | 2018-06-20 14:30:08 +0100 | [diff] [blame] | 924 |   remap_orig_internal.clear(); | 
 | 925 |   remap_copy_internal.clear(); | 
 | 926 |   remap_incoming.clear(); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 927 |  | 
 | 928 |   // Check whether remapping info corresponds to loop peeling. | 
 | 929 |   CollectRemappingInfoForPeelUnroll(/* to_unroll*/ false, | 
 | 930 |                                     common_loop_info, | 
 | 931 |                                     &remap_orig_internal, | 
 | 932 |                                     &remap_copy_internal, | 
 | 933 |                                     &remap_incoming); | 
 | 934 |  | 
 | 935 |   peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) && | 
 | 936 |                           EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) && | 
 | 937 |                           EdgeHashSetsEqual(&remap_incoming, remap_incoming_); | 
 | 938 |  | 
 | 939 |   return peeling_or_unrolling; | 
 | 940 | } | 
 | 941 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 942 | void SuperblockCloner::Run() { | 
 | 943 |   DCHECK(bb_map_ != nullptr); | 
 | 944 |   DCHECK(hir_map_ != nullptr); | 
 | 945 |   DCHECK(remap_orig_internal_ != nullptr && | 
 | 946 |          remap_copy_internal_ != nullptr && | 
 | 947 |          remap_incoming_ != nullptr); | 
 | 948 |   DCHECK(IsSubgraphClonable()); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 949 |   DCHECK(IsFastCase()); | 
 | 950 |  | 
 | 951 |   if (kSuperblockClonerLogging) { | 
 | 952 |     DumpInputSets(); | 
 | 953 |   } | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 954 |  | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 955 |   CollectLiveOutsAndCheckClonable(&live_outs_); | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 956 |   // Find an area in the graph for which control flow information should be adjusted. | 
 | 957 |   FindAndSetLocalAreaForAdjustments(); | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 958 |   ConstructSubgraphClosedSSA(); | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 959 |   // Clone the basic blocks from the orig_bb_set_; data flow is invalid after the call and is to be | 
 | 960 |   // adjusted. | 
 | 961 |   CloneBasicBlocks(); | 
 | 962 |   // Connect the blocks together/remap successors and fix phis which are directly affected my the | 
 | 963 |   // remapping. | 
 | 964 |   RemapEdgesSuccessors(); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 965 |  | 
 | 966 |   // Check that the subgraph is connected. | 
 | 967 |   if (kIsDebugBuild) { | 
 | 968 |     HBasicBlockSet work_set(arena_, orig_bb_set_.GetSizeOf(), true, kArenaAllocSuperblockCloner); | 
 | 969 |  | 
 | 970 |     // Add original and copy blocks of the subgraph to the work set. | 
 | 971 |     for (auto iter : *bb_map_) { | 
 | 972 |       work_set.SetBit(iter.first->GetBlockId());   // Original block. | 
 | 973 |       work_set.SetBit(iter.second->GetBlockId());  // Copy block. | 
 | 974 |     } | 
 | 975 |     CHECK(IsSubgraphConnected(&work_set, graph_)); | 
 | 976 |   } | 
 | 977 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 978 |   // Recalculate dominance and backedge information which is required by the next stage. | 
 | 979 |   AdjustControlFlowInfo(); | 
 | 980 |   // Fix data flow of the graph. | 
 | 981 |   ResolveDataFlow(); | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 982 |   FixSubgraphClosedSSAAfterCloning(); | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 983 | } | 
 | 984 |  | 
 | 985 | void SuperblockCloner::CleanUp() { | 
 | 986 |   CleanUpControlFlow(); | 
 | 987 |  | 
 | 988 |   // Remove phis which have all inputs being same. | 
 | 989 |   // When a block has a single predecessor it must not have any phis. However after the | 
 | 990 |   // transformation it could happen that there is such block with a phi with a single input. | 
 | 991 |   // As this is needed to be processed we also simplify phis with multiple same inputs here. | 
 | 992 |   for (auto entry : *bb_map_) { | 
 | 993 |     HBasicBlock* orig_block = entry.first; | 
 | 994 |     for (HInstructionIterator inst_it(orig_block->GetPhis()); !inst_it.Done(); inst_it.Advance()) { | 
 | 995 |       HPhi* phi = inst_it.Current()->AsPhi(); | 
 | 996 |       if (ArePhiInputsTheSame(phi)) { | 
 | 997 |         phi->ReplaceWith(phi->InputAt(0)); | 
 | 998 |         orig_block->RemovePhi(phi); | 
 | 999 |       } | 
 | 1000 |     } | 
 | 1001 |  | 
 | 1002 |     HBasicBlock* copy_block = GetBlockCopy(orig_block); | 
 | 1003 |     for (HInstructionIterator inst_it(copy_block->GetPhis()); !inst_it.Done(); inst_it.Advance()) { | 
 | 1004 |       HPhi* phi = inst_it.Current()->AsPhi(); | 
 | 1005 |       if (ArePhiInputsTheSame(phi)) { | 
 | 1006 |         phi->ReplaceWith(phi->InputAt(0)); | 
 | 1007 |         copy_block->RemovePhi(phi); | 
 | 1008 |       } | 
 | 1009 |     } | 
 | 1010 |   } | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1011 |  | 
| Artem Serov | 121f203 | 2017-10-23 19:19:06 +0100 | [diff] [blame] | 1012 |   if (kIsDebugBuild) { | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1013 |     VerifyGraph(); | 
 | 1014 |   } | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 1015 | } | 
 | 1016 |  | 
 | 1017 | HBasicBlock* SuperblockCloner::CloneBasicBlock(const HBasicBlock* orig_block) { | 
 | 1018 |   HGraph* graph = orig_block->GetGraph(); | 
 | 1019 |   HBasicBlock* copy_block = new (arena_) HBasicBlock(graph, orig_block->GetDexPc()); | 
 | 1020 |   graph->AddBlock(copy_block); | 
 | 1021 |  | 
 | 1022 |   // Clone all the phis and add them to the map. | 
 | 1023 |   for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) { | 
 | 1024 |     HInstruction* orig_instr = it.Current(); | 
 | 1025 |     HInstruction* copy_instr = orig_instr->Clone(arena_); | 
 | 1026 |     copy_block->AddPhi(copy_instr->AsPhi()); | 
 | 1027 |     copy_instr->AsPhi()->RemoveAllInputs(); | 
 | 1028 |     DCHECK(!orig_instr->HasEnvironment()); | 
 | 1029 |     hir_map_->Put(orig_instr, copy_instr); | 
 | 1030 |   } | 
 | 1031 |  | 
 | 1032 |   // Clone all the instructions and add them to the map. | 
 | 1033 |   for (HInstructionIterator it(orig_block->GetInstructions()); !it.Done(); it.Advance()) { | 
 | 1034 |     HInstruction* orig_instr = it.Current(); | 
 | 1035 |     HInstruction* copy_instr = orig_instr->Clone(arena_); | 
 | 1036 |     ReplaceInputsWithCopies(copy_instr); | 
 | 1037 |     copy_block->AddInstruction(copy_instr); | 
 | 1038 |     if (orig_instr->HasEnvironment()) { | 
 | 1039 |       DeepCloneEnvironmentWithRemapping(copy_instr, orig_instr->GetEnvironment()); | 
 | 1040 |     } | 
 | 1041 |     hir_map_->Put(orig_instr, copy_instr); | 
 | 1042 |   } | 
 | 1043 |  | 
 | 1044 |   return copy_block; | 
 | 1045 | } | 
 | 1046 |  | 
 | 1047 | void SuperblockCloner::CloneBasicBlocks() { | 
 | 1048 |   // By this time ReversePostOrder must be valid: in 'CloneBasicBlock' inputs of the copied | 
 | 1049 |   // instructions might be replaced by copies of the original inputs (depending where those inputs | 
 | 1050 |   // are defined). So the definitions of the original inputs must be visited before their original | 
 | 1051 |   // uses. The property of the reducible graphs "if 'A' dom 'B' then rpo_num('A') >= rpo_num('B')" | 
 | 1052 |   // guarantees that. | 
 | 1053 |   for (HBasicBlock* orig_block : graph_->GetReversePostOrder()) { | 
 | 1054 |     if (!IsInOrigBBSet(orig_block)) { | 
 | 1055 |       continue; | 
 | 1056 |     } | 
 | 1057 |     HBasicBlock* copy_block = CloneBasicBlock(orig_block); | 
 | 1058 |     bb_map_->Put(orig_block, copy_block); | 
 | 1059 |     if (kSuperblockClonerLogging) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 1060 |       LOG(INFO) << "new block :" << copy_block->GetBlockId() << ": " << orig_block->GetBlockId(); | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 1061 |     } | 
 | 1062 |   } | 
 | 1063 | } | 
 | 1064 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1065 | // | 
 | 1066 | // Stand-alone methods. | 
 | 1067 | // | 
 | 1068 |  | 
 | 1069 | void CollectRemappingInfoForPeelUnroll(bool to_unroll, | 
 | 1070 |                                        HLoopInformation* loop_info, | 
 | 1071 |                                        HEdgeSet* remap_orig_internal, | 
 | 1072 |                                        HEdgeSet* remap_copy_internal, | 
 | 1073 |                                        HEdgeSet* remap_incoming) { | 
 | 1074 |   DCHECK(loop_info != nullptr); | 
 | 1075 |   HBasicBlock* loop_header = loop_info->GetHeader(); | 
 | 1076 |   // Set up remap_orig_internal edges set - set is empty. | 
 | 1077 |   // Set up remap_copy_internal edges set. | 
 | 1078 |   for (HBasicBlock* back_edge_block : loop_info->GetBackEdges()) { | 
 | 1079 |     HEdge e = HEdge(back_edge_block, loop_header); | 
 | 1080 |     if (to_unroll) { | 
| Vladimir Marko | 54159c6 | 2018-06-20 14:30:08 +0100 | [diff] [blame] | 1081 |       remap_orig_internal->insert(e); | 
 | 1082 |       remap_copy_internal->insert(e); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1083 |     } else { | 
| Vladimir Marko | 54159c6 | 2018-06-20 14:30:08 +0100 | [diff] [blame] | 1084 |       remap_copy_internal->insert(e); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1085 |     } | 
 | 1086 |   } | 
 | 1087 |  | 
 | 1088 |   // Set up remap_incoming edges set. | 
| Artem Serov | 72411e6 | 2017-10-19 16:18:07 +0100 | [diff] [blame] | 1089 |   if (!to_unroll) { | 
| Vladimir Marko | 54159c6 | 2018-06-20 14:30:08 +0100 | [diff] [blame] | 1090 |     remap_incoming->insert(HEdge(loop_info->GetPreHeader(), loop_header)); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1091 |   } | 
 | 1092 | } | 
 | 1093 |  | 
 | 1094 | bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph) { | 
 | 1095 |   ArenaVector<HBasicBlock*> entry_blocks( | 
 | 1096 |       graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
 | 1097 |  | 
 | 1098 |   // Find subgraph entry blocks. | 
 | 1099 |   for (uint32_t orig_block_id : work_set->Indexes()) { | 
 | 1100 |     HBasicBlock* block = graph->GetBlocks()[orig_block_id]; | 
 | 1101 |     for (HBasicBlock* pred : block->GetPredecessors()) { | 
 | 1102 |       if (!work_set->IsBitSet(pred->GetBlockId())) { | 
 | 1103 |         entry_blocks.push_back(block); | 
 | 1104 |         break; | 
 | 1105 |       } | 
 | 1106 |     } | 
 | 1107 |   } | 
 | 1108 |  | 
 | 1109 |   for (HBasicBlock* entry_block : entry_blocks) { | 
 | 1110 |     if (work_set->IsBitSet(entry_block->GetBlockId())) { | 
 | 1111 |       TraverseSubgraphForConnectivity(entry_block, work_set); | 
 | 1112 |     } | 
 | 1113 |   } | 
 | 1114 |  | 
 | 1115 |   // Return whether there are unvisited - unreachable - blocks. | 
 | 1116 |   return work_set->NumSetBits() == 0; | 
 | 1117 | } | 
 | 1118 |  | 
 | 1119 | HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) { | 
 | 1120 |   if (loop1 == nullptr || loop2 == nullptr) { | 
 | 1121 |     return nullptr; | 
 | 1122 |   } | 
 | 1123 |  | 
 | 1124 |   if (loop1->IsIn(*loop2)) { | 
 | 1125 |     return loop2; | 
 | 1126 |   } | 
 | 1127 |  | 
 | 1128 |   HLoopInformation* current = loop1; | 
 | 1129 |   while (current != nullptr && !loop2->IsIn(*current)) { | 
 | 1130 |     current = current->GetPreHeader()->GetLoopInformation(); | 
 | 1131 |   } | 
 | 1132 |  | 
 | 1133 |   return current; | 
 | 1134 | } | 
 | 1135 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 1136 | bool LoopClonerHelper::IsLoopClonable(HLoopInformation* loop_info) { | 
 | 1137 |   LoopClonerHelper helper( | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 1138 |       loop_info, /* bb_map= */ nullptr, /* hir_map= */ nullptr, /* induction_range= */ nullptr); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1139 |   return helper.IsLoopClonable(); | 
 | 1140 | } | 
 | 1141 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 1142 | HBasicBlock* LoopClonerHelper::DoLoopTransformationImpl(TransformationKind transformation) { | 
 | 1143 |   // For now do transformations only for natural loops. | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1144 |   DCHECK(!loop_info_->IsIrreducible()); | 
 | 1145 |  | 
 | 1146 |   HBasicBlock* loop_header = loop_info_->GetHeader(); | 
| Artem Serov | 72411e6 | 2017-10-19 16:18:07 +0100 | [diff] [blame] | 1147 |   // Check that loop info is up-to-date. | 
 | 1148 |   DCHECK(loop_info_ == loop_header->GetLoopInformation()); | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1149 |   HGraph* graph = loop_header->GetGraph(); | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 1150 |  | 
 | 1151 |   if (kSuperblockClonerLogging) { | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 1152 |     LOG(INFO) << "Method: " << graph->PrettyMethod(); | 
 | 1153 |     std::ostringstream oss; | 
 | 1154 |     oss << "Scalar loop "; | 
 | 1155 |     switch (transformation) { | 
 | 1156 |       case TransformationKind::kPeeling: | 
 | 1157 |         oss << "peeling"; | 
 | 1158 |         break; | 
 | 1159 |       case TransformationKind::kUnrolling: | 
 | 1160 |         oss<< "unrolling"; | 
 | 1161 |         break; | 
 | 1162 |       case TransformationKind::kVersioning: | 
 | 1163 |         oss << "versioning"; | 
 | 1164 |         break; | 
 | 1165 |       default: | 
 | 1166 |         LOG(FATAL) << "Unreachable"; | 
 | 1167 |         UNREACHABLE(); | 
 | 1168 |     } | 
 | 1169 |     oss << " was applied to the loop <" << loop_header->GetBlockId() << ">."; | 
 | 1170 |     LOG(INFO) << oss.str(); | 
| Artem Serov | ca210e3 | 2017-12-15 13:43:20 +0000 | [diff] [blame] | 1171 |   } | 
 | 1172 |  | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1173 |   ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool()); | 
 | 1174 |  | 
 | 1175 |   HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
 | 1176 |   HEdgeSet remap_copy_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
 | 1177 |   HEdgeSet remap_incoming(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)); | 
 | 1178 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 1179 |   // No remapping needed for loop versioning. | 
 | 1180 |   if (transformation != TransformationKind::kVersioning) { | 
 | 1181 |     CollectRemappingInfoForPeelUnroll(transformation == TransformationKind::kUnrolling, | 
 | 1182 |                                       loop_info_, | 
 | 1183 |                                       &remap_orig_internal, | 
 | 1184 |                                       &remap_copy_internal, | 
 | 1185 |                                       &remap_incoming); | 
 | 1186 |   } | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1187 |  | 
 | 1188 |   cloner_.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming); | 
 | 1189 |   cloner_.Run(); | 
 | 1190 |   cloner_.CleanUp(); | 
 | 1191 |  | 
| Artem Serov | 72411e6 | 2017-10-19 16:18:07 +0100 | [diff] [blame] | 1192 |   // Check that loop info is preserved. | 
 | 1193 |   DCHECK(loop_info_ == loop_header->GetLoopInformation()); | 
 | 1194 |  | 
 | 1195 |   return loop_header; | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1196 | } | 
 | 1197 |  | 
| Artem Serov | 0f5b2bf | 2019-10-23 14:07:41 +0100 | [diff] [blame] | 1198 | LoopClonerSimpleHelper::LoopClonerSimpleHelper(HLoopInformation* info, | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 1199 |                                                InductionVarRange* induction_range) | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1200 |   : bb_map_(std::less<HBasicBlock*>(), | 
 | 1201 |             info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)), | 
 | 1202 |     hir_map_(std::less<HInstruction*>(), | 
 | 1203 |              info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)), | 
| Nicolas Geoffray | 256c94b | 2019-04-29 10:55:09 +0100 | [diff] [blame] | 1204 |     helper_(info, &bb_map_, &hir_map_, induction_range) {} | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1205 |  | 
| Artem Serov | 7f4aff6 | 2017-06-21 17:02:18 +0100 | [diff] [blame] | 1206 | }  // namespace art | 
| Artem Serov | 02eebcf | 2017-12-13 19:48:31 +0000 | [diff] [blame] | 1207 |  | 
 | 1208 | namespace std { | 
 | 1209 |  | 
 | 1210 | ostream& operator<<(ostream& os, const art::HEdge& e) { | 
 | 1211 |   e.Dump(os); | 
 | 1212 |   return os; | 
 | 1213 | } | 
 | 1214 |  | 
 | 1215 | }  // namespace std |