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