blob: 4770fa2ecab49ac2f7a16007c9c91e718311e2f2 [file] [log] [blame]
Nicolas Geoffray622d9c32014-05-12 16:11:02 +01001/*
2 * Copyright (C) 2014 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
Mathieu Chartierb666f482015-02-18 14:33:14 -080017#include "base/arena_allocator.h"
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010018#include "builder.h"
19#include "dex_file.h"
20#include "dex_instruction.h"
21#include "nodes.h"
22#include "optimizing_unit_test.h"
23#include "ssa_liveness_analysis.h"
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010024#include "pretty_printer.h"
25
26#include "gtest/gtest.h"
27
28namespace art {
29
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +010030static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) {
Nicolas Geoffray0a23d742015-05-07 11:57:35 +010031 HGraph* graph = CreateGraph(allocator);
David Brazdil5e8b1372015-01-23 14:39:08 +000032 HGraphBuilder builder(graph);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010033 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
David Brazdil5e8b1372015-01-23 14:39:08 +000034 builder.BuildGraph(*item);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010035 graph->BuildDominatorTree();
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010036 return graph;
37}
38
39TEST(FindLoopsTest, CFG1) {
40 // Constant is not used.
41 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
42 Instruction::CONST_4 | 0 | 0,
43 Instruction::RETURN_VOID);
44
45 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +010046 ArenaAllocator allocator(&arena);
47 HGraph* graph = TestCode(data, &allocator);
Vladimir Markofa6b93c2015-09-15 10:15:55 +010048 for (HBasicBlock* block : graph->GetBlocks()) {
49 ASSERT_EQ(block->GetLoopInformation(), nullptr);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010050 }
51}
52
53TEST(FindLoopsTest, CFG2) {
54 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
55 Instruction::CONST_4 | 0 | 0,
56 Instruction::RETURN);
57
58 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +010059 ArenaAllocator allocator(&arena);
60 HGraph* graph = TestCode(data, &allocator);
Vladimir Markofa6b93c2015-09-15 10:15:55 +010061 for (HBasicBlock* block : graph->GetBlocks()) {
62 ASSERT_EQ(block->GetLoopInformation(), nullptr);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010063 }
64}
65
66TEST(FindLoopsTest, CFG3) {
67 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
68 Instruction::CONST_4 | 3 << 12 | 0,
69 Instruction::CONST_4 | 4 << 12 | 1 << 8,
70 Instruction::ADD_INT_2ADDR | 1 << 12,
71 Instruction::GOTO | 0x100,
72 Instruction::RETURN);
73
74 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +010075 ArenaAllocator allocator(&arena);
76 HGraph* graph = TestCode(data, &allocator);
Vladimir Markofa6b93c2015-09-15 10:15:55 +010077 for (HBasicBlock* block : graph->GetBlocks()) {
78 ASSERT_EQ(block->GetLoopInformation(), nullptr);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010079 }
80}
81
82TEST(FindLoopsTest, CFG4) {
83 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
84 Instruction::CONST_4 | 0 | 0,
85 Instruction::IF_EQ, 4,
86 Instruction::CONST_4 | 4 << 12 | 0,
87 Instruction::GOTO | 0x200,
88 Instruction::CONST_4 | 5 << 12 | 0,
89 Instruction::RETURN | 0 << 8);
90
91 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +010092 ArenaAllocator allocator(&arena);
93 HGraph* graph = TestCode(data, &allocator);
Vladimir Markofa6b93c2015-09-15 10:15:55 +010094 for (HBasicBlock* block : graph->GetBlocks()) {
95 ASSERT_EQ(block->GetLoopInformation(), nullptr);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +010096 }
97}
98
99TEST(FindLoopsTest, CFG5) {
100 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
101 Instruction::CONST_4 | 0 | 0,
102 Instruction::IF_EQ, 3,
103 Instruction::CONST_4 | 4 << 12 | 0,
104 Instruction::RETURN | 0 << 8);
105
106 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100107 ArenaAllocator allocator(&arena);
108 HGraph* graph = TestCode(data, &allocator);
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100109 for (HBasicBlock* block : graph->GetBlocks()) {
110 ASSERT_EQ(block->GetLoopInformation(), nullptr);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100111 }
112}
113
114static void TestBlock(HGraph* graph,
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100115 uint32_t block_id,
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100116 bool is_loop_header,
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100117 uint32_t parent_loop_header_id,
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100118 const int* blocks_in_loop = nullptr,
119 size_t number_of_blocks = 0) {
Vladimir Markoec7802a2015-10-01 20:57:57 +0100120 HBasicBlock* block = graph->GetBlocks()[block_id];
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100121 ASSERT_EQ(block->IsLoopHeader(), is_loop_header);
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100122 if (parent_loop_header_id == kInvalidBlockId) {
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100123 ASSERT_EQ(block->GetLoopInformation(), nullptr);
124 } else {
125 ASSERT_EQ(block->GetLoopInformation()->GetHeader()->GetBlockId(), parent_loop_header_id);
126 }
127
128 if (blocks_in_loop != nullptr) {
129 HLoopInformation* info = block->GetLoopInformation();
130 const BitVector& blocks = info->GetBlocks();
131 ASSERT_EQ(blocks.NumSetBits(), number_of_blocks);
132 for (size_t i = 0; i < number_of_blocks; ++i) {
133 ASSERT_TRUE(blocks.IsBitSet(blocks_in_loop[i]));
134 }
135 } else {
136 ASSERT_FALSE(block->IsLoopHeader());
137 }
138}
139
140TEST(FindLoopsTest, Loop1) {
141 // Simple loop with one preheader and one back edge.
142 // var a = 0;
143 // while (a == a) {
144 // }
145 // return;
146 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
147 Instruction::CONST_4 | 0 | 0,
148 Instruction::IF_EQ, 3,
149 Instruction::GOTO | 0xFE00,
150 Instruction::RETURN_VOID);
151
152 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100153 ArenaAllocator allocator(&arena);
154 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100155
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100156 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
157 TestBlock(graph, 1, false, kInvalidBlockId); // pre header
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100158 const int blocks2[] = {2, 3};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100159 TestBlock(graph, 2, true, 2, blocks2, 2); // loop header
160 TestBlock(graph, 3, false, 2); // block in loop
161 TestBlock(graph, 4, false, kInvalidBlockId); // return block
162 TestBlock(graph, 5, false, kInvalidBlockId); // exit block
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100163}
164
165TEST(FindLoopsTest, Loop2) {
166 // Make sure we support a preheader of a loop not being the first predecessor
167 // in the predecessor list of the header.
168 // var a = 0;
169 // while (a == a) {
170 // }
171 // return a;
172 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
173 Instruction::CONST_4 | 0 | 0,
174 Instruction::GOTO | 0x400,
175 Instruction::IF_EQ, 4,
176 Instruction::GOTO | 0xFE00,
177 Instruction::GOTO | 0xFD00,
178 Instruction::RETURN | 0 << 8);
179
180 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100181 ArenaAllocator allocator(&arena);
182 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100183
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100184 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
185 TestBlock(graph, 1, false, kInvalidBlockId); // goto block
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100186 const int blocks2[] = {2, 3};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100187 TestBlock(graph, 2, true, 2, blocks2, 2); // loop header
188 TestBlock(graph, 3, false, 2); // block in loop
189 TestBlock(graph, 4, false, kInvalidBlockId); // pre header
190 TestBlock(graph, 5, false, kInvalidBlockId); // return block
191 TestBlock(graph, 6, false, kInvalidBlockId); // exit block
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100192}
193
194TEST(FindLoopsTest, Loop3) {
195 // Make sure we create a preheader of a loop when a header originally has two
196 // incoming blocks and one back edge.
197 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
198 Instruction::CONST_4 | 0 | 0,
199 Instruction::IF_EQ, 3,
200 Instruction::GOTO | 0x100,
201 Instruction::IF_EQ, 3,
202 Instruction::GOTO | 0xFE00,
203 Instruction::RETURN | 0 << 8);
204
205 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100206 ArenaAllocator allocator(&arena);
207 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100208
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100209 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
210 TestBlock(graph, 1, false, kInvalidBlockId); // goto block
211 TestBlock(graph, 2, false, kInvalidBlockId);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100212 const int blocks2[] = {3, 4};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100213 TestBlock(graph, 3, true, 3, blocks2, 2); // loop header
214 TestBlock(graph, 4, false, 3); // block in loop
215 TestBlock(graph, 5, false, kInvalidBlockId); // pre header
216 TestBlock(graph, 6, false, kInvalidBlockId); // return block
217 TestBlock(graph, 7, false, kInvalidBlockId); // exit block
218 TestBlock(graph, 8, false, kInvalidBlockId); // synthesized pre header
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100219}
220
221TEST(FindLoopsTest, Loop4) {
222 // Test loop with originally two back edges.
223 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
224 Instruction::CONST_4 | 0 | 0,
225 Instruction::IF_EQ, 6,
226 Instruction::IF_EQ, 3,
227 Instruction::GOTO | 0xFC00,
228 Instruction::GOTO | 0xFB00,
229 Instruction::RETURN | 0 << 8);
230
231 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100232 ArenaAllocator allocator(&arena);
233 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100234
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100235 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
236 TestBlock(graph, 1, false, kInvalidBlockId); // pre header
Nicolas Geoffraydb216f42015-05-05 17:02:20 +0100237 const int blocks2[] = {2, 3, 4, 5};
238 TestBlock(graph, 2, true, 2, blocks2, arraysize(blocks2)); // loop header
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100239 TestBlock(graph, 3, false, 2); // block in loop
240 TestBlock(graph, 4, false, 2); // back edge
241 TestBlock(graph, 5, false, 2); // back edge
242 TestBlock(graph, 6, false, kInvalidBlockId); // return block
243 TestBlock(graph, 7, false, kInvalidBlockId); // exit block
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100244}
245
246
247TEST(FindLoopsTest, Loop5) {
248 // Test loop with two exit edges.
249 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
250 Instruction::CONST_4 | 0 | 0,
251 Instruction::IF_EQ, 6,
252 Instruction::IF_EQ, 3,
253 Instruction::GOTO | 0x0200,
254 Instruction::GOTO | 0xFB00,
255 Instruction::RETURN | 0 << 8);
256
257 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100258 ArenaAllocator allocator(&arena);
259 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100260
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100261 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
262 TestBlock(graph, 1, false, kInvalidBlockId); // pre header
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100263 const int blocks2[] = {2, 3, 5};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100264 TestBlock(graph, 2, true, 2, blocks2, 3); // loop header
265 TestBlock(graph, 3, false, 2); // block in loop
266 TestBlock(graph, 4, false, kInvalidBlockId); // loop exit
267 TestBlock(graph, 5, false, 2); // back edge
268 TestBlock(graph, 6, false, kInvalidBlockId); // return block
269 TestBlock(graph, 7, false, kInvalidBlockId); // exit block
270 TestBlock(graph, 8, false, kInvalidBlockId); // synthesized block at the loop exit
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100271}
272
273TEST(FindLoopsTest, InnerLoop) {
274 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
275 Instruction::CONST_4 | 0 | 0,
276 Instruction::IF_EQ, 6,
277 Instruction::IF_EQ, 3,
278 Instruction::GOTO | 0xFE00, // inner loop
279 Instruction::GOTO | 0xFB00,
280 Instruction::RETURN | 0 << 8);
281
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100282 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100283 ArenaAllocator allocator(&arena);
284 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100285
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100286 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
287 TestBlock(graph, 1, false, kInvalidBlockId); // pre header of outer loop
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100288 const int blocks2[] = {2, 3, 4, 5, 8};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100289 TestBlock(graph, 2, true, 2, blocks2, 5); // outer loop header
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100290 const int blocks3[] = {3, 4};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100291 TestBlock(graph, 3, true, 3, blocks3, 2); // inner loop header
292 TestBlock(graph, 4, false, 3); // back edge on inner loop
293 TestBlock(graph, 5, false, 2); // back edge on outer loop
294 TestBlock(graph, 6, false, kInvalidBlockId); // return block
295 TestBlock(graph, 7, false, kInvalidBlockId); // exit block
296 TestBlock(graph, 8, false, 2); // synthesized block as pre header of inner loop
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100297
Vladimir Markoec7802a2015-10-01 20:57:57 +0100298 ASSERT_TRUE(graph->GetBlocks()[3]->GetLoopInformation()->IsIn(
299 *graph->GetBlocks()[2]->GetLoopInformation()));
300 ASSERT_FALSE(graph->GetBlocks()[2]->GetLoopInformation()->IsIn(
301 *graph->GetBlocks()[3]->GetLoopInformation()));
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100302}
303
304TEST(FindLoopsTest, TwoLoops) {
305 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
306 Instruction::CONST_4 | 0 | 0,
307 Instruction::IF_EQ, 3,
308 Instruction::GOTO | 0xFE00, // first loop
309 Instruction::IF_EQ, 3,
310 Instruction::GOTO | 0xFE00, // second loop
311 Instruction::RETURN | 0 << 8);
312
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100313 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100314 ArenaAllocator allocator(&arena);
315 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100316
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100317 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
318 TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100319 const int blocks2[] = {2, 3};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100320 TestBlock(graph, 2, true, 2, blocks2, 2); // first loop header
321 TestBlock(graph, 3, false, 2); // back edge of first loop
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100322 const int blocks4[] = {4, 5};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100323 TestBlock(graph, 4, true, 4, blocks4, 2); // second loop header
324 TestBlock(graph, 5, false, 4); // back edge of second loop
325 TestBlock(graph, 6, false, kInvalidBlockId); // return block
326 TestBlock(graph, 7, false, kInvalidBlockId); // exit block
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100327
Vladimir Markoec7802a2015-10-01 20:57:57 +0100328 ASSERT_FALSE(graph->GetBlocks()[4]->GetLoopInformation()->IsIn(
329 *graph->GetBlocks()[2]->GetLoopInformation()));
330 ASSERT_FALSE(graph->GetBlocks()[2]->GetLoopInformation()->IsIn(
331 *graph->GetBlocks()[4]->GetLoopInformation()));
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100332}
333
334TEST(FindLoopsTest, NonNaturalLoop) {
335 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
336 Instruction::CONST_4 | 0 | 0,
337 Instruction::IF_EQ, 3,
338 Instruction::GOTO | 0x0100,
339 Instruction::IF_EQ, 3,
340 Instruction::GOTO | 0xFD00,
341 Instruction::RETURN | 0 << 8);
342
343 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100344 ArenaAllocator allocator(&arena);
345 HGraph* graph = TestCode(data, &allocator);
Vladimir Markoec7802a2015-10-01 20:57:57 +0100346 ASSERT_TRUE(graph->GetBlocks()[3]->IsLoopHeader());
347 HLoopInformation* info = graph->GetBlocks()[3]->GetLoopInformation();
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100348 ASSERT_EQ(1u, info->NumberOfBackEdges());
349 ASSERT_FALSE(info->GetHeader()->Dominates(info->GetBackEdges()[0]));
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100350}
351
352TEST(FindLoopsTest, DoWhileLoop) {
353 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
354 Instruction::CONST_4 | 0 | 0,
355 Instruction::GOTO | 0x0100,
356 Instruction::IF_EQ, 0xFFFF,
357 Instruction::RETURN | 0 << 8);
358
359 ArenaPool arena;
Nicolas Geoffraye6c96d12014-09-10 10:34:01 +0100360 ArenaAllocator allocator(&arena);
361 HGraph* graph = TestCode(data, &allocator);
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100362
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100363 TestBlock(graph, 0, false, kInvalidBlockId); // entry block
364 TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100365 const int blocks2[] = {2, 3, 6};
Vladimir Markofa6b93c2015-09-15 10:15:55 +0100366 TestBlock(graph, 2, true, 2, blocks2, 3); // loop header
367 TestBlock(graph, 3, false, 2); // back edge of first loop
368 TestBlock(graph, 4, false, kInvalidBlockId); // return block
369 TestBlock(graph, 5, false, kInvalidBlockId); // exit block
370 TestBlock(graph, 6, false, 2); // synthesized block to avoid a critical edge
Nicolas Geoffray622d9c32014-05-12 16:11:02 +0100371}
372
373} // namespace art