blob: fad7729956b5f35374fe4b9198475b7ec0ab0a48 [file] [log] [blame]
Artem Serov7f4aff62017-06-21 17:02:18 +01001/*
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 "graph_checker.h"
21
22#include <iostream>
23
24namespace art {
25
26using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
27using HInstructionMap = SuperblockCloner::HInstructionMap;
28using HBasicBlockSet = SuperblockCloner::HBasicBlockSet;
29using HEdgeSet = SuperblockCloner::HEdgeSet;
30
31void HEdge::Dump(std::ostream& stream) const {
32 stream << "(" << from_ << "->" << to_ << ")";
33}
34
35//
36// Static helper methods.
37//
38
39// Returns whether instruction has any uses (regular or environmental) outside the region,
40// defined by basic block set.
41static bool IsUsedOutsideRegion(const HInstruction* instr, const HBasicBlockSet& bb_set) {
42 auto& uses = instr->GetUses();
43 for (auto use_node = uses.begin(), e = uses.end(); use_node != e; ++use_node) {
44 HInstruction* user = use_node->GetUser();
45 if (!bb_set.IsBitSet(user->GetBlock()->GetBlockId())) {
46 return true;
47 }
48 }
49
50 auto& env_uses = instr->GetEnvUses();
51 for (auto use_node = env_uses.begin(), e = env_uses.end(); use_node != e; ++use_node) {
52 HInstruction* user = use_node->GetUser()->GetHolder();
53 if (!bb_set.IsBitSet(user->GetBlock()->GetBlockId())) {
54 return true;
55 }
56 }
57
58 return false;
59}
60
61// Returns whether the phi's inputs are the same HInstruction.
62static bool ArePhiInputsTheSame(const HPhi* phi) {
63 HInstruction* first_input = phi->InputAt(0);
64 for (size_t i = 1, e = phi->InputCount(); i < e; i++) {
65 if (phi->InputAt(i) != first_input) {
66 return false;
67 }
68 }
69
70 return true;
71}
72
Artem Serov02eebcf2017-12-13 19:48:31 +000073// Returns whether two Edge sets are equal (ArenaHashSet doesn't have "Equal" method).
74static bool EdgeHashSetsEqual(const HEdgeSet* set1, const HEdgeSet* set2) {
75 if (set1->Size() != set2->Size()) {
76 return false;
Artem Serov7f4aff62017-06-21 17:02:18 +010077 }
78
Artem Serov02eebcf2017-12-13 19:48:31 +000079 for (auto e : *set1) {
80 if (set2->Find(e) == set2->end()) {
81 return false;
82 }
Artem Serov7f4aff62017-06-21 17:02:18 +010083 }
Artem Serov02eebcf2017-12-13 19:48:31 +000084 return true;
Artem Serov7f4aff62017-06-21 17:02:18 +010085}
86
87// Calls HGraph::OrderLoopHeaderPredecessors for each loop in the graph.
88static void OrderLoopsHeadersPredecessors(HGraph* graph) {
89 for (HBasicBlock* block : graph->GetPostOrder()) {
90 if (block->IsLoopHeader()) {
91 graph->OrderLoopHeaderPredecessors(block);
92 }
93 }
94}
95
Artem Serov02eebcf2017-12-13 19:48:31 +000096// Performs DFS on the subgraph (specified by 'bb_set') starting from the specified block; while
97// traversing the function removes basic blocks from the bb_set (instead of traditional DFS
98// 'marking'). So what is left in the 'bb_set' after the traversal is not reachable from the start
99// block.
100static void TraverseSubgraphForConnectivity(HBasicBlock* block, HBasicBlockSet* bb_set) {
101 DCHECK(bb_set->IsBitSet(block->GetBlockId()));
102 bb_set->ClearBit(block->GetBlockId());
103
104 for (HBasicBlock* succ : block->GetSuccessors()) {
105 if (bb_set->IsBitSet(succ->GetBlockId())) {
106 TraverseSubgraphForConnectivity(succ, bb_set);
107 }
108 }
109}
110
Artem Serov7f4aff62017-06-21 17:02:18 +0100111//
112// Helpers for CloneBasicBlock.
113//
114
115void SuperblockCloner::ReplaceInputsWithCopies(HInstruction* copy_instr) {
116 DCHECK(!copy_instr->IsPhi());
117 for (size_t i = 0, e = copy_instr->InputCount(); i < e; i++) {
118 // Copy instruction holds the same input as the original instruction holds.
119 HInstruction* orig_input = copy_instr->InputAt(i);
120 if (!IsInOrigBBSet(orig_input->GetBlock())) {
121 // Defined outside the subgraph.
122 continue;
123 }
124 HInstruction* copy_input = GetInstrCopy(orig_input);
125 // copy_instr will be registered as a user of copy_inputs after returning from this function:
126 // 'copy_block->AddInstruction(copy_instr)'.
127 copy_instr->SetRawInputAt(i, copy_input);
128 }
129}
130
131void SuperblockCloner::DeepCloneEnvironmentWithRemapping(HInstruction* copy_instr,
132 const HEnvironment* orig_env) {
133 if (orig_env->GetParent() != nullptr) {
134 DeepCloneEnvironmentWithRemapping(copy_instr, orig_env->GetParent());
135 }
136 HEnvironment* copy_env = new (arena_) HEnvironment(arena_, *orig_env, copy_instr);
137
138 for (size_t i = 0; i < orig_env->Size(); i++) {
139 HInstruction* env_input = orig_env->GetInstructionAt(i);
140 if (env_input != nullptr && IsInOrigBBSet(env_input->GetBlock())) {
141 env_input = GetInstrCopy(env_input);
142 DCHECK(env_input != nullptr && env_input->GetBlock() != nullptr);
143 }
144 copy_env->SetRawEnvAt(i, env_input);
145 if (env_input != nullptr) {
146 env_input->AddEnvUseAt(copy_env, i);
147 }
148 }
149 // InsertRawEnvironment assumes that instruction already has an environment that's why we use
150 // SetRawEnvironment in the 'else' case.
151 // As this function calls itself recursively with the same copy_instr - this copy_instr may
152 // have partially copied chain of HEnvironments.
153 if (copy_instr->HasEnvironment()) {
154 copy_instr->InsertRawEnvironment(copy_env);
155 } else {
156 copy_instr->SetRawEnvironment(copy_env);
157 }
158}
159
160//
161// Helpers for RemapEdgesSuccessors.
162//
163
164void SuperblockCloner::RemapOrigInternalOrIncomingEdge(HBasicBlock* orig_block,
165 HBasicBlock* orig_succ) {
166 DCHECK(IsInOrigBBSet(orig_succ));
167 HBasicBlock* copy_succ = GetBlockCopy(orig_succ);
168
169 size_t this_index = orig_succ->GetPredecessorIndexOf(orig_block);
170 size_t phi_input_count = 0;
171 // This flag reflects whether the original successor has at least one phi and this phi
172 // has been already processed in the loop. Used for validation purposes in DCHECK to check that
173 // in the end all of the phis in the copy successor have the same number of inputs - the number
174 // of copy successor's predecessors.
175 bool first_phi_met = false;
176 for (HInstructionIterator it(orig_succ->GetPhis()); !it.Done(); it.Advance()) {
177 HPhi* orig_phi = it.Current()->AsPhi();
178 HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi();
179 HInstruction* orig_phi_input = orig_phi->InputAt(this_index);
180 // Remove corresponding input for original phi.
181 orig_phi->RemoveInputAt(this_index);
182 // Copy phi doesn't yet have either orig_block as predecessor or the input that corresponds
183 // to orig_block, so add the input at the end of the list.
184 copy_phi->AddInput(orig_phi_input);
185 if (!first_phi_met) {
186 phi_input_count = copy_phi->InputCount();
187 first_phi_met = true;
188 } else {
189 DCHECK_EQ(phi_input_count, copy_phi->InputCount());
190 }
191 }
192 // orig_block will be put at the end of the copy_succ's predecessors list; that corresponds
193 // to the previously added phi inputs position.
194 orig_block->ReplaceSuccessor(orig_succ, copy_succ);
195 DCHECK(!first_phi_met || copy_succ->GetPredecessors().size() == phi_input_count);
196}
197
198void SuperblockCloner::AddCopyInternalEdge(HBasicBlock* orig_block,
199 HBasicBlock* orig_succ) {
200 DCHECK(IsInOrigBBSet(orig_succ));
201 HBasicBlock* copy_block = GetBlockCopy(orig_block);
202 HBasicBlock* copy_succ = GetBlockCopy(orig_succ);
203 copy_block->AddSuccessor(copy_succ);
204
205 size_t orig_index = orig_succ->GetPredecessorIndexOf(orig_block);
206 for (HInstructionIterator it(orig_succ->GetPhis()); !it.Done(); it.Advance()) {
207 HPhi* orig_phi = it.Current()->AsPhi();
208 HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi();
209 HInstruction* orig_phi_input = orig_phi->InputAt(orig_index);
210 copy_phi->AddInput(orig_phi_input);
211 }
212}
213
214void SuperblockCloner::RemapCopyInternalEdge(HBasicBlock* orig_block,
215 HBasicBlock* orig_succ) {
216 DCHECK(IsInOrigBBSet(orig_succ));
217 HBasicBlock* copy_block = GetBlockCopy(orig_block);
218 copy_block->AddSuccessor(orig_succ);
219 DCHECK(copy_block->HasSuccessor(orig_succ));
220
221 size_t orig_index = orig_succ->GetPredecessorIndexOf(orig_block);
222 for (HInstructionIterator it(orig_succ->GetPhis()); !it.Done(); it.Advance()) {
223 HPhi* orig_phi = it.Current()->AsPhi();
224 HInstruction* orig_phi_input = orig_phi->InputAt(orig_index);
225 orig_phi->AddInput(orig_phi_input);
226 }
227}
228
229//
230// Local versions of CF calculation/adjustment routines.
231//
232
233// TODO: merge with the original version in nodes.cc. The concern is that we don't want to affect
234// the performance of the base version by checking the local set.
235// TODO: this version works when updating the back edges info for natural loop-based local_set.
236// Check which exactly types of subgraphs can be analysed or rename it to
237// FindBackEdgesInTheNaturalLoop.
238void SuperblockCloner::FindBackEdgesLocal(HBasicBlock* entry_block, ArenaBitVector* local_set) {
239 ArenaBitVector visited(arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
240 // "visited" must be empty on entry, it's an output argument for all visited (i.e. live) blocks.
241 DCHECK_EQ(visited.GetHighestBitSet(), -1);
242
243 // Nodes that we're currently visiting, indexed by block id.
244 ArenaBitVector visiting(arena_, graph_->GetBlocks().size(), false, kArenaAllocGraphBuilder);
245 // Number of successors visited from a given node, indexed by block id.
246 ArenaVector<size_t> successors_visited(graph_->GetBlocks().size(),
247 0u,
248 arena_->Adapter(kArenaAllocGraphBuilder));
249 // Stack of nodes that we're currently visiting (same as marked in "visiting" above).
250 ArenaVector<HBasicBlock*> worklist(arena_->Adapter(kArenaAllocGraphBuilder));
251 constexpr size_t kDefaultWorklistSize = 8;
252 worklist.reserve(kDefaultWorklistSize);
253
254 visited.SetBit(entry_block->GetBlockId());
255 visiting.SetBit(entry_block->GetBlockId());
256 worklist.push_back(entry_block);
257
258 while (!worklist.empty()) {
259 HBasicBlock* current = worklist.back();
260 uint32_t current_id = current->GetBlockId();
261 if (successors_visited[current_id] == current->GetSuccessors().size()) {
262 visiting.ClearBit(current_id);
263 worklist.pop_back();
264 } else {
265 HBasicBlock* successor = current->GetSuccessors()[successors_visited[current_id]++];
266 uint32_t successor_id = successor->GetBlockId();
267 if (!local_set->IsBitSet(successor_id)) {
268 continue;
269 }
270
271 if (visiting.IsBitSet(successor_id)) {
272 DCHECK(ContainsElement(worklist, successor));
273 successor->AddBackEdgeWhileUpdating(current);
274 } else if (!visited.IsBitSet(successor_id)) {
275 visited.SetBit(successor_id);
276 visiting.SetBit(successor_id);
277 worklist.push_back(successor);
278 }
279 }
280 }
281}
282
283void SuperblockCloner::RecalculateBackEdgesInfo(ArenaBitVector* outer_loop_bb_set) {
Artem Serov7f4aff62017-06-21 17:02:18 +0100284 HBasicBlock* block_entry = nullptr;
285
286 if (outer_loop_ == nullptr) {
287 for (auto block : graph_->GetBlocks()) {
288 if (block != nullptr) {
289 outer_loop_bb_set->SetBit(block->GetBlockId());
290 HLoopInformation* info = block->GetLoopInformation();
291 if (info != nullptr) {
292 info->ResetBasicBlockData();
293 }
294 }
295 }
296 block_entry = graph_->GetEntryBlock();
297 } else {
298 outer_loop_bb_set->Copy(&outer_loop_bb_set_);
299 block_entry = outer_loop_->GetHeader();
300
301 // Add newly created copy blocks.
302 for (auto entry : *bb_map_) {
303 outer_loop_bb_set->SetBit(entry.second->GetBlockId());
304 }
305
306 // Clear loop_info for the whole outer loop.
307 for (uint32_t idx : outer_loop_bb_set->Indexes()) {
308 HBasicBlock* block = GetBlockById(idx);
309 HLoopInformation* info = block->GetLoopInformation();
310 if (info != nullptr) {
311 info->ResetBasicBlockData();
312 }
313 }
314 }
315
316 FindBackEdgesLocal(block_entry, outer_loop_bb_set);
317
318 for (uint32_t idx : outer_loop_bb_set->Indexes()) {
319 HBasicBlock* block = GetBlockById(idx);
320 HLoopInformation* info = block->GetLoopInformation();
321 // Reset LoopInformation for regular blocks and old headers which are no longer loop headers.
322 if (info != nullptr &&
323 (info->GetHeader() != block || info->NumberOfBackEdges() == 0)) {
324 block->SetLoopInformation(nullptr);
325 }
326 }
327}
328
329// This is a modified version of HGraph::AnalyzeLoops.
330GraphAnalysisResult SuperblockCloner::AnalyzeLoopsLocally(ArenaBitVector* outer_loop_bb_set) {
331 // We iterate post order to ensure we visit inner loops before outer loops.
332 // `PopulateRecursive` needs this guarantee to know whether a natural loop
333 // contains an irreducible loop.
334 for (HBasicBlock* block : graph_->GetPostOrder()) {
335 if (!outer_loop_bb_set->IsBitSet(block->GetBlockId())) {
336 continue;
337 }
338 if (block->IsLoopHeader()) {
339 if (block->IsCatchBlock()) {
340 // TODO: Dealing with exceptional back edges could be tricky because
341 // they only approximate the real control flow. Bail out for now.
342 return kAnalysisFailThrowCatchLoop;
343 }
344 block->GetLoopInformation()->Populate();
345 }
346 }
347
348 for (HBasicBlock* block : graph_->GetPostOrder()) {
349 if (!outer_loop_bb_set->IsBitSet(block->GetBlockId())) {
350 continue;
351 }
352 if (block->IsLoopHeader()) {
353 HLoopInformation* cur_loop = block->GetLoopInformation();
354 HLoopInformation* outer_loop = cur_loop->GetPreHeader()->GetLoopInformation();
355 if (outer_loop != nullptr) {
356 outer_loop->PopulateInnerLoopUpwards(cur_loop);
357 }
358 }
359 }
360
361 return kAnalysisSuccess;
362}
363
364void SuperblockCloner::CleanUpControlFlow() {
365 // TODO: full control flow clean up for now, optimize it.
366 graph_->ClearDominanceInformation();
367
368 ArenaBitVector outer_loop_bb_set(
369 arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
370 RecalculateBackEdgesInfo(&outer_loop_bb_set);
371
372 // TODO: do it locally.
373 graph_->SimplifyCFG();
374 graph_->ComputeDominanceInformation();
375
376 // AnalyzeLoopsLocally requires a correct post-ordering information which was calculated just
377 // before in ComputeDominanceInformation.
378 GraphAnalysisResult result = AnalyzeLoopsLocally(&outer_loop_bb_set);
379 DCHECK_EQ(result, kAnalysisSuccess);
380
381 // TODO: do it locally
382 OrderLoopsHeadersPredecessors(graph_);
383
384 graph_->ComputeTryBlockInformation();
385}
386
387//
388// Helpers for ResolveDataFlow
389//
390
391void SuperblockCloner::ResolvePhi(HPhi* phi) {
392 HBasicBlock* phi_block = phi->GetBlock();
393 for (size_t i = 0, e = phi->InputCount(); i < e; i++) {
394 HInstruction* input = phi->InputAt(i);
395 HBasicBlock* input_block = input->GetBlock();
396
397 // Originally defined outside the region.
398 if (!IsInOrigBBSet(input_block)) {
399 continue;
400 }
401 HBasicBlock* corresponding_block = phi_block->GetPredecessors()[i];
402 if (!IsInOrigBBSet(corresponding_block)) {
403 phi->ReplaceInput(GetInstrCopy(input), i);
404 }
405 }
406}
407
408//
409// Main algorithm methods.
410//
411
412void SuperblockCloner::SearchForSubgraphExits(ArenaVector<HBasicBlock*>* exits) {
413 DCHECK(exits->empty());
414 for (uint32_t block_id : orig_bb_set_.Indexes()) {
415 HBasicBlock* block = GetBlockById(block_id);
416 for (HBasicBlock* succ : block->GetSuccessors()) {
417 if (!IsInOrigBBSet(succ)) {
418 exits->push_back(succ);
419 }
420 }
421 }
422}
423
424void SuperblockCloner::FindAndSetLocalAreaForAdjustments() {
425 DCHECK(outer_loop_ == nullptr);
426 ArenaVector<HBasicBlock*> exits(arena_->Adapter(kArenaAllocSuperblockCloner));
427 SearchForSubgraphExits(&exits);
428
429 // For a reducible graph we need to update back-edges and dominance information only for
430 // the outermost loop which is affected by the transformation - it can be found by picking
431 // the common most outer loop of loops to which the subgraph exits blocks belong.
432 // Note: it can a loop or the whole graph (outer_loop_ will be nullptr in this case).
433 for (HBasicBlock* exit : exits) {
434 HLoopInformation* loop_exit_loop_info = exit->GetLoopInformation();
435 if (loop_exit_loop_info == nullptr) {
436 outer_loop_ = nullptr;
437 break;
438 }
Artem Serov02eebcf2017-12-13 19:48:31 +0000439 if (outer_loop_ == nullptr) {
440 // We should not use the initial outer_loop_ value 'nullptr' when finding the most outer
441 // common loop.
442 outer_loop_ = loop_exit_loop_info;
443 }
Artem Serov7f4aff62017-06-21 17:02:18 +0100444 outer_loop_ = FindCommonLoop(outer_loop_, loop_exit_loop_info);
445 }
446
447 if (outer_loop_ != nullptr) {
448 // Save the loop population info as it will be changed later.
449 outer_loop_bb_set_.Copy(&outer_loop_->GetBlocks());
450 }
451}
452
453void SuperblockCloner::RemapEdgesSuccessors() {
454 // Redirect incoming edges.
455 for (HEdge e : *remap_incoming_) {
456 HBasicBlock* orig_block = GetBlockById(e.GetFrom());
457 HBasicBlock* orig_succ = GetBlockById(e.GetTo());
458 RemapOrigInternalOrIncomingEdge(orig_block, orig_succ);
459 }
460
461 // Redirect internal edges.
462 for (uint32_t orig_block_id : orig_bb_set_.Indexes()) {
463 HBasicBlock* orig_block = GetBlockById(orig_block_id);
464
465 for (HBasicBlock* orig_succ : orig_block->GetSuccessors()) {
466 uint32_t orig_succ_id = orig_succ->GetBlockId();
467
468 // Check for outgoing edge.
469 if (!IsInOrigBBSet(orig_succ)) {
470 HBasicBlock* copy_block = GetBlockCopy(orig_block);
471 copy_block->AddSuccessor(orig_succ);
472 continue;
473 }
474
475 auto orig_redir = remap_orig_internal_->Find(HEdge(orig_block_id, orig_succ_id));
476 auto copy_redir = remap_copy_internal_->Find(HEdge(orig_block_id, orig_succ_id));
477
478 // Due to construction all successors of copied block were set to original.
479 if (copy_redir != remap_copy_internal_->end()) {
480 RemapCopyInternalEdge(orig_block, orig_succ);
481 } else {
482 AddCopyInternalEdge(orig_block, orig_succ);
483 }
484
485 if (orig_redir != remap_orig_internal_->end()) {
486 RemapOrigInternalOrIncomingEdge(orig_block, orig_succ);
487 }
488 }
489 }
490}
491
492void SuperblockCloner::AdjustControlFlowInfo() {
493 ArenaBitVector outer_loop_bb_set(
494 arena_, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
495 RecalculateBackEdgesInfo(&outer_loop_bb_set);
496
497 graph_->ClearDominanceInformation();
498 // TODO: Do it locally.
499 graph_->ComputeDominanceInformation();
500}
501
502// TODO: Current FastCase restriction guarantees that instructions' inputs are already mapped to
503// the valid values; only phis' inputs must be adjusted.
504void SuperblockCloner::ResolveDataFlow() {
505 for (auto entry : *bb_map_) {
506 HBasicBlock* orig_block = entry.first;
507
508 for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) {
509 HPhi* orig_phi = it.Current()->AsPhi();
510 HPhi* copy_phi = GetInstrCopy(orig_phi)->AsPhi();
511 ResolvePhi(orig_phi);
512 ResolvePhi(copy_phi);
513 }
514 if (kIsDebugBuild) {
515 // Inputs of instruction copies must be already mapped to correspondent inputs copies.
516 for (HInstructionIterator it(orig_block->GetInstructions()); !it.Done(); it.Advance()) {
517 CheckInstructionInputsRemapping(it.Current());
518 }
519 }
520 }
521}
522
523//
524// Debug and logging methods.
525//
526
Artem Serov02eebcf2017-12-13 19:48:31 +0000527// Debug function to dump graph' BasicBlocks info.
528void DumpBB(HGraph* graph) {
529 for (HBasicBlock* bb : graph->GetBlocks()) {
530 if (bb == nullptr) {
531 continue;
532 }
533 std::cout << bb->GetBlockId();
534 std::cout << " <- ";
535 for (HBasicBlock* pred : bb->GetPredecessors()) {
536 std::cout << pred->GetBlockId() << " ";
537 }
538 std::cout << " -> ";
539 for (HBasicBlock* succ : bb->GetSuccessors()) {
540 std::cout << succ->GetBlockId() << " ";
541 }
542
543 if (bb->GetDominator()) {
544 std::cout << " dom " << bb->GetDominator()->GetBlockId();
545 }
546
547 if (bb->GetLoopInformation()) {
548 std::cout << "\tloop: " << bb->GetLoopInformation()->GetHeader()->GetBlockId();
549 }
550
551 std::cout << std::endl;
552 }
553}
554
Artem Serov7f4aff62017-06-21 17:02:18 +0100555void SuperblockCloner::CheckInstructionInputsRemapping(HInstruction* orig_instr) {
556 DCHECK(!orig_instr->IsPhi());
557 HInstruction* copy_instr = GetInstrCopy(orig_instr);
558 for (size_t i = 0, e = orig_instr->InputCount(); i < e; i++) {
559 HInstruction* orig_input = orig_instr->InputAt(i);
560 DCHECK(orig_input->GetBlock()->Dominates(orig_instr->GetBlock()));
561
562 // If original input is defined outside the region then it will remain for both original
563 // instruction and the copy after the transformation.
564 if (!IsInOrigBBSet(orig_input->GetBlock())) {
565 continue;
566 }
567 HInstruction* copy_input = GetInstrCopy(orig_input);
568 DCHECK(copy_input->GetBlock()->Dominates(copy_instr->GetBlock()));
569 }
570
571 // Resolve environment.
572 if (orig_instr->HasEnvironment()) {
573 HEnvironment* orig_env = orig_instr->GetEnvironment();
574
575 for (size_t i = 0, e = orig_env->Size(); i < e; ++i) {
576 HInstruction* orig_input = orig_env->GetInstructionAt(i);
577
578 // If original input is defined outside the region then it will remain for both original
579 // instruction and the copy after the transformation.
580 if (orig_input == nullptr || !IsInOrigBBSet(orig_input->GetBlock())) {
581 continue;
582 }
583
584 HInstruction* copy_input = GetInstrCopy(orig_input);
585 DCHECK(copy_input->GetBlock()->Dominates(copy_instr->GetBlock()));
586 }
587 }
588}
589
Artem Serov02eebcf2017-12-13 19:48:31 +0000590bool SuperblockCloner::CheckRemappingInfoIsValid() {
591 for (HEdge edge : *remap_orig_internal_) {
592 if (!IsEdgeValid(edge, graph_) ||
593 !IsInOrigBBSet(edge.GetFrom()) ||
594 !IsInOrigBBSet(edge.GetTo())) {
595 return false;
596 }
597 }
598
599 for (auto edge : *remap_copy_internal_) {
600 if (!IsEdgeValid(edge, graph_) ||
601 !IsInOrigBBSet(edge.GetFrom()) ||
602 !IsInOrigBBSet(edge.GetTo())) {
603 return false;
604 }
605 }
606
607 for (auto edge : *remap_incoming_) {
608 if (!IsEdgeValid(edge, graph_) ||
609 IsInOrigBBSet(edge.GetFrom()) ||
610 !IsInOrigBBSet(edge.GetTo())) {
611 return false;
612 }
613 }
614
615 return true;
616}
617
618void SuperblockCloner::VerifyGraph() {
619 for (auto it : *hir_map_) {
620 HInstruction* orig_instr = it.first;
621 HInstruction* copy_instr = it.second;
622 if (!orig_instr->IsPhi() && !orig_instr->IsSuspendCheck()) {
623 DCHECK(it.first->GetBlock() != nullptr);
624 }
625 if (!copy_instr->IsPhi() && !copy_instr->IsSuspendCheck()) {
626 DCHECK(it.second->GetBlock() != nullptr);
627 }
628 }
629
630 GraphChecker checker(graph_);
631 checker.Run();
632 if (!checker.IsValid()) {
633 for (const std::string& error : checker.GetErrors()) {
634 std::cout << error << std::endl;
635 }
636 LOG(FATAL) << "GraphChecker failed: superblock cloner\n";
637 }
638}
639
640void DumpBBSet(const ArenaBitVector* set) {
641 for (uint32_t idx : set->Indexes()) {
642 std::cout << idx << "\n";
643 }
644}
645
646void SuperblockCloner::DumpInputSets() {
647 std::cout << graph_->PrettyMethod() << "\n";
648 std::cout << "orig_bb_set:\n";
649 for (uint32_t idx : orig_bb_set_.Indexes()) {
650 std::cout << idx << "\n";
651 }
652 std::cout << "remap_orig_internal:\n";
653 for (HEdge e : *remap_orig_internal_) {
654 std::cout << e << "\n";
655 }
656 std::cout << "remap_copy_internal:\n";
657 for (auto e : *remap_copy_internal_) {
658 std::cout << e << "\n";
659 }
660 std::cout << "remap_incoming:\n";
661 for (auto e : *remap_incoming_) {
662 std::cout << e << "\n";
663 }
664}
665
Artem Serov7f4aff62017-06-21 17:02:18 +0100666//
667// Public methods.
668//
669
670SuperblockCloner::SuperblockCloner(HGraph* graph,
671 const HBasicBlockSet* orig_bb_set,
672 HBasicBlockMap* bb_map,
673 HInstructionMap* hir_map)
674 : graph_(graph),
675 arena_(graph->GetAllocator()),
676 orig_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner),
677 remap_orig_internal_(nullptr),
678 remap_copy_internal_(nullptr),
679 remap_incoming_(nullptr),
680 bb_map_(bb_map),
681 hir_map_(hir_map),
682 outer_loop_(nullptr),
683 outer_loop_bb_set_(arena_, orig_bb_set->GetSizeOf(), true, kArenaAllocSuperblockCloner) {
684 orig_bb_set_.Copy(orig_bb_set);
685}
686
687void SuperblockCloner::SetSuccessorRemappingInfo(const HEdgeSet* remap_orig_internal,
688 const HEdgeSet* remap_copy_internal,
689 const HEdgeSet* remap_incoming) {
690 remap_orig_internal_ = remap_orig_internal;
691 remap_copy_internal_ = remap_copy_internal;
692 remap_incoming_ = remap_incoming;
Artem Serov02eebcf2017-12-13 19:48:31 +0000693 DCHECK(CheckRemappingInfoIsValid());
Artem Serov7f4aff62017-06-21 17:02:18 +0100694}
695
696bool SuperblockCloner::IsSubgraphClonable() const {
697 // TODO: Support irreducible graphs and graphs with try-catch.
698 if (graph_->HasIrreducibleLoops() || graph_->HasTryCatch()) {
699 return false;
700 }
701
702 // Check that there are no instructions defined in the subgraph and used outside.
703 // TODO: Improve this by accepting graph with such uses but only one exit.
704 for (uint32_t idx : orig_bb_set_.Indexes()) {
705 HBasicBlock* block = GetBlockById(idx);
706
707 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) {
708 HInstruction* instr = it.Current();
709 if (!instr->IsClonable() ||
710 IsUsedOutsideRegion(instr, orig_bb_set_)) {
711 return false;
712 }
713 }
714
715 for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) {
716 HInstruction* instr = it.Current();
717 if (!instr->IsClonable() ||
718 IsUsedOutsideRegion(instr, orig_bb_set_)) {
719 return false;
720 }
721 }
722 }
723
724 return true;
725}
726
Artem Serov02eebcf2017-12-13 19:48:31 +0000727bool SuperblockCloner::IsFastCase() const {
728 // Check that loop unrolling/loop peeling is being conducted.
729 // Check that all the basic blocks belong to the same loop.
730 bool flag = false;
731 HLoopInformation* common_loop_info = nullptr;
732 for (uint32_t idx : orig_bb_set_.Indexes()) {
733 HBasicBlock* block = GetBlockById(idx);
734 HLoopInformation* block_loop_info = block->GetLoopInformation();
735 if (!flag) {
736 common_loop_info = block_loop_info;
737 } else {
738 if (block_loop_info != common_loop_info) {
739 return false;
740 }
741 }
742 }
743
744 // Check that orig_bb_set_ corresponds to loop peeling/unrolling.
745 if (common_loop_info == nullptr || !orig_bb_set_.SameBitsSet(&common_loop_info->GetBlocks())) {
746 return false;
747 }
748
749 bool peeling_or_unrolling = false;
750 HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
751 HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
752 HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
753
754
755 // Check whether remapping info corresponds to loop unrolling.
756 CollectRemappingInfoForPeelUnroll(/* to_unroll*/ true,
757 common_loop_info,
758 &remap_orig_internal,
759 &remap_copy_internal,
760 &remap_incoming);
761
762 peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) &&
763 EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) &&
764 EdgeHashSetsEqual(&remap_incoming, remap_incoming_);
765
766 remap_orig_internal.Clear();
767 remap_copy_internal.Clear();
768 remap_incoming.Clear();
769
770 // Check whether remapping info corresponds to loop peeling.
771 CollectRemappingInfoForPeelUnroll(/* to_unroll*/ false,
772 common_loop_info,
773 &remap_orig_internal,
774 &remap_copy_internal,
775 &remap_incoming);
776
777 peeling_or_unrolling |= EdgeHashSetsEqual(&remap_orig_internal, remap_orig_internal_) &&
778 EdgeHashSetsEqual(&remap_copy_internal, remap_copy_internal_) &&
779 EdgeHashSetsEqual(&remap_incoming, remap_incoming_);
780
781 return peeling_or_unrolling;
782}
783
Artem Serov7f4aff62017-06-21 17:02:18 +0100784void SuperblockCloner::Run() {
785 DCHECK(bb_map_ != nullptr);
786 DCHECK(hir_map_ != nullptr);
787 DCHECK(remap_orig_internal_ != nullptr &&
788 remap_copy_internal_ != nullptr &&
789 remap_incoming_ != nullptr);
790 DCHECK(IsSubgraphClonable());
Artem Serov02eebcf2017-12-13 19:48:31 +0000791 DCHECK(IsFastCase());
792
793 if (kSuperblockClonerLogging) {
794 DumpInputSets();
795 }
Artem Serov7f4aff62017-06-21 17:02:18 +0100796
797 // Find an area in the graph for which control flow information should be adjusted.
798 FindAndSetLocalAreaForAdjustments();
799 // Clone the basic blocks from the orig_bb_set_; data flow is invalid after the call and is to be
800 // adjusted.
801 CloneBasicBlocks();
802 // Connect the blocks together/remap successors and fix phis which are directly affected my the
803 // remapping.
804 RemapEdgesSuccessors();
Artem Serov02eebcf2017-12-13 19:48:31 +0000805
806 // Check that the subgraph is connected.
807 if (kIsDebugBuild) {
808 HBasicBlockSet work_set(arena_, orig_bb_set_.GetSizeOf(), true, kArenaAllocSuperblockCloner);
809
810 // Add original and copy blocks of the subgraph to the work set.
811 for (auto iter : *bb_map_) {
812 work_set.SetBit(iter.first->GetBlockId()); // Original block.
813 work_set.SetBit(iter.second->GetBlockId()); // Copy block.
814 }
815 CHECK(IsSubgraphConnected(&work_set, graph_));
816 }
817
Artem Serov7f4aff62017-06-21 17:02:18 +0100818 // Recalculate dominance and backedge information which is required by the next stage.
819 AdjustControlFlowInfo();
820 // Fix data flow of the graph.
821 ResolveDataFlow();
822}
823
824void SuperblockCloner::CleanUp() {
825 CleanUpControlFlow();
826
827 // Remove phis which have all inputs being same.
828 // When a block has a single predecessor it must not have any phis. However after the
829 // transformation it could happen that there is such block with a phi with a single input.
830 // As this is needed to be processed we also simplify phis with multiple same inputs here.
831 for (auto entry : *bb_map_) {
832 HBasicBlock* orig_block = entry.first;
833 for (HInstructionIterator inst_it(orig_block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
834 HPhi* phi = inst_it.Current()->AsPhi();
835 if (ArePhiInputsTheSame(phi)) {
836 phi->ReplaceWith(phi->InputAt(0));
837 orig_block->RemovePhi(phi);
838 }
839 }
840
841 HBasicBlock* copy_block = GetBlockCopy(orig_block);
842 for (HInstructionIterator inst_it(copy_block->GetPhis()); !inst_it.Done(); inst_it.Advance()) {
843 HPhi* phi = inst_it.Current()->AsPhi();
844 if (ArePhiInputsTheSame(phi)) {
845 phi->ReplaceWith(phi->InputAt(0));
846 copy_block->RemovePhi(phi);
847 }
848 }
849 }
Artem Serov02eebcf2017-12-13 19:48:31 +0000850
Artem Serov121f2032017-10-23 19:19:06 +0100851 if (kIsDebugBuild) {
Artem Serov02eebcf2017-12-13 19:48:31 +0000852 VerifyGraph();
853 }
Artem Serov7f4aff62017-06-21 17:02:18 +0100854}
855
856HBasicBlock* SuperblockCloner::CloneBasicBlock(const HBasicBlock* orig_block) {
857 HGraph* graph = orig_block->GetGraph();
858 HBasicBlock* copy_block = new (arena_) HBasicBlock(graph, orig_block->GetDexPc());
859 graph->AddBlock(copy_block);
860
861 // Clone all the phis and add them to the map.
862 for (HInstructionIterator it(orig_block->GetPhis()); !it.Done(); it.Advance()) {
863 HInstruction* orig_instr = it.Current();
864 HInstruction* copy_instr = orig_instr->Clone(arena_);
865 copy_block->AddPhi(copy_instr->AsPhi());
866 copy_instr->AsPhi()->RemoveAllInputs();
867 DCHECK(!orig_instr->HasEnvironment());
868 hir_map_->Put(orig_instr, copy_instr);
869 }
870
871 // Clone all the instructions and add them to the map.
872 for (HInstructionIterator it(orig_block->GetInstructions()); !it.Done(); it.Advance()) {
873 HInstruction* orig_instr = it.Current();
874 HInstruction* copy_instr = orig_instr->Clone(arena_);
875 ReplaceInputsWithCopies(copy_instr);
876 copy_block->AddInstruction(copy_instr);
877 if (orig_instr->HasEnvironment()) {
878 DeepCloneEnvironmentWithRemapping(copy_instr, orig_instr->GetEnvironment());
879 }
880 hir_map_->Put(orig_instr, copy_instr);
881 }
882
883 return copy_block;
884}
885
886void SuperblockCloner::CloneBasicBlocks() {
887 // By this time ReversePostOrder must be valid: in 'CloneBasicBlock' inputs of the copied
888 // instructions might be replaced by copies of the original inputs (depending where those inputs
889 // are defined). So the definitions of the original inputs must be visited before their original
890 // uses. The property of the reducible graphs "if 'A' dom 'B' then rpo_num('A') >= rpo_num('B')"
891 // guarantees that.
892 for (HBasicBlock* orig_block : graph_->GetReversePostOrder()) {
893 if (!IsInOrigBBSet(orig_block)) {
894 continue;
895 }
896 HBasicBlock* copy_block = CloneBasicBlock(orig_block);
897 bb_map_->Put(orig_block, copy_block);
898 if (kSuperblockClonerLogging) {
899 std::cout << "new block :" << copy_block->GetBlockId() << ": " << orig_block->GetBlockId() <<
900 std::endl;
901 }
902 }
903}
904
Artem Serov02eebcf2017-12-13 19:48:31 +0000905//
906// Stand-alone methods.
907//
908
909void CollectRemappingInfoForPeelUnroll(bool to_unroll,
910 HLoopInformation* loop_info,
911 HEdgeSet* remap_orig_internal,
912 HEdgeSet* remap_copy_internal,
913 HEdgeSet* remap_incoming) {
914 DCHECK(loop_info != nullptr);
915 HBasicBlock* loop_header = loop_info->GetHeader();
916 // Set up remap_orig_internal edges set - set is empty.
917 // Set up remap_copy_internal edges set.
918 for (HBasicBlock* back_edge_block : loop_info->GetBackEdges()) {
919 HEdge e = HEdge(back_edge_block, loop_header);
920 if (to_unroll) {
921 remap_orig_internal->Insert(e);
922 remap_copy_internal->Insert(e);
923 } else {
Artem Serov72411e62017-10-19 16:18:07 +0100924 remap_copy_internal->Insert(e);
Artem Serov02eebcf2017-12-13 19:48:31 +0000925 }
926 }
927
928 // Set up remap_incoming edges set.
Artem Serov72411e62017-10-19 16:18:07 +0100929 if (!to_unroll) {
Artem Serov02eebcf2017-12-13 19:48:31 +0000930 remap_incoming->Insert(HEdge(loop_info->GetPreHeader(), loop_header));
931 }
932}
933
934bool IsSubgraphConnected(SuperblockCloner::HBasicBlockSet* work_set, HGraph* graph) {
935 ArenaVector<HBasicBlock*> entry_blocks(
936 graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
937
938 // Find subgraph entry blocks.
939 for (uint32_t orig_block_id : work_set->Indexes()) {
940 HBasicBlock* block = graph->GetBlocks()[orig_block_id];
941 for (HBasicBlock* pred : block->GetPredecessors()) {
942 if (!work_set->IsBitSet(pred->GetBlockId())) {
943 entry_blocks.push_back(block);
944 break;
945 }
946 }
947 }
948
949 for (HBasicBlock* entry_block : entry_blocks) {
950 if (work_set->IsBitSet(entry_block->GetBlockId())) {
951 TraverseSubgraphForConnectivity(entry_block, work_set);
952 }
953 }
954
955 // Return whether there are unvisited - unreachable - blocks.
956 return work_set->NumSetBits() == 0;
957}
958
959HLoopInformation* FindCommonLoop(HLoopInformation* loop1, HLoopInformation* loop2) {
960 if (loop1 == nullptr || loop2 == nullptr) {
961 return nullptr;
962 }
963
964 if (loop1->IsIn(*loop2)) {
965 return loop2;
966 }
967
968 HLoopInformation* current = loop1;
969 while (current != nullptr && !loop2->IsIn(*current)) {
970 current = current->GetPreHeader()->GetLoopInformation();
971 }
972
973 return current;
974}
975
976bool PeelUnrollHelper::IsLoopClonable(HLoopInformation* loop_info) {
977 PeelUnrollHelper helper(loop_info, nullptr, nullptr);
978 return helper.IsLoopClonable();
979}
980
981HBasicBlock* PeelUnrollHelper::DoPeelUnrollImpl(bool to_unroll) {
982 // For now do peeling only for natural loops.
983 DCHECK(!loop_info_->IsIrreducible());
984
985 HBasicBlock* loop_header = loop_info_->GetHeader();
Artem Serov72411e62017-10-19 16:18:07 +0100986 // Check that loop info is up-to-date.
987 DCHECK(loop_info_ == loop_header->GetLoopInformation());
988
Artem Serov02eebcf2017-12-13 19:48:31 +0000989 HGraph* graph = loop_header->GetGraph();
990 ArenaAllocator allocator(graph->GetAllocator()->GetArenaPool());
991
992 HEdgeSet remap_orig_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
993 HEdgeSet remap_copy_internal(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
994 HEdgeSet remap_incoming(graph->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
995
996 CollectRemappingInfoForPeelUnroll(to_unroll,
997 loop_info_,
998 &remap_orig_internal,
999 &remap_copy_internal,
1000 &remap_incoming);
1001
1002 cloner_.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming);
1003 cloner_.Run();
1004 cloner_.CleanUp();
1005
Artem Serov72411e62017-10-19 16:18:07 +01001006 // Check that loop info is preserved.
1007 DCHECK(loop_info_ == loop_header->GetLoopInformation());
1008
1009 return loop_header;
Artem Serov02eebcf2017-12-13 19:48:31 +00001010}
1011
1012PeelUnrollSimpleHelper::PeelUnrollSimpleHelper(HLoopInformation* info)
1013 : bb_map_(std::less<HBasicBlock*>(),
1014 info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)),
1015 hir_map_(std::less<HInstruction*>(),
1016 info->GetHeader()->GetGraph()->GetAllocator()->Adapter(kArenaAllocSuperblockCloner)),
1017 helper_(info, &bb_map_, &hir_map_) {}
1018
Artem Serov7f4aff62017-06-21 17:02:18 +01001019} // namespace art
Artem Serov02eebcf2017-12-13 19:48:31 +00001020
1021namespace std {
1022
1023ostream& operator<<(ostream& os, const art::HEdge& e) {
1024 e.Dump(os);
1025 return os;
1026}
1027
1028} // namespace std