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