blob: dcae46b4bd3657ca6e3c2c8d1aed3f81e95e5c2a [file] [log] [blame]
Nicolas Geoffraya7062e02014-05-22 12:50:17 +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
17#include "builder.h"
18#include "code_generator.h"
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010019#include "code_generator_x86.h"
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010020#include "dex_file.h"
21#include "dex_instruction.h"
22#include "nodes.h"
23#include "optimizing_unit_test.h"
24#include "register_allocator.h"
25#include "ssa_liveness_analysis.h"
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +010026#include "ssa_phi_elimination.h"
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010027#include "utils/arena_allocator.h"
28
29#include "gtest/gtest.h"
30
31namespace art {
32
33// Note: the register allocator tests rely on the fact that constants have live
34// intervals and registers get allocated to them.
35
36static bool Check(const uint16_t* data) {
37 ArenaPool pool;
38 ArenaAllocator allocator(&pool);
39 HGraphBuilder builder(&allocator);
40 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
41 HGraph* graph = builder.BuildGraph(*item);
42 graph->BuildDominatorTree();
43 graph->TransformToSSA();
44 graph->FindNaturalLoops();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010045 x86::CodeGeneratorX86 codegen(graph);
46 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010047 liveness.Analyze();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010048 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010049 register_allocator.AllocateRegisters();
50 return register_allocator.Validate(false);
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010051}
52
53/**
54 * Unit testing of RegisterAllocator::ValidateIntervals. Register allocator
55 * tests are based on this validation method.
56 */
57TEST(RegisterAllocatorTest, ValidateIntervals) {
58 ArenaPool pool;
59 ArenaAllocator allocator(&pool);
60 HGraph* graph = new (&allocator) HGraph(&allocator);
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010061 x86::CodeGeneratorX86 codegen(graph);
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010062 GrowableArray<LiveInterval*> intervals(&allocator, 0);
63
64 // Test with two intervals of the same range.
65 {
66 static constexpr size_t ranges[][2] = {{0, 42}};
67 intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 0));
68 intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010069 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010070 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010071
72 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010073 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010074 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010075 intervals.Reset();
76 }
77
78 // Test with two non-intersecting intervals.
79 {
80 static constexpr size_t ranges1[][2] = {{0, 42}};
81 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
82 static constexpr size_t ranges2[][2] = {{42, 43}};
83 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010084 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010085 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010086
87 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010088 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010089 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010090 intervals.Reset();
91 }
92
93 // Test with two non-intersecting intervals, with one with a lifetime hole.
94 {
95 static constexpr size_t ranges1[][2] = {{0, 42}, {45, 48}};
96 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
97 static constexpr size_t ranges2[][2] = {{42, 43}};
98 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010099 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100100 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100101
102 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100103 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100104 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100105 intervals.Reset();
106 }
107
108 // Test with intersecting intervals.
109 {
110 static constexpr size_t ranges1[][2] = {{0, 42}, {44, 48}};
111 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
112 static constexpr size_t ranges2[][2] = {{42, 47}};
113 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100114 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100115 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100116
117 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100118 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100119 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100120 intervals.Reset();
121 }
122
123 // Test with siblings.
124 {
125 static constexpr size_t ranges1[][2] = {{0, 42}, {44, 48}};
126 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
127 intervals.Get(0)->SplitAt(43);
128 static constexpr size_t ranges2[][2] = {{42, 47}};
129 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100130 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100131 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100132
133 intervals.Get(1)->SetRegister(0);
134 // Sibling of the first interval has no register allocated to it.
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100135 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100136 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100137
138 intervals.Get(0)->GetNextSibling()->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100139 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100140 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100141 }
142}
143
144TEST(RegisterAllocatorTest, CFG1) {
145 /*
146 * Test the following snippet:
147 * return 0;
148 *
149 * Which becomes the following graph:
150 * constant0
151 * goto
152 * |
153 * return
154 * |
155 * exit
156 */
157 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
158 Instruction::CONST_4 | 0 | 0,
159 Instruction::RETURN);
160
161 ASSERT_TRUE(Check(data));
162}
163
164TEST(RegisterAllocatorTest, Loop1) {
165 /*
166 * Test the following snippet:
167 * int a = 0;
168 * while (a == a) {
169 * a = 4;
170 * }
171 * return 5;
172 *
173 * Which becomes the following graph:
174 * constant0
175 * constant4
176 * constant5
177 * goto
178 * |
179 * goto
180 * |
181 * phi
182 * equal
183 * if +++++
184 * | \ +
185 * | goto
186 * |
187 * return
188 * |
189 * exit
190 */
191
192 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
193 Instruction::CONST_4 | 0 | 0,
194 Instruction::IF_EQ, 4,
195 Instruction::CONST_4 | 4 << 12 | 0,
196 Instruction::GOTO | 0xFD00,
197 Instruction::CONST_4 | 5 << 12 | 1 << 8,
198 Instruction::RETURN | 1 << 8);
199
200 ASSERT_TRUE(Check(data));
201}
202
203TEST(RegisterAllocatorTest, Loop2) {
204 /*
205 * Test the following snippet:
206 * int a = 0;
207 * while (a == 8) {
208 * a = 4 + 5;
209 * }
210 * return 6 + 7;
211 *
212 * Which becomes the following graph:
213 * constant0
214 * constant4
215 * constant5
216 * constant6
217 * constant7
218 * constant8
219 * goto
220 * |
221 * goto
222 * |
223 * phi
224 * equal
225 * if +++++
226 * | \ +
227 * | 4 + 5
228 * | goto
229 * |
230 * 6 + 7
231 * return
232 * |
233 * exit
234 */
235
236 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
237 Instruction::CONST_4 | 0 | 0,
238 Instruction::CONST_4 | 8 << 12 | 1 << 8,
239 Instruction::IF_EQ | 1 << 8, 7,
240 Instruction::CONST_4 | 4 << 12 | 0 << 8,
241 Instruction::CONST_4 | 5 << 12 | 1 << 8,
242 Instruction::ADD_INT, 1 << 8 | 0,
243 Instruction::GOTO | 0xFA00,
244 Instruction::CONST_4 | 6 << 12 | 1 << 8,
245 Instruction::CONST_4 | 7 << 12 | 1 << 8,
246 Instruction::ADD_INT, 1 << 8 | 0,
247 Instruction::RETURN | 1 << 8);
248
249 ASSERT_TRUE(Check(data));
250}
251
252static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) {
253 HGraphBuilder builder(allocator);
254 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
255 HGraph* graph = builder.BuildGraph(*item);
256 graph->BuildDominatorTree();
257 graph->TransformToSSA();
258 graph->FindNaturalLoops();
259 return graph;
260}
261
262TEST(RegisterAllocatorTest, Loop3) {
263 /*
264 * Test the following snippet:
265 * int a = 0
266 * do {
267 * b = a;
268 * a++;
269 * } while (a != 5)
270 * return b;
271 *
272 * Which becomes the following graph:
273 * constant0
274 * constant1
275 * constant5
276 * goto
277 * |
278 * goto
279 * |++++++++++++
280 * phi +
281 * a++ +
282 * equals +
283 * if +
284 * |++++++++++++
285 * return
286 * |
287 * exit
288 */
289
290 const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
291 Instruction::CONST_4 | 0 | 0,
292 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8,
293 Instruction::CONST_4 | 5 << 12 | 2 << 8,
294 Instruction::IF_NE | 1 << 8 | 2 << 12, 3,
295 Instruction::RETURN | 0 << 8,
296 Instruction::MOVE | 1 << 12 | 0 << 8,
297 Instruction::GOTO | 0xF900);
298
299 ArenaPool pool;
300 ArenaAllocator allocator(&pool);
301 HGraph* graph = BuildSSAGraph(data, &allocator);
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100302 x86::CodeGeneratorX86 codegen(graph);
303 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100304 liveness.Analyze();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100305 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100306 register_allocator.AllocateRegisters();
307 ASSERT_TRUE(register_allocator.Validate(false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100308
309 HBasicBlock* loop_header = graph->GetBlocks().Get(2);
310 HPhi* phi = loop_header->GetFirstPhi()->AsPhi();
311
312 LiveInterval* phi_interval = phi->GetLiveInterval();
313 LiveInterval* loop_update = phi->InputAt(1)->GetLiveInterval();
314 ASSERT_TRUE(phi_interval->HasRegister());
315 ASSERT_TRUE(loop_update->HasRegister());
316 ASSERT_NE(phi_interval->GetRegister(), loop_update->GetRegister());
317
318 HBasicBlock* return_block = graph->GetBlocks().Get(3);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100319 HReturn* ret = return_block->GetLastInstruction()->AsReturn();
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100320 ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister());
321}
322
Nicolas Geoffrayde025a72014-06-19 17:06:46 +0100323TEST(RegisterAllocatorTest, FirstRegisterUse) {
324 const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
325 Instruction::CONST_4 | 0 | 0,
326 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8,
327 Instruction::ADD_INT_LIT8 | 0 << 8, 1 << 8,
328 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8 | 1,
329 Instruction::RETURN_VOID);
330
331 ArenaPool pool;
332 ArenaAllocator allocator(&pool);
333 HGraph* graph = BuildSSAGraph(data, &allocator);
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100334 x86::CodeGeneratorX86 codegen(graph);
335 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffrayde025a72014-06-19 17:06:46 +0100336 liveness.Analyze();
337
338 HAdd* first_add = graph->GetBlocks().Get(1)->GetFirstInstruction()->AsAdd();
339 HAdd* last_add = graph->GetBlocks().Get(1)->GetLastInstruction()->GetPrevious()->AsAdd();
340 ASSERT_EQ(last_add->InputAt(0), first_add);
341 LiveInterval* interval = first_add->GetLiveInterval();
342 ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition() + 1);
343 ASSERT_TRUE(interval->GetNextSibling() == nullptr);
344
345 // We need a register for the output of the instruction.
346 ASSERT_EQ(interval->FirstRegisterUse(), first_add->GetLifetimePosition());
347
348 // Split at the next instruction.
349 interval = interval->SplitAt(first_add->GetLifetimePosition() + 2);
350 // The user of the split is the last add.
351 ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
352
353 // Split before the last add.
354 LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1);
355 // Ensure the current interval has no register use...
356 ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime);
357 // And the new interval has it for the last add.
358 ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition() + 1);
359}
360
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100361TEST(RegisterAllocatorTest, DeadPhi) {
362 /* Test for a dead loop phi taking as back-edge input a phi that also has
363 * this loop phi as input. Walking backwards in SsaDeadPhiElimination
364 * does not solve the problem because the loop phi will be visited last.
365 *
366 * Test the following snippet:
367 * int a = 0
368 * do {
369 * if (true) {
370 * a = 2;
371 * }
372 * } while (true);
373 */
374
375 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
376 Instruction::CONST_4 | 0 | 0,
377 Instruction::CONST_4 | 1 << 8 | 0,
378 Instruction::IF_NE | 1 << 8 | 1 << 12, 3,
379 Instruction::CONST_4 | 2 << 12 | 0 << 8,
380 Instruction::GOTO | 0xFD00,
381 Instruction::RETURN_VOID);
382
383 ArenaPool pool;
384 ArenaAllocator allocator(&pool);
385 HGraph* graph = BuildSSAGraph(data, &allocator);
386 SsaDeadPhiElimination(graph).Run();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100387 x86::CodeGeneratorX86 codegen(graph);
388 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100389 liveness.Analyze();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100390 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100391 register_allocator.AllocateRegisters();
392 ASSERT_TRUE(register_allocator.Validate(false));
393}
394
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100395} // namespace art