blob: a46334bfca6b26f5c1073107e0305267abbce95b [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
Vladimir Marko0a516052019-10-14 13:00:44 +000024namespace art {
Artem Serovcced8ba2017-07-19 18:18:09 +010025
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 Serov15f95b12018-06-29 15:30:36 +010033class SuperblockClonerTest : public ImprovedOptimizingUnitTest {
Evgeny Astigeevich52506e22019-12-04 15:59:37 +000034 private:
35 void CreateParameters() override {
36 parameters_.push_back(new (GetAllocator()) HParameterValue(graph_->GetDexFile(),
37 dex::TypeIndex(0),
38 0,
39 DataType::Type::kInt32));
40 }
41
Artem Serovcced8ba2017-07-19 18:18:09 +010042 public:
Artem Serov02eebcf2017-12-13 19:48:31 +000043 void CreateBasicLoopControlFlow(HBasicBlock* position,
44 HBasicBlock* successor,
45 /* out */ HBasicBlock** header_p,
46 /* out */ HBasicBlock** body_p) {
47 HBasicBlock* loop_preheader = new (GetAllocator()) HBasicBlock(graph_);
48 HBasicBlock* loop_header = new (GetAllocator()) HBasicBlock(graph_);
49 HBasicBlock* loop_body = new (GetAllocator()) HBasicBlock(graph_);
50
51 graph_->AddBlock(loop_preheader);
52 graph_->AddBlock(loop_header);
53 graph_->AddBlock(loop_body);
54
55 position->ReplaceSuccessor(successor, loop_preheader);
56
57 loop_preheader->AddSuccessor(loop_header);
58 // Loop exit first to have a proper exit condition/target for HIf.
59 loop_header->AddSuccessor(successor);
60 loop_header->AddSuccessor(loop_body);
61 loop_body->AddSuccessor(loop_header);
62
63 *header_p = loop_header;
64 *body_p = loop_body;
65 }
66
Artem Serovcced8ba2017-07-19 18:18:09 +010067 void CreateBasicLoopDataFlow(HBasicBlock* loop_header, HBasicBlock* loop_body) {
68 uint32_t dex_pc = 0;
69
70 // Entry block.
71 HIntConstant* const_0 = graph_->GetIntConstant(0);
72 HIntConstant* const_1 = graph_->GetIntConstant(1);
73 HIntConstant* const_128 = graph_->GetIntConstant(128);
74
75 // Header block.
76 HPhi* phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32);
77 HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck();
Artem Serov02eebcf2017-12-13 19:48:31 +000078 HInstruction* loop_check = new (GetAllocator()) HGreaterThanOrEqual(phi, const_128);
Artem Serovcced8ba2017-07-19 18:18:09 +010079
80 loop_header->AddPhi(phi);
81 loop_header->AddInstruction(suspend_check);
Artem Serov02eebcf2017-12-13 19:48:31 +000082 loop_header->AddInstruction(loop_check);
83 loop_header->AddInstruction(new (GetAllocator()) HIf(loop_check));
Artem Serovcced8ba2017-07-19 18:18:09 +010084
85 // Loop body block.
Evgeny Astigeevich52506e22019-12-04 15:59:37 +000086 HInstruction* null_check = new (GetAllocator()) HNullCheck(parameters_[0], dex_pc);
Artem Serovcced8ba2017-07-19 18:18:09 +010087 HInstruction* array_length = new (GetAllocator()) HArrayLength(null_check, dex_pc);
88 HInstruction* bounds_check = new (GetAllocator()) HBoundsCheck(phi, array_length, dex_pc);
89 HInstruction* array_get =
90 new (GetAllocator()) HArrayGet(null_check, bounds_check, DataType::Type::kInt32, dex_pc);
91 HInstruction* add = new (GetAllocator()) HAdd(DataType::Type::kInt32, array_get, const_1);
Artem Serov02eebcf2017-12-13 19:48:31 +000092 HInstruction* array_set = new (GetAllocator()) HArraySet(
93 null_check, bounds_check, add, DataType::Type::kInt32, dex_pc);
Artem Serovcced8ba2017-07-19 18:18:09 +010094 HInstruction* induction_inc = new (GetAllocator()) HAdd(DataType::Type::kInt32, phi, const_1);
95
96 loop_body->AddInstruction(null_check);
97 loop_body->AddInstruction(array_length);
98 loop_body->AddInstruction(bounds_check);
99 loop_body->AddInstruction(array_get);
100 loop_body->AddInstruction(add);
101 loop_body->AddInstruction(array_set);
102 loop_body->AddInstruction(induction_inc);
103 loop_body->AddInstruction(new (GetAllocator()) HGoto());
104
105 phi->AddInput(const_0);
106 phi->AddInput(induction_inc);
107
108 graph_->SetHasBoundsChecks(true);
109
110 // Adjust HEnvironment for each instruction which require that.
Evgeny Astigeevich52506e22019-12-04 15:59:37 +0000111 ArenaVector<HInstruction*> current_locals({phi, const_128, parameters_[0]},
Artem Serovcced8ba2017-07-19 18:18:09 +0100112 GetAllocator()->Adapter(kArenaAllocInstruction));
113
114 HEnvironment* env = ManuallyBuildEnvFor(suspend_check, &current_locals);
115 null_check->CopyEnvironmentFrom(env);
116 bounds_check->CopyEnvironmentFrom(env);
117 }
Artem Serovcced8ba2017-07-19 18:18:09 +0100118};
119
Artem Serov5e399b82017-12-21 14:28:35 +0000120TEST_F(SuperblockClonerTest, IndividualInstrCloner) {
Artem Serovcced8ba2017-07-19 18:18:09 +0100121 HBasicBlock* header = nullptr;
122 HBasicBlock* loop_body = nullptr;
123
Artem Serov02eebcf2017-12-13 19:48:31 +0000124 InitGraph();
125 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
Artem Serovcced8ba2017-07-19 18:18:09 +0100126 CreateBasicLoopDataFlow(header, loop_body);
127 graph_->BuildDominatorTree();
Artem Serov02eebcf2017-12-13 19:48:31 +0000128 EXPECT_TRUE(CheckGraph());
Artem Serovcced8ba2017-07-19 18:18:09 +0100129
130 HSuspendCheck* old_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
131 CloneAndReplaceInstructionVisitor visitor(graph_);
132 // Do instruction cloning and replacement twice with different visiting order.
133
134 visitor.VisitInsertionOrder();
135 size_t instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
136 EXPECT_EQ(instr_replaced_by_clones_count, 12u);
137 EXPECT_TRUE(CheckGraph());
138
139 visitor.VisitReversePostOrder();
140 instr_replaced_by_clones_count = visitor.GetInstrReplacedByClonesCount();
141 EXPECT_EQ(instr_replaced_by_clones_count, 24u);
142 EXPECT_TRUE(CheckGraph());
143
144 HSuspendCheck* new_suspend_check = header->GetLoopInformation()->GetSuspendCheck();
145 EXPECT_NE(new_suspend_check, old_suspend_check);
146 EXPECT_NE(new_suspend_check, nullptr);
147}
148
Artem Serov7f4aff62017-06-21 17:02:18 +0100149// Tests SuperblockCloner::CloneBasicBlocks - check instruction cloning and initial remapping of
150// instructions' inputs.
151TEST_F(SuperblockClonerTest, CloneBasicBlocks) {
152 HBasicBlock* header = nullptr;
153 HBasicBlock* loop_body = nullptr;
154 ArenaAllocator* arena = graph_->GetAllocator();
155
Artem Serov02eebcf2017-12-13 19:48:31 +0000156 InitGraph();
157 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
Artem Serov7f4aff62017-06-21 17:02:18 +0100158 CreateBasicLoopDataFlow(header, loop_body);
159 graph_->BuildDominatorTree();
160 ASSERT_TRUE(CheckGraph());
161
162 ArenaBitVector orig_bb_set(
163 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
164 HBasicBlockMap bb_map(std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner));
165 HInstructionMap hir_map(std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner));
166
167 HLoopInformation* loop_info = header->GetLoopInformation();
168 orig_bb_set.Union(&loop_info->GetBlocks());
169
170 SuperblockCloner cloner(graph_,
171 &orig_bb_set,
172 &bb_map,
Nicolas Geoffray256c94b2019-04-29 10:55:09 +0100173 &hir_map,
174 /* induction_range= */ nullptr);
Artem Serov7f4aff62017-06-21 17:02:18 +0100175 EXPECT_TRUE(cloner.IsSubgraphClonable());
176
177 cloner.CloneBasicBlocks();
178
179 EXPECT_EQ(bb_map.size(), 2u);
180 EXPECT_EQ(hir_map.size(), 12u);
181
182 for (auto it : hir_map) {
183 HInstruction* orig_instr = it.first;
184 HInstruction* copy_instr = it.second;
185
186 EXPECT_EQ(cloner.GetBlockCopy(orig_instr->GetBlock()), copy_instr->GetBlock());
187 EXPECT_EQ(orig_instr->GetKind(), copy_instr->GetKind());
188 EXPECT_EQ(orig_instr->GetType(), copy_instr->GetType());
189
190 if (orig_instr->IsPhi()) {
191 continue;
192 }
193
194 EXPECT_EQ(orig_instr->InputCount(), copy_instr->InputCount());
195
196 // Check that inputs match.
197 for (size_t i = 0, e = orig_instr->InputCount(); i < e; i++) {
198 HInstruction* orig_input = orig_instr->InputAt(i);
199 HInstruction* copy_input = copy_instr->InputAt(i);
200 if (cloner.IsInOrigBBSet(orig_input->GetBlock())) {
201 EXPECT_EQ(cloner.GetInstrCopy(orig_input), copy_input);
202 } else {
203 EXPECT_EQ(orig_input, copy_input);
204 }
205 }
206
207 EXPECT_EQ(orig_instr->HasEnvironment(), copy_instr->HasEnvironment());
208
209 // Check that environments match.
210 if (orig_instr->HasEnvironment()) {
211 HEnvironment* orig_env = orig_instr->GetEnvironment();
212 HEnvironment* copy_env = copy_instr->GetEnvironment();
213
214 EXPECT_EQ(copy_env->GetParent(), nullptr);
215 EXPECT_EQ(orig_env->Size(), copy_env->Size());
216
217 for (size_t i = 0, e = orig_env->Size(); i < e; i++) {
218 HInstruction* orig_input = orig_env->GetInstructionAt(i);
219 HInstruction* copy_input = copy_env->GetInstructionAt(i);
220 if (cloner.IsInOrigBBSet(orig_input->GetBlock())) {
221 EXPECT_EQ(cloner.GetInstrCopy(orig_input), copy_input);
222 } else {
223 EXPECT_EQ(orig_input, copy_input);
224 }
225 }
226 }
227 }
228}
229
230// SuperblockCloner::CleanUpControlFlow - checks algorithms of local adjustments of the control
231// flow.
232TEST_F(SuperblockClonerTest, AdjustControlFlowInfo) {
233 HBasicBlock* header = nullptr;
234 HBasicBlock* loop_body = nullptr;
235 ArenaAllocator* arena = graph_->GetAllocator();
236
Artem Serov02eebcf2017-12-13 19:48:31 +0000237 InitGraph();
238 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
Artem Serov7f4aff62017-06-21 17:02:18 +0100239 CreateBasicLoopDataFlow(header, loop_body);
240 graph_->BuildDominatorTree();
241 ASSERT_TRUE(CheckGraph());
242
243 ArenaBitVector orig_bb_set(
244 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
245
246 HLoopInformation* loop_info = header->GetLoopInformation();
247 orig_bb_set.Union(&loop_info->GetBlocks());
248
249 SuperblockCloner cloner(graph_,
250 &orig_bb_set,
Nicolas Geoffray256c94b2019-04-29 10:55:09 +0100251 /* bb_map= */ nullptr,
252 /* hir_map= */ nullptr,
253 /* induction_range= */ nullptr);
Artem Serov7f4aff62017-06-21 17:02:18 +0100254 EXPECT_TRUE(cloner.IsSubgraphClonable());
255
256 cloner.FindAndSetLocalAreaForAdjustments();
257 cloner.CleanUpControlFlow();
258
259 EXPECT_TRUE(CheckGraph());
260
261 EXPECT_TRUE(entry_block_->Dominates(header));
262 EXPECT_TRUE(entry_block_->Dominates(exit_block_));
263
264 EXPECT_EQ(header->GetLoopInformation(), loop_info);
265 EXPECT_EQ(loop_info->GetHeader(), header);
266 EXPECT_TRUE(loop_info->Contains(*loop_body));
267 EXPECT_TRUE(loop_info->IsBackEdge(*loop_body));
268}
269
Artem Serov02eebcf2017-12-13 19:48:31 +0000270// Tests IsSubgraphConnected function for negative case.
271TEST_F(SuperblockClonerTest, IsGraphConnected) {
272 HBasicBlock* header = nullptr;
273 HBasicBlock* loop_body = nullptr;
274 ArenaAllocator* arena = graph_->GetAllocator();
275
276 InitGraph();
277 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
278 CreateBasicLoopDataFlow(header, loop_body);
279 HBasicBlock* unreachable_block = new (GetAllocator()) HBasicBlock(graph_);
280 graph_->AddBlock(unreachable_block);
281
282 HBasicBlockSet bb_set(
283 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
284 bb_set.SetBit(header->GetBlockId());
285 bb_set.SetBit(loop_body->GetBlockId());
286 bb_set.SetBit(unreachable_block->GetBlockId());
287
288 EXPECT_FALSE(IsSubgraphConnected(&bb_set, graph_));
289 EXPECT_EQ(bb_set.NumSetBits(), 1u);
290 EXPECT_TRUE(bb_set.IsBitSet(unreachable_block->GetBlockId()));
291}
292
293// Tests SuperblockCloner for loop peeling case.
294//
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100295// See an ASCII graphics example near LoopClonerHelper::DoPeeling.
Artem Serov02eebcf2017-12-13 19:48:31 +0000296TEST_F(SuperblockClonerTest, LoopPeeling) {
297 HBasicBlock* header = nullptr;
298 HBasicBlock* loop_body = nullptr;
299
300 InitGraph();
301 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
302 CreateBasicLoopDataFlow(header, loop_body);
303 graph_->BuildDominatorTree();
304 EXPECT_TRUE(CheckGraph());
305
306 HBasicBlockMap bb_map(
307 std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
308 HInstructionMap hir_map(
309 std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
310
311 HLoopInformation* loop_info = header->GetLoopInformation();
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100312 LoopClonerHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
Artem Serov02eebcf2017-12-13 19:48:31 +0000313 EXPECT_TRUE(helper.IsLoopClonable());
314 HBasicBlock* new_header = helper.DoPeeling();
315 HLoopInformation* new_loop_info = new_header->GetLoopInformation();
316
317 EXPECT_TRUE(CheckGraph());
318
319 // Check loop body successors.
320 EXPECT_EQ(loop_body->GetSingleSuccessor(), header);
321 EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header);
322
323 // Check loop structure.
324 EXPECT_EQ(header, new_header);
325 EXPECT_EQ(new_loop_info->GetHeader(), header);
326 EXPECT_EQ(new_loop_info->GetBackEdges().size(), 1u);
327 EXPECT_EQ(new_loop_info->GetBackEdges()[0], loop_body);
328}
329
330// Tests SuperblockCloner for loop unrolling case.
331//
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100332// See an ASCII graphics example near LoopClonerHelper::DoUnrolling.
Artem Serov02eebcf2017-12-13 19:48:31 +0000333TEST_F(SuperblockClonerTest, LoopUnrolling) {
334 HBasicBlock* header = nullptr;
335 HBasicBlock* loop_body = nullptr;
336
337 InitGraph();
338 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
339 CreateBasicLoopDataFlow(header, loop_body);
340 graph_->BuildDominatorTree();
341 EXPECT_TRUE(CheckGraph());
342
343 HBasicBlockMap bb_map(
344 std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
345 HInstructionMap hir_map(
346 std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
347
348 HLoopInformation* loop_info = header->GetLoopInformation();
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100349 LoopClonerHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
Artem Serov02eebcf2017-12-13 19:48:31 +0000350 EXPECT_TRUE(helper.IsLoopClonable());
351 HBasicBlock* new_header = helper.DoUnrolling();
352
353 EXPECT_TRUE(CheckGraph());
354
355 // Check loop body successors.
356 EXPECT_EQ(loop_body->GetSingleSuccessor(), bb_map.Get(header));
357 EXPECT_EQ(bb_map.Get(loop_body)->GetSingleSuccessor(), header);
358
359 // Check loop structure.
360 EXPECT_EQ(header, new_header);
361 EXPECT_EQ(loop_info, new_header->GetLoopInformation());
362 EXPECT_EQ(loop_info->GetHeader(), new_header);
363 EXPECT_EQ(loop_info->GetBackEdges().size(), 1u);
364 EXPECT_EQ(loop_info->GetBackEdges()[0], bb_map.Get(loop_body));
365}
366
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100367// Tests SuperblockCloner for loop versioning case.
368//
369// See an ASCII graphics example near LoopClonerHelper::DoVersioning.
370TEST_F(SuperblockClonerTest, LoopVersioning) {
371 HBasicBlock* header = nullptr;
372 HBasicBlock* loop_body = nullptr;
373
374 InitGraph();
375 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
376 CreateBasicLoopDataFlow(header, loop_body);
377 graph_->BuildDominatorTree();
378 EXPECT_TRUE(CheckGraph());
379
380 HBasicBlockMap bb_map(
381 std::less<HBasicBlock*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
382 HInstructionMap hir_map(
383 std::less<HInstruction*>(), graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
384
385 HLoopInformation* loop_info = header->GetLoopInformation();
386 HBasicBlock* original_preheader = loop_info->GetPreHeader();
387 LoopClonerHelper helper(loop_info, &bb_map, &hir_map, /* induction_range= */ nullptr);
388 EXPECT_TRUE(helper.IsLoopClonable());
389 HBasicBlock* new_header = helper.DoVersioning();
390 EXPECT_EQ(header, new_header);
391
392 EXPECT_TRUE(CheckGraph());
393
394 HBasicBlock* second_header = bb_map.Get(header);
395 HBasicBlock* second_body = bb_map.Get(loop_body);
396 HLoopInformation* second_loop_info = second_header->GetLoopInformation();
397
398 // Check loop body successors.
399 EXPECT_EQ(loop_body->GetSingleSuccessor(), header);
400 EXPECT_EQ(second_body->GetSingleSuccessor(), second_header);
401
402 // Check loop structure.
403 EXPECT_EQ(loop_info, header->GetLoopInformation());
404 EXPECT_EQ(loop_info->GetHeader(), header);
405 EXPECT_EQ(second_loop_info->GetHeader(), second_header);
406
407 EXPECT_EQ(loop_info->GetBackEdges().size(), 1u);
408 EXPECT_EQ(second_loop_info->GetBackEdges().size(), 1u);
409
410 EXPECT_EQ(loop_info->GetBackEdges()[0], loop_body);
411 EXPECT_EQ(second_loop_info->GetBackEdges()[0], second_body);
412
413 EXPECT_EQ(original_preheader->GetSuccessors().size(), 2u);
414}
415
Artem Serov02eebcf2017-12-13 19:48:31 +0000416// Checks that loop unrolling works fine for a loop with multiple back edges. Tests that after
417// the transformation the loop has a single preheader.
418TEST_F(SuperblockClonerTest, LoopPeelingMultipleBackEdges) {
419 HBasicBlock* header = nullptr;
420 HBasicBlock* loop_body = nullptr;
421
422 InitGraph();
423 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
424 CreateBasicLoopDataFlow(header, loop_body);
425
426 // Transform a basic loop to have multiple back edges.
427 HBasicBlock* latch = header->GetSuccessors()[1];
428 HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph_);
429 HBasicBlock* temp1 = new (GetAllocator()) HBasicBlock(graph_);
430 graph_->AddBlock(if_block);
431 graph_->AddBlock(temp1);
432 header->ReplaceSuccessor(latch, if_block);
433 if_block->AddSuccessor(latch);
434 if_block->AddSuccessor(temp1);
435 temp1->AddSuccessor(header);
436
Evgeny Astigeevich52506e22019-12-04 15:59:37 +0000437 if_block->AddInstruction(new (GetAllocator()) HIf(parameters_[0]));
Artem Serov02eebcf2017-12-13 19:48:31 +0000438
439 HInstructionIterator it(header->GetPhis());
440 DCHECK(!it.Done());
441 HPhi* loop_phi = it.Current()->AsPhi();
442 HInstruction* temp_add = new (GetAllocator()) HAdd(DataType::Type::kInt32,
443 loop_phi,
444 graph_->GetIntConstant(2));
445 temp1->AddInstruction(temp_add);
446 temp1->AddInstruction(new (GetAllocator()) HGoto());
447 loop_phi->AddInput(temp_add);
448
449 graph_->BuildDominatorTree();
450 EXPECT_TRUE(CheckGraph());
451
452 HLoopInformation* loop_info = header->GetLoopInformation();
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100453 LoopClonerSimpleHelper helper(loop_info, /* induction_range= */ nullptr);
Artem Serov02eebcf2017-12-13 19:48:31 +0000454 HBasicBlock* new_header = helper.DoPeeling();
455 EXPECT_EQ(header, new_header);
456
457 EXPECT_TRUE(CheckGraph());
458 EXPECT_EQ(header->GetPredecessors().size(), 3u);
459}
460
461static void CheckLoopStructureForLoopPeelingNested(HBasicBlock* loop1_header,
462 HBasicBlock* loop2_header,
463 HBasicBlock* loop3_header) {
464 EXPECT_EQ(loop1_header->GetLoopInformation()->GetHeader(), loop1_header);
465 EXPECT_EQ(loop2_header->GetLoopInformation()->GetHeader(), loop2_header);
466 EXPECT_EQ(loop3_header->GetLoopInformation()->GetHeader(), loop3_header);
467 EXPECT_EQ(loop1_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr);
468 EXPECT_EQ(loop2_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation(), nullptr);
469 EXPECT_EQ(loop3_header->GetLoopInformation()->GetPreHeader()->GetLoopInformation()->GetHeader(),
470 loop2_header);
471}
472
473TEST_F(SuperblockClonerTest, LoopPeelingNested) {
474 HBasicBlock* header = nullptr;
475 HBasicBlock* loop_body = nullptr;
476
477 InitGraph();
478
479 // Create the following nested structure of loops
480 // Headers: 1 2 3
481 // [ ], [ [ ] ]
482 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
483 CreateBasicLoopDataFlow(header, loop_body);
484 HBasicBlock* loop1_header = header;
485
486 CreateBasicLoopControlFlow(header, return_block_, &header, &loop_body);
487 CreateBasicLoopDataFlow(header, loop_body);
488 HBasicBlock* loop2_header = header;
489
490 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
491 CreateBasicLoopDataFlow(header, loop_body);
492 HBasicBlock* loop3_header = header;
493
494 graph_->BuildDominatorTree();
495 EXPECT_TRUE(CheckGraph());
496
497 HLoopInformation* loop2_info_before = loop2_header->GetLoopInformation();
498 HLoopInformation* loop3_info_before = loop3_header->GetLoopInformation();
499
500 // Check nested loops structure.
501 CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100502 LoopClonerSimpleHelper helper(loop1_header->GetLoopInformation(), /* induction_range= */ nullptr);
Artem Serov02eebcf2017-12-13 19:48:31 +0000503 helper.DoPeeling();
504 // Check that nested loops structure has not changed after the transformation.
505 CheckLoopStructureForLoopPeelingNested(loop1_header, loop2_header, loop3_header);
506
507 // Test that the loop info is preserved.
508 EXPECT_EQ(loop2_info_before, loop2_header->GetLoopInformation());
509 EXPECT_EQ(loop3_info_before, loop3_header->GetLoopInformation());
510
511 EXPECT_EQ(loop3_info_before->GetPreHeader()->GetLoopInformation(), loop2_info_before);
512 EXPECT_EQ(loop2_info_before->GetPreHeader()->GetLoopInformation(), nullptr);
513
514 EXPECT_EQ(helper.GetRegionToBeAdjusted(), nullptr);
515
516 EXPECT_TRUE(CheckGraph());
517}
518
519// Checks that the loop population is correctly propagated after an inner loop is peeled.
520TEST_F(SuperblockClonerTest, OuterLoopPopulationAfterInnerPeeled) {
521 HBasicBlock* header = nullptr;
522 HBasicBlock* loop_body = nullptr;
523
524 InitGraph();
525
526 // Create the following nested structure of loops
527 // Headers: 1 2 3 4
528 // [ [ [ ] ] ], [ ]
529 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
530 CreateBasicLoopDataFlow(header, loop_body);
531 HBasicBlock* loop1_header = header;
532
533 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
534 CreateBasicLoopDataFlow(header, loop_body);
535 HBasicBlock* loop2_header = header;
536
537 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
538 CreateBasicLoopDataFlow(header, loop_body);
539 HBasicBlock* loop3_header = header;
540
541 CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body);
542 CreateBasicLoopDataFlow(header, loop_body);
543 HBasicBlock* loop4_header = header;
544
545 graph_->BuildDominatorTree();
546 EXPECT_TRUE(CheckGraph());
547
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100548 LoopClonerSimpleHelper helper(loop3_header->GetLoopInformation(), /* induction_range= */ nullptr);
Artem Serov02eebcf2017-12-13 19:48:31 +0000549 helper.DoPeeling();
550 HLoopInformation* loop1 = loop1_header->GetLoopInformation();
551 HLoopInformation* loop2 = loop2_header->GetLoopInformation();
552 HLoopInformation* loop3 = loop3_header->GetLoopInformation();
553 HLoopInformation* loop4 = loop4_header->GetLoopInformation();
554
555 EXPECT_TRUE(loop1->Contains(*loop2_header));
556 EXPECT_TRUE(loop1->Contains(*loop3_header));
557 EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader()));
558
559 // Check that loop4 info has not been touched after local run of AnalyzeLoops.
560 EXPECT_EQ(loop4, loop4_header->GetLoopInformation());
561
562 EXPECT_TRUE(loop1->IsIn(*loop1));
563 EXPECT_TRUE(loop2->IsIn(*loop1));
564 EXPECT_TRUE(loop3->IsIn(*loop1));
565 EXPECT_TRUE(loop3->IsIn(*loop2));
566 EXPECT_TRUE(!loop4->IsIn(*loop1));
567
568 EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), nullptr);
569
570 EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop2);
571
572 EXPECT_TRUE(CheckGraph());
573}
574
575// Checks the case when inner loop have an exit not to its immediate outer_loop but some other loop
576// in the hierarchy. Loop population information must be valid after loop peeling.
577TEST_F(SuperblockClonerTest, NestedCaseExitToOutermost) {
578 HBasicBlock* header = nullptr;
579 HBasicBlock* loop_body = nullptr;
580
581 InitGraph();
582
583 // Create the following nested structure of loops then peel loop3.
584 // Headers: 1 2 3
585 // [ [ [ ] ] ]
586 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
587 CreateBasicLoopDataFlow(header, loop_body);
588 HBasicBlock* loop1_header = header;
589 HBasicBlock* loop_body1 = loop_body;
590
591 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
592 CreateBasicLoopDataFlow(header, loop_body);
593
594 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
595 CreateBasicLoopDataFlow(header, loop_body);
596 HBasicBlock* loop3_header = header;
597 HBasicBlock* loop_body3 = loop_body;
598
599 // Change the loop3 - insert an exit which leads to loop1.
600 HBasicBlock* loop3_extra_if_block = new (GetAllocator()) HBasicBlock(graph_);
601 graph_->AddBlock(loop3_extra_if_block);
Evgeny Astigeevich52506e22019-12-04 15:59:37 +0000602 loop3_extra_if_block->AddInstruction(new (GetAllocator()) HIf(parameters_[0]));
Artem Serov02eebcf2017-12-13 19:48:31 +0000603
604 loop3_header->ReplaceSuccessor(loop_body3, loop3_extra_if_block);
605 loop3_extra_if_block->AddSuccessor(loop_body1); // Long exit.
606 loop3_extra_if_block->AddSuccessor(loop_body3);
607
608 graph_->BuildDominatorTree();
609 EXPECT_TRUE(CheckGraph());
610
611 HBasicBlock* loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0];
612 EXPECT_TRUE(loop1_header->GetLoopInformation()->Contains(*loop3_long_exit));
613
Artem Serov0f5b2bf2019-10-23 14:07:41 +0100614 LoopClonerSimpleHelper helper(loop3_header->GetLoopInformation(), /* induction_range= */ nullptr);
Artem Serov02eebcf2017-12-13 19:48:31 +0000615 helper.DoPeeling();
616
617 HLoopInformation* loop1 = loop1_header->GetLoopInformation();
618 // Check that after the transformation the local area for CF adjustments has been chosen
619 // correctly and loop population has been updated.
620 loop3_long_exit = loop3_extra_if_block->GetSuccessors()[0];
621 EXPECT_TRUE(loop1->Contains(*loop3_long_exit));
622
623 EXPECT_EQ(helper.GetRegionToBeAdjusted(), loop1);
624
625 EXPECT_TRUE(loop1->Contains(*loop3_header));
626 EXPECT_TRUE(loop1->Contains(*loop3_header->GetLoopInformation()->GetPreHeader()));
627
628 EXPECT_TRUE(CheckGraph());
629}
630
631TEST_F(SuperblockClonerTest, FastCaseCheck) {
632 HBasicBlock* header = nullptr;
633 HBasicBlock* loop_body = nullptr;
634 ArenaAllocator* arena = graph_->GetAllocator();
635
636 InitGraph();
637 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
638 CreateBasicLoopDataFlow(header, loop_body);
639 graph_->BuildDominatorTree();
640
641 HLoopInformation* loop_info = header->GetLoopInformation();
642
643 ArenaBitVector orig_bb_set(
644 arena, graph_->GetBlocks().size(), false, kArenaAllocSuperblockCloner);
645 orig_bb_set.Union(&loop_info->GetBlocks());
646
647 HEdgeSet remap_orig_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
648 HEdgeSet remap_copy_internal(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
649 HEdgeSet remap_incoming(graph_->GetAllocator()->Adapter(kArenaAllocSuperblockCloner));
650
651 CollectRemappingInfoForPeelUnroll(true,
652 loop_info,
653 &remap_orig_internal,
654 &remap_copy_internal,
655 &remap_incoming);
656
657 // Insert some extra nodes and edges.
658 HBasicBlock* preheader = loop_info->GetPreHeader();
659 orig_bb_set.SetBit(preheader->GetBlockId());
660
661 // Adjust incoming edges.
Vladimir Marko54159c62018-06-20 14:30:08 +0100662 remap_incoming.clear();
663 remap_incoming.insert(HEdge(preheader->GetSinglePredecessor(), preheader));
Artem Serov02eebcf2017-12-13 19:48:31 +0000664
665 HBasicBlockMap bb_map(std::less<HBasicBlock*>(), arena->Adapter(kArenaAllocSuperblockCloner));
666 HInstructionMap hir_map(std::less<HInstruction*>(), arena->Adapter(kArenaAllocSuperblockCloner));
667
668 SuperblockCloner cloner(graph_,
669 &orig_bb_set,
670 &bb_map,
Nicolas Geoffray256c94b2019-04-29 10:55:09 +0100671 &hir_map,
672 /* induction_range= */ nullptr);
Artem Serov02eebcf2017-12-13 19:48:31 +0000673 cloner.SetSuccessorRemappingInfo(&remap_orig_internal, &remap_copy_internal, &remap_incoming);
674
675 EXPECT_FALSE(cloner.IsFastCase());
676}
677
678// Helper for FindCommonLoop which also check that FindCommonLoop is symmetric.
679static HLoopInformation* FindCommonLoopCheck(HLoopInformation* loop1, HLoopInformation* loop2) {
680 HLoopInformation* common_loop12 = FindCommonLoop(loop1, loop2);
681 HLoopInformation* common_loop21 = FindCommonLoop(loop2, loop1);
682 EXPECT_EQ(common_loop21, common_loop12);
683 return common_loop12;
684}
685
686// Tests FindCommonLoop function on a loop nest.
687TEST_F(SuperblockClonerTest, FindCommonLoop) {
688 HBasicBlock* header = nullptr;
689 HBasicBlock* loop_body = nullptr;
690
691 InitGraph();
692
693 // Create the following nested structure of loops
694 // Headers: 1 2 3 4 5
695 // [ [ [ ] ], [ ] ], [ ]
696 CreateBasicLoopControlFlow(entry_block_, return_block_, &header, &loop_body);
697 CreateBasicLoopDataFlow(header, loop_body);
698 HBasicBlock* loop1_header = header;
699
700 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
701 CreateBasicLoopDataFlow(header, loop_body);
702 HBasicBlock* loop2_header = header;
703
704 CreateBasicLoopControlFlow(header, header->GetSuccessors()[1], &header, &loop_body);
705 CreateBasicLoopDataFlow(header, loop_body);
706 HBasicBlock* loop3_header = header;
707
708 CreateBasicLoopControlFlow(loop2_header, loop2_header->GetSuccessors()[0], &header, &loop_body);
709 CreateBasicLoopDataFlow(header, loop_body);
710 HBasicBlock* loop4_header = header;
711
712 CreateBasicLoopControlFlow(loop1_header, return_block_, &header, &loop_body);
713 CreateBasicLoopDataFlow(header, loop_body);
714 HBasicBlock* loop5_header = header;
715
716 graph_->BuildDominatorTree();
717 EXPECT_TRUE(CheckGraph());
718
719 HLoopInformation* loop1 = loop1_header->GetLoopInformation();
720 HLoopInformation* loop2 = loop2_header->GetLoopInformation();
721 HLoopInformation* loop3 = loop3_header->GetLoopInformation();
722 HLoopInformation* loop4 = loop4_header->GetLoopInformation();
723 HLoopInformation* loop5 = loop5_header->GetLoopInformation();
724
725 EXPECT_TRUE(loop1->IsIn(*loop1));
726 EXPECT_TRUE(loop2->IsIn(*loop1));
727 EXPECT_TRUE(loop3->IsIn(*loop1));
728 EXPECT_TRUE(loop3->IsIn(*loop2));
729 EXPECT_TRUE(loop4->IsIn(*loop1));
730
731 EXPECT_FALSE(loop5->IsIn(*loop1));
732 EXPECT_FALSE(loop4->IsIn(*loop2));
733 EXPECT_FALSE(loop4->IsIn(*loop3));
734
735 EXPECT_EQ(loop1->GetPreHeader()->GetLoopInformation(), nullptr);
736 EXPECT_EQ(loop4->GetPreHeader()->GetLoopInformation(), loop1);
737
738 EXPECT_EQ(FindCommonLoopCheck(nullptr, nullptr), nullptr);
739 EXPECT_EQ(FindCommonLoopCheck(loop2, nullptr), nullptr);
740
741 EXPECT_EQ(FindCommonLoopCheck(loop1, loop1), loop1);
742 EXPECT_EQ(FindCommonLoopCheck(loop1, loop2), loop1);
743 EXPECT_EQ(FindCommonLoopCheck(loop1, loop3), loop1);
744 EXPECT_EQ(FindCommonLoopCheck(loop1, loop4), loop1);
745 EXPECT_EQ(FindCommonLoopCheck(loop1, loop5), nullptr);
746
747 EXPECT_EQ(FindCommonLoopCheck(loop2, loop3), loop2);
748 EXPECT_EQ(FindCommonLoopCheck(loop2, loop4), loop1);
749 EXPECT_EQ(FindCommonLoopCheck(loop2, loop5), nullptr);
750
751 EXPECT_EQ(FindCommonLoopCheck(loop3, loop4), loop1);
752 EXPECT_EQ(FindCommonLoopCheck(loop3, loop5), nullptr);
753
754 EXPECT_EQ(FindCommonLoopCheck(loop4, loop5), nullptr);
755
756 EXPECT_EQ(FindCommonLoopCheck(loop5, loop5), loop5);
757}
758
Artem Serovcced8ba2017-07-19 18:18:09 +0100759} // namespace art