xref: /aosp_15_r20/art/compiler/optimizing/superblock_cloner.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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"
20 #include "induction_var_range.h"
21 #include "graph_checker.h"
22 
23 #include <sstream>
24 
25 namespace art HIDDEN {
26 
27 using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
28 using HInstructionMap = SuperblockCloner::HInstructionMap;
29 using HBasicBlockSet = SuperblockCloner::HBasicBlockSet;
30 using HEdgeSet = SuperblockCloner::HEdgeSet;
31 
Dump(std::ostream & stream) const32 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.
IsUsedOutsideRegion(const HInstruction * instr,const HBasicBlockSet & bb_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.
ArePhiInputsTheSame(const HPhi * phi)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 
74 // Returns whether two Edge sets are equal (ArenaHashSet doesn't have "Equal" method).
EdgeHashSetsEqual(const HEdgeSet * set1,const HEdgeSet * set2)75 static bool EdgeHashSetsEqual(const HEdgeSet* set1, const HEdgeSet* set2) {
76   if (set1->size() != set2->size()) {
77     return false;
78   }
79 
80   for (auto e : *set1) {
81     if (set2->find(e) == set2->end()) {
82       return false;
83     }
84   }
85   return true;
86 }
87 
88 // Calls HGraph::OrderLoopHeaderPredecessors for each loop in the graph.
OrderLoopsHeadersPredecessors(HGraph * 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 
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.
TraverseSubgraphForConnectivity(HBasicBlock * block,HBasicBlockSet * bb_set)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 
112 //
113 // Helpers for CloneBasicBlock.
114 //
115 
ReplaceInputsWithCopies(HInstruction * copy_instr)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 
DeepCloneEnvironmentWithRemapping(HInstruction * copy_instr,const HEnvironment * orig_env)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 = HEnvironment::Create(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 
RemapOrigInternalOrIncomingEdge(HBasicBlock * orig_block,HBasicBlock * orig_succ)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_IMPLIES(first_phi_met, copy_succ->GetPredecessors().size() == phi_input_count);
197 }
198 
AddCopyInternalEdge(HBasicBlock * orig_block,HBasicBlock * orig_succ)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 
RemapCopyInternalEdge(HBasicBlock * orig_block,HBasicBlock * orig_succ)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 
IsRemapInfoForVersioning() const230 bool SuperblockCloner::IsRemapInfoForVersioning() const {
231   return remap_incoming_->empty() &&
232          remap_orig_internal_->empty() &&
233          remap_copy_internal_->empty();
234 }
235 
CopyIncomingEdgesForVersioning()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 
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.
FindBackEdgesLocal(HBasicBlock * entry_block,ArenaBitVector * local_set)273 void SuperblockCloner::FindBackEdgesLocal(HBasicBlock* entry_block, ArenaBitVector* local_set) {
274   ArenaBitVector visited(arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
275 
276   // Nodes that we're currently visiting, indexed by block id.
277   ArenaBitVector visiting(arena_, graph_->GetBlocks().size(), false, kArenaAllocGraphBuilder);
278   // Number of successors visited from a given node, indexed by block id.
279   ArenaVector<size_t> successors_visited(graph_->GetBlocks().size(),
280                                          0u,
281                                          arena_->Adapter(kArenaAllocGraphBuilder));
282   // Stack of nodes that we're currently visiting (same as marked in "visiting" above).
283   ArenaVector<HBasicBlock*> worklist(arena_->Adapter(kArenaAllocGraphBuilder));
284   constexpr size_t kDefaultWorklistSize = 8;
285   worklist.reserve(kDefaultWorklistSize);
286 
287   visited.SetBit(entry_block->GetBlockId());
288   visiting.SetBit(entry_block->GetBlockId());
289   worklist.push_back(entry_block);
290 
291   while (!worklist.empty()) {
292     HBasicBlock* current = worklist.back();
293     uint32_t current_id = current->GetBlockId();
294     if (successors_visited[current_id] == current->GetSuccessors().size()) {
295       visiting.ClearBit(current_id);
296       worklist.pop_back();
297     } else {
298       HBasicBlock* successor = current->GetSuccessors()[successors_visited[current_id]++];
299       uint32_t successor_id = successor->GetBlockId();
300       if (!local_set->IsBitSet(successor_id)) {
301         continue;
302       }
303 
304       if (visiting.IsBitSet(successor_id)) {
305         DCHECK(ContainsElement(worklist, successor));
306         successor->AddBackEdgeWhileUpdating(current);
307       } else if (!visited.IsBitSet(successor_id)) {
308         visited.SetBit(successor_id);
309         visiting.SetBit(successor_id);
310         worklist.push_back(successor);
311       }
312     }
313   }
314 }
315 
RecalculateBackEdgesInfo(ArenaBitVector * outer_loop_bb_set)316 void SuperblockCloner::RecalculateBackEdgesInfo(ArenaBitVector* outer_loop_bb_set) {
317   HBasicBlock* block_entry = nullptr;
318 
319   if (outer_loop_ == nullptr) {
320     for (auto block : graph_->GetBlocks()) {
321       if (block != nullptr) {
322         outer_loop_bb_set->SetBit(block->GetBlockId());
323         HLoopInformation* info = block->GetLoopInformation();
324         if (info != nullptr) {
325           info->ResetBasicBlockData();
326         }
327       }
328     }
329     block_entry = graph_->GetEntryBlock();
330   } else {
331     outer_loop_bb_set->Copy(&outer_loop_bb_set_);
332     block_entry = outer_loop_->GetHeader();
333 
334     // Add newly created copy blocks.
335     for (auto entry : *bb_map_) {
336       outer_loop_bb_set->SetBit(entry.second->GetBlockId());
337     }
338 
339     // Clear loop_info for the whole outer loop.
340     for (uint32_t idx : outer_loop_bb_set->Indexes()) {
341       HBasicBlock* block = GetBlockById(idx);
342       HLoopInformation* info = block->GetLoopInformation();
343       if (info != nullptr) {
344         info->ResetBasicBlockData();
345       }
346     }
347   }
348 
349   FindBackEdgesLocal(block_entry, outer_loop_bb_set);
350 
351   for (uint32_t idx : outer_loop_bb_set->Indexes()) {
352     HBasicBlock* block = GetBlockById(idx);
353     HLoopInformation* info = block->GetLoopInformation();
354     // Reset LoopInformation for regular blocks and old headers which are no longer loop headers.
355     if (info != nullptr &&
356         (info->GetHeader() != block || info->NumberOfBackEdges() == 0)) {
357       block->SetLoopInformation(nullptr);
358     }
359   }
360 }
361 
362 // This is a modified version of HGraph::AnalyzeLoops.
AnalyzeLoopsLocally(ArenaBitVector * outer_loop_bb_set)363 GraphAnalysisResult SuperblockCloner::AnalyzeLoopsLocally(ArenaBitVector* outer_loop_bb_set) {
364   // We iterate post order to ensure we visit inner loops before outer loops.
365   // `PopulateRecursive` needs this guarantee to know whether a natural loop
366   // contains an irreducible loop.
367   for (HBasicBlock* block : graph_->GetPostOrder()) {
368     if (!outer_loop_bb_set->IsBitSet(block->GetBlockId())) {
369       continue;
370     }
371     if (block->IsLoopHeader()) {
372       if (block->IsCatchBlock()) {
373         // TODO: Dealing with exceptional back edges could be tricky because
374         //       they only approximate the real control flow. Bail out for now.
375         return kAnalysisFailThrowCatchLoop;
376       }
377       block->GetLoopInformation()->Populate();
378     }
379   }
380 
381   for (HBasicBlock* block : graph_->GetPostOrder()) {
382     if (!outer_loop_bb_set->IsBitSet(block->GetBlockId())) {
383       continue;
384     }
385     if (block->IsLoopHeader()) {
386       HLoopInformation* cur_loop = block->GetLoopInformation();
387       HLoopInformation* outer_loop = cur_loop->GetPreHeader()->GetLoopInformation();
388       if (outer_loop != nullptr) {
389         outer_loop->PopulateInnerLoopUpwards(cur_loop);
390       }
391     }
392   }
393 
394   return kAnalysisSuccess;
395 }
396 
CleanUpControlFlow()397 void SuperblockCloner::CleanUpControlFlow() {
398   // TODO: full control flow clean up for now, optimize it.
399   graph_->ClearDominanceInformation();
400 
401   ArenaBitVector outer_loop_bb_set(
402       arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
403   RecalculateBackEdgesInfo(&outer_loop_bb_set);
404 
405   // TODO: do it locally.
406   graph_->SimplifyCFG();
407   graph_->ComputeDominanceInformation();
408 
409   // AnalyzeLoopsLocally requires a correct post-ordering information which was calculated just
410   // before in ComputeDominanceInformation.
411   GraphAnalysisResult result = AnalyzeLoopsLocally(&outer_loop_bb_set);
412   DCHECK_EQ(result, kAnalysisSuccess);
413 
414   // TODO: do it locally
415   OrderLoopsHeadersPredecessors(graph_);
416 
417   graph_->ComputeTryBlockInformation();
418 }
419 
420 //
421 // Helpers for ResolveDataFlow
422 //
423 
ResolvePhi(HPhi * phi)424 void SuperblockCloner::ResolvePhi(HPhi* phi) {
425   HBasicBlock* phi_block = phi->GetBlock();
426   for (size_t i = 0, e = phi->InputCount(); i < e; i++) {
427     HInstruction* input = phi->InputAt(i);
428     HBasicBlock* input_block = input->GetBlock();
429 
430     // Originally defined outside the region.
431     if (!IsInOrigBBSet(input_block)) {
432       continue;
433     }
434     HBasicBlock* corresponding_block = phi_block->GetPredecessors()[i];
435     if (!IsInOrigBBSet(corresponding_block)) {
436       phi->ReplaceInput(GetInstrCopy(input), i);
437     }
438   }
439 }
440 
441 //
442 // Main algorithm methods.
443 //
444 
SearchForSubgraphExits(ArenaVector<HBasicBlock * > * exits) const445 void SuperblockCloner::SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits) const {
446   DCHECK(exits->empty());
447   for (uint32_t block_id : orig_bb_set_.Indexes()) {
448     HBasicBlock* block = GetBlockById(block_id);
449     for (HBasicBlock* succ : block->GetSuccessors()) {
450       if (!IsInOrigBBSet(succ)) {
451         exits->push_back(succ);
452       }
453     }
454   }
455 }
456 
FindAndSetLocalAreaForAdjustments()457 void SuperblockCloner::FindAndSetLocalAreaForAdjustments() {
458   DCHECK(outer_loop_ == nullptr);
459   ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner));
460   SearchForSubgraphExits(&exits);
461 
462   // For a reducible graph we need to update back-edges and dominance information only for
463   // the outermost loop which is affected by the transformation - it can be found by picking
464   // the common most outer loop of loops to which the subgraph exits blocks belong.
465   // Note: it can a loop or the whole graph (outer_loop_ will be nullptr in this case).
466   for (HBasicBlock* exit : exits) {
467     HLoopInformation* loop_exit_loop_info = exit->GetLoopInformation();
468     if (loop_exit_loop_info == nullptr) {
469       outer_loop_ = nullptr;
470       break;
471     }
472     if (outer_loop_ == nullptr) {
473       // We should not use the initial outer_loop_ value 'nullptr' when finding the most outer
474       // common loop.
475       outer_loop_ = loop_exit_loop_info;
476     }
477     outer_loop_ = FindCommonLoop(outer_loop_, loop_exit_loop_info);
478   }
479 
480   if (outer_loop_ != nullptr) {
481     // Save the loop population info as it will be changed later.
482     outer_loop_bb_set_.Copy(&outer_loop_->GetBlocks());
483   }
484 }
485 
RemapEdgesSuccessors()486 void SuperblockCloner::RemapEdgesSuccessors() {
487   // By this stage all the blocks have been copied, copy phis - created with no inputs;
488   // no copy edges have been created so far.
489   if (IsRemapInfoForVersioning()) {
490     CopyIncomingEdgesForVersioning();
491   }
492 
493   // Redirect incoming edges.
494   for (HEdge e : *remap_incoming_) {
495     HBasicBlock* orig_block = GetBlockById(e.GetFrom());
496     HBasicBlock* orig_succ = GetBlockById(e.GetTo());
497     RemapOrigInternalOrIncomingEdge(orig_block, orig_succ);
498   }
499 
500   // Redirect internal edges.
501   for (uint32_t orig_block_id : orig_bb_set_.Indexes()) {
502     HBasicBlock* orig_block = GetBlockById(orig_block_id);
503 
504     for (HBasicBlock* orig_succ : orig_block->GetSuccessors()) {
505       uint32_t orig_succ_id = orig_succ->GetBlockId();
506 
507       // Check for outgoing edge.
508       if (!IsInOrigBBSet(orig_succ)) {
509         HBasicBlock* copy_block = GetBlockCopy(orig_block);
510         copy_block->AddSuccessor(orig_succ);
511         continue;
512       }
513 
514       auto orig_redir = remap_orig_internal_->find(HEdge(orig_block_id, orig_succ_id));
515       auto copy_redir = remap_copy_internal_->find(HEdge(orig_block_id, orig_succ_id));
516 
517       // Due to construction all successors of copied block were set to original.
518       if (copy_redir != remap_copy_internal_->end()) {
519         RemapCopyInternalEdge(orig_block, orig_succ);
520       } else {
521         AddCopyInternalEdge(orig_block, orig_succ);
522       }
523 
524       if (orig_redir != remap_orig_internal_->end()) {
525         RemapOrigInternalOrIncomingEdge(orig_block, orig_succ);
526       }
527     }
528   }
529 }
530 
AdjustControlFlowInfo()531 void SuperblockCloner::AdjustControlFlowInfo() {
532   ArenaBitVector outer_loop_bb_set(
533       arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
534   RecalculateBackEdgesInfo(&outer_loop_bb_set);
535 
536   graph_->ClearDominanceInformation();
537   // TODO: Do it locally.
538   graph_->ComputeDominanceInformation();
539 }
540 
541 // TODO: Current FastCase restriction guarantees that instructions' inputs are already mapped to
542 // the valid values; only phis' inputs must be adjusted.
ResolveDataFlow()543 void SuperblockCloner::ResolveDataFlow() {
544   for (auto entry : *bb_map_) {
545     HBasicBlock* orig_block = entry.first;
546 
547     for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) {
548       HPhi* orig_phi = it.Current()->AsPhi();
549       HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi();
550       ResolvePhi(orig_phi);
551       ResolvePhi(copy_phi);
552     }
553     if (kIsDebugBuild) {
554       // Inputs of instruction copies must be already mapped to correspondent inputs copies.
555       for (HInstructionIterator it(orig_block->GetInstructions()); !it.Done(); it.Advance()) {
556         CheckInstructionInputsRemapping(it.Current());
557       }
558     }
559   }
560 }
561 
562 //
563 // Helpers for live-outs processing and Subgraph-closed SSA.
564 //
565 
CollectLiveOutsAndCheckClonable(HInstructionMap * live_outs) const566 bool SuperblockCloner::CollectLiveOutsAndCheckClonable(HInstructionMap* live_outs) const {
567   DCHECK(live_outs->empty());
568   for (uint32_t idx : orig_bb_set_.Indexes()) {
569     HBasicBlock* block = GetBlockById(idx);
570 
571     for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
572       HInstruction* instr = it.Current();
573       DCHECK(instr->IsClonable());
574 
575       if (IsUsedOutsideRegion(instr, orig_bb_set_)) {
576         live_outs->FindOrAdd(instr, instr);
577       }
578     }
579 
580     for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
581       HInstruction* instr = it.Current();
582       if (!instr->IsClonable()) {
583         return false;
584       }
585 
586       if (IsUsedOutsideRegion(instr, orig_bb_set_)) {
587         // TODO: Investigate why HNewInstance, HCheckCast has a requirement for the input.
588         if (instr->IsLoadClass()) {
589           return false;
590         }
591         live_outs->FindOrAdd(instr, instr);
592       }
593     }
594   }
595   return true;
596 }
597 
UpdateInductionRangeInfoOf(HInstruction * user,HInstruction * old_instruction,HInstruction * replacement)598 void SuperblockCloner::UpdateInductionRangeInfoOf(
599       HInstruction* user, HInstruction* old_instruction, HInstruction* replacement) {
600   if (induction_range_ != nullptr) {
601     induction_range_->Replace(user, old_instruction, replacement);
602   }
603 }
604 
ConstructSubgraphClosedSSA()605 void SuperblockCloner::ConstructSubgraphClosedSSA() {
606   if (live_outs_.empty()) {
607     return;
608   }
609 
610   ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner));
611   SearchForSubgraphExits(&exits);
612   if (exits.empty()) {
613     DCHECK(live_outs_.empty());
614     return;
615   }
616 
617   DCHECK_EQ(exits.size(), 1u);
618   HBasicBlock* exit_block = exits[0];
619   // There should be no critical edges.
620   DCHECK_EQ(exit_block->GetPredecessors().size(), 1u);
621   DCHECK(exit_block->GetPhis().IsEmpty());
622 
623   // For each live-out value insert a phi into the loop exit and replace all the value's uses
624   // external to the loop with this phi. The phi will have the original value as its only input;
625   // after copying is done FixSubgraphClosedSSAAfterCloning will add a corresponding copy of the
626   // original value as the second input thus merging data flow from the original and copy parts of
627   // the subgraph. Also update the record in the live_outs_ map from (value, value) to
628   // (value, new_phi).
629   for (auto live_out_it = live_outs_.begin(); live_out_it != live_outs_.end(); ++live_out_it) {
630     HInstruction* value = live_out_it->first;
631     HPhi* phi = new (arena_) HPhi(arena_, kNoRegNumber, 0, value->GetType());
632 
633     if (value->GetType() == DataType::Type::kReference) {
634       phi->SetReferenceTypeInfoIfValid(value->GetReferenceTypeInfo());
635     }
636 
637     exit_block->AddPhi(phi);
638     live_out_it->second = phi;
639 
640     const HUseList<HInstruction*>& uses = value->GetUses();
641     for (auto it = uses.begin(), end = uses.end(); it != end; /* ++it below */) {
642       HInstruction* user = it->GetUser();
643       size_t index = it->GetIndex();
644       // Increment `it` now because `*it` may disappear thanks to user->ReplaceInput().
645       ++it;
646       if (!IsInOrigBBSet(user->GetBlock())) {
647         user->ReplaceInput(phi, index);
648         UpdateInductionRangeInfoOf(user, value, phi);
649       }
650     }
651 
652     const HUseList<HEnvironment*>& env_uses = value->GetEnvUses();
653     for (auto it = env_uses.begin(), e = env_uses.end(); it != e; /* ++it below */) {
654       HEnvironment* env = it->GetUser();
655       size_t index = it->GetIndex();
656       ++it;
657       if (!IsInOrigBBSet(env->GetHolder()->GetBlock())) {
658         env->ReplaceInput(phi, index);
659       }
660     }
661 
662     phi->AddInput(value);
663   }
664 }
665 
FixSubgraphClosedSSAAfterCloning()666 void SuperblockCloner::FixSubgraphClosedSSAAfterCloning() {
667   for (auto it : live_outs_) {
668     DCHECK(it.first != it.second);
669     HInstruction* orig_value = it.first;
670     HPhi* phi = it.second->AsPhi();
671     HInstruction* copy_value = GetInstrCopy(orig_value);
672     // Copy edges are inserted after the original so we can just add new input to the phi.
673     phi->AddInput(copy_value);
674   }
675 }
676 
677 //
678 // Debug and logging methods.
679 //
680 
681 // Debug function to dump graph' BasicBlocks info.
DumpBB(HGraph * graph)682 void DumpBB(HGraph* graph) {
683   for (HBasicBlock* bb : graph->GetBlocks()) {
684     if (bb == nullptr) {
685       continue;
686     }
687     std::ostringstream oss;
688     oss << bb->GetBlockId();
689     oss << " <- ";
690     for (HBasicBlock* pred : bb->GetPredecessors()) {
691       oss << pred->GetBlockId() << " ";
692     }
693     oss << " -> ";
694     for (HBasicBlock* succ : bb->GetSuccessors()) {
695       oss << succ->GetBlockId()  << " ";
696     }
697 
698     if (bb->GetDominator()) {
699       oss << " dom " << bb->GetDominator()->GetBlockId();
700     }
701 
702     if (bb->GetLoopInformation()) {
703       oss <<  "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId();
704     }
705 
706     LOG(INFO) << oss.str();
707   }
708 }
709 
CheckInstructionInputsRemapping(HInstruction * orig_instr)710 void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) {
711   DCHECK(!orig_instr->IsPhi());
712   HInstruction* copy_instr = GetInstrCopy(orig_instr);
713   for (size_t i = 0, e = orig_instr->InputCount(); i < e; i++) {
714     HInstruction* orig_input = orig_instr->InputAt(i);
715     DCHECK(orig_input->GetBlock()->Dominates(orig_instr->GetBlock()));
716 
717     // If original input is defined outside the region then it will remain for both original
718     // instruction and the copy after the transformation.
719     if (!IsInOrigBBSet(orig_input->GetBlock())) {
720       continue;
721     }
722     HInstruction* copy_input = GetInstrCopy(orig_input);
723     DCHECK(copy_input->GetBlock()->Dominates(copy_instr->GetBlock()));
724   }
725 
726   // Resolve environment.
727   if (orig_instr->HasEnvironment()) {
728     HEnvironment* orig_env = orig_instr->GetEnvironment();
729 
730     for (size_t i = 0, e = orig_env->Size(); i < e; ++i) {
731       HInstruction* orig_input = orig_env->GetInstructionAt(i);
732 
733       // If original input is defined outside the region then it will remain for both original
734       // instruction and the copy after the transformation.
735       if (orig_input == nullptr || !IsInOrigBBSet(orig_input->GetBlock())) {
736         continue;
737       }
738 
739       HInstruction* copy_input = GetInstrCopy(orig_input);
740       DCHECK(copy_input->GetBlock()->Dominates(copy_instr->GetBlock()));
741     }
742   }
743 }
744 
CheckRemappingInfoIsValid()745 bool SuperblockCloner::CheckRemappingInfoIsValid() {
746   for (HEdge edge : *remap_orig_internal_) {
747     if (!IsEdgeValid(edge, graph_) ||
748         !IsInOrigBBSet(edge.GetFrom()) ||
749         !IsInOrigBBSet(edge.GetTo())) {
750       return false;
751     }
752   }
753 
754   for (auto edge : *remap_copy_internal_) {
755     if (!IsEdgeValid(edge, graph_) ||
756         !IsInOrigBBSet(edge.GetFrom()) ||
757         !IsInOrigBBSet(edge.GetTo())) {
758       return false;
759     }
760   }
761 
762   for (auto edge : *remap_incoming_) {
763     if (!IsEdgeValid(edge, graph_) ||
764         IsInOrigBBSet(edge.GetFrom()) ||
765         !IsInOrigBBSet(edge.GetTo())) {
766       return false;
767     }
768   }
769 
770   return true;
771 }
772 
VerifyGraph()773 void SuperblockCloner::VerifyGraph() {
774   for (auto it : *hir_map_) {
775     HInstruction* orig_instr = it.first;
776     HInstruction* copy_instr = it.second;
777     if (!orig_instr->IsPhi() && !orig_instr->IsSuspendCheck()) {
778       DCHECK(it.first->GetBlock() != nullptr);
779     }
780     if (!copy_instr->IsPhi() && !copy_instr->IsSuspendCheck()) {
781       DCHECK(it.second->GetBlock() != nullptr);
782     }
783   }
784   if (kSuperblockClonerVerify) {
785     GraphChecker checker(graph_);
786     checker.Run();
787     if (!checker.IsValid()) {
788       for (const std::string& error : checker.GetErrors()) {
789         LOG(ERROR) << error;
790       }
791       LOG(FATAL) << "GraphChecker failed: superblock cloner";
792     }
793   }
794 }
795 
DumpBBSet(const ArenaBitVector * set)796 void DumpBBSet(const ArenaBitVector* set) {
797   for (uint32_t idx : set->Indexes()) {
798     LOG(INFO) << idx;
799   }
800 }
801 
DumpInputSets()802 void SuperblockCloner::DumpInputSets() {
803   LOG(INFO) << "orig_bb_set:";
804   for (uint32_t idx : orig_bb_set_.Indexes()) {
805     LOG(INFO) << idx;
806   }
807   LOG(INFO) << "remap_orig_internal:";
808   for (HEdge e : *remap_orig_internal_) {
809     LOG(INFO) << e;
810   }
811   LOG(INFO) << "remap_copy_internal:";
812   for (auto e : *remap_copy_internal_) {
813     LOG(INFO) << e;
814   }
815   LOG(INFO) << "remap_incoming:";
816   for (auto e : *remap_incoming_) {
817     LOG(INFO) << e;
818   }
819 }
820 
821 //
822 // Public methods.
823 //
824 
SuperblockCloner(HGraph * graph,const HBasicBlockSet * orig_bb_set,HBasicBlockMap * bb_map,HInstructionMap * hir_map,InductionVarRange * induction_range)825 SuperblockCloner::SuperblockCloner(HGraph* graph,
826                                    const HBasicBlockSet* orig_bb_set,
827                                    HBasicBlockMap* bb_map,
828                                    HInstructionMap* hir_map,
829                                    InductionVarRange* induction_range)
830   : graph_(graph),
831     arena_(graph->GetAllocator()),
832     orig_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner),
833     remap_orig_internal_(nullptr),
834     remap_copy_internal_(nullptr),
835     remap_incoming_(nullptr),
836     bb_map_(bb_map),
837     hir_map_(hir_map),
838     induction_range_(induction_range),
839     outer_loop_(nullptr),
840     outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner),
841     live_outs_(std::less<HInstruction*>(),
842         graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)) {
843   orig_bb_set_.Copy(orig_bb_set);
844 }
845 
SetSuccessorRemappingInfo(const HEdgeSet * remap_orig_internal,const HEdgeSet * remap_copy_internal,const HEdgeSet * remap_incoming)846 void SuperblockCloner::SetSuccessorRemappingInfo(const HEdgeSet* remap_orig_internal,
847                                                  const HEdgeSet* remap_copy_internal,
848                                                  const HEdgeSet* remap_incoming) {
849   remap_orig_internal_ = remap_orig_internal;
850   remap_copy_internal_ = remap_copy_internal;
851   remap_incoming_ = remap_incoming;
852   DCHECK(CheckRemappingInfoIsValid());
853 }
854 
IsSubgraphClonable() const855 bool SuperblockCloner::IsSubgraphClonable() const {
856   // TODO: Support irreducible graphs and subgraphs with try-catch.
857   if (graph_->HasIrreducibleLoops()) {
858     return false;
859   }
860 
861   for (HBasicBlock* block : graph_->GetReversePostOrder()) {
862     if (!IsInOrigBBSet(block)) {
863       continue;
864     }
865     if (block->GetTryCatchInformation() != nullptr) {
866       return false;
867     }
868   }
869 
870   HInstructionMap live_outs(
871       std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
872 
873   if (!CollectLiveOutsAndCheckClonable(&live_outs)) {
874     return false;
875   }
876 
877   ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner));
878   SearchForSubgraphExits(&exits);
879 
880   // The only loops with live-outs which are currently supported are loops with a single exit.
881   if (!live_outs.empty() && exits.size() != 1) {
882     return false;
883   }
884 
885   // The values in live_outs should be defined in a block that dominates exit_block.
886   for (const auto& live_out : live_outs) {
887     DCHECK_EQ(exits.size(), 1u);
888     HInstruction* value = live_out.first;
889     if (!value->GetBlock()->Dominates(exits[0])) {
890       // This case can only happen when `value` is used in a catch phi, so the graph must contain a
891       // catch block.
892       DCHECK(graph_->HasTryCatch());
893       return false;
894     }
895   }
896 
897   return true;
898 }
899 
900 // Checks that loop unrolling/peeling/versioning is being conducted.
IsFastCase() const901 bool SuperblockCloner::IsFastCase() const {
902   // Check that all the basic blocks belong to the same loop.
903   bool flag = false;
904   HLoopInformation* common_loop_info = nullptr;
905   for (uint32_t idx : orig_bb_set_.Indexes()) {
906     HBasicBlock* block = GetBlockById(idx);
907     HLoopInformation* block_loop_info = block->GetLoopInformation();
908     if (!flag) {
909       common_loop_info = block_loop_info;
910     } else {
911       if (block_loop_info != common_loop_info) {
912         return false;
913       }
914     }
915   }
916 
917   // Check that orig_bb_set_ corresponds to loop peeling/unrolling/versioning.
918   if (common_loop_info == nullptr || !orig_bb_set_.SameBitsSet(&common_loop_info->GetBlocks())) {
919     return false;
920   }
921 
922   if (IsRemapInfoForVersioning()) {
923     return true;
924   }
925 
926   bool peeling_or_unrolling = false;
927   HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
928   HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
929   HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
930 
931 
932   // Check whether remapping info corresponds to loop unrolling.
933   CollectRemappingInfoForPeelUnroll(/* to_unroll*/ true,
934                                     common_loop_info,
935                                     &remap_orig_internal,
936                                     &remap_copy_internal,
937                                     &remap_incoming);
938 
939   peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) &&
940                           EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) &&
941                           EdgeHashSetsEqual(&remap_incoming, remap_incoming_);
942 
943   remap_orig_internal.clear();
944   remap_copy_internal.clear();
945   remap_incoming.clear();
946 
947   // Check whether remapping info corresponds to loop peeling.
948   CollectRemappingInfoForPeelUnroll(/* to_unroll*/ false,
949                                     common_loop_info,
950                                     &remap_orig_internal,
951                                     &remap_copy_internal,
952                                     &remap_incoming);
953 
954   peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) &&
955                           EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) &&
956                           EdgeHashSetsEqual(&remap_incoming, remap_incoming_);
957 
958   return peeling_or_unrolling;
959 }
960 
Run()961 void SuperblockCloner::Run() {
962   DCHECK(bb_map_ != nullptr);
963   DCHECK(hir_map_ != nullptr);
964   DCHECK(remap_orig_internal_ != nullptr &&
965          remap_copy_internal_ != nullptr &&
966          remap_incoming_ != nullptr);
967   DCHECK(IsSubgraphClonable());
968   DCHECK(IsFastCase());
969 
970   if (kSuperblockClonerLogging) {
971     DumpInputSets();
972   }
973 
974   bool result = CollectLiveOutsAndCheckClonable(&live_outs_);
975   DCHECK(result);
976   // Find an area in the graph for which control flow information should be adjusted.
977   FindAndSetLocalAreaForAdjustments();
978   ConstructSubgraphClosedSSA();
979   // Clone the basic blocks from the orig_bb_set_; data flow is invalid after the call and is to be
980   // adjusted.
981   CloneBasicBlocks();
982   // Connect the blocks together/remap successors and fix phis which are directly affected my the
983   // remapping.
984   RemapEdgesSuccessors();
985 
986   // Check that the subgraph is connected.
987   if (kIsDebugBuild) {
988     HBasicBlockSet work_set(arena_, orig_bb_set_.GetSizeOf(), true, kArenaAllocSuperblockCloner);
989 
990     // Add original and copy blocks of the subgraph to the work set.
991     for (auto iter : *bb_map_) {
992       work_set.SetBit(iter.first->GetBlockId());   // Original block.
993       work_set.SetBit(iter.second->GetBlockId());  // Copy block.
994     }
995     CHECK(IsSubgraphConnected(&work_set, graph_));
996   }
997 
998   // Recalculate dominance and backedge information which is required by the next stage.
999   AdjustControlFlowInfo();
1000   // Fix data flow of the graph.
1001   ResolveDataFlow();
1002   FixSubgraphClosedSSAAfterCloning();
1003 }
1004 
CleanUp()1005 void SuperblockCloner::CleanUp() {
1006   CleanUpControlFlow();
1007 
1008   // Remove phis which have all inputs being same.
1009   // When a block has a single predecessor it must not have any phis. However after the
1010   // transformation it could happen that there is such block with a phi with a single input.
1011   // As this is needed to be processed we also simplify phis with multiple same inputs here.
1012   for (auto entry : *bb_map_) {
1013     for (HBasicBlock* block : {entry.first, entry.second}) {
1014       for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
1015         HPhi* phi = inst_it.Current()->AsPhi();
1016         if (ArePhiInputsTheSame(phi)) {
1017           phi->ReplaceWith(phi->InputAt(0));
1018           block->RemovePhi(phi);
1019         }
1020       }
1021     }
1022   }
1023 
1024   if (kIsDebugBuild) {
1025     VerifyGraph();
1026   }
1027 }
1028 
CloneBasicBlock(const HBasicBlock * orig_block)1029 HBasicBlock* SuperblockCloner::CloneBasicBlock(const HBasicBlock* orig_block) {
1030   HGraph* graph = orig_block->GetGraph();
1031   HBasicBlock* copy_block = new (arena_) HBasicBlock(graph, orig_block->GetDexPc());
1032   graph->AddBlock(copy_block);
1033 
1034   // Clone all the phis and add them to the map.
1035   for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) {
1036     HInstruction* orig_instr = it.Current();
1037     HInstruction* copy_instr = orig_instr->Clone(arena_);
1038     copy_block->AddPhi(copy_instr->AsPhi());
1039     copy_instr->AsPhi()->RemoveAllInputs();
1040     DCHECK(!orig_instr->HasEnvironment());
1041     hir_map_->Put(orig_instr, copy_instr);
1042   }
1043 
1044   // Clone all the instructions and add them to the map.
1045   for (HInstructionIterator it(orig_block->GetInstructions()); !it.Done(); it.Advance()) {
1046     HInstruction* orig_instr = it.Current();
1047     HInstruction* copy_instr = orig_instr->Clone(arena_);
1048     ReplaceInputsWithCopies(copy_instr);
1049     copy_block->AddInstruction(copy_instr);
1050     if (orig_instr->HasEnvironment()) {
1051       DeepCloneEnvironmentWithRemapping(copy_instr, orig_instr->GetEnvironment());
1052     }
1053     hir_map_->Put(orig_instr, copy_instr);
1054   }
1055 
1056   return copy_block;
1057 }
1058 
CloneBasicBlocks()1059 void SuperblockCloner::CloneBasicBlocks() {
1060   // By this time ReversePostOrder must be valid: in 'CloneBasicBlock' inputs of the copied
1061   // instructions might be replaced by copies of the original inputs (depending where those inputs
1062   // are defined). So the definitions of the original inputs must be visited before their original
1063   // uses. The property of the reducible graphs "if 'A' dom 'B' then rpo_num('A') >= rpo_num('B')"
1064   // guarantees that.
1065   for (HBasicBlock* orig_block : graph_->GetReversePostOrder()) {
1066     if (!IsInOrigBBSet(orig_block)) {
1067       continue;
1068     }
1069     HBasicBlock* copy_block = CloneBasicBlock(orig_block);
1070     bb_map_->Put(orig_block, copy_block);
1071     if (kSuperblockClonerLogging) {
1072       LOG(INFO) << "new block :" << copy_block->GetBlockId() << ": " << orig_block->GetBlockId();
1073     }
1074   }
1075 }
1076 
1077 //
1078 // Stand-alone methods.
1079 //
1080 
CollectRemappingInfoForPeelUnroll(bool to_unroll,HLoopInformation * loop_info,HEdgeSet * remap_orig_internal,HEdgeSet * remap_copy_internal,HEdgeSet * remap_incoming)1081 void CollectRemappingInfoForPeelUnroll(bool to_unroll,
1082                                        HLoopInformation* loop_info,
1083                                        HEdgeSet* remap_orig_internal,
1084                                        HEdgeSet* remap_copy_internal,
1085                                        HEdgeSet* remap_incoming) {
1086   DCHECK(loop_info != nullptr);
1087   HBasicBlock* loop_header = loop_info->GetHeader();
1088   // Set up remap_orig_internal edges set - set is empty.
1089   // Set up remap_copy_internal edges set.
1090   for (HBasicBlock* back_edge_block : loop_info->GetBackEdges()) {
1091     HEdge e = HEdge(back_edge_block, loop_header);
1092     if (to_unroll) {
1093       remap_orig_internal->insert(e);
1094       remap_copy_internal->insert(e);
1095     } else {
1096       remap_copy_internal->insert(e);
1097     }
1098   }
1099 
1100   // Set up remap_incoming edges set.
1101   if (!to_unroll) {
1102     remap_incoming->insert(HEdge(loop_info->GetPreHeader(), loop_header));
1103   }
1104 }
1105 
IsSubgraphConnected(SuperblockCloner::HBasicBlockSet * work_set,HGraph * graph)1106 bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph) {
1107   ArenaVector<HBasicBlock*> entry_blocks(
1108       graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
1109 
1110   // Find subgraph entry blocks.
1111   for (uint32_t orig_block_id : work_set->Indexes()) {
1112     HBasicBlock* block = graph->GetBlocks()[orig_block_id];
1113     for (HBasicBlock* pred : block->GetPredecessors()) {
1114       if (!work_set->IsBitSet(pred->GetBlockId())) {
1115         entry_blocks.push_back(block);
1116         break;
1117       }
1118     }
1119   }
1120 
1121   for (HBasicBlock* entry_block : entry_blocks) {
1122     if (work_set->IsBitSet(entry_block->GetBlockId())) {
1123       TraverseSubgraphForConnectivity(entry_block, work_set);
1124     }
1125   }
1126 
1127   // Return whether there are unvisited - unreachable - blocks.
1128   return work_set->NumSetBits() == 0;
1129 }
1130 
FindCommonLoop(HLoopInformation * loop1,HLoopInformation * loop2)1131 HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) {
1132   if (loop1 == nullptr || loop2 == nullptr) {
1133     return nullptr;
1134   }
1135 
1136   if (loop1->IsIn(*loop2)) {
1137     return loop2;
1138   }
1139 
1140   HLoopInformation* current = loop1;
1141   while (current != nullptr && !loop2->IsIn(*current)) {
1142     current = current->GetPreHeader()->GetLoopInformation();
1143   }
1144 
1145   return current;
1146 }
1147 
IsLoopClonable(HLoopInformation * loop_info)1148 bool LoopClonerHelper::IsLoopClonable(HLoopInformation* loop_info) {
1149   LoopClonerHelper helper(
1150       loop_info, /* bb_map= */ nullptr, /* hir_map= */ nullptr, /* induction_range= */ nullptr);
1151   return helper.IsLoopClonable();
1152 }
1153 
DoLoopTransformationImpl(TransformationKind transformation)1154 HBasicBlock* LoopClonerHelper::DoLoopTransformationImpl(TransformationKind transformation) {
1155   // For now do transformations only for natural loops.
1156   DCHECK(!loop_info_->IsIrreducible());
1157 
1158   HBasicBlock* loop_header = loop_info_->GetHeader();
1159   // Check that loop info is up-to-date.
1160   DCHECK(loop_info_ == loop_header->GetLoopInformation());
1161   HGraph* graph = loop_header->GetGraph();
1162 
1163   if (kSuperblockClonerLogging) {
1164     LOG(INFO) << "Method: " << graph->PrettyMethod();
1165     std::ostringstream oss;
1166     oss << "Scalar loop ";
1167     switch (transformation) {
1168       case TransformationKind::kPeeling:
1169         oss << "peeling";
1170         break;
1171       case TransformationKind::kUnrolling:
1172         oss<< "unrolling";
1173         break;
1174       case TransformationKind::kVersioning:
1175         oss << "versioning";
1176         break;
1177     }
1178     oss << " was applied to the loop <" << loop_header->GetBlockId() << ">.";
1179     LOG(INFO) << oss.str();
1180   }
1181 
1182   ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool());
1183 
1184   HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
1185   HEdgeSet remap_copy_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
1186   HEdgeSet remap_incoming(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
1187 
1188   // No remapping needed for loop versioning.
1189   if (transformation != TransformationKind::kVersioning) {
1190     CollectRemappingInfoForPeelUnroll(transformation == TransformationKind::kUnrolling,
1191                                       loop_info_,
1192                                       &remap_orig_internal,
1193                                       &remap_copy_internal,
1194                                       &remap_incoming);
1195   }
1196 
1197   cloner_.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming);
1198   cloner_.Run();
1199   cloner_.CleanUp();
1200 
1201   // Check that loop info is preserved.
1202   DCHECK(loop_info_ == loop_header->GetLoopInformation());
1203 
1204   return loop_header;
1205 }
1206 
LoopClonerSimpleHelper(HLoopInformation * info,InductionVarRange * induction_range)1207 LoopClonerSimpleHelper::LoopClonerSimpleHelper(HLoopInformation* info,
1208                                                InductionVarRange* induction_range)
1209   : bb_map_(std::less<HBasicBlock*>(),
1210             info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)),
1211     hir_map_(std::less<HInstruction*>(),
1212              info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)),
1213     helper_(info, &bb_map_, &hir_map_, induction_range) {}
1214 
1215 }  // namespace art
1216 
1217 namespace std {
1218 
operator <<(ostream & os,const art::HEdge & e)1219 ostream& operator<<(ostream& os, const art::HEdge& e) {
1220   e.Dump(os);
1221   return os;
1222 }
1223 
1224 }  // namespace std
1225