blob: df2e517afff01691b89aa7ad8f8bc688e816d1cc [file] [log] [blame]
Artem Serovcced8ba2017-07-19 18:18:09 +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 "graph_checker.h"
18#include "nodes.h"
19#include "optimizing_unit_test.h"
Artem Serov7f4aff62017-06-21 17:02:18 +010020#include "superblock_cloner.h"
Artem Serovcced8ba2017-07-19 18:18:09 +010021
22#include "gtest/gtest.h"
23
24namespace art {
25
Artem Serov7f4aff62017-06-21 17:02:18 +010026using HBasicBlockMap = SuperblockCloner::HBasicBlockMap;
27using HInstructionMap = SuperblockCloner::HInstructionMap;
Artem Serov02eebcf2017-12-13 19:48:31 +000028using HBasicBlockSet = SuperblockCloner::HBasicBlockSet;
29using HEdgeSet = SuperblockCloner::HEdgeSet;
Artem Serov7f4aff62017-06-21 17:02:18 +010030
Artem Serovcced8ba2017-07-19 18:18:09 +010031// This class provides methods and helpers for testing various cloning and copying routines:
32// individual instruction cloning and cloning of the more coarse-grain structures.
Artem Serov5e399b82017-12-21 14:28:35 +000033class SuperblockClonerTest : public OptimizingUnitTest {
Artem Serovcced8ba2017-07-19 18:18:09 +010034 public:
Artem Serov02eebcf2017-12-13 19:48:31 +000035 SuperblockClonerTest() : graph_(CreateGraph()),
36 entry_block_(nullptr),
37 return_block_(nullptr),
38 exit_block_(nullptr),
39 parameter_(nullptr) {}
Artem Serovcced8ba2017-07-19 18:18:09 +010040
Artem Serov02eebcf2017-12-13 19:48:31 +000041 void InitGraph() {
Artem Serovcced8ba2017-07-19 18:18:09 +010042 entry_block_ = new (GetAllocator()) HBasicBlock(graph_);
43 graph_->AddBlock(entry_block_);
44 graph_->SetEntryBlock(entry_block_);
45
Artem Serov02eebcf2017-12-13 19:48:31 +000046 return_block_ = new (GetAllocator()) HBasicBlock(graph_);
47 graph_->AddBlock(return_block_);
Artem Serovcced8ba2017-07-19 18:18:09 +010048
49 exit_block_ = new (GetAllocator()) HBasicBlock(graph_);
50 graph_->AddBlock(exit_block_);
51 graph_->SetExitBlock(exit_block_);
52
Artem Serov02eebcf2017-12-13 19:48:31 +000053 entry_block_->AddSuccessor(return_block_);
54 return_block_->AddSuccessor(exit_block_);
Artem Serovcced8ba2017-07-19 18:18:09 +010055
56 parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
57 dex::TypeIndex(0),
58 0,
59 DataType::Type::kInt32);
60 entry_block_->AddInstruction(parameter_);
Artem Serov02eebcf2017-12-13 19:48:31 +000061 return_block_->AddInstruction(new (GetAllocator()) HReturnVoid());
Artem Serovcced8ba2017-07-19 18:18:09 +010062 exit_block_->AddInstruction(new (GetAllocator()) HExit());
63 }
64
Artem Serov02eebcf2017-12-13 19:48:31 +000065 void CreateBasicLoopControlFlow(HBasicBlock* position,
66 HBasicBlock* successor,
67 /* out */ HBasicBlock** header_p,
68 /* out */ HBasicBlock** body_p) {
69 HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
70 HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
71 HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
72
73 graph_->AddBlock(loop_preheader);
74 graph_->AddBlock(loop_header);
75 graph_->AddBlock(loop_body);
76
77 position->ReplaceSuccessor(successor, loop_preheader);
78
79 loop_preheader->AddSuccessor(loop_header);
80 // Loop exit first to have a proper exit condition/target for HIf.
81 loop_header->AddSuccessor(successor);
82 loop_header->AddSuccessor(loop_body);
83 loop_body->AddSuccessor(loop_header);
84
85 *header_p = loop_header;
86 *body_p = loop_body;
87 }
88
Artem Serovcced8ba2017-07-19 18:18:09 +010089 void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) {
90 uint32_t dex_pc = 0;
91
92 // Entry block.
93 HIntConstant* const_0 = graph_->GetIntConstant(0);
94 HIntConstant* const_1 = graph_->GetIntConstant(1);
95 HIntConstant* const_128 = graph_->GetIntConstant(128);
96
97 // Header block.
98 HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
99 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
Artem Serov02eebcf2017-12-13 19:48:31 +0000100 HInstruction* loop_check = new (GetAllocator()) HGreaterThanOrEqual(phi, const_128);
Artem Serovcced8ba2017-07-19 18:18:09 +0100101
102 loop_header->AddPhi(phi);
103 loop_header->AddInstruction(suspend_check);
Artem Serov02eebcf2017-12-13 19:48:31 +0000104 loop_header->AddInstruction(loop_check);
105 loop_header->AddInstruction(new (GetAllocator()) HIf(loop_check));
Artem Serovcced8ba2017-07-19 18:18:09 +0100106
107 // Loop body block.
108 HInstruction* null_check = new (GetAllocator()) HNullCheck(parameter_, dex_pc);
109 HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc);
110 HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc);
111 HInstruction* array_get =
112 new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc);
113 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1);
Artem Serov02eebcf2017-12-13 19:48:31 +0000114 HInstruction* array_set = new (GetAllocator()) HArraySet(
115 null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
Artem Serovcced8ba2017-07-19 18:18:09 +0100116 HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1);
117
118 loop_body->AddInstruction(null_check);
119 loop_body->AddInstruction(array_length);
120 loop_body->AddInstruction(bounds_check);
121 loop_body->AddInstruction(array_get);
122 loop_body->AddInstruction(add);
123 loop_body->AddInstruction(array_set);
124 loop_body->AddInstruction(induction_inc);
125 loop_body->AddInstruction(new (GetAllocator()) HGoto());
126
127 phi->AddInput(const_0);
128 phi->AddInput(induction_inc);
129
130 graph_->SetHasBoundsChecks(true);
131
132 // Adjust HEnvironment for each instruction which require that.
133 ArenaVector<HInstruction*> current_locals({phi, const_128, parameter_},
134 GetAllocator()->Adapter(kArenaAllocInstruction));
135
136 HEnvironment* env = ManuallyBuildEnvFor(suspend_check, &current_locals);
137 null_check->CopyEnvironmentFrom(env);
138 bounds_check->CopyEnvironmentFrom(env);
139 }
140
141 HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction,
142 ArenaVector<HInstruction*>* current_locals) {
143 HEnvironment* environment = new (GetAllocator()) HEnvironment(
144 (GetAllocator()),
145 current_locals->size(),
146 graph_->GetArtMethod(),
147 instruction->GetDexPc(),
148 instruction);
149
150 environment->CopyFrom(ArrayRef<HInstruction* const>(*current_locals));
151 instruction->SetRawEnvironment(environment);
152 return environment;
153 }
154
155 bool CheckGraph() {
156 GraphChecker checker(graph_);
157 checker.Run();
158 if (!checker.IsValid()) {
159 for (const std::string& error : checker.GetErrors()) {
160 std::cout << error << std::endl;
161 }
162 return false;
163 }
164 return true;
165 }
166
167 HGraph* graph_;
168
169 HBasicBlock* entry_block_;
Artem Serov02eebcf2017-12-13 19:48:31 +0000170 HBasicBlock* return_block_;
Artem Serovcced8ba2017-07-19 18:18:09 +0100171 HBasicBlock* exit_block_;
172
173 HInstruction* parameter_;
174};
175
Artem Serov5e399b82017-12-21 14:28:35 +0000176TEST_F(SuperblockClonerTest, IndividualInstrCloner) {
Artem Serovcced8ba2017-07-19 18:18:09 +0100177 HBasicBlock* header = nullptr;
178 HBasicBlock* loop_body = nullptr;
179
Artem Serov02eebcf2017-12-13 19:48:31 +0000180 InitGraph();
181 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
Artem Serovcced8ba2017-07-19 18:18:09 +0100182 CreateBasicLoopDataFlow(header, loop_body);
183 graph_->BuildDominatorTree();
Artem Serov02eebcf2017-12-13 19:48:31 +0000184 EXPECT_TRUE(CheckGraph());
Artem Serovcced8ba2017-07-19 18:18:09 +0100185
186 HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
187 CloneAndReplaceInstructionVisitor visitor(graph_);
188 // Do instruction cloning and replacement twice with different visiting order.
189
190 visitor.VisitInsertionOrder();
191 size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
192 EXPECT_EQ(instr_replaced_by_clones_count, 12u);
193 EXPECT_TRUE(CheckGraph());
194
195 visitor.VisitReversePostOrder();
196 instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
197 EXPECT_EQ(instr_replaced_by_clones_count, 24u);
198 EXPECT_TRUE(CheckGraph());
199
200 HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
201 EXPECT_NE(new_suspend_check, old_suspend_check);
202 EXPECT_NE(new_suspend_check, nullptr);
203}
204
Artem Serov7f4aff62017-06-21 17:02:18 +0100205// Tests SuperblockCloner::CloneBasicBlocks - check instruction cloning and initial remapping of
206// instructions' inputs.
207TEST_F(SuperblockClonerTest, CloneBasicBlocks) {
208 HBasicBlock* header = nullptr;
209 HBasicBlock* loop_body = nullptr;
210 ArenaAllocator* arena = graph_->GetAllocator();
211
Artem Serov02eebcf2017-12-13 19:48:31 +0000212 InitGraph();
213 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
Artem Serov7f4aff62017-06-21 17:02:18 +0100214 CreateBasicLoopDataFlow(header, loop_body);
215 graph_->BuildDominatorTree();
216 ASSERT_TRUE(CheckGraph());
217
218 ArenaBitVector orig_bb_set(
219 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
220 HBasicBlockMap bb_map(std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner));
221 HInstructionMap hir_map(std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner));
222
223 HLoopInformation* loop_info = header->GetLoopInformation();
224 orig_bb_set.Union(&loop_info->GetBlocks());
225
226 SuperblockCloner cloner(graph_,
227 &orig_bb_set,
228 &bb_map,
229 &hir_map);
230 EXPECT_TRUE(cloner.IsSubgraphClonable());
231
232 cloner.CloneBasicBlocks();
233
234 EXPECT_EQ(bb_map.size(), 2u);
235 EXPECT_EQ(hir_map.size(), 12u);
236
237 for (auto it : hir_map) {
238 HInstruction* orig_instr = it.first;
239 HInstruction* copy_instr = it.second;
240
241 EXPECT_EQ(cloner.GetBlockCopy(orig_instr->GetBlock()), copy_instr->GetBlock());
242 EXPECT_EQ(orig_instr->GetKind(), copy_instr->GetKind());
243 EXPECT_EQ(orig_instr->GetType(), copy_instr->GetType());
244
245 if (orig_instr->IsPhi()) {
246 continue;
247 }
248
249 EXPECT_EQ(orig_instr->InputCount(), copy_instr->InputCount());
250
251 // Check that inputs match.
252 for (size_t i = 0, e = orig_instr->InputCount(); i < e; i++) {
253 HInstruction* orig_input = orig_instr->InputAt(i);
254 HInstruction* copy_input = copy_instr->InputAt(i);
255 if (cloner.IsInOrigBBSet(orig_input->GetBlock())) {
256 EXPECT_EQ(cloner.GetInstrCopy(orig_input), copy_input);
257 } else {
258 EXPECT_EQ(orig_input, copy_input);
259 }
260 }
261
262 EXPECT_EQ(orig_instr->HasEnvironment(), copy_instr->HasEnvironment());
263
264 // Check that environments match.
265 if (orig_instr->HasEnvironment()) {
266 HEnvironment* orig_env = orig_instr->GetEnvironment();
267 HEnvironment* copy_env = copy_instr->GetEnvironment();
268
269 EXPECT_EQ(copy_env->GetParent(), nullptr);
270 EXPECT_EQ(orig_env->Size(), copy_env->Size());
271
272 for (size_t i = 0, e = orig_env->Size(); i < e; i++) {
273 HInstruction* orig_input = orig_env->GetInstructionAt(i);
274 HInstruction* copy_input = copy_env->GetInstructionAt(i);
275 if (cloner.IsInOrigBBSet(orig_input->GetBlock())) {
276 EXPECT_EQ(cloner.GetInstrCopy(orig_input), copy_input);
277 } else {
278 EXPECT_EQ(orig_input, copy_input);
279 }
280 }
281 }
282 }
283}
284
285// SuperblockCloner::CleanUpControlFlow - checks algorithms of local adjustments of the control
286// flow.
287TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) {
288 HBasicBlock* header = nullptr;
289 HBasicBlock* loop_body = nullptr;
290 ArenaAllocator* arena = graph_->GetAllocator();
291
Artem Serov02eebcf2017-12-13 19:48:31 +0000292 InitGraph();
293 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
Artem Serov7f4aff62017-06-21 17:02:18 +0100294 CreateBasicLoopDataFlow(header, loop_body);
295 graph_->BuildDominatorTree();
296 ASSERT_TRUE(CheckGraph());
297
298 ArenaBitVector orig_bb_set(
299 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
300
301 HLoopInformation* loop_info = header->GetLoopInformation();
302 orig_bb_set.Union(&loop_info->GetBlocks());
303
304 SuperblockCloner cloner(graph_,
305 &orig_bb_set,
306 nullptr,
307 nullptr);
308 EXPECT_TRUE(cloner.IsSubgraphClonable());
309
310 cloner.FindAndSetLocalAreaForAdjustments();
311 cloner.CleanUpControlFlow();
312
313 EXPECT_TRUE(CheckGraph());
314
315 EXPECT_TRUE(entry_block_->Dominates(header));
316 EXPECT_TRUE(entry_block_->Dominates(exit_block_));
317
318 EXPECT_EQ(header->GetLoopInformation(), loop_info);
319 EXPECT_EQ(loop_info->GetHeader(), header);
320 EXPECT_TRUE(loop_info->Contains(*loop_body));
321 EXPECT_TRUE(loop_info->IsBackEdge(*loop_body));
322}
323
Artem Serov02eebcf2017-12-13 19:48:31 +0000324// Tests IsSubgraphConnected function for negative case.
325TEST_F(SuperblockClonerTest, IsGraphConnected) {
326 HBasicBlock* header = nullptr;
327 HBasicBlock* loop_body = nullptr;
328 ArenaAllocator* arena = graph_->GetAllocator();
329
330 InitGraph();
331 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
332 CreateBasicLoopDataFlow(header, loop_body);
333 HBasicBlock* unreachable_block = new (GetAllocator()) HBasicBlock(graph_);
334 graph_->AddBlock(unreachable_block);
335
336 HBasicBlockSet bb_set(
337 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
338 bb_set.SetBit(header->GetBlockId());
339 bb_set.SetBit(loop_body->GetBlockId());
340 bb_set.SetBit(unreachable_block->GetBlockId());
341
342 EXPECT_FALSE(IsSubgraphConnected(&bb_set, graph_));
343 EXPECT_EQ(bb_set.NumSetBits(), 1u);
344 EXPECT_TRUE(bb_set.IsBitSet(unreachable_block->GetBlockId()));
345}
346
347// Tests SuperblockCloner for loop peeling case.
348//
349// Control Flow of the example (ignoring critical edges splitting).
350//
351// Before After
352//
353// |B| |B|
354// | |
355// v v
356// |1| |1|
357// | |
358// v v
359// |2|<-\ (6) |2A|
360// / \ / / \
361// v v/ / v
362// |4| |3| / |3A| (7)
363// | / /
364// v | v
365// |E| \ |2|<-\
366// \ / \ /
367// v v /
368// |4| |3|
369// |
370// v
371// |E|
372TEST_F(SuperblockClonerTest, LoopPeeling) {
373 HBasicBlock* header = nullptr;
374 HBasicBlock* loop_body = nullptr;
375
376 InitGraph();
377 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
378 CreateBasicLoopDataFlow(header, loop_body);
379 graph_->BuildDominatorTree();
380 EXPECT_TRUE(CheckGraph());
381
382 HBasicBlockMap bb_map(
383 std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
384 HInstructionMap hir_map(
385 std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
386
387 HLoopInformation* loop_info = header->GetLoopInformation();
388 PeelUnrollHelper helper(loop_info, &bb_map, &hir_map);
389 EXPECT_TRUE(helper.IsLoopClonable());
390 HBasicBlock* new_header = helper.DoPeeling();
391 HLoopInformation* new_loop_info = new_header->GetLoopInformation();
392
393 EXPECT_TRUE(CheckGraph());
394
395 // Check loop body successors.
396 EXPECT_EQ(loop_body->GetSingleSuccessor(), header);
397 EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header);
398
399 // Check loop structure.
400 EXPECT_EQ(header, new_header);
401 EXPECT_EQ(new_loop_info->GetHeader(), header);
402 EXPECT_EQ(new_loop_info->GetBackEdges().size(), 1u);
403 EXPECT_EQ(new_loop_info->GetBackEdges()[0], loop_body);
404}
405
406// Tests SuperblockCloner for loop unrolling case.
407//
408// Control Flow of the example (ignoring critical edges splitting).
409//
410// Before After
411//
412// |B| |B|
413// | |
414// v v
415// |1| |1|
416// | |
417// v v
418// |2|<-\ (6) |2A|<-\
419// / \ / / \ \
420// v v/ / v \
421// |4| |3| /(7)|3A| |
422// | / / /
423// v | v /
424// |E| \ |2| /
425// \ / \ /
426// v v/
427// |4| |3|
428// |
429// v
430// |E|
431TEST_F(SuperblockClonerTest, LoopUnrolling) {
432 HBasicBlock* header = nullptr;
433 HBasicBlock* loop_body = nullptr;
434
435 InitGraph();
436 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
437 CreateBasicLoopDataFlow(header, loop_body);
438 graph_->BuildDominatorTree();
439 EXPECT_TRUE(CheckGraph());
440
441 HBasicBlockMap bb_map(
442 std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
443 HInstructionMap hir_map(
444 std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
445
446 HLoopInformation* loop_info = header->GetLoopInformation();
447 PeelUnrollHelper helper(loop_info, &bb_map, &hir_map);
448 EXPECT_TRUE(helper.IsLoopClonable());
449 HBasicBlock* new_header = helper.DoUnrolling();
450
451 EXPECT_TRUE(CheckGraph());
452
453 // Check loop body successors.
454 EXPECT_EQ(loop_body->GetSingleSuccessor(), bb_map.Get(header));
455 EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header);
456
457 // Check loop structure.
458 EXPECT_EQ(header, new_header);
459 EXPECT_EQ(loop_info, new_header->GetLoopInformation());
460 EXPECT_EQ(loop_info->GetHeader(), new_header);
461 EXPECT_EQ(loop_info->GetBackEdges().size(), 1u);
462 EXPECT_EQ(loop_info->GetBackEdges()[0], bb_map.Get(loop_body));
463}
464
465// Checks that loop unrolling works fine for a loop with multiple back edges. Tests that after
466// the transformation the loop has a single preheader.
467TEST_F(SuperblockClonerTest, LoopPeelingMultipleBackEdges) {
468 HBasicBlock* header = nullptr;
469 HBasicBlock* loop_body = nullptr;
470
471 InitGraph();
472 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
473 CreateBasicLoopDataFlow(header, loop_body);
474
475 // Transform a basic loop to have multiple back edges.
476 HBasicBlock* latch = header->GetSuccessors()[1];
477 HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_);
478 HBasicBlock* temp1 = new (GetAllocator()) HBasicBlock(graph_);
479 graph_->AddBlock(if_block);
480 graph_->AddBlock(temp1);
481 header->ReplaceSuccessor(latch, if_block);
482 if_block->AddSuccessor(latch);
483 if_block->AddSuccessor(temp1);
484 temp1->AddSuccessor(header);
485
486 if_block->AddInstruction(new (GetAllocator()) HIf(parameter_));
487
488 HInstructionIterator it(header->GetPhis());
489 DCHECK(!it.Done());
490 HPhi* loop_phi = it.Current()->AsPhi();
491 HInstruction* temp_add = new (GetAllocator()) HAdd(DataType::Type::kInt32,
492 loop_phi,
493 graph_->GetIntConstant(2));
494 temp1->AddInstruction(temp_add);
495 temp1->AddInstruction(new (GetAllocator()) HGoto());
496 loop_phi->AddInput(temp_add);
497
498 graph_->BuildDominatorTree();
499 EXPECT_TRUE(CheckGraph());
500
501 HLoopInformation* loop_info = header->GetLoopInformation();
502 PeelUnrollSimpleHelper helper(loop_info);
503 HBasicBlock* new_header = helper.DoPeeling();
504 EXPECT_EQ(header, new_header);
505
506 EXPECT_TRUE(CheckGraph());
507 EXPECT_EQ(header->GetPredecessors().size(), 3u);
508}
509
510static void CheckLoopStructureForLoopPeelingNested(HBasicBlock* loop1_header,
511 HBasicBlock* loop2_header,
512 HBasicBlock* loop3_header) {
513 EXPECT_EQ(loop1_header->GetLoopInformation()->GetHeader(), loop1_header);
514 EXPECT_EQ(loop2_header->GetLoopInformation()->GetHeader(), loop2_header);
515 EXPECT_EQ(loop3_header->GetLoopInformation()->GetHeader(), loop3_header);
516 EXPECT_EQ(loop1_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr);
517 EXPECT_EQ(loop2_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr);
518 EXPECT_EQ(loop3_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation()->GetHeader(),
519 loop2_header);
520}
521
522TEST_F(SuperblockClonerTest, LoopPeelingNested) {
523 HBasicBlock* header = nullptr;
524 HBasicBlock* loop_body = nullptr;
525
526 InitGraph();
527
528 // Create the following nested structure of loops
529 // Headers: 1 2 3
530 // [ ], [ [ ] ]
531 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
532 CreateBasicLoopDataFlow(header, loop_body);
533 HBasicBlock* loop1_header = header;
534
535 CreateBasicLoopControlFlow(header, return_block_, &header, &loop_body);
536 CreateBasicLoopDataFlow(header, loop_body);
537 HBasicBlock* loop2_header = header;
538
539 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
540 CreateBasicLoopDataFlow(header, loop_body);
541 HBasicBlock* loop3_header = header;
542
543 graph_->BuildDominatorTree();
544 EXPECT_TRUE(CheckGraph());
545
546 HLoopInformation* loop2_info_before = loop2_header->GetLoopInformation();
547 HLoopInformation* loop3_info_before = loop3_header->GetLoopInformation();
548
549 // Check nested loops structure.
550 CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
551 PeelUnrollSimpleHelper helper(loop1_header->GetLoopInformation());
552 helper.DoPeeling();
553 // Check that nested loops structure has not changed after the transformation.
554 CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
555
556 // Test that the loop info is preserved.
557 EXPECT_EQ(loop2_info_before, loop2_header->GetLoopInformation());
558 EXPECT_EQ(loop3_info_before, loop3_header->GetLoopInformation());
559
560 EXPECT_EQ(loop3_info_before->GetPreHeader()->GetLoopInformation(), loop2_info_before);
561 EXPECT_EQ(loop2_info_before->GetPreHeader()->GetLoopInformation(), nullptr);
562
563 EXPECT_EQ(helper.GetRegionToBeAdjusted(), nullptr);
564
565 EXPECT_TRUE(CheckGraph());
566}
567
568// Checks that the loop population is correctly propagated after an inner loop is peeled.
569TEST_F(SuperblockClonerTest, OuterLoopPopulationAfterInnerPeeled) {
570 HBasicBlock* header = nullptr;
571 HBasicBlock* loop_body = nullptr;
572
573 InitGraph();
574
575 // Create the following nested structure of loops
576 // Headers: 1 2 3 4
577 // [ [ [ ] ] ], [ ]
578 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
579 CreateBasicLoopDataFlow(header, loop_body);
580 HBasicBlock* loop1_header = header;
581
582 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
583 CreateBasicLoopDataFlow(header, loop_body);
584 HBasicBlock* loop2_header = header;
585
586 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
587 CreateBasicLoopDataFlow(header, loop_body);
588 HBasicBlock* loop3_header = header;
589
590 CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body);
591 CreateBasicLoopDataFlow(header, loop_body);
592 HBasicBlock* loop4_header = header;
593
594 graph_->BuildDominatorTree();
595 EXPECT_TRUE(CheckGraph());
596
597 PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation());
598 helper.DoPeeling();
599 HLoopInformation* loop1 = loop1_header->GetLoopInformation();
600 HLoopInformation* loop2 = loop2_header->GetLoopInformation();
601 HLoopInformation* loop3 = loop3_header->GetLoopInformation();
602 HLoopInformation* loop4 = loop4_header->GetLoopInformation();
603
604 EXPECT_TRUE(loop1->Contains(*loop2_header));
605 EXPECT_TRUE(loop1->Contains(*loop3_header));
606 EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader()));
607
608 // Check that loop4 info has not been touched after local run of AnalyzeLoops.
609 EXPECT_EQ(loop4, loop4_header->GetLoopInformation());
610
611 EXPECT_TRUE(loop1->IsIn(*loop1));
612 EXPECT_TRUE(loop2->IsIn(*loop1));
613 EXPECT_TRUE(loop3->IsIn(*loop1));
614 EXPECT_TRUE(loop3->IsIn(*loop2));
615 EXPECT_TRUE(!loop4->IsIn(*loop1));
616
617 EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), nullptr);
618
619 EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop2);
620
621 EXPECT_TRUE(CheckGraph());
622}
623
624// Checks the case when inner loop have an exit not to its immediate outer_loop but some other loop
625// in the hierarchy. Loop population information must be valid after loop peeling.
626TEST_F(SuperblockClonerTest, NestedCaseExitToOutermost) {
627 HBasicBlock* header = nullptr;
628 HBasicBlock* loop_body = nullptr;
629
630 InitGraph();
631
632 // Create the following nested structure of loops then peel loop3.
633 // Headers: 1 2 3
634 // [ [ [ ] ] ]
635 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
636 CreateBasicLoopDataFlow(header, loop_body);
637 HBasicBlock* loop1_header = header;
638 HBasicBlock* loop_body1 = loop_body;
639
640 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
641 CreateBasicLoopDataFlow(header, loop_body);
642
643 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
644 CreateBasicLoopDataFlow(header, loop_body);
645 HBasicBlock* loop3_header = header;
646 HBasicBlock* loop_body3 = loop_body;
647
648 // Change the loop3 - insert an exit which leads to loop1.
649 HBasicBlock* loop3_extra_if_block = new (GetAllocator()) HBasicBlock(graph_);
650 graph_->AddBlock(loop3_extra_if_block);
651 loop3_extra_if_block->AddInstruction(new (GetAllocator()) HIf(parameter_));
652
653 loop3_header->ReplaceSuccessor(loop_body3, loop3_extra_if_block);
654 loop3_extra_if_block->AddSuccessor(loop_body1); // Long exit.
655 loop3_extra_if_block->AddSuccessor(loop_body3);
656
657 graph_->BuildDominatorTree();
658 EXPECT_TRUE(CheckGraph());
659
660 HBasicBlock* loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0];
661 EXPECT_TRUE(loop1_header->GetLoopInformation()->Contains(*loop3_long_exit));
662
663 PeelUnrollSimpleHelper helper(loop3_header->GetLoopInformation());
664 helper.DoPeeling();
665
666 HLoopInformation* loop1 = loop1_header->GetLoopInformation();
667 // Check that after the transformation the local area for CF adjustments has been chosen
668 // correctly and loop population has been updated.
669 loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0];
670 EXPECT_TRUE(loop1->Contains(*loop3_long_exit));
671
672 EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop1);
673
674 EXPECT_TRUE(loop1->Contains(*loop3_header));
675 EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader()));
676
677 EXPECT_TRUE(CheckGraph());
678}
679
680TEST_F(SuperblockClonerTest, FastCaseCheck) {
681 HBasicBlock* header = nullptr;
682 HBasicBlock* loop_body = nullptr;
683 ArenaAllocator* arena = graph_->GetAllocator();
684
685 InitGraph();
686 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
687 CreateBasicLoopDataFlow(header, loop_body);
688 graph_->BuildDominatorTree();
689
690 HLoopInformation* loop_info = header->GetLoopInformation();
691
692 ArenaBitVector orig_bb_set(
693 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
694 orig_bb_set.Union(&loop_info->GetBlocks());
695
696 HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
697 HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
698 HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
699
700 CollectRemappingInfoForPeelUnroll(true,
701 loop_info,
702 &remap_orig_internal,
703 &remap_copy_internal,
704 &remap_incoming);
705
706 // Insert some extra nodes and edges.
707 HBasicBlock* preheader = loop_info->GetPreHeader();
708 orig_bb_set.SetBit(preheader->GetBlockId());
709
710 // Adjust incoming edges.
711 remap_incoming.Clear();
712 remap_incoming.Insert(HEdge(preheader->GetSinglePredecessor(), preheader));
713
714 HBasicBlockMap bb_map(std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner));
715 HInstructionMap hir_map(std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner));
716
717 SuperblockCloner cloner(graph_,
718 &orig_bb_set,
719 &bb_map,
720 &hir_map);
721 cloner.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming);
722
723 EXPECT_FALSE(cloner.IsFastCase());
724}
725
726// Helper for FindCommonLoop which also check that FindCommonLoop is symmetric.
727static HLoopInformation* FindCommonLoopCheck(HLoopInformation* loop1, HLoopInformation* loop2) {
728 HLoopInformation* common_loop12 = FindCommonLoop(loop1, loop2);
729 HLoopInformation* common_loop21 = FindCommonLoop(loop2, loop1);
730 EXPECT_EQ(common_loop21, common_loop12);
731 return common_loop12;
732}
733
734// Tests FindCommonLoop function on a loop nest.
735TEST_F(SuperblockClonerTest, FindCommonLoop) {
736 HBasicBlock* header = nullptr;
737 HBasicBlock* loop_body = nullptr;
738
739 InitGraph();
740
741 // Create the following nested structure of loops
742 // Headers: 1 2 3 4 5
743 // [ [ [ ] ], [ ] ], [ ]
744 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
745 CreateBasicLoopDataFlow(header, loop_body);
746 HBasicBlock* loop1_header = header;
747
748 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
749 CreateBasicLoopDataFlow(header, loop_body);
750 HBasicBlock* loop2_header = header;
751
752 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
753 CreateBasicLoopDataFlow(header, loop_body);
754 HBasicBlock* loop3_header = header;
755
756 CreateBasicLoopControlFlow(loop2_header, loop2_header->GetSuccessors()[0], &header, &loop_body);
757 CreateBasicLoopDataFlow(header, loop_body);
758 HBasicBlock* loop4_header = header;
759
760 CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body);
761 CreateBasicLoopDataFlow(header, loop_body);
762 HBasicBlock* loop5_header = header;
763
764 graph_->BuildDominatorTree();
765 EXPECT_TRUE(CheckGraph());
766
767 HLoopInformation* loop1 = loop1_header->GetLoopInformation();
768 HLoopInformation* loop2 = loop2_header->GetLoopInformation();
769 HLoopInformation* loop3 = loop3_header->GetLoopInformation();
770 HLoopInformation* loop4 = loop4_header->GetLoopInformation();
771 HLoopInformation* loop5 = loop5_header->GetLoopInformation();
772
773 EXPECT_TRUE(loop1->IsIn(*loop1));
774 EXPECT_TRUE(loop2->IsIn(*loop1));
775 EXPECT_TRUE(loop3->IsIn(*loop1));
776 EXPECT_TRUE(loop3->IsIn(*loop2));
777 EXPECT_TRUE(loop4->IsIn(*loop1));
778
779 EXPECT_FALSE(loop5->IsIn(*loop1));
780 EXPECT_FALSE(loop4->IsIn(*loop2));
781 EXPECT_FALSE(loop4->IsIn(*loop3));
782
783 EXPECT_EQ(loop1->GetPreHeader()->GetLoopInformation(), nullptr);
784 EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), loop1);
785
786 EXPECT_EQ(FindCommonLoopCheck(nullptr, nullptr), nullptr);
787 EXPECT_EQ(FindCommonLoopCheck(loop2, nullptr), nullptr);
788
789 EXPECT_EQ(FindCommonLoopCheck(loop1, loop1), loop1);
790 EXPECT_EQ(FindCommonLoopCheck(loop1, loop2), loop1);
791 EXPECT_EQ(FindCommonLoopCheck(loop1, loop3), loop1);
792 EXPECT_EQ(FindCommonLoopCheck(loop1, loop4), loop1);
793 EXPECT_EQ(FindCommonLoopCheck(loop1, loop5), nullptr);
794
795 EXPECT_EQ(FindCommonLoopCheck(loop2, loop3), loop2);
796 EXPECT_EQ(FindCommonLoopCheck(loop2, loop4), loop1);
797 EXPECT_EQ(FindCommonLoopCheck(loop2, loop5), nullptr);
798
799 EXPECT_EQ(FindCommonLoopCheck(loop3, loop4), loop1);
800 EXPECT_EQ(FindCommonLoopCheck(loop3, loop5), nullptr);
801
802 EXPECT_EQ(FindCommonLoopCheck(loop4, loop5), nullptr);
803
804 EXPECT_EQ(FindCommonLoopCheck(loop5, loop5), loop5);
805}
806
Artem Serovcced8ba2017-07-19 18:18:09 +0100807} // namespace art