blob: cb5010afcded1a7dd1b2e5e1a06b83e502d7a9a4 [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"
Calin Juravlecd6dffe2015-01-08 17:35:35 +000022#include "driver/compiler_options.h"
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010023#include "nodes.h"
24#include "optimizing_unit_test.h"
25#include "register_allocator.h"
26#include "ssa_liveness_analysis.h"
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +010027#include "ssa_phi_elimination.h"
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010028#include "utils/arena_allocator.h"
29
30#include "gtest/gtest.h"
31
32namespace art {
33
34// Note: the register allocator tests rely on the fact that constants have live
35// intervals and registers get allocated to them.
36
37static bool Check(const uint16_t* data) {
38 ArenaPool pool;
39 ArenaAllocator allocator(&pool);
40 HGraphBuilder builder(&allocator);
41 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
42 HGraph* graph = builder.BuildGraph(*item);
Nicolas Geoffraye53798a2014-12-01 10:31:54 +000043 graph->TryBuildingSsa();
Calin Juravlecd6dffe2015-01-08 17:35:35 +000044 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010045 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010046 liveness.Analyze();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010047 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +010048 register_allocator.AllocateRegisters();
49 return register_allocator.Validate(false);
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010050}
51
52/**
53 * Unit testing of RegisterAllocator::ValidateIntervals. Register allocator
54 * tests are based on this validation method.
55 */
56TEST(RegisterAllocatorTest, ValidateIntervals) {
57 ArenaPool pool;
58 ArenaAllocator allocator(&pool);
59 HGraph* graph = new (&allocator) HGraph(&allocator);
Calin Juravlecd6dffe2015-01-08 17:35:35 +000060 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010061 GrowableArray<LiveInterval*> intervals(&allocator, 0);
62
63 // Test with two intervals of the same range.
64 {
65 static constexpr size_t ranges[][2] = {{0, 42}};
66 intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 0));
67 intervals.Add(BuildInterval(ranges, arraysize(ranges), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010068 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010069 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010070
71 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010072 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010073 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010074 intervals.Reset();
75 }
76
77 // Test with two non-intersecting intervals.
78 {
79 static constexpr size_t ranges1[][2] = {{0, 42}};
80 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
81 static constexpr size_t ranges2[][2] = {{42, 43}};
82 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010083 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010084 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010085
86 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010087 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010088 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +010089 intervals.Reset();
90 }
91
92 // Test with two non-intersecting intervals, with one with a lifetime hole.
93 {
94 static constexpr size_t ranges1[][2] = {{0, 42}, {45, 48}};
95 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
96 static constexpr size_t ranges2[][2] = {{42, 43}};
97 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +010098 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +010099 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100100
101 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100102 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100103 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100104 intervals.Reset();
105 }
106
107 // Test with intersecting intervals.
108 {
109 static constexpr size_t ranges1[][2] = {{0, 42}, {44, 48}};
110 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
111 static constexpr size_t ranges2[][2] = {{42, 47}};
112 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100113 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100114 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100115
116 intervals.Get(1)->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100117 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100118 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100119 intervals.Reset();
120 }
121
122 // Test with siblings.
123 {
124 static constexpr size_t ranges1[][2] = {{0, 42}, {44, 48}};
125 intervals.Add(BuildInterval(ranges1, arraysize(ranges1), &allocator, 0));
126 intervals.Get(0)->SplitAt(43);
127 static constexpr size_t ranges2[][2] = {{42, 47}};
128 intervals.Add(BuildInterval(ranges2, arraysize(ranges2), &allocator, 1));
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100129 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100130 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100131
132 intervals.Get(1)->SetRegister(0);
133 // Sibling of the first interval has no register allocated to it.
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100134 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100135 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100136
137 intervals.Get(0)->GetNextSibling()->SetRegister(0);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100138 ASSERT_FALSE(RegisterAllocator::ValidateIntervals(
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100139 intervals, 0, 0, codegen, &allocator, true, false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100140 }
141}
142
143TEST(RegisterAllocatorTest, CFG1) {
144 /*
145 * Test the following snippet:
146 * return 0;
147 *
148 * Which becomes the following graph:
149 * constant0
150 * goto
151 * |
152 * return
153 * |
154 * exit
155 */
156 const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
157 Instruction::CONST_4 | 0 | 0,
158 Instruction::RETURN);
159
160 ASSERT_TRUE(Check(data));
161}
162
163TEST(RegisterAllocatorTest, Loop1) {
164 /*
165 * Test the following snippet:
166 * int a = 0;
167 * while (a == a) {
168 * a = 4;
169 * }
170 * return 5;
171 *
172 * Which becomes the following graph:
173 * constant0
174 * constant4
175 * constant5
176 * goto
177 * |
178 * goto
179 * |
180 * phi
181 * equal
182 * if +++++
183 * | \ +
184 * | goto
185 * |
186 * return
187 * |
188 * exit
189 */
190
191 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
192 Instruction::CONST_4 | 0 | 0,
193 Instruction::IF_EQ, 4,
194 Instruction::CONST_4 | 4 << 12 | 0,
195 Instruction::GOTO | 0xFD00,
196 Instruction::CONST_4 | 5 << 12 | 1 << 8,
197 Instruction::RETURN | 1 << 8);
198
199 ASSERT_TRUE(Check(data));
200}
201
202TEST(RegisterAllocatorTest, Loop2) {
203 /*
204 * Test the following snippet:
205 * int a = 0;
206 * while (a == 8) {
207 * a = 4 + 5;
208 * }
209 * return 6 + 7;
210 *
211 * Which becomes the following graph:
212 * constant0
213 * constant4
214 * constant5
215 * constant6
216 * constant7
217 * constant8
218 * goto
219 * |
220 * goto
221 * |
222 * phi
223 * equal
224 * if +++++
225 * | \ +
226 * | 4 + 5
227 * | goto
228 * |
229 * 6 + 7
230 * return
231 * |
232 * exit
233 */
234
235 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
236 Instruction::CONST_4 | 0 | 0,
237 Instruction::CONST_4 | 8 << 12 | 1 << 8,
238 Instruction::IF_EQ | 1 << 8, 7,
239 Instruction::CONST_4 | 4 << 12 | 0 << 8,
240 Instruction::CONST_4 | 5 << 12 | 1 << 8,
241 Instruction::ADD_INT, 1 << 8 | 0,
242 Instruction::GOTO | 0xFA00,
243 Instruction::CONST_4 | 6 << 12 | 1 << 8,
244 Instruction::CONST_4 | 7 << 12 | 1 << 8,
245 Instruction::ADD_INT, 1 << 8 | 0,
246 Instruction::RETURN | 1 << 8);
247
248 ASSERT_TRUE(Check(data));
249}
250
251static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) {
252 HGraphBuilder builder(allocator);
253 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data);
254 HGraph* graph = builder.BuildGraph(*item);
Nicolas Geoffraye53798a2014-12-01 10:31:54 +0000255 graph->TryBuildingSsa();
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100256 return graph;
257}
258
259TEST(RegisterAllocatorTest, Loop3) {
260 /*
261 * Test the following snippet:
262 * int a = 0
263 * do {
264 * b = a;
265 * a++;
266 * } while (a != 5)
267 * return b;
268 *
269 * Which becomes the following graph:
270 * constant0
271 * constant1
272 * constant5
273 * goto
274 * |
275 * goto
276 * |++++++++++++
277 * phi +
278 * a++ +
279 * equals +
280 * if +
281 * |++++++++++++
282 * return
283 * |
284 * exit
285 */
286
287 const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
288 Instruction::CONST_4 | 0 | 0,
289 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8,
290 Instruction::CONST_4 | 5 << 12 | 2 << 8,
291 Instruction::IF_NE | 1 << 8 | 2 << 12, 3,
292 Instruction::RETURN | 0 << 8,
293 Instruction::MOVE | 1 << 12 | 0 << 8,
294 Instruction::GOTO | 0xF900);
295
296 ArenaPool pool;
297 ArenaAllocator allocator(&pool);
298 HGraph* graph = BuildSSAGraph(data, &allocator);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000299 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100300 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffray31d76b42014-06-09 15:02:22 +0100301 liveness.Analyze();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100302 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100303 register_allocator.AllocateRegisters();
304 ASSERT_TRUE(register_allocator.Validate(false));
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100305
306 HBasicBlock* loop_header = graph->GetBlocks().Get(2);
307 HPhi* phi = loop_header->GetFirstPhi()->AsPhi();
308
309 LiveInterval* phi_interval = phi->GetLiveInterval();
310 LiveInterval* loop_update = phi->InputAt(1)->GetLiveInterval();
311 ASSERT_TRUE(phi_interval->HasRegister());
312 ASSERT_TRUE(loop_update->HasRegister());
313 ASSERT_NE(phi_interval->GetRegister(), loop_update->GetRegister());
314
315 HBasicBlock* return_block = graph->GetBlocks().Get(3);
Nicolas Geoffray86dbb9a2014-06-04 11:12:39 +0100316 HReturn* ret = return_block->GetLastInstruction()->AsReturn();
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100317 ASSERT_EQ(phi_interval->GetRegister(), ret->InputAt(0)->GetLiveInterval()->GetRegister());
318}
319
Nicolas Geoffrayde025a72014-06-19 17:06:46 +0100320TEST(RegisterAllocatorTest, FirstRegisterUse) {
321 const uint16_t data[] = THREE_REGISTERS_CODE_ITEM(
322 Instruction::CONST_4 | 0 | 0,
323 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8,
324 Instruction::ADD_INT_LIT8 | 0 << 8, 1 << 8,
325 Instruction::ADD_INT_LIT8 | 1 << 8, 1 << 8 | 1,
326 Instruction::RETURN_VOID);
327
328 ArenaPool pool;
329 ArenaAllocator allocator(&pool);
330 HGraph* graph = BuildSSAGraph(data, &allocator);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000331 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100332 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffrayde025a72014-06-19 17:06:46 +0100333 liveness.Analyze();
334
335 HAdd* first_add = graph->GetBlocks().Get(1)->GetFirstInstruction()->AsAdd();
336 HAdd* last_add = graph->GetBlocks().Get(1)->GetLastInstruction()->GetPrevious()->AsAdd();
337 ASSERT_EQ(last_add->InputAt(0), first_add);
338 LiveInterval* interval = first_add->GetLiveInterval();
Nicolas Geoffrayfd680d82014-09-29 09:46:03 +0100339 ASSERT_EQ(interval->GetEnd(), last_add->GetLifetimePosition());
Nicolas Geoffrayde025a72014-06-19 17:06:46 +0100340 ASSERT_TRUE(interval->GetNextSibling() == nullptr);
341
342 // We need a register for the output of the instruction.
343 ASSERT_EQ(interval->FirstRegisterUse(), first_add->GetLifetimePosition());
344
345 // Split at the next instruction.
346 interval = interval->SplitAt(first_add->GetLifetimePosition() + 2);
347 // The user of the split is the last add.
Nicolas Geoffray1f897b92014-10-21 17:14:05 +0100348 ASSERT_EQ(interval->FirstRegisterUse(), last_add->GetLifetimePosition());
Nicolas Geoffrayde025a72014-06-19 17:06:46 +0100349
350 // Split before the last add.
351 LiveInterval* new_interval = interval->SplitAt(last_add->GetLifetimePosition() - 1);
352 // Ensure the current interval has no register use...
353 ASSERT_EQ(interval->FirstRegisterUse(), kNoLifetime);
354 // And the new interval has it for the last add.
Nicolas Geoffray1f897b92014-10-21 17:14:05 +0100355 ASSERT_EQ(new_interval->FirstRegisterUse(), last_add->GetLifetimePosition());
Nicolas Geoffrayde025a72014-06-19 17:06:46 +0100356}
357
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100358TEST(RegisterAllocatorTest, DeadPhi) {
359 /* Test for a dead loop phi taking as back-edge input a phi that also has
360 * this loop phi as input. Walking backwards in SsaDeadPhiElimination
361 * does not solve the problem because the loop phi will be visited last.
362 *
363 * Test the following snippet:
364 * int a = 0
365 * do {
366 * if (true) {
367 * a = 2;
368 * }
369 * } while (true);
370 */
371
372 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
373 Instruction::CONST_4 | 0 | 0,
374 Instruction::CONST_4 | 1 << 8 | 0,
375 Instruction::IF_NE | 1 << 8 | 1 << 12, 3,
376 Instruction::CONST_4 | 2 << 12 | 0 << 8,
377 Instruction::GOTO | 0xFD00,
378 Instruction::RETURN_VOID);
379
380 ArenaPool pool;
381 ArenaAllocator allocator(&pool);
382 HGraph* graph = BuildSSAGraph(data, &allocator);
383 SsaDeadPhiElimination(graph).Run();
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000384 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100385 SsaLivenessAnalysis liveness(*graph, &codegen);
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100386 liveness.Analyze();
Nicolas Geoffray8a16d972014-09-11 10:30:02 +0100387 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
Nicolas Geoffray3ac17fc2014-08-06 23:02:54 +0100388 register_allocator.AllocateRegisters();
389 ASSERT_TRUE(register_allocator.Validate(false));
390}
391
Nicolas Geoffrayaac0f392014-09-16 14:11:14 +0100392/**
393 * Test that the TryAllocateFreeReg method works in the presence of inactive intervals
394 * that share the same register. It should split the interval it is currently
395 * allocating for at the minimum lifetime position between the two inactive intervals.
396 */
397TEST(RegisterAllocatorTest, FreeUntil) {
398 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
399 Instruction::CONST_4 | 0 | 0,
400 Instruction::RETURN);
401
402 ArenaPool pool;
403 ArenaAllocator allocator(&pool);
404 HGraph* graph = BuildSSAGraph(data, &allocator);
405 SsaDeadPhiElimination(graph).Run();
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000406 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffrayaac0f392014-09-16 14:11:14 +0100407 SsaLivenessAnalysis liveness(*graph, &codegen);
408 liveness.Analyze();
409 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
410
411 // Add an artifical range to cover the temps that will be put in the unhandled list.
412 LiveInterval* unhandled = graph->GetEntryBlock()->GetFirstInstruction()->GetLiveInterval();
413 unhandled->AddLoopRange(0, 60);
Mingyao Yang296bd602014-10-06 16:47:28 -0700414 // For SSA value intervals, only an interval resulted from a split may intersect
415 // with inactive intervals.
416 unhandled = register_allocator.Split(unhandled, 5);
Nicolas Geoffrayaac0f392014-09-16 14:11:14 +0100417
418 // Add three temps holding the same register, and starting at different positions.
419 // Put the one that should be picked in the middle of the inactive list to ensure
420 // we do not depend on an order.
Mingyao Yang296bd602014-10-06 16:47:28 -0700421 LiveInterval* interval = LiveInterval::MakeInterval(&allocator, Primitive::kPrimInt);
Nicolas Geoffrayaac0f392014-09-16 14:11:14 +0100422 interval->SetRegister(0);
423 interval->AddRange(40, 50);
424 register_allocator.inactive_.Add(interval);
425
Mingyao Yang296bd602014-10-06 16:47:28 -0700426 interval = LiveInterval::MakeInterval(&allocator, Primitive::kPrimInt);
Nicolas Geoffrayaac0f392014-09-16 14:11:14 +0100427 interval->SetRegister(0);
428 interval->AddRange(20, 30);
429 register_allocator.inactive_.Add(interval);
430
Mingyao Yang296bd602014-10-06 16:47:28 -0700431 interval = LiveInterval::MakeInterval(&allocator, Primitive::kPrimInt);
Nicolas Geoffrayaac0f392014-09-16 14:11:14 +0100432 interval->SetRegister(0);
433 interval->AddRange(60, 70);
434 register_allocator.inactive_.Add(interval);
435
436 register_allocator.number_of_registers_ = 1;
437 register_allocator.registers_array_ = allocator.AllocArray<size_t>(1);
438 register_allocator.processing_core_registers_ = true;
439 register_allocator.unhandled_ = &register_allocator.unhandled_core_intervals_;
440
Mingyao Yang296bd602014-10-06 16:47:28 -0700441 ASSERT_TRUE(register_allocator.TryAllocateFreeReg(unhandled));
Nicolas Geoffrayaac0f392014-09-16 14:11:14 +0100442
443 // Check that we have split the interval.
444 ASSERT_EQ(1u, register_allocator.unhandled_->Size());
445 // Check that we know need to find a new register where the next interval
446 // that uses the register starts.
447 ASSERT_EQ(20u, register_allocator.unhandled_->Get(0)->GetStart());
448}
449
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100450static HGraph* BuildIfElseWithPhi(ArenaAllocator* allocator,
451 HPhi** phi,
452 HInstruction** input1,
453 HInstruction** input2) {
454 HGraph* graph = new (allocator) HGraph(allocator);
455 HBasicBlock* entry = new (allocator) HBasicBlock(graph);
456 graph->AddBlock(entry);
457 graph->SetEntryBlock(entry);
458 HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
459 entry->AddInstruction(parameter);
460
461 HBasicBlock* block = new (allocator) HBasicBlock(graph);
462 graph->AddBlock(block);
463 entry->AddSuccessor(block);
464
465 HInstruction* test = new (allocator) HInstanceFieldGet(
Calin Juravle52c48962014-12-16 17:02:57 +0000466 parameter, Primitive::kPrimBoolean, MemberOffset(22), false);
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100467 block->AddInstruction(test);
468 block->AddInstruction(new (allocator) HIf(test));
469 HBasicBlock* then = new (allocator) HBasicBlock(graph);
470 HBasicBlock* else_ = new (allocator) HBasicBlock(graph);
471 HBasicBlock* join = new (allocator) HBasicBlock(graph);
472 graph->AddBlock(then);
473 graph->AddBlock(else_);
474 graph->AddBlock(join);
475
476 block->AddSuccessor(then);
477 block->AddSuccessor(else_);
478 then->AddSuccessor(join);
479 else_->AddSuccessor(join);
480 then->AddInstruction(new (allocator) HGoto());
481 else_->AddInstruction(new (allocator) HGoto());
482
483 *phi = new (allocator) HPhi(allocator, 0, 0, Primitive::kPrimInt);
484 join->AddPhi(*phi);
Calin Juravle52c48962014-12-16 17:02:57 +0000485 *input1 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
486 MemberOffset(42), false);
487 *input2 = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
488 MemberOffset(42), false);
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100489 then->AddInstruction(*input1);
490 else_->AddInstruction(*input2);
491 join->AddInstruction(new (allocator) HExit());
492 (*phi)->AddInput(*input1);
493 (*phi)->AddInput(*input2);
494
495 graph->BuildDominatorTree();
Nicolas Geoffrayf5370122014-12-02 11:51:19 +0000496 graph->AnalyzeNaturalLoops();
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100497 return graph;
498}
499
500TEST(RegisterAllocatorTest, PhiHint) {
501 ArenaPool pool;
502 ArenaAllocator allocator(&pool);
503 HPhi *phi;
504 HInstruction *input1, *input2;
505
506 {
507 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000508 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100509 SsaLivenessAnalysis liveness(*graph, &codegen);
510 liveness.Analyze();
511
512 // Check that the register allocator is deterministic.
513 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
514 register_allocator.AllocateRegisters();
515
516 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 0);
517 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 0);
518 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 0);
519 }
520
521 {
522 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000523 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100524 SsaLivenessAnalysis liveness(*graph, &codegen);
525 liveness.Analyze();
526
527 // Set the phi to a specific register, and check that the inputs get allocated
528 // the same register.
Nicolas Geoffray56b9ee62014-10-09 11:47:51 +0100529 phi->GetLocations()->SetOut(Location::RegisterLocation(2));
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100530 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
531 register_allocator.AllocateRegisters();
532
533 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2);
534 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2);
535 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2);
536 }
537
538 {
539 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000540 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100541 SsaLivenessAnalysis liveness(*graph, &codegen);
542 liveness.Analyze();
543
544 // Set input1 to a specific register, and check that the phi and other input get allocated
545 // the same register.
Nicolas Geoffray56b9ee62014-10-09 11:47:51 +0100546 input1->GetLocations()->SetOut(Location::RegisterLocation(2));
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100547 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
548 register_allocator.AllocateRegisters();
549
550 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2);
551 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2);
552 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2);
553 }
554
555 {
556 HGraph* graph = BuildIfElseWithPhi(&allocator, &phi, &input1, &input2);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000557 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100558 SsaLivenessAnalysis liveness(*graph, &codegen);
559 liveness.Analyze();
560
561 // Set input2 to a specific register, and check that the phi and other input get allocated
562 // the same register.
Nicolas Geoffray56b9ee62014-10-09 11:47:51 +0100563 input2->GetLocations()->SetOut(Location::RegisterLocation(2));
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100564 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
565 register_allocator.AllocateRegisters();
566
567 ASSERT_EQ(input1->GetLiveInterval()->GetRegister(), 2);
568 ASSERT_EQ(input2->GetLiveInterval()->GetRegister(), 2);
569 ASSERT_EQ(phi->GetLiveInterval()->GetRegister(), 2);
570 }
571}
572
573static HGraph* BuildFieldReturn(ArenaAllocator* allocator,
574 HInstruction** field,
575 HInstruction** ret) {
576 HGraph* graph = new (allocator) HGraph(allocator);
577 HBasicBlock* entry = new (allocator) HBasicBlock(graph);
578 graph->AddBlock(entry);
579 graph->SetEntryBlock(entry);
580 HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimNot);
581 entry->AddInstruction(parameter);
582
583 HBasicBlock* block = new (allocator) HBasicBlock(graph);
584 graph->AddBlock(block);
585 entry->AddSuccessor(block);
586
Calin Juravle52c48962014-12-16 17:02:57 +0000587 *field = new (allocator) HInstanceFieldGet(parameter, Primitive::kPrimInt,
588 MemberOffset(42), false);
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100589 block->AddInstruction(*field);
590 *ret = new (allocator) HReturn(*field);
591 block->AddInstruction(*ret);
592
593 HBasicBlock* exit = new (allocator) HBasicBlock(graph);
594 graph->AddBlock(exit);
595 block->AddSuccessor(exit);
596 exit->AddInstruction(new (allocator) HExit());
597 return graph;
598}
599
600TEST(RegisterAllocatorTest, ExpectedInRegisterHint) {
601 ArenaPool pool;
602 ArenaAllocator allocator(&pool);
603 HInstruction *field, *ret;
604
605 {
606 HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000607 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100608 SsaLivenessAnalysis liveness(*graph, &codegen);
609 liveness.Analyze();
610
611 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
612 register_allocator.AllocateRegisters();
613
614 // Sanity check that in normal conditions, the register should be hinted to 0 (EAX).
615 ASSERT_EQ(field->GetLiveInterval()->GetRegister(), 0);
616 }
617
618 {
619 HGraph* graph = BuildFieldReturn(&allocator, &field, &ret);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000620 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100621 SsaLivenessAnalysis liveness(*graph, &codegen);
622 liveness.Analyze();
623
624 // Check that the field gets put in the register expected by its use.
Nicolas Geoffrayf43083d2014-11-07 10:48:10 +0000625 // Don't use SetInAt because we are overriding an already allocated location.
626 ret->GetLocations()->inputs_.Put(0, Location::RegisterLocation(2));
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100627
628 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
629 register_allocator.AllocateRegisters();
630
631 ASSERT_EQ(field->GetLiveInterval()->GetRegister(), 2);
632 }
633}
634
635static HGraph* BuildTwoAdds(ArenaAllocator* allocator,
636 HInstruction** first_add,
637 HInstruction** second_add) {
638 HGraph* graph = new (allocator) HGraph(allocator);
639 HBasicBlock* entry = new (allocator) HBasicBlock(graph);
640 graph->AddBlock(entry);
641 graph->SetEntryBlock(entry);
642 HInstruction* parameter = new (allocator) HParameterValue(0, Primitive::kPrimInt);
643 HInstruction* constant1 = new (allocator) HIntConstant(0);
644 HInstruction* constant2 = new (allocator) HIntConstant(0);
645 entry->AddInstruction(parameter);
646 entry->AddInstruction(constant1);
647 entry->AddInstruction(constant2);
648
649 HBasicBlock* block = new (allocator) HBasicBlock(graph);
650 graph->AddBlock(block);
651 entry->AddSuccessor(block);
652
653 *first_add = new (allocator) HAdd(Primitive::kPrimInt, parameter, constant1);
654 block->AddInstruction(*first_add);
655 *second_add = new (allocator) HAdd(Primitive::kPrimInt, *first_add, constant2);
656 block->AddInstruction(*second_add);
657
658 block->AddInstruction(new (allocator) HExit());
659 return graph;
660}
661
662TEST(RegisterAllocatorTest, SameAsFirstInputHint) {
663 ArenaPool pool;
664 ArenaAllocator allocator(&pool);
665 HInstruction *first_add, *second_add;
666
667 {
668 HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000669 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100670 SsaLivenessAnalysis liveness(*graph, &codegen);
671 liveness.Analyze();
672
673 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
674 register_allocator.AllocateRegisters();
675
676 // Sanity check that in normal conditions, the registers are the same.
677 ASSERT_EQ(first_add->GetLiveInterval()->GetRegister(), 1);
678 ASSERT_EQ(second_add->GetLiveInterval()->GetRegister(), 1);
679 }
680
681 {
682 HGraph* graph = BuildTwoAdds(&allocator, &first_add, &second_add);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000683 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100684 SsaLivenessAnalysis liveness(*graph, &codegen);
685 liveness.Analyze();
686
687 // check that both adds get the same register.
Nicolas Geoffrayf43083d2014-11-07 10:48:10 +0000688 // Don't use SetOutput because output is already allocated.
689 first_add->InputAt(0)->GetLocations()->output_ = Location::RegisterLocation(2);
Nicolas Geoffray01ef3452014-10-01 11:32:17 +0100690 ASSERT_EQ(first_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
691 ASSERT_EQ(second_add->GetLocations()->Out().GetPolicy(), Location::kSameAsFirstInput);
692
693 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
694 register_allocator.AllocateRegisters();
695
696 ASSERT_EQ(first_add->GetLiveInterval()->GetRegister(), 2);
697 ASSERT_EQ(second_add->GetLiveInterval()->GetRegister(), 2);
698 }
699}
700
Calin Juravled0d48522014-11-04 16:40:20 +0000701static HGraph* BuildDiv(ArenaAllocator* allocator,
702 HInstruction** div) {
703 HGraph* graph = new (allocator) HGraph(allocator);
704 HBasicBlock* entry = new (allocator) HBasicBlock(graph);
705 graph->AddBlock(entry);
706 graph->SetEntryBlock(entry);
707 HInstruction* first = new (allocator) HParameterValue(0, Primitive::kPrimInt);
708 HInstruction* second = new (allocator) HParameterValue(0, Primitive::kPrimInt);
709 entry->AddInstruction(first);
710 entry->AddInstruction(second);
711
712 HBasicBlock* block = new (allocator) HBasicBlock(graph);
713 graph->AddBlock(block);
714 entry->AddSuccessor(block);
715
Calin Juravled6fb6cf2014-11-11 19:07:44 +0000716 *div = new (allocator) HDiv(Primitive::kPrimInt, first, second, 0); // don't care about dex_pc.
Calin Juravled0d48522014-11-04 16:40:20 +0000717 block->AddInstruction(*div);
718
719 block->AddInstruction(new (allocator) HExit());
720 return graph;
721}
722
723TEST(RegisterAllocatorTest, ExpectedExactInRegisterAndSameOutputHint) {
724 ArenaPool pool;
725 ArenaAllocator allocator(&pool);
726 HInstruction *div;
727
728 {
729 HGraph* graph = BuildDiv(&allocator, &div);
Calin Juravlecd6dffe2015-01-08 17:35:35 +0000730 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Calin Juravled0d48522014-11-04 16:40:20 +0000731 SsaLivenessAnalysis liveness(*graph, &codegen);
732 liveness.Analyze();
733
734 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
735 register_allocator.AllocateRegisters();
736
737 // div on x86 requires its first input in eax and the output be the same as the first input.
738 ASSERT_EQ(div->GetLiveInterval()->GetRegister(), 0);
739 }
740}
741
Nicolas Geoffraydd8f8872015-01-15 15:37:37 +0000742// Test a bug in the register allocator, where allocating a blocked
743// register would lead to spilling an inactive interval at the wrong
744// position.
745TEST(RegisterAllocatorTest, SpillInactive) {
746 ArenaPool pool;
747
748 // Create a synthesized graph to please the register_allocator and
749 // ssa_liveness_analysis code.
750 ArenaAllocator allocator(&pool);
751 HGraph* graph = new (&allocator) HGraph(&allocator);
752 HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
753 graph->AddBlock(entry);
754 graph->SetEntryBlock(entry);
755 HInstruction* one = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
756 HInstruction* two = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
757 HInstruction* three = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
758 HInstruction* four = new (&allocator) HParameterValue(0, Primitive::kPrimInt);
759 entry->AddInstruction(one);
760 entry->AddInstruction(two);
761 entry->AddInstruction(three);
762 entry->AddInstruction(four);
763
764 HBasicBlock* block = new (&allocator) HBasicBlock(graph);
765 graph->AddBlock(block);
766 entry->AddSuccessor(block);
767 block->AddInstruction(new (&allocator) HExit());
768
769 // We create a synthesized user requesting a register, to avoid just spilling the
770 // intervals.
771 HPhi* user = new (&allocator) HPhi(&allocator, 0, 1, Primitive::kPrimInt);
772 user->AddInput(one);
773 user->SetBlock(block);
774 LocationSummary* locations = new (&allocator) LocationSummary(user, LocationSummary::kNoCall);
775 locations->SetInAt(0, Location::RequiresRegister());
776 static constexpr size_t phi_ranges[][2] = {{20, 30}};
777 BuildInterval(phi_ranges, arraysize(phi_ranges), &allocator, -1, user);
778
779 // Create an interval with lifetime holes.
780 static constexpr size_t ranges1[][2] = {{0, 2}, {4, 6}, {8, 10}};
781 LiveInterval* first = BuildInterval(ranges1, arraysize(ranges1), &allocator, -1, one);
782 first->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, first->first_use_);
783 first->first_use_ = new(&allocator) UsePosition(user, 0, false, 7, first->first_use_);
784 first->first_use_ = new(&allocator) UsePosition(user, 0, false, 6, first->first_use_);
785
786 locations = new (&allocator) LocationSummary(first->GetDefinedBy(), LocationSummary::kNoCall);
787 locations->SetOut(Location::RequiresRegister());
788 first = first->SplitAt(1);
789
790 // Create an interval that conflicts with the next interval, to force the next
791 // interval to call `AllocateBlockedReg`.
792 static constexpr size_t ranges2[][2] = {{2, 4}};
793 LiveInterval* second = BuildInterval(ranges2, arraysize(ranges2), &allocator, -1, two);
794 locations = new (&allocator) LocationSummary(second->GetDefinedBy(), LocationSummary::kNoCall);
795 locations->SetOut(Location::RequiresRegister());
796
797 // Create an interval that will lead to splitting the first interval. The bug occured
798 // by splitting at a wrong position, in this case at the next intersection between
799 // this interval and the first interval. We would have then put the interval with ranges
800 // "[0, 2(, [4, 6(" in the list of handled intervals, even though we haven't processed intervals
801 // before lifetime position 6 yet.
802 static constexpr size_t ranges3[][2] = {{2, 4}, {8, 10}};
803 LiveInterval* third = BuildInterval(ranges3, arraysize(ranges3), &allocator, -1, three);
804 third->first_use_ = new(&allocator) UsePosition(user, 0, false, 8, third->first_use_);
805 third->first_use_ = new(&allocator) UsePosition(user, 0, false, 4, third->first_use_);
806 third->first_use_ = new(&allocator) UsePosition(user, 0, false, 3, third->first_use_);
807 locations = new (&allocator) LocationSummary(third->GetDefinedBy(), LocationSummary::kNoCall);
808 locations->SetOut(Location::RequiresRegister());
809 third = third->SplitAt(3);
810
811 // Because the first part of the split interval was considered handled, this interval
812 // was free to allocate the same register, even though it conflicts with it.
813 static constexpr size_t ranges4[][2] = {{4, 6}};
814 LiveInterval* fourth = BuildInterval(ranges4, arraysize(ranges4), &allocator, -1, four);
815 locations = new (&allocator) LocationSummary(fourth->GetDefinedBy(), LocationSummary::kNoCall);
816 locations->SetOut(Location::RequiresRegister());
817
Calin Juravled426a8f2015-01-20 12:54:52 +0000818 x86::CodeGeneratorX86 codegen(graph, CompilerOptions());
Nicolas Geoffraydd8f8872015-01-15 15:37:37 +0000819 SsaLivenessAnalysis liveness(*graph, &codegen);
820
821 RegisterAllocator register_allocator(&allocator, &codegen, liveness);
822 register_allocator.unhandled_core_intervals_.Add(fourth);
823 register_allocator.unhandled_core_intervals_.Add(third);
824 register_allocator.unhandled_core_intervals_.Add(second);
825 register_allocator.unhandled_core_intervals_.Add(first);
826
827 // Set just one register available to make all intervals compete for the same.
828 register_allocator.number_of_registers_ = 1;
829 register_allocator.registers_array_ = allocator.AllocArray<size_t>(1);
830 register_allocator.processing_core_registers_ = true;
831 register_allocator.unhandled_ = &register_allocator.unhandled_core_intervals_;
832 register_allocator.LinearScan();
833
834 // Test that there is no conflicts between intervals.
835 GrowableArray<LiveInterval*> intervals(&allocator, 0);
836 intervals.Add(first);
837 intervals.Add(second);
838 intervals.Add(third);
839 intervals.Add(fourth);
840 ASSERT_TRUE(RegisterAllocator::ValidateIntervals(
841 intervals, 0, 0, codegen, &allocator, true, false));
842}
843
Nicolas Geoffraya7062e02014-05-22 12:50:17 +0100844} // namespace art